-
互聯(lián)網(wǎng)安全法,互聯(lián)網(wǎng)凈網(wǎng)行動
-
”凈網(wǎng)2020”落實好維護網(wǎng)絡(luò)安全責任
-
關(guān)于端午節(jié)放假通知-宇眾網(wǎng)絡(luò)
-
宇眾網(wǎng)絡(luò)春節(jié)放假通知
-
關(guān)于公司收款銀行賬戶變更通知函-宇眾網(wǎng)絡(luò)
-
關(guān)于網(wǎng)上有人冒充我公司名義進行詐騙的公告。
-
關(guān)于端午節(jié)放假通知,節(jié)日放假,但是我們業(yè)務(wù)不“放假”-宇眾網(wǎng)絡(luò)
-
工信部進一步加強未備案網(wǎng)站管理工作的通知-宇眾網(wǎng)絡(luò)
-
關(guān)于東莞市宇眾網(wǎng)絡(luò)科技有限公司香港數(shù)據(jù)中心(香港機房)路由優(yōu)化通知
-
宇眾網(wǎng)絡(luò)慶祝五·一勞動節(jié)快樂
-
東莞東城機房網(wǎng)絡(luò)升級通知
-
臨近過年,互聯(lián)網(wǎng)IDC貴圈也有被騙的,請認準宇眾網(wǎng)絡(luò)公司官方聯(lián)系方式
-
我司已獲得ISP/ICP/IDC三證資格,更好的為客戶服務(wù)
-
關(guān)于浙江金華高防機房網(wǎng)絡(luò)線路切割通知
-
工信部近日下發(fā)關(guān)于進一步規(guī)范域名備案工作的通知
行業(yè)資訊
- 首頁
- 新聞中心
- 行業(yè)資訊
手游服務(wù)端框架之跨服匹配服,手游服務(wù)器租用
如今的手游世界,如果沒搞個跨服賽事,都不好意思說它是一個手游了。
說到跨服,就不得不說下匹配服了。比如一個跨服天梯賽事,需要滿足不同服的玩家能夠同屏PK。為了能夠把實力接近的玩家作為對手,我們需要一個獨立的匹配服來收集數(shù)據(jù),然后進行房間分配。匹配服,也是跨服賽設(shè)計的基礎(chǔ)。
典型的匹配服通信層我們可以采用http,也可以采用socket。本文將采用http作為游戲服與匹配服的通信層。選擇http方式,我們可以搭個tomcat服務(wù),非常方便。當然,如果不使用tomcat的話,我們也可以使用mina或者netty本身的http服務(wù)。
設(shè)計思路也非常簡單,有點像游戲服的業(yè)務(wù)處理器。我們需要做到,對于不同的請求,我們都綁定一個方法與之對應(yīng)。而對于數(shù)據(jù)的編解碼,由于匹配服的通信數(shù)據(jù)一般都比較短,我們直接用json進行序列化即可。
下面,開始我們的編碼。
★如有手游服務(wù)器租用可咨詢宇眾臨風,QQ:2850293179 Tel:15999932452 訂購網(wǎng)址:www.yelaoxs.com
搭建mina的http服務(wù)
在前面游戲后臺設(shè)計中,我們已經(jīng)看到如何使用mina搭建http服務(wù)了。
-
/**
-
* 匹配服http服務(wù)
-
* @author kingston
-
*/
-
public class MatchServer {
-
private Logger logger = LoggerFactory.getLogger(MatchServer.class);
-
private IoAcceptor acceptor;
-
//http端口
-
int port = 8899;
-
public void start() throws Exception {
-
acceptor = new NioSocketAcceptor();
-
acceptor.getFilterChain().addLast("codec", new HttpServerCodec());
-
acceptor.setHandler(new HttpServerHandle());
-
acceptor.bind(new InetSocketAddress(port));
-
logger.error("---------> http server start at port:{}", port);
-
}
-
public void shutdown() {
-
if (acceptor != null) {
-
acceptor.unbind();
-
acceptor.dispose();
-
}
-
logger.error("---------> http server stop at port:{}", port);
-
}
-
}
-
class HttpServerHandle extends IoHandlerAdapter {
-
private static Logger logger = LoggerFactory.getLogger(MatchServer.class);
-
@Override
-
public void exceptionCaught(IoSession session, Throwable cause)
-
throws Exception {
-
}
-
@Override
-
public void messageReceived(IoSession session, Object urlParams)
-
throws Exception {
-
if (urlParams instanceof HttpRequest) {
-
// 請求,解碼器將請求轉(zhuǎn)換成HttpRequest對象
-
HttpRequest request = (HttpRequest) urlParams;
-
Message msg = parseHttpRequest(request);
-
UrlDispatcher.getInstance().dispatch(session, msg);
-
}
-
}
-
@SuppressWarnings("unchecked")
-
private Message parseHttpRequest(HttpRequest httpReq) {
-
String service = httpReq.getParameter("service");
-
if (StringUtils.isEmpty(service)) {
-
return null;
-
}
-
Class<?> clazz = UrlDispatcher.getInstance().getMessageClazzBy(service);
-
String paramJson = httpReq.getParameter("param");
-
if (StringUtils.isNotEmpty(paramJson)) {
-
try{
-
return (Message)new Gson().fromJson(URLDecoder.decode(paramJson), clazz);
-
}catch(Exception e) {
-
e.printStackTrace();
-
}
-
}
-
return null;
-
}
-
}
消息通信
在游戲服,我們發(fā)出一條http請求。匹配服為了將請求分發(fā)到對應(yīng)的處理器,我們需要為每一條消息作一個標記。最簡單的,可以使用請求消息的類名。所以,我們必須把業(yè)務(wù)簽名和參數(shù)都融合到url里面去。也就是說,一個有效的url可能是這樣:
http://localhost:8899?service=MReqLadderApplyMessage¶m={"playerId":0,"score":0,"power":0}
為了能區(qū)別游戲服和匹配服的消息類型,我們匹配服的消息,都加一個M(Match)前綴,那么請求協(xié)議就MReq,響應(yīng)協(xié)議就是MRes了。
對于游戲服來說,發(fā)出的請求屬于Message的子類,返回的消息也是Message的子類。底層幫我們實現(xiàn)了消息的編解碼。我們可以看下代碼實現(xiàn)。
-
public class MatchHttpUtil {
-
public static Message submit(Message request) throws IOException {
-
String signature = request.getClass().getSimpleName();
-
String data = new Gson().toJson(request);
-
String param = HttpUtil.buildUrlParam("service", signature,
-
"param", data);
-
String url = "http://localhost:8899" + "?" + param;
-
System.err.println("發(fā)送url:" + url);
-
String resultJson = HttpUtil.get(url);
-
UrlResponse urlResponse = new Gson().fromJson(resultJson, UrlResponse.class);
-
String respClazz = urlResponse.getAttachemt();
-
Class<?> msgClazz = MatchMessageFactory.getInstance().getMessageBy(respClazz);
-
Message msgResponse = (Message)new Gson().fromJson(urlResponse.getMessage(), msgClazz);
-
return msgResponse;
-
}
-
}
業(yè)務(wù)處理器
我們依然使用 @Controller注解來標識一個模塊處理器,使用@RequestMapper注解來標記業(yè)務(wù)處理方法。不同的是,在游戲服我們每個消息的元信息都帶有一個模塊號和子類型號。在匹配服,我們就不這里處理了。因為匹配服的業(yè)務(wù)比較少。我們直接用消息類的名稱作為業(yè)務(wù)簽名即可。
在業(yè)務(wù)分發(fā)器,我們保存每一個方法簽名,與對應(yīng)的方法處理器。
-
public class UrlDispatcher {
-
private Logger logger = LoggerFactory.getLogger(getClass());
-
private volatile static UrlDispatcher instance;
-
/** [message signature, CmdExecutor] */
-
private static final Map<String, CmdExecutor> service2Handler = new HashMap<>();
-
private static final Map<String, Class<?>> signature2Message = new HashMap<>();
-
}
匹配服在收到一個http請求,通過參數(shù)解析得到對應(yīng)的業(yè)務(wù)簽名,同時通過json反序列化得到請求消息的參數(shù)。將消息分發(fā)到對應(yīng)的業(yè)務(wù)處理器。代碼如下:
-
public void dispatch(IoSession session, Message message) {
-
String signature = buildSignature(message.getClass());
-
CmdExecutor cmdExecutor = service2Handler.get(signature);
-
if (cmdExecutor == null) {
-
logger.error("message executor missed, signature={}", signature);
-
return;
-
}
-
Object[] params = convertToMethodParams(session, cmdExecutor.getParams(), message);
-
Object controller = cmdExecutor.getHandler();
-
try {
-
//通過反射
-
cmdExecutor.getMethod().invoke(controller, params);
-
}catch(Exception e) {
-
logger.error("", e);
-
}
-
}
一個完整的業(yè)務(wù)處理器,代碼如下 (可以看出,跟游戲服是非常類似的):
-
@Controller
-
public class LadderController {
-
@RequestMapping
-
public void apply(IoSession session, MReqLadderApplyMessage request) {
-
HttpMessagePusher.push(session, new MResLadderApplySuccMessage());
-
}
-
}
示例代碼
啟動匹配服服務(wù)器(MatchStartup.java)
再執(zhí)行游戲服的單元測試
-
public class TestMatchHttp {
-
@Test
-
public void httpRquest() throws IOException {
-
Message response = MatchHttpUtil.submit(new MReqLadderApplyMessage());
-
System.err.println("收到響應(yīng)<<<<<<<<<" + response);
-
}
-
}
手游服務(wù)端開源框架系列完整的代碼請移步github ->> jforgame