本节主要和大家聊聊消息处理的细节。
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程序员上班那点事儿》是一本面向即将从事Java职业的新手以及初入行的程序员的实用指南。本书旨在帮助读者更好地理解Java程序员这一职业的发展路径,从求职到职场生存再到职业发展,全方位地为读者提供宝贵的...
《Java程序员上班那点事儿》这本书,正如其名,旨在揭示Java程序员在日常工作中的点滴细节,帮助初入职场或有经验的开发者更好地理解和应对工作中的挑战。书中涵盖了许多实际问题和解决方案,旨在提升读者的技能水平...
总的来说,“Java程序员上班那点事儿”涵盖了广泛的技能和知识领域,从基础编程到高级架构,从个人技能到团队协作,每一个环节都对他们的工作质量和效率产生深远影响。只有不断学习和实践,才能在这个快速变化的IT...
本书《Java程序员上班那点事儿》旨在为即将踏入这个领域的新人提供全面的指引,帮助他们更好地理解这个职业,并为入职后的职业生涯规划提供清晰的方向。 首先,Java是一种广泛使用的面向对象的编程语言,其强大的跨...
通过以上内容的梳理,我们可以看出,《Java程序员+上班那点事儿》这本书旨在帮助即将步入职场或已从事Java开发工作的人员更好地理解和适应软件行业,从求职准备到职业规划提供了全方位的指导和支持。无论是技术层面...
《Java程序员+上班那点事儿》可能提供了关于如何制定职业发展计划的建议,包括技术深造(如学习框架Spring、MyBatis等)、获取认证(如Oracle Certified Professional, Java SE 8 Programmer)以及向架构师或项目...
让我们深入探讨一下Java程序员在日常工作中的关键知识点。 首先,学习Java编程语言是基础。Java以其“一次编写,到处运行”的特性,成为跨平台开发的首选。初学者应该掌握基本语法、面向对象编程概念(如封装、继承...
### Java程序员职场生存与发展知识点概览 #### 一、求职准备与面试技巧 - **自我定位**:在求职过程中,明确自身的技术栈和发展方向至关重要。求职者应当根据自身的兴趣和技术专长来选择合适的职位,避免盲目追求...
03_关于互联网Java工程师面试突击训练课程的几点说明 04_体验一下面试官对于消息队列的7个连环炮 05_知其然而知其所以然:如何进行消息队列的技术选型? 06_引入消息队列之后该如何保证其高可用性? 07_我的天!我为...
以下是一些关于Java程序员上班日常的关键知识点,这些知识将帮助你更好地理解这个职位的职责和挑战。 首先,Java语言基础是必不可少的。你需要熟悉类、对象、接口、继承、多态等核心概念,这些都是构建面向对象程序...
三层架构和MVC那点事儿 Java帝国之拨云见日识回调 小张的Duck Typing JDBC的诞生 JDBC后传 一个不安分的JDBC驱动 Java帝国之 Java Bean(上) Java帝国之 Java Bean(下) Java帝国之 函数式编程(上) Java...