一个简单的基于Protobuf的RPC框架

  在喜迎新年时刻大家都忙着划水摸鱼,而且这段时间系统变更都封版冻结了,也算是可以正当地清闲的一段时间了。在阅读logcabin项目代码的时候受作者之启发,于是便沿着原作者的思路做了一个简单的C++专用RPC开发框架tzrpc,目前该项目的基本功能已经完成,其代码也开源共享了。


  可能让大家觉得RPC开发框架名字咋一听起来比较唬人,但是正如自己在之前的文章中所提到的:RPC框架的核心就是统一规范化数据编解码和网络传输方面的细节,方便业务端可以快速实现网络程序开发而已,如果究其本质内容的话,这个过程和普通的互联网开发相比没有什么差异的,所以自然不足为奇。之所以自己要做这个东西,一方面是因为目前工作中所用到的基于socket网络开发库的确是不怎么的好,在生产上用起来问题很多,比如在封装上就以一个简单的两字节长度做为头部,然后就直接追加流式消息体了,如果将一个非法的请求错误地打向这个端口,那么这个服务就会卡死甚至崩溃掉,还有一些客户端调用设置了超时,如果服务端负载很重导致客户端超时返回,下一次复用连接的客户端请求和响应会和上一个请求的响应串话,这在生产上已经暴露出问题了,当然和这个框架的健壮性不佳已经使用者不规范使用都有关系;另一方面Thrift和grpc很好很强大,但是如果想要做一些个性化的定制和功能增加,就会觉得难以下手,而我想这个原因估计也是很多互联网公司为了满足自己业务需求,另起炉灶开发内部RPC框架的最主要原因了。而我写这个工具的初衷,也就是想整理出一套自己摸得透、掌控得了、可以按需求随意定制的网络开发库,增加后续网络服务的开发效率,增加在网络服务开发中的积累。
  
  一如既往地,tzrpc的底层也是采用基于boost.asio实现的高并发、高性能的异步框架,以保证网络层的高性能传输,而数据编解码使用最常见的protobuf 2,当然也可以扩充采用其他数据(反)序列化的方式。网络连接支持并且强烈建议使用长连接,除了性能考量因素之外,主要是避免TPS过高的情况下导致大量TIME_WAIT占用端口的问题;数据方面则分别在网络传输层和上层应用层进行了层层封装,当接收方校验失败的数据报错给调用方,增加了服务整体的可靠性;同时还在错误码设计上进行细致的考量,以方便调用者根据不同的异常情形选择合理的处理方式。目前项目还仅仅处于实现了基本功能的阶段,后续还期待会逐步完善并发和过载保护、线程池自动伸缩、监控和告警等各项功能,当然后续遇到的其他RPC框架较好的特性也会努力引入到这个项目中来。
  和之前的heracles项目一样,tzrpc提供了服务端和客户端的支持,客户端只需要一个库文件和若干个头文件就足够了,数据编解码和网络传输的细节也被封装了起来,这样就方便和其他项目简单快速地集成起来。
  
  在写这个项目的时候,也顺便将之前的tzhttpd这个HTTP服务库做了一次彻底的重构工作,这个过程中看见自己自己之前写的代码真的是混乱不堪啊。该库之前设计最大的缺陷是业务处理Handler是在HTTP请求接收和解析完之后,直接嵌入到boost.asio线程池中同步化执行的,所以如果Handler处理的时间比较长的话,会严重影响到整个网络IO的并发和传输性能,而且也无法记录负载并灵活的增加线程池或丢弃过载请求。这次的实现按照tzrpc一致的框架模式,将请求接收和解析完毕之后放置到一个内存队列中,然后交由独立的工作线程组去按序处理,业务处理完的响应结果也放到boost.asio异步框架中高效的返回给调用端;各个虚拟主机采用独立可配置的线程池处理,也实现了跨服务间资源的隔离保护;整个项目的组织和结构也进行了调整,变的更加的清晰和易于理解了。不过项目减少了对参数动态更新的支持,主要是感觉为了支持参数的动态更新,需要考虑到很多的临界情况、作出很多的竞争态保护工作,整个项目因此变得更加的复杂,而实际上非关键服务直接重启一下也不困难,而核心服务也可以通过ZooKeeper这类服务进行治理,进行灰度操作也能达到不停服务更新的效果。
  当然现在tzhttpd和tzrpc两个项目算是高度相似了,以后任何一个的改进更新都可以选择性地同步到另外一方去,总体的维护成本都大大降低了。
  
  顺便要跟大家说一下的是:后面本站关于软件工具、协议等方面的学习整理型的文章可能会写的比较少了,现在倾向于使用思维导图来管理知识内容了,学习整理的内容今后可能会开源出来一并同大家分享。本站争取能更加倾向于分享项目中遇到的问题、学习到的经验,以及一些和开发和管理相关的心得体会,当然生活、学习、社会、文学方面的感触和想法也会在这里吐露出来,总之还是希望大家能持续关注。