集群的安全启动/终止
典型地,我们需要在GS和各个服务都启动完成之后,再打开网关接入网络连接,这需要通过master节点进行监控,GS和服务也需要向cluster汇报自己的状态(启动中,运行中),master在关键服务都启动完成并状态正常之后,再通知gateway开放连接。基于cluster中的控制信息,这一点不难做到。集群的终止流程也类似。
前几天看一些Go框架的源码和相关工具,由于之前没有在正式项目中用过Go,看起来是有些吃力的,有一种顾头不顾尾的感觉,到之后仍然是困于其中,不得精髓。这和本人的一些学习习惯有关:迷信源码,觉得只有理解其源码,才能运用自如,不知道是不是部分受于C/C++出身的影响,底层思维根深蒂固。之前看的一些框架(skynet,firefly,kbengine等)也是一样,虽然直接看的源码,但是由于缺少实践,对其运用场景,优缺点没有足够的认识,能够吸收的干货也比较有限。加之工作上开始忙起来,难免急躁,事倍功半。
在分布式系统中,你可能最容易犯下的错误就是认为远程节点是永远可用的,尽管这可以通过添加更多的硬件(比如主从)来实现,但这也带来了冗余性。网络可能随时因为断电,硬件故障,自然因素,人为因素等不可用,在这种情况下,保证你的程序能够在远程节点或者第三方服务不可用时,能够正常运行是非常重要的。Erlang除了能够监测到外部服务失去连接(或者不能响应)之外,没有任何的其它措施,毕竟,除了你自己以外,谁也不知道某个组件有多重要。
注意,Erlang跨节点的monitor和link操作是比较危险的,因为一旦远程节点不可用,将触发关于该节点所有的远程monitor和link,这可能会引起一场网络风暴,给系统带来意料之外的高负载。在不可靠的网络上构建可靠的分布式应用,需要你随时准备面临这种突发状况,并且保证系统能够继续正常稳定地工作。
在使用cluster_server做gameserver后台集群支撑的过程中,逐步暴露出一些问题,在此记之。
我们按照业务职责将集群节点分为,agent,player,map,alliance,battle等,这些节点可以在一台物理机上(操作系统进程间交互),也有可能部署到不同的物理机上(网络IO交互),而玩家一个业务逻辑可能涉及到多个节点,形成一个节点交互链:如玩家在大地图上进行一场战斗,按照流程需要走:agent -> player -> map -> battle -> map -> player -> agent。整个过程都是异步的。这种交互是频繁的且低效的。
简单介绍一下Erlang常用数据结构的内部实现和特性,主要参考Erlang OTP 18.0源码,和网上很多优秀博客(参见附录),整理了一些自己项目中常用到的。
Erlang虚拟机使用一个字(64/32位)来表示所有类型的数据,即Eterm。具体的实施方案通过占用Eterm的后几位作为类型标签,然后根据标签类型来解释剩余位的用途。这个标签是多层级的,最外层占用两位,有三种类型:
这三种类型是Erlang类型的大框架,前两者是可以看做是引用类型,立即数相当于是值类型,但无论对于哪种类型,Erlang Eterm本身只占用一个字,理解这一点是很重要的。
在游戏服务器中,通常要面临对象到模型的映射,以及对象到协议的映射,前者用于GS和DB交互,后者用于GS和Client交互。我们的项目中做到了对象到模型的自动映射,这样在开发过程中无需关心GS和DB的交互,很方便。
而现在我们还没有实现对象(map)到协议(record)的自动映射,我觉得这个特性是比较有用的,特别是在同步一些实体数据的时候。无需写一堆Packer函数来将对象数据打包为协议。因此就研究了一下如何将map的数据自动映射到protobuffer,也就是转换为record。