java游戏架构那点事儿(四)

阅读更多
本节主要和大家聊聊消息处理的细节。

1、java的字节序
这是一个不经常遇到的问题,写在这里以备不时之需。
字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。

Little endian:将低序字节存储在起始地址
Big endian:将高序字节存储在起始地址

所谓的JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序是Big endian;而网络字节序是指数据在网络上传输时是大头还是小头的,在Internet的网络字节序也是Big endian。

如果通信的一方是JAVA程序、一方是C/C++程序时,则需要在C/C++一侧使用以上几个方法进行字节序的转换,而JAVA一侧,则不需要做任何处理,因为JAVA字节序与网络字节序都是 Big endian,只要C/C++一侧能正确进行转换即可(发送前从主机序到网络序,接收时反变换)。如果通信的双方都是JAVA,则根本不用考虑字节序的问题了。

2、支持多消息打包处理
    我们在项目中经常遇到一个页面上包含两个或者两个以上功能,而服务器端的架构又是各模块间相互独立的,这时候就需要客户端在请求时将多个协议封装在一起请求,而服务器端按照客户端请求的协议顺序依次处理,并将处理后的消息体打包发送给客户端。
在使用多协议封装模式时,协议处理顺序和处理过程中的异常处理需要特别注意。这里对异常处理进行着重说明。
   
    当多协议封装模式下某一单独协议处理出现问题时,该单独协议将抛出异常。此时需要将异常提示信息单独发送给客户端,并将之前处理过的协议信息清除(如有必要的话还需要将处理过的协议信息回滚,当然如果需要回滚的协议不建议使用封装模式发送)。
    如果是长连接状态下,我们可以直接调用netty的channel直接将提示信息发送给客户端,然后将channel关闭就ok了。如果是短连接状态下,需要在response中设置状态值,每次单个协议处理完时检测状态值,并作出相应处理。

以下是http模式下协议处理的代码片段
				boolean sendFlag = true;
				StringBuilder responseMessage = new StringBuilder();
				StringBuilder contentMessage = new StringBuilder();
				if (httpRequest.getRequestType() == REQUEST_TYPE_USERDEFINE) {
					responseMessage
							.append("[")
							.append(httpRequest.getUserDefinedCommandId())
							.append(",");
				}
				long allBegin = System.currentTimeMillis();
				IUser user = userContainer.getUser(httpRequest.getChannelId());
				HttpRTGameResponse tempResponse = new HttpRTGameResponse(
						httpRequest, user);
				for (Command command : httpRequest.getCommandList()) {
					int messageId = command.getCommandId();
					HttpGameHandler handler = handlerMap.get(messageId);
					if (handler != null) {
						HttpGameResponse httpResponse = new HttpGameResponse(
								httpRequest.getChannelId(), command, user);
						try {
							HttpGameContext.initGameContext();
							HttpGameContext.setResponse(httpResponse);
							handler.execute(httpRequest, httpResponse);
							if (!httpResponse.isClose()) {
								contentMessage.append(
										httpResponse.getResponseData());
								if(httpResponse.getCommandId()<1000){
									break;
								}else{
									contentMessage.append(",");
								}			
							} else {
								sendFlag = false;
								contentMessage=new StringBuilder();
								contentMessage.append(
										httpResponse.getResponseData());
							}
							tempResponse.setClose(httpResponse.isDelayClose());
							if (!httpResponse.isDelayClose()) {
								tempResponse.addDelayResp(httpResponse
										.getDelaySend());
							}
						} catch (Exception e) {
							sendFlag = false;
							if (user != null) {
								logger.error("[userId = " + user.getId()
										+ "]\t"
										+ httpResponse.getCommand().getData(),
										e);
							} else {
								logger.error(httpResponse.getCommand()
										.getData(), e);
							}
						} finally {
							HttpGameContext.clear();
						}
						if (!sendFlag) {
							break;
						}
					} else {
						sendFlag = false;
						if (user != null) {
							logger.warn("[userId = {}]  指令 [{}]找不到",
									user.getId(), messageId);
						} else {
							logger.warn("指令 [{}]找不到", messageId);
						}
					}
				}
				if (sendFlag) {

					responseMessage.append(contentMessage);
					if (httpRequest.getRequestType() == GameRequest.REQUEST_TYPE_USERDEFINE) {
						responseMessage.setCharAt(responseMessage.length() - 1,
								']');
					} else {
						responseMessage
								.deleteCharAt(responseMessage.length() - 1);
					}
				
					tempResponse.setData(responseMessage.toString());
					logger.info("response:"+responseMessage.toString());
					HttpServerHandler.sendHttpResponse(httpRequest.getCtx(),
							httpRequest.getReq(), tempResponse.getResp());
				}else{
					if(contentMessage.length()>0){
						tempResponse.setData(contentMessage.toString());
						logger.info("response:"+contentMessage.toString());
						HttpServerHandler.sendHttpResponse(httpRequest.getCtx(),
								httpRequest.getReq(), tempResponse.getResp());
					}else{
						HttpServerHandler.sendHttpResponse(httpRequest.getCtx(),
								httpRequest.getReq(), new DefaultFullHttpResponse(HTTP_1_1,
								FORBIDDEN));
					}
				}
				if (!tempResponse.isClose()) {
					for (HttpGameResponse response : tempResponse
							.getDelaySend()) {
						HttpServerHandler.sendHttpResponse(httpRequest.getCtx(),
								httpRequest.getReq(), response.getResp());
						logger.info("response:"+responseMessage.toString());
					}
				}
				long allEnd = System.currentTimeMillis();
				long diff = (allEnd - allBegin);
				if (diff / httpRequest.getCommandList().size() >= 500) {
					if (httpRequest.getRequestType() == REQUEST_TYPE_USERDEFINE) {
						logger.warn("协议 {} 总耗时:{}ms",
								httpRequest.getUserDefinedCommandId(),
								(int) diff);
					} else {
						logger.warn("协议 {} 总耗时:{}ms", httpRequest
								.getCommandList().get(0).getCommandId(),
								(int) diff);
					}
				}
			


3、beforeExecute和afterExecute
我们在真正处理execute之前和之后,需要有一些公共的事物需要处理。
我们通常在beforeExecute中进行资源检测、权限检测、模块开放等级检测等;而在afterExecute中通常判断是否有需要推送的消息等。

你可能感兴趣的:(游戏,java)