云风开发笔记(25) : 改进的 RPC

自从动了重新实现 skynet 的念头,最近忙的跟狗一样。每天 10 点醒来就忙着写代码,一句废话都不想说,一直到晚上 11 点回家睡觉。连续干了快一个月了。

到昨天,终于把全部代码基本移植到了新框架下,正常启动了起来。这项工作算是搞一段落。庆幸的是,我这个月的工作,并没有影响到其他人对游戏逻辑的开发。只是我单方面的同步不断新增的逻辑逻辑代码。

Skynet 的重写,实际上在半个月前就已经完成。那时,已经可以用新的服务器承载原有的独立的用户认证系统了。那么后半个月的这些琐碎工作,其实都是在移植那些游戏逻辑代码。

在 Skynet 原始设计的时候,api 是比较简洁的,原则上讲,是可以透明替换。但实际上,在使用中,增加了许多阴暗角落。一些接口层的小变动,增加的隐式特性,使得并不能百分百兼容。另外,原来的一些通讯协议和约定不算太合理,在重新制作时,我换掉了部分的方案,但需要编写一个兼容的链路层。

比如:以前,我们把通过 tcp 接入的 client 和 server 内部同进程内的服务等同处理。认为它们都是通过相同的二进制数据包协议通讯。但是,同进程内的服务间通讯明显是可以被优化的,他们可以通过 C 结构而不是被编码过的数据包交换信息,并可以做到由发起请求方分配内存,接受方释放内存,减少无谓的数据复制。在老的版本中,强行把两者统一了起来,失去了许多优化空间。在新版本里,我增加了较少的约定,修改了一点接口,就大幅度提升了进程内服务间信息交换的效率。

另一方面,一旦固定采用单进程多线程方案,之前的多进程共享数据的模块就显得过于厚重了。新的方案更为轻量,也更适合 lua 使用。这项工作在上一篇 blog 中提到过。这和 skynet 的重写原本是两件事情,但我强行放在一起做迁移,增加了许多难度。但考虑到,原本我就需要梳理一次我们的全部服务器端代码(包括大量我没有 review 过的),就把这两件事情同时做了。

在这个过程中,可以剔除许多冗余代码,去掉一些我们曾经以为会用到,到实际废弃的模块。彻底解决一些历史变更引起的问题。过程很痛苦,但很值得。新写的代码各种类型检查更严格,就此发现了老的逻辑层代码中许多隐藏的 bug 。一些原有用 erlang 实现的模块,重新用 lua 实现了一遍,混合太多语言做开发,一些很疼的地方,经历过的人自然清楚。以后如非必要,尽量不用 lua 之外的语言往这个系统里增加组件了。

btw, 新系统还没有经过压力测试。一些优化工作也没有展开。但初步看起来,还是卓有成效的。至少,改进了数据共享模块,以及提出许多冗余后,整个系统的内存占用量下降到原来的 1/5 不到。CPU 占用率也有大幅度的下降。当然,这几乎不关 C 还是 Erlang 做开发的事,重点得益于经过半年的需求总结,以及我梳理了大部分模块后做的整体改进。


今天想重点谈谈下面一段时间我希望做的改进。是关于服务间 RPC 的。

目前能找到年初的一篇记录 ,经过大半年的演化,已经不完全是记录的那个样子了。但大体上的思路一直沿用着。

这个方案的优点在于,使用通用的 google proto buffer 协议做严格的 RPC 协议定义。但缺点也是很明显的,最麻烦的地方在于,在一个需要大量服务间交互的应用环境内,新实现一组 RPC 需要做大量的工作,这对程序员是一个负担。

程序员需要:一,在一个协议描述文件中,定义出协议名以及对应的协议号;二,在对应名字的 proto buffer 文件中,定义出协议对应的输入参数和输出参数列表;三,在特定的位置,创建特定名字的 lua 源文件,在里面实现特定名字的协议过程。

有时,这个流程是好事,它能够在 lua 这种弱类型系统中,辅助检查出潜在的错误;但对开发效率的影响也是显著的。同时,这个长长的流程,以及对应的各种编解码工作,也引起了部分性能损失。

我希望在以后的系统中,引入更为方便简洁的 RPC 机制。

简单的说,我的最终需求是:程序员不需要为 RPC 调用和本地调用写不同的代码。程序员需要心里了解一次调用是远程调用,但他不必为实现这些方法做额外的事情。在他不需要把一些方法定义为远程方法时,只需要把源文件换个位置,或是重新组织一下代码加载的过程,就可以轻松的完成。远程对象和本地对象对调用者来说,也应该尽可能的透明。

我今天把这个想法基本实现了。

经过重写 skynet ,我对这类事务的处理稍微建立了一点模式。首先,应该把同进程内的服务间 RPC 调用同跨进程的调用区分开。

跨进程(跨机)调用,可以通过增加一个特定的服务,在链路层把它们接起来即可。应该专心实现同进程内,不同服务间的高性能 RPC 才是重点。等这一步完成,只需要为异地对象建立一个本地副本做消息中转就够了。

我们应该专心考虑 Lua State 实现的服务,而不必过于考虑不同语言间的 RPC 调用。一起以 Lua 为主语言来考虑功能。

首先我引入了之前实现好的 Lua 数据序列化模块 。并对它做了一些改动 ,合并到 skynet 项目中。

这个改动是,让序列化模块底层理解远程对象。增加了一个远程对象类型。因为所有的 Lua State 其实是在一个系统进程内的,数据交换工作通过 Lua 的 C 扩展库就可以完成。在同个进程内,每个远程对象都有唯一的数字 id 。传递远程对象,只需要传递这个 id 即可。

每个 Lua State 中,维护一张表,记录所有在这个 State 中创建出来的远程对象,以及对应的 id 。在序列化模块中,一旦发现提到的远程对象是自己进程内的,就自动翻译成本地对象。

序列化模块本身不处理任何远程对象的特殊行为。它把这个工作交给外部注入。skynet 模块把远程调用的方法注入到远程对象中。

所谓 RPC 调用,就是一个远程对象加一个方法名,加上若干参数。通过序列化方法,打包成一个数据包,查询到远程对象所在的服务地址,发送过去即可。

而所谓远程对象,其实只是在 lua table 里设置一个叫 __remote 的字段,设入数字 handle 就可以让序列化模块识别了。

这个模块并不复杂,我实现在了 skynet.lua 中,不到 100 行代码。

作为一个范例,我实现了一个叫 root 的远程对象,让它在一个 独立服务 中启动。它可以提供一个基本的名字服务。在一个 简单的 test 程序 中,我们可以看到一个远程对象把自己注册到 root 里,别的服务从 root 拿到这个对象,就可以向本地对象一样调用上面的方法了。
转自:https://blog.codingnow.com/2012/08/dev_note_25.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值