2016年5月18日我提交了rpcx的第一段代码,那是一个基于标准rpc库的扩展,期望能够扩展rpc的功能,提供更多的服务治理的功能。
2016年初我的第一本技术书《Scala集合技术手册》出版了,这本书花费我太多的心血在编写和技术呈现上,但是我终于能腾出时间做点其它的事情。
正好,从Motorola出来后的创业公司被Comcast收购,Comcast的很多业务都是使用Go做开发的,虽然从03年到这个时候我还是使用Java开发,但是也在尝试使用Go做一些东西。一开始我对Go是有一定的误解的,首先对于它的非C风格的语法有些抵触,另外还认为它只能用来写一些小工具,根本没有意识到它的企业级应用的价值。随着慢慢地对它的熟悉,才渐渐感觉到它的强大,逐渐被它的简洁和并发编程所吸引,并且相信它是未来应用广阔的编程语言之一。
来到微博后就基本全时间的使用Go做开发,开发了服务发现和配置中心、消息队列、区块链、redis云服务等系统,越来越对Go语言有了更深的认识,也越来越喜欢上了这个简洁的开发语言。现在回过头来看2016年的一些探索,真是走对了方向。所以说, 2016年是rpcx的元年,5.18是它的诞生日。
1.0
最初的想法,是想创建一个项目,用来熟悉和学习Go语言。但是我不想纯粹写hello world的程序,而是想写一个有价值的项目,一个能长久活下去的项目,一个能给其它人带来价值的项目。
dubbo是Java生态圈非常有名的一个rpc服务治理的框架。虽然在阿里的内部竞争中不行败下阵来(这里面应该有一段错综复杂的故事),但是在其它一些电商却得到了广泛的应用,这是出乎我意料的。因为在我想象中,这些电商的技术能力应该是很强大的,应该使用自研的rpc服务治理的框架,不屑于dubbo这种不再活跃的开源项目,况且它的很多依赖包括spring框架都是非常老的了,如果使用它意味着公司的技术栈依赖的库都是要非常老的版本。我是一个现实主义者,相信存在即合理,相信大家使用dubbo框架是因为它的功能,它的优势。(当然现在dubbo框架又活跃起来了,还成为了apache的项目,依赖的库也得到了更新)
dubbo的很多概念都是很使用的,服务治理的功能也很强大,并且还可以扩展。我当时站在北京的香山之巅鬼见愁的悬崖上,抚剑环顾四周,白茫茫一片,还没有看到Go语言中有一个可以匹敌的框架。尽管优秀的web框架比如beego不断的涌现,但是rpc服务治理框架还没有踪影。这是一个空白!
很显然,rpc服务治理框架是互联网企业迈不过的一道技术门槛,Go语言要想在企业中后台中大规模的应用,必然需要一个rpc服务治理的框架。
虽然当时离grpc的初版发布了有一年了,thrift也开始支持Go语言,但是明显这些rpc的框架的方向是朝向跨语言的路数上走了,dubbo、motan走的是服务治理的一条路。即使是现在,我也不认为grpc是一个服务治理的框架,虽然现在也开始支持load balancing这样的功能,但是路由还是很简单,离整体的服务治理框架还有很大的差距。如果在企业中大规模的应用,肯定还有做很多的开发工作,这些工作的汇总在一起可能会诞生一个新的框架,貌似有些公司在做这样的一些工作,但是扩展起来还是很有难度的,因为grpc的扩展需要考虑到跨平台的能力,一旦涉及到很多的语言就有一个tradeoff的考虑。
Go标准库的rpc是一个非常赞的库,代码量不算太多,但是性能非常优异,远远大于很多的服务治理的框架。它的设计也很简单,服务器启动的时候注册服务,客户度通过tcp连接和服务器通讯,协议分为header和payload两部分,header很简单,包括服务名、方法和seq,payload包括序列化的数据。
最让人赞叹的是,标准库设计的非常的简洁。rpc的本质是什么,远程方法调用。即使是一个初学者,会写hello world,就会写rpc服务了,你只需要写一个带两个参数的方法就可以了。
部署和调用也很简单。服务器启动的时候注册一下,指定启动的地址和端口就可以了。客户端指定连接服务器的地址和端口,传入要调用的服务和方法就可以了。
非常的简单!你不要以为这是很平常的一件事,看看现在市面上的现有的rpc框架,你就知道这是一点多么了不起的事情了,也明白很多rpc服务治理框架有多渣渣。只是简单写一个一行的hello wolrd的例子,你可能创建很多的数据结构和方法,还要进行几十行甚至上百行的配置文件,而且服务器和客户端还要不同的配置文件。
对于复杂性这一点,grpc、thrift虽然也创建复杂的数据结构,但是对开发者来说,它是"隐藏"的,而且是自动生成的,这还是可以接受的,但是如果需要的你手工创建一些辅助的类型、方法、对象,这绝对是对开发者一种精神的折磨。
复杂的配置文件也是一种精神折磨。虽然需要定制一些额外的服务治理的功能,我们需要设置一些属性,但是如果将这些配置设计成非常复杂的结构,貌似很有条理,实际就是一坨。最简单的调用处理peer的ip和端口,以及调用的服务和方法,其它都是不需要配置的。需要配置的东西也是可以链式调用一行完成。
很遗憾目前我看到很多的rpc框架都是涉及的非常复杂,所以每次看到大师实现的标准rpc库都是由衷的敬佩,阅读起来代码就像听音乐一样让人愉悦。
rpc又提供了灵活性,本身它还提供了jsonrpc 1.0的功能,也有第三方库提供protobuf、msgpack的编解码器,还有jsonrpc 2.0的支持。
虽然rpc库的开发已经被冻结,但是如果你想在Go程序中实现点对点的rpc通讯,标准库还是首选,如果你想实现跨语言的点对点的rpc通讯,优先选择grpc,如果你想实现dubbo一样的服务治理的框架,2016年还没有选择。
所以我参考dubbo的功能,基于标准库的基础上逐步实现了服务治理的功能,包括zookeeper、etcd等注册中心、各种fail的处理、丰富的路由功能等等。
最初的设计就是包装标准库,在它的基础上提供插件的功能,各种服务治理的功能通过插件的方式去实现。
2.x
随着rpcx的功能不断完善,知名度也慢慢地扩大,越来越的开发者开始关注到这个项目,包括国外的一些开发者和使用者,大家提出了一些新的需求。在分析和实现这些需求的过程中,我发现依赖的标准库成了rpcx进一步开发的制约因素,很多功能可以通过简单的修改标准库来实现,但是因为无法修改标准库而无法实现这些需求,或者实现这些需求特别复杂。
所以在2.0版中,我把标准库复制出来,rpcx不再依赖Go的标准库,而是使用复制的rpc库,这样我就可以在复制的库中进行修改,这大大增加了rpcx的灵活性。
在这个版本中,rpcx的主要功能都已经成型,包括:
- 数据的传输方式,支持TCP、unix domain、HTTP、QUIC、KCP等
- 注册中心支持 点对点、点对多、zookeeper、etcd、consul等
- 多种编解码方式如protobuf、json、msgpack等
- 失败处理模式如FailFast、Failover、Failtry、Fork、Broadcast等
- 各种路由如轮询、随机、一致性哈希、权重、网络质量优先、地理位置优先等
- 元数据等
- 限流、别名、web管理工具等
并且,有网友反馈使用定制的rpcx服务替换原先的java服务,用在了游戏服务器的防御日志上,减少了一半的服务器,并且服务器的负载都很低,每个月能减少50万的费用,这是很令人鼓舞的事情。
3.x
3.0进行的重新,去掉了复制出来的标准库,完全自己重写。原因在于标准库的通讯协议过于简陋,相对rpcx做更复杂的功能不是太好扩展。
重新实现内存通讯协议的想法来源两个。一个是在微博实现包捕获和重放工具的时候,积累了协议涉及和实现的经验,第二个很重要的启发式微博张雷周晶他们正在做的新一代的Motan设计。微博内部广泛的使用Motan服务,为了应对微服务化的趋势,张雷他们对Motan进行了升级,既保持向下兼容,又重新设计了Motan协议。rpcx 3.0深受Motan协议的启发,结合标准rpc库的时候,rpcx实现了新一代的通讯协议。
新一代的rpcx协议包括header、变长信息(service名称、方法名、元数据)、payload三部分。header有固定的长度,采用二进制的方式压缩占用空间,也参考了TCP等协议栈的数据结构,变长信息采用LV的格式(长度+数值),因为都是字符串类型,所以TLV协议中的T(type)可以省略。Payload是序列化后的byte slice。
协议采用尽量少的额外数据,又不失灵活性,保证协议的可扩展性和可升级。
同时,有用户反映rpcx引用的包太多,很多不需要使用的库都不得Registy不下载下来,比如使用etcd注册中心,zookeeper库也不得不下载下来。经过权衡,决定采用go tags的方式,通过tag将主要的可移除组件进行分来,可以有选择的进行下载,目前支持的tags包括:reuseport quic kcp zookeeper etcd consul ping utp
。
这也给Go初学者带来的麻烦,不止一次的在QQ群中给新人介绍为什么EtcXXXXXX
文件本地存在,而编辑器无法找到对应的struct。事实上目前的IDE如vscode、golang、vim-go等都支持设置tag。但是对于没有使用过tag不了解go tag的开发者来说,确实是一件让人困惑的事情。
当然也可以通过拆成子package的方式,就像各种数据库驱动实现的方式一样,可是这样子package太多了,而且每个子package中只有一两个文件,过于分散。
目前还是保留tag的方式,如果将来有更好的方式再做替换。
同时创建了丰富的示例,文档不足示例来补,基本上为rpcx的每一个特性都编写的可运行的示例,而且示例代码不足百行,很容易学习。
受grpc gateway的启发, 3.1中增加了 rpcx gateway的一个项目,可以部署一个gateway,支持各种编程语言的http调用,由gateway进行转换成rpcx协议进行转发,解决了跨语言的客户端调用问题。
更进一步,直接由rpcx服务器直接提供http调用,不用使用gateway转发。而且通过cmux库,实现一个监控可以同时支持纯的rpcx调用喝http调用。
同时增加了双向通讯的功能,这样服务器也可以给客户端推送消息。
增加了一个xgen工具,可以为指定的go代码生成服务器端的注册和服务启动功能,纯粹是一个辅助工具。
4.0
rpcx开发步入正轨,基本每一年发布一个大的版本。
4.0支持更多的可信赖的udp的实现。提供了OneClient
对象,一个对象可以调用多个服务。完全实现了压缩的功能。增加了更多的监控的插件,优雅关机等功能。
小米的热心同学提供的rpcx-java, 可以使用Java实现服务器端和客户端的代码,而且保留和扩展了服务治理的功能,这样rpcx在Java生态圈也可以得到很好应用。
注册了 https://rpcx.io 域名,提供了rpcx的官网。
提供了中文文档。
5.0
今年年底发布rpcx 5.0版,增加了对opentracing和opencensus的支持,目前这两个项目已经合并,rpcx也会提供对合并后的规范的支持。
提供了JSONRPC 2.0的支持。
支持CROS跨域调用。
内置了client pool的支持。
目前为止,rpcx支持的主要功能包括:
- 数据的传输方式,支持TCP、unix domain、HTTP、QUIC、KCP等
- 注册中心支持 点对点、点对多、zookeeper、etcd、consul、mDNS等
- 多种编解码方式如protobuf、json、msgpack、thrift等
- 失败处理模式如FailFast、Failover、Failtry、Fork、Broadcast等
- 各种路由如轮询、随机、一致性哈希、权重、网络质量优先、地理位置优先等
- 元数据 身份认证
- 限流
- 别名
- metric监控
- 超时
- 心跳
- 分组
- 熔断器
- gateway
- http调用
- JSONRPC 2.0
- 双向通讯
- opentracing和opencensus
未来
rpcx越来越多的被一些创业公司和成长型的公司所使用。比如拍够购:
但也有网友提出了rpcx前景的一些问题。
1、现在大家都service mesh了,传统的rpc服务治理框架还有前景吗
两个方面,对于istio这种重度依赖kubernetes的service mesh,需要公司对k8s有很好的把控,开发和运维要求都很高,这限制了这种技术栈的被广泛的采用。另外一个是不使用k8s,那么需要你自研service mesh,自研的service mesh也可以方便的使用rpcx实现。
我不认为service mesh会是一统天下的微服务模式,传统的胖客户端的rpc模式也很有竞争力,如果加上容器化自动部署的功能,可能运维上的成本会小的很多。
2、rpcx没有大厂使用,连微博都不使用,是不是没前途
大厂很少采用开源的微服务框架实现它们主要服务的。大厂那么的开发人员,会有专门开发小组开发部门去实现自己的rpc框架,比如阿里巴巴、蚂蚁金服、微博、京东等等。比如微博,自创建开始就使用Motan自研的rpc框架,现在主要服务都使用Motan开发,不可能要求成千上万个服务都重写使用新的rpc框架。同时大厂也要保证自己的技术栈的掌控度。
但是对于创业公司和成长性的公司,主要的目标是技术要配合业务的快速发展,提供规模可扩展的技术栈以适应业务的变化和急速发展。采用的技术栈开发要简单(人少)、性能要强大(钱少要求多)、规模可扩展(业务急速发展)。我觉得rpcx在这些公司中的市场还是很大的,并且公司发展到大厂一样的规模后,rpcx依然还能保持它的灵活性和支撑业务的发展。
3、rpcx要支持多久
没有一个软件是永远能活下去的,但是rpcx会一直坚持下去,直到有一天一个能超越rpcx的框架出现,得到大家的认可,大家不再关注rpcx了,那个时候才是rpcx停下的时候。目前我还没看到这种机会出现。
引用IntellJ的一句话作为结束语:
Try it. Test it. If you feel it’s better, use it!