from: http://ybak.iteye.com/blog/1853335

半年前跟朋友开始创业做手机游戏.我负责一个人开发服务端,这是一个卡牌类多人在线回合制对战网游.开始时第一考虑的是能快速出实现需求.其次是功能的可扩展性(应对频繁变更的需求),再次才是服务器性能.

目前游戏开发的主流语言仍是C++,但因为自己最熟悉的是java,转C++的成本太高,且国内页游服务器,已经开始大规模应用java作为后端服务器.最终还是确定使用java来开发.

这里,有两条路可选,1是使用现有的服务器作扩展,2是自己开发服务器.
   
   市面上的成熟的java 游戏服务器寥寥无几,选择并不像web框架那样丰富,可选的收费服务器以smartfox为代表.还有一些开源的但充其量只能作为demo的框架(比如menacher).当时网易的pomelo也还没有出现.

虽然smartfox宣传有很多成功案例.但看了文档后.发现smartfox最成功的案例是棋牌类游戏和社交类游戏.这和我们要开发的卡牌类RPG手游的游戏交互方面还是有很大的差异.而且smartfox对于游戏的逻辑封装过多.比如基本的概念就是进入游戏房间等.这种封装并不适合于我们要开发的游戏类型.所以购买smartfox没有太多的性价比.果断放弃之.

接下来就是自己开发游戏服务器的socket server了.

在实现socket server时,先参考了两个开源的游戏服务器实现.一个是永恒之塔的开源私服实现aionxemu,另一个是简单的socket server实现menacher.

我在看了许多aionxemu的代码实现后.发现这些代码编写开发者一定都是多年的编程老手.代码不光实现了数量庞大的游戏服务接口,还在可扩展性(OO方面)和性能优化上做了很多考究的设计.aionxemu游戏服务器是分为gameserver loginserver和chatserver在gameserver中,接口协议的解析和序列化直接使用的是java nio来操作字节流.直接使用java nio比较繁琐,而操作字节流解析和封装协议也存在同样的问题.虽然能提高性能,但在开发成本和可维护性上有一定弊端,不适合目前我们对进度要求十分紧迫的情况.值得一提的是:aionxemu虽然只是一个私服,但其中具有完整的mmorpg的逻辑实现. 对于我们游戏中的逻辑开发具有相当高的借鉴意义.

menacher中使用了netty作为NIO操作类库,在其代码中展示了netty的简洁性.比较有意思的是menacher使用了jetlang作为游戏业务事件驱动的reactor处理框架,可以方便的提供逻辑的扩展并提供性能调整配置.但遗憾的是menacher并没有在服务器的横向扩展上做太多的考虑..

在参考了以上两个实现,再结合项目的实现情况(服务器提供的服务接口可能多达100多个,而且这些接口后续出现变更),在协议处理上我选择了protoc buffer.   Protbuf的跨平台使使用C++的客户端可以方便的序列化协议对象.使用客户端和服务器双方更关注于游戏协议,而非字节流的处理细节.有利于客户端和服务端快速交流. 最后在socket server上采用了netty+protbuf两个框架来实现.netty负责socket处理,protbuf负责处理游戏协议解析.
   协议的基本结构如图:

protobuf的客户端消息协议基本如下所示.

  1. message PBClientPacket {
  2. required PBClientRequestType clientRequestType = 1; //请求类型
  3. optional bytes requestData = 2; //请求数据
  4. }
  5. enum PBClientRequestType {
  6. DUMMY_REQUEST = 0; // PBDummyRequest
  7. UPDATE_REQUEST = 1; // PBUpdateRequest
  8. REGIST_REQUEST = 2; // PBRegistRequest
  9. LOGIN_REQUEST = 3; // PBLoginRequest
  10. ......
  11. }
  12. ....
  13. message PBDummyRequest {
  14. optional string payload = 1;
  15. }
  16. message PBUpdateRequest {
  17. required string clientVersion = 1;
  18. ......
  19. }
  20. ......

protobuf的服务端消息协议基本如下所示.

  1. message PBServerPacket {
  2. required PBPayloadType payloadType = 1; //服务端报文类型
  3. optional PBResponseCode code = 2; //
  4. optional string desc = 3; //
  5. optional bytes payloadData = 4; //此字段中包含服务端返回的响应内容
  6. }
  7. enum PBPayloadType {
  8. DUMMY_RESPONSE = 0;
  9. UPDATE_RESPONSE = 2; //PBUpdateResponse
  10. ......
  11. }
  12. enum PBResponseCode {
  13. OK = 0;
  14. ERROR_UNKOWN = 1;
  15. SERVER_ERROR = 2;
  16. ......
  17. }
  18. message PBUpdateResponse {
  19. optional string resourceURL = 1;
  20. optional bytes resoruces = 2;
  21. ......
  22. }

客户端和服务器端的消息结构类似.,第一个字段表示消息类型,程序在知道消息类型后.就可以根据协议来化序列化后面的报文字段.在具体的请求对象外再封装一层统计的交互对象,会有利于netty与protobuf的集成.

netty本身提供的protobuf demo展示了如何方便的与protobuf的集成方式.

对于每种客户端消息,需要找到对应的业务逻辑处理单元.而作为JavaEE开发者,习惯于使用spring来管理这些逻辑处理单元.在spring管理的bean上可以,轻松的添加业务监控功能.服务逻辑上需要使用一种方法来将消息和业务bean映射起来.这里使用了一种比较讨巧的方式来处理,将消息类型作为bean的名称.

比如对于clientRequestType类型为LOGIN_REQUEST的报文.定义了LoginRequestHandler来处理:

  1. @Component("LOGIN_REQUEST")
  2. public class LoginRequestHandler extends AbstractRequestHandler {
  3. @Override
  4. public void handle(ByteString packetData, Channel channel) throws InvalidProtocolBufferException {
  5. PBLoginRequest request = PBLoginRequest.parseFrom(packetData);//反序列化请求
  6. .......
  7. }
  8. }

这样在服务器收到报文后.可以方便的根据报文类型选择对应的handler来进行处理.

  1. @Component
  2. public class RequestDispatcher {
  3. private Map<PBClientRequestType, RequestHandler> handlerMap = new HashMap<PBClientRequestType, RequestHandler>();
  4. public void dispatchClientPacket(final PBClientPacket clientPacket, final Channel channel) {
  5. PBClientRequestType clientRequestType = clientPacket.getClientRequestType();
  6. try {
  7. handlerMap.get(clientRequestType).handleRequestData(clientPacket.getRequestData(), channel);
  8. } catch (BadRequestException e) {
  9. }
  10. }

以下是集成测试中,模拟客户端构造请求报文的代码:

  1. PBLoginRequest loginRequest = PBLoginRequest.newBuilder().setUsername(username).setPassword(password).build();
  2. PBClientPacket clientPacket = PBClientPacket.newBuilder().setClientRequestType(PBClientRequestType.LOGIN_REQUEST)
  3. .setRequestData(loginRequest.toByteString()).build();
  4. hannel.write(clientPacket );

TBD

转: 基于netty+ protobuf +spring + hibernate + jgroups开发的游戏服务端的更多相关文章

  1. 基于Netty的私有协议栈的开发

    基于Netty的私有协议栈的开发 书是人类进步的阶梯,每读一本书都使自己得以提升,以前看书都是看了就看了,当时感觉受益匪浅,时间一长就又还回到书本了!所以说,好记性不如烂笔头,以后每次看完一本书都写一 ...

  2. Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务

    Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具:Spr ...

  3. Struts2+Spring+Hibernate整合开发(Maven多模块搭建)

    Struts2+Spring+Hibernate整合开发(Maven多模块搭建) 0.项目结构 Struts2:web层 Spring:对象的容器 Hibernate:数据库持久化操作 1.父模块导入 ...

  4. 适合新手:从零开发一个IM服务端(基于Netty,有完整源码)

    本文由“yuanrw”分享,博客:juejin.im/user/5cefab8451882510eb758606,收录时内容有改动和修订. 0.引言 站长提示:本文适合IM新手阅读,但最好有一定的网络 ...

  5. Pomelo:网易开源基于 Node.js 的游戏服务端框架

    Pomelo:网易开源基于 Node.js 的游戏服务端框架 https://github.com/NetEase/pomelo/wiki/Home-in-Chinese

  6. ubuntu下安装 gSOAP 用于C/C++开发web service服务端与客户端

    昨天在ubuntu下进行安装gSOAP,费了很多时间,没成功,今天又来找了大量教程资料,终于一次成功,这里写下自己的安装步骤和方法,供大家参考. 首先下载gsoap,我下载的是gsoap-2.8.1. ...

  7. 网络编程之TCP客户端开发和TCP服务端开发

    开发 TCP 客户端程序开发步骤 创建客户端套接字对象 和服务端套接字建立连接 发送数据 接收数据 关闭客户端套接字 import socket if __name__ == '__main__': ...

  8. go语言游戏服务端开发(三)——服务机制

    五邑隐侠,本名关健昌,12年游戏生涯. 本教程以Go语言为例.   P2P网络为服务进程间.服务进程与客户端间通信提供了便利,在这个基础上可以搭建服务. 在服务层,通信包可以通过定义协议号来确定该包怎 ...

  9. SpringMVC+Spring+Hibernate整合开发

    最近突然想认真研究下java web常用框架,虽然现在一直在用,但实现的整体流程不是很了解,就在网上搜索资料,尝试自己搭建,以下是自己的搭建及测试过程. 一.准备工作: 1/安装并配置java运行环境 ...

随机推荐

  1. 一个通用的分页存储过程实现-SqlServer(附上sql源码,一键执行即刻搭建运行环境)

    使用前提 查询表必须有ID字段,且该字段不能重复,建议为自增主键 背景 如果使用ADO.NET进行开发,在查询分页数据的时候一般都是使用分页存储过程来实现的,本文提供一种通用的分页存储过程,只需要传入 ...

  2. java启动的一些参数

    -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9001 -Dcom.sun.management.jmxremo ...

  3. ogre3d环境配置与简单程序示例

    SDK安装以及编译 1.下载SDK,地址如下 http://www.ogre3d.org/download/sdk 2.安装SDK,直接解压到相应目录,如D:/ogreSDK 3.我用的是1.8版本, ...

  4. 解读Loadrunner网页细分图(Web Page Diagnostics)

    [转载的地址]https://www.cnblogs.com/littlecat15/p/9456376.html 一.启用网页细分图 首先在Controller场景设计运行之前,需要在菜单栏中设置D ...

  5. day05_04 数据类型-数值、布尔值、字符串简介

    数据运算 数据类型初识 数字 整数 int(integer) 整型 长整型 在python3里面已经不区分整型和长整型了,统一都叫整型 在32位机器上,整数的位数为32位,取值范围为-2**31-2* ...

  6. Wannafly挑战赛11

    就做了两个数学题 链接:https://www.nowcoder.com/acm/contest/73/A来源:牛客网 白兔的分身术 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 2 ...

  7. HDU——4565So Easy!(矩阵快速幂)

    So Easy! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  8. CodeForces 618D Hamiltonian Spanning Tree

    题意:要把所有的节点都访问一次,并且不能重复访问,有两种方式访问,一种是根据树上的路径 走和当前节点连接的下一个节点cost x, 或者可以不走树上边,直接跳到不与当前节点连接的节点,cost y 分 ...

  9. POJ 2228 naptime

    环形DP 先考虑如果只是一天,我们可以用线性DP写出转移方程,注意初始化 如果是一个环的话,我们发现少了一种第n天和第一天连起来的情况,那么我们就再进行一次DP 强制这种情况 #include < ...

  10. 防止点击asp.net的button按钮刷新页面(保留button的外观)

    原文发布时间为:2008-08-06 -- 来源于本人的百度文章 [由搬家工具导入] Button btn=new Button(); 1、如果用 btn.Enabled=false;是可以防止刷新的 ...