`
snake1987
  • 浏览: 71820 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

关于社交游戏中的同步服务器(长连接服务器)

    博客分类:
  • java
阅读更多
博客好久没打理了,今天有时间,把在公司wiki上写的一些东西移植一下吧



我们的游戏至今已经上线第四个同步玩法了

有点想法,也有些疑惑,在这里发一下,欢迎各位tx各抒己见

准确点说,这里所谓的“同步服务器”其实包括两个部分的内容,第一是说明通信方式是长连接,第二是服务器不具有动态扩容的能力,也就是说想增加用户数不能通过增加机器的方式来做
当然,同步服务器按道理说只包含第一部分的内容,但在这里算是一个特殊的语境吧

1.区别(主要区别于普通的web服务器)
a.通信模式很显然的,不多说了
b.冲突的解决方式
(这里的特殊语境,是指做游戏常用的数据库:腾讯的cmem,redis)
现在我所知道的冲突解决方案有这么几种
一致性缓存锁:这种锁的劣势是没有普通锁的notify机制,也就是说,必须事先加锁,锁失败了只能退出,一些在逻辑中间的事务性操作用这种方式就不能实现了
一致性缓存cas:cas是cpu操作原语,也就是compare and swap,通常在一致性缓存中是基于乐观锁的方式(对比版本号)来实现的
redis作者曾说了这种方式性能是非常不好的,但是如果我们在程序逻辑做一些优化,也未尝不可
普通锁:这种锁也不好:很不优雅,必须将逻辑框在一个个的try finally中,而且容易出错,而且一旦在逻辑中作为一个常态出现了,那么就不得不把整个逻辑当做了一个巨大的耦合体(除非规范定得很好,能将锁分离,但大部分情况下貌似很难,起码我做不到)
cpu cas:比较推荐这种方式,优点很明显,风险低,效率高,缺点也很明显,通常cas只能用于解决单个变量的冲突,一旦涉及到了多个变量,代码就很难写了

同步服务器比较偏向于使用后两种,而普通的web则一般只会用前两种

c.思维方式
这方面其实我一直都没想透
比如通常我们写功能都习惯于使用请求响应模式,但显然这种模式用户体验是偏差的
实际应用中,同步服务器其实能给予更好的用户体验,也就是由服务器去驱动一些事件,注意,这里说的是事件
也就是说服务端给前端返回的不是状态了,而是事件,去驱动前端
虽然写了第四个了,但是由于种种原因,一直都逃离不开请求响应模式,主要不知道前端应该怎么改变它的架构
只是在局部做了一些服务器驱动的功能

2.协议
(指的是flash做客户端,java做服务器端的特殊场景)
由于个人经验也不是太足,这里只讨论两种协议,protobuf跟amf,而也只考虑跟flash进行交互
之前仔细斟酌过amf好还是protobuf好的问题
首先先做一个对比
数据大小:
protobuf无疑是最小的,除了数据,它只多了一个类型编号,跟一个位置索引,当然,该压缩的都压缩了
amf:amf对数据也会做压缩,而对string还会进行重用(这点有可能如果传输大量的类似的文本信息,amf的压缩比会更高),编码方式跟protobuf差别不大
但是有一点很致命,它会将对象的类型信息加进去,包括变量名等信息,也就是说,如果是数组,可能会好一点,如果是每个对象只用一次,传n个对象,无疑这部分的数据量是很大的

效率:
由于游戏服务器基本都是出流量远远高于入流量,这里只考虑服务器向客户端发的情况
protobuf用的方式是生成代码,而amf是反射,虽然没做具体的测试,但无疑,差距应该是很大的,由于是同步服务器一般给前端发的都是短消息,频度高

易用性:
flash默认兼容amf协议,从通道中可以直接简单地解析出对象,非常方便,protobuf则需要编写文件,生成代码,重新编译,数据转换,等等
但是否amf真的完胜了?
经过与我们的首席架构师跟前端的讨论,其实不是这样的
同步服务器一般需要比普通的web更严谨的编码风格,在协议上也是这样的
protobuf定义了前后端交互的协议,非常明确,再也不需要前端debug,查看这样的方式,也就是说,即使我们用amf,我们也需要一份这样的协议,而且是纯手工写的(如一样的dto),个人觉得,会比protobuf产生的更麻烦
当然,为了使用protobuf,还需要一些额外的编码,但这些是一次性的

一比较之下,显然protobuf更好,更优秀,但如果是非常简单的应用,用amf也未尝不可

再补充一点
最近发现前端非常依赖于数据,归根到底其实是amf惹的祸,因为amf返回给前端的是一个动态对象,所以前端想要知道对象里面的值,都习惯性的debug,所以每次,都必须得后端写完,提供数据,前端才能开始做一些事情
这样的前后端合作模式显然是有问题的

相对而言,protobuf会给他们生成一个类文件,对象里面的变量都是确定的,能一定程度上的改变前端这个坏习惯

3.业务架构
现在4个同步项目的业务架构,都沿用了三国里面的面向服务的架构方式,也就是service,bo,这样的方式,在现在所写的4种业务中,这样的方式挺好用的
大体的区别是:
service后面默认都添加了一个参数:“连接上下文”
增加了两个配置文件
把返回的map去掉了,返回数据通过“连接上下文”发送
多了一种叫task的东西,跟bo同一层次,是一种主动运行的事件(这到底在我们业务模型里面应该是一个什么东西呢?之前考虑过让他居于服务层,让他去调用服务,但貌似也不好,因为task调用的逻辑与前端调用的逻辑没有任何地方可重用的,而且会破坏endpoint调用service的方式)

现在有一个问题是:这样的架构如果用于复杂的游戏,例如mmo,能不能用,可用性怎么样?
我们知道,像我们社交游戏的业务,都有一个特点,层次简单,比如过:用户,下一层是兵营,将领,客栈……都处于同一层次上,就没有更深入的层次了
然而复杂的游戏,显然会有很多层,比如说之前写的同步战棋战场,整个服务器有多个战场房间,战场包含着小据点,据点中又包含用户,用户带着将领,都是一层嵌套一层的,这就会我们拿数据的时候,每一个service都需要一层一层地找到数据,然后操作,我们看到的也许就是多个大bo,我们写方法可能都是Abo.doSomething,然后Abo又在方法内调Bbo,这样其实是挺不好的

还有一点,越复杂的业务,task的逻辑就会越多,最后task会越来越多,越来越乱

跟我们的首席架构师多次讨论过这个问题,虽然看法不一致,但我还是觉得,这种业务模型应该还是适用的,起码在有新的方式之前,个人感觉还是利大于弊的

4.异常
在同步服务器中,异常的控制很关键,必须考虑所有可能的情况,定义异常,并捕获,比如之前我在写cas避免冲突的时候,考虑到必须限制cas的次数,否则会造成数据库压力,但是当cas连续失败多次之后,怎么办呢?
以前是直接抛出一个异常,就不管了
但显然,在同步服务器中,这是有严重问题的
我们之所以用同步的方式,除了数据及时性,有一个很重要的部分:通常我们需要写的业务存在着大量的数据竞争。显然用非同步服务器来解决冲突是很困难,并且很不靠谱的一件事,它往往只适合数据分离的业务。
基于这一点,我们如果也像之前一样,抛出一个异常,那很可能会引起数据的不一致,然后是迅速地连锁反应,蔓延,相对而言,我们普通的业务对数据一致性的要求低很多,因为影响的最多是1,2个用户而已

5.同步模型
同步1v1,3v3,老-虎-机用的方式都是房间内单线程,将同步相关的操作封装在了线程池内
国战用的则是锁,所有都是显式加锁,写得很麻烦,而且不好看

总的来说,能不用锁最好还是不用的好,房间内单线程模型,既不用考虑可见性,也不用考虑冲突,而且可以没有限制地使用jdk的集合类,相对起来编码简单很多,房间内操作在锁的层次基本没有任何的耦合,而且不容易错,之前也考虑过,如果写复杂的游戏,也许我们可以把所有场景都抽象成一个个房间,这样,我们只需要考虑房间之间的切换就可以了,然后其他的都是房间内操作,这样的话,我们大部分业务都不需要考虑同步问题了

6.服务器组
我们知道,单服,又不可扩容,对游戏的限制是很大的,于是,有了服务器组的实现(现在做的新游戏原计划是做多区多服的,全部请求都用长连接实现(最后改变了初衷))
所以我们一个区,其实会需求一个服务器组,按业务去划分,比如说:场景1服务器,场景2服务器,交易服务器,战斗服务器,用户状态服务器,等
这其实会有个很关键的需求:网关服务器
既然用了java,肯定希望要用就用全套的,网关服务器也不会考虑用c的实现了
大家都说java比c慢,但慢多少?大家都没有个准确的说法,网上的文章看了几篇,不是测试错误就是检测方法有问题,没有可参照性
按之前在做消息服务器时,与我们的架构师tx做的c版本的比较
c版本:单线程,cpu100%,极限大概是32000qps
java版本:4核多线程,cpu60%-70%,极限大概是68000qps
如果按这个看的话,其实没有太大的区别(个人认为,如果每组能撑10w同时在线,那改为撑7,8w,也不是什么大不了的事情,也就是说性能如果没达到150%,可以忽略不计)
后来也试着在内网做了下压力测试,可是没把服务器压死,先把公司的路由器压死了,后来就没测了

出于这个计划,仔细研究了下mina,netty(还sb兮兮去看了下的游戏开源服务器darkstar,想去看看别人怎么设计网关服务器的,谁知道就一个分布式的业务框架,跟游戏没一毛钱的关系)
发现一个问题,在decoder的时候,mina都会将byte[]从directbuff拷贝一遍到heapbuff中(而且这块代码是写死的,很难动)
如果我们用netty或者mina做这个网关服务器,无疑,我们的做法大部分应该是将byte[]从入口拷贝到出口去,然后发送,这样就会涉及到byte[]的2次拷贝,相当于netty和mina中做的大量的细节优化,都被这简单的两次拷贝给抹杀了

当试着去修改netty的源码时,资料片改为了用类似普通社交的玩法实现,这个计划就不了了之了

之后也去问了下别的公司的开发者(java的),都是说现在这浮躁的网页游戏行业很少有服务器组的实现了,都是单服务器,撑3,5千人就差不多了

c++的代码很多这样的源码,像传奇,魔兽世界,等等,java难道做不了么?

7.时间问题
在同步服务器中,时间是个很恶心的东西,因为客户端是有渲染时间的,但后端没有时间的概念, 后端是按事件去触发,去响应的,
举个简单的例子,一个简单的限制,A从x点移动到y点,时间,距离,后端其实是需要检测的,不然就各种外挂,但是如果前端每走一步又跟后端请求一遍,玩家会疯掉的
还有像资料片中的连招的释放,你必须在固定时间内按键,才算正确,又还有国战中我们需要按各自战斗时间决定排队顺序(谁跟谁打,什么时候打)

关于移动网上有的是各式各样的算法,这里就不罗嗦了
说一下在国战中和在资料片中是怎么解决这个问题的

(待续)
1
0
分享到:
评论
2 楼 snake1987 2012-12-19  
milk_36 写道
终于找到是用java的了!LZ想问一下你们项目中protobuf,message在经过网络传输后是怎么还原的?
如果在知道协议消息名称,该如何生成对应的java对象呢?


自己翻一下api啊~有很简单的方法的
1 楼 milk_36 2012-12-18  
终于找到是用java的了!LZ想问一下你们项目中protobuf,message在经过网络传输后是怎么还原的?
如果在知道协议消息名称,该如何生成对应的java对象呢?

相关推荐

    WeCenter社交化问答系统 v3.1.9社交问答 问答系统 问答程序

    Wecenter是通过积累来源于微信,微薄,APP,社区等用户的碎片信息,利用社交互动的模式,分析数据,提炼数据,最终帮助企业和组织积累符合他们需求的知识百科! WeCenter是一个微信公众平台社区解决方案,用户在...

    ssb-server:Secure Scuttlebutt的八卦和复制服务器-分布式社交网络

    ssb服务器ssb-server是一个开源对等日志存储,用作数据库,身份提供程序和消息传递系统。 它具有: 全局复制文件同步端到端加密ssb-server行为就像。 在后台,它与已知的对等方同步。 对等点不一定是受信任的,并且...

    WeCenter社交化问答系统 v3.3.0

    Wecenter是通过积累来源于微信,微薄,APP,社区等用户的碎片信息,利用社交互动的模式,分析数据,提炼数据,最终帮助企业和组织积累符合他们需求的知识百科!WeCenter是一个微信公众平台社区解决方案,用户在...

    nakama-js:用TypeScript编写的Nakama服务器JavaScript客户端

    功能包括用户帐户,聊天,社交,媒人,实时多人游戏。 该客户端通过服务器实现完整的API和套接字选项。 它以TypeScript编写,具有最小的依赖关系,以便与所有现代浏览器和React Native兼容。 完整的文档在线入门您...

    Werewolf:使用Javascript和Websocket的社交欺骗游戏Werewolf的在线,基于大厅的多人版本。 仍在积极发展中

    这是为了在面对面的社交环境中促进游戏并提供实用性/便利性-不能控制游戏流程的所有方面。 该应用程序允许玩家创建或加入状态同步的游戏大厅。 游戏的创建者可以根据提供的标准卡组或用户创建的任意数量的自定义卡...

    react-native-sdr:React Native的服务器驱动渲染(SDR)组件

    适用于React Native的服务器驱动渲染(SDR) 安装 $ npm install react - native - sdr -- save 或者 $ yarn add react - native - sdr ...与服务器自动同步 在您的客户上: import { Provider , SDRClient }

    java开源包4

    WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...

    FDVT:社交网络数据评估工具。「FDVT: Social Network Data Valuation Tool」-crx插件

    -用户从其建立连接的非同步系统-货币Facebook:trade_mark:与您的帐户相关联-在会话期间打印的新闻源帖子(不同于广告)的数量,但不包含其内容。 -在会话期间打印的朋友/页面/群组的数量,但不包括其内容。 -...

    BingSNS社交互动平台 v3.0 R社区版.zip

    BingSNS社交互动平台r版(Bingsns手机端即将发布) ********************************************* BingSNS是一个以社交网络为基础的专业应用平台,分为供销、论坛和社区型产品,都是针对垂直领域的服务和运营,...

    hydrus:一个个人booru风格的媒体标记器,可以从您的硬盘驱动器和流行的网站中导入文件和标记。 内容可以通过用户运行的服务器与其他用户共享

    Hydrus网络(客户端和服务器) hydrus网络客户端是为Anon和其他拥有大型图像/ swf / webm收藏夹的互联网... 我在Github上的活动并不活跃,并且通常很难与社交媒体保持同步,但是我欢迎任何形式的反馈,最终会赶上并回复

    1010分钟搭建微信小程序服务器.zip

    微信小程序的优势在于它方便快捷、轻量级、跨平台、丰富的推广方式、丰富的功能接口、数据分析与优化、结合微信支付、支持多场景应用、社交功能以及多端同步等。这些优点使得小程序能够满足用户的多种需求,提供更好...

    想疯了(联想共享传输工具)v1.3.04最新安装版

    让用户摆脱数据线、蓝牙等复杂的分享方式,用一种新颖的交互让用户体验到近距离线下社交中与人分享的快乐! 功能特点 安全:所有的数据都经过加密进行同步,不怕被盗取; 快速:通过MEG高速服务器进行传送,多线程...

    WebSocket客户端和服务端实例源码

    轮询,原理简单易懂,就是客户端通过一定的时间间隔以频繁请求的方式向服务器发送请求,来保持客户端和服务器端的数据同步。问题很明显,当客户端以固定频率向服务器端发送请求时,服务器端的数据可能并没有更新,...

    即时通讯APP源码 IM聊天社交APP+ios可上架+安卓苹果双端+pc端+H5端+微信端

    APP 端历史聊天记录、图片以及前台程序缓存在本地,页面秒开,支持云端同步聊天记录,断网状态页面之间也可以切换,流畅性媲美原生。 技术路线: 后台开发语言:PHP (原生架构) 前台开发语言:uniapp socket 推送...

    ThinkOX:基于Onethink的开源轻量级社交程序,包含微博,贴吧,活动,商城,聊天等功能。

    本版本为开发版,数据库同步请于后台升级补丁工具内部选择同步,建议在同步数据库之前先审查一下脚本,确认无误再行导入。 注意:本项目为开源项目,项目维护人员利益有限,无法对自行开发的朋友解答开发过程中碰到...

    云盒子 v4.0.1.8

    首创的双模虚拟盘实现文件夹同步,与Windows系统无缝融合,操作云文件就像本地文件,让文件存取皆于云端。安全存储将文档数据存储在自己的设备中,拥有文档存储、分发的绝对控制权。从客户端到服务器端,采用最高...

    java开源包1

    WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...

    java开源包11

    WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...

    java开源包2

    WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...

    java开源包3

    WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 ...

Global site tag (gtag.js) - Google Analytics