打通游戏服务端框架的C++20协程改造的最后一环
前言
我们终于在年初的时候最后完成了整体服务器框架对C++20协程的支持和接入。虽然之前陆陆续续抽时间改造一些组件,让它支持C++20协程,期间也记录了一些早期的设计思路和踩的坑(包括 《libcopp接入C++20 Coroutine和一些过渡期的设计》和《libcopp对C++20协程的接入和接口设计》),其中不乏一些C++20协程使用上可能打破我们常规思路细节和编译器的BUG。而且这些都是各个组件的改造,并没有最后整合到一起。
我们终于在年初的时候最后完成了整体服务器框架对C++20协程的支持和接入。虽然之前陆陆续续抽时间改造一些组件,让它支持C++20协程,期间也记录了一些早期的设计思路和踩的坑(包括 《libcopp接入C++20 Coroutine和一些过渡期的设计》和《libcopp对C++20协程的接入和接口设计》),其中不乏一些C++20协程使用上可能打破我们常规思路细节和编译器的BUG。而且这些都是各个组件的改造,并没有最后整合到一起。
年前就计划把以前项目的一些理念和设计方案融合到sample里来。但是内容比较多,一直也没太多时间去完成它。所幸虽然断断续续但终归是完成了。并且在之前的一些实现上还做了一些细节的优化。内容比较多我感觉我自己写的也比较乱,仅当作一个参照和小计吧。
最近在抽时间整理之气的游戏服务器框架和解决方案里atsf4g-co,现在的架构是使用etcd的是atproxy。简单得说就是服务集群是分组的,每个分组有分组代理服务atproxy做组间通信。然后atproxy之间使用etcd来做分组服务的服务发现和保活,并且以此来实现平行扩容。
之前做服务间通信组件libatbus的时候也提到了有一个暂时没实现的功能,就是全局路由表的自动通知。但是这个功能的实现主要也是用于后面不同种服务间感知到哪些节点是可用的,哪些是不可用的。而且我的简单实现必然是走心跳的模式,因为心跳的形式肯定不能把心跳设置得太短,同时也要考虑网络异常抖动和断线重连和丢包,所以肯定不是丢一个心跳包就认为丢失。所以故障或者扩缩容期间的感知时间就会比较长一些。另外就是因为可能有网络孤岛问题,所以可能短期内数据不一致(当然肯定会保证最终一致性)。
再加上由于libatbus是支持多级父子节点关系的,所以变化通知和同步包就要考虑自己与父节点、兄弟节点、自己与子节点的不同关系并作不同的同步流向,会比较复杂。比如:子节点下线,既要通知父节点,又要通知兄弟节点。那么这时候给兄弟节点通知就有两个通路,一个是经由父节点中转,另一种是直接发。当然这时候并不一定和兄弟节点有直接通路。所以可能兄弟节点会收到两次通知,一次来自兄弟节点,另一次来自公共父节点。然后又会有其他问题,就是万一又收到一条冲突的消息,来自父节点和来自兄弟节点的顺序是没有保证的,这里又得加入版本机制。总的来说,细节会比较复杂,具体在实现libatbus的这个功能的时候在谈吧。
上面说的libatbus的功能暂时没实现的最重要原因是etcd可以比较完美的解决上面的延迟问题和不一致问题。缺点就是请求的消耗会高于使用libatbus的通信机制。不过这玩意本身不是高频操作,而且故障和容灾本身不是一个频发的事情所以关系不大。而之前etcd的接入是直接写死在atproxy里的,那么这次重构的目的主要就是能够抽象出模块化的工具,以便后面不同的服务可以根据需要取用。
按现在的功能划分,etcd的接入总共被分为3个模块,etcd_cluster、etcd_keepalive和etcd_watcher以及一个通用工具etcd_packer。etcd_packer用于对etcd的一些通用的打解包操作。
etcd v3版本内部的通信已经使用了grpc。本来我是想等他的grpc接口进入官方文档并且提供出的grpc的proto再接入的,可是它一直没有整理出直接grpc的proto文件列表。另外我看了一下它的proto文件里用到了一些gogoprotobuf的扩展,其他语言不一定可以无缝接入。考虑到etcd使用了grpc-gateway提供HTTP+JSON的网关层,所以我还是基于他的HTTP接入层来做。因为这里身频次不高,也没有那么在意性能。而且一组etcd服务的QPS也就在十万的级别,只要管理好连接,不要老新建立和关闭连接,HTTP的性能还是够的。