随着公司规模的扩大以及业务量与用户量的激增,为了满足系统高可用、高并发的要求,单体应用逐步演化为服务 / 微服务的架构模式。此时,我们急需一种高效的应用程序之间的通讯手段来满足这种需求,因此 RPC 得以大显身手。
RPC 即远程过程调用协议(Remote Procedure Call Protocol),可以让我们像调用本地对象一样发起远程调用。RPC 凭借其强大的治理功能,成为解决分布式系统通信问题的一大利器。
然而,怎样选择一个合适的 RPC 框架依然是个令人困扰的问题。虽然社区里已经有很多开源的 RPC 项目,它们简单易用,但从框架特性、性能、成熟度、技术支持、社区活跃度等等方面综合考虑,适合自己的却很少。而开发一个完整的 RPC 框架,对于大部分中小团队来说,人力成本和时间成本都是无法接受的。
到底该如何选择一套适合自己的 RPC 框架?自研 RPC 框架的过程中遇到困难该如何破解?从事 RPC 框架的开发怎样才能少走弯路?针对以上问题,我们邀请了转转架构部服务治理负责人王建新老师,来和大家一起谈谈转转 RPC 框架的进化史,以及 RPC 开发和个人成长的经验,希望能给大家在工作和个人成长上带来帮助。
同时王建新老师还在 QCon+ 案例研习社【分布式锁业务实践】专题中为大家带来了 [转转分布式锁原理及最佳实践] 的分享,欢迎收看!
以下是对王建新老师的专访:
王建新:我目前在转转主要负责服务治理相关的工作,主要以 RPC 框架为核心,还有其周边的一些配套系统,包括注册中心、配置中心、分布式调用跟踪系统等。同时我也负责其他一些中间件的开发工作,例如 Codis 分布式锁、线程池监控、数据库连接池监控等。
王建新:仅从通信的角度来讲,直接使用 HttpClient 和 RPC 框架是没有什么区别的,而且有的 RPC 框架底层还支持使用 HTTP 协议进行通信,比如 Spring Cloud Feign 和 Dubbo。RPC 框架的侧重点在于,它不仅仅是为了通信,还是为了更简单、更易用地通信。RPC 框架通过动态代理技术,实现了调用远程方法和调用本地方法同样的体验,不需要手动构造请求体,也不需要考虑参数和返回值的序列化,这一切都被 RPC 框架进行了很好地封装。
许多 RPC 框架都设计了私有的通信协议,和 HTTP 协议相比更加紧凑,传输数据量更小,请求耗时更短。在某些复杂链路中可能会有几千次的 RPC 请求,高性能的 RPC 框架对效率的提升是很可观的。而且私有的 RPC 协议往往支持连接的多路复用,减少连接数量,节省系统资源。
RPC 框架内部往往也集成了更多的高级功能,比如 Router、Filter、LoadBalance 等,并且允许用户自由扩展,以实现更加高级的功能。更完善一些的系统还包括监控、分布式调用跟踪、统一服务治理平台等。可以说 RPC 框架并不是仅仅为了满足通信的需求,更是可以提供一个体系支撑。
王建新:我认为中间件的选型是开发还是自研,其实并没有什么银弹,一切都要从公司的实际情况出发。转转的 RPC 框架继承自 58 的 RPC 框架,转转是从 58 独立出来的,虽然在业务上已经没有太多交集,但是在技术上仍然藕断丝连。许多转转的服务仍然依赖 58 的服务,这就要求转转的 RPC 框架必须保持和 58 的 RPC 框架在协议上的一致性。而 RPC 框架的 API 更是和业务代码高度融合,因此我们无法抛弃现有的 RPC 框架而选择开源实现。
另外从技术角度来看,转转架构部也有实力进行 RPC 框架的自研。团队自研中间件不仅更能贴合自身的实际应用场景进行高度定制化的开发,在进行问题排查时也更能得心应手,毕竟每一行代码都很熟悉。而开源 RPC 框架为了满足各种各样不同的需求,就会产生一定的弊端,比如在功能上有些过于丰富,代码量巨大,引入公司内部使用时遇到问题更不容易排查等等。
王建新:RPC 框架重构面临的最大困难就是兼容问题。我们要保证 RPC 框架的新增功能尽量不破坏现有的 API,如果确实无法避免不兼容的情况出现,也要尽量减少对业务的影响,减少 RD 的修改成本。
对于已知的不兼容情况,在发版之前,我们会对全公司的代码仓库进行扫描,提前通知服务负责人,并给出解决方案。对于无法提前预料的不兼容情况,我们先对服务进行等级划分,从重要到不重要分为 A、B、C、D、E、F 六个等级。每个等级的 POM 文件使用不同版本的 parent,在 parent 中规定了 RPC 框架的版本号。工程效率部门在进行项目编译时,禁止直接在项目中指定 RPC 框架的版本号。
这种技术方案可以实现 RPC 框架的无感知升级,每次进行 RPC 框架发版都从不重要的服务开始进行灰度发布,逐渐过渡到重要的服务。如果有不兼容的情况或者出现 bug,也能在低等级的服务上提前发现,减少损失。
RPC 框架作为公司的基础设施,可谓牵一发而动全身。在转转 RPC 框架升级过程中,我们也曾遇到过线上事故。事后我们进行了深入思考、复盘,制定了一系列的流程规范(包括上述的服务等级划分)以及出现事故后的快速响应机制,来避免下一次事故的发生,并能够做到及时止损。
我认为技术手段能解决的问题,往往不是问题,比技术更难的是人的问题。RPC 框架出现不兼容,对 RD 的工作或多或少都会造成影响。如何照顾到 RD 的情绪、如何说服 RD 从过期的 API 迁移到新版 API,这些问题更加考验个人的智慧和能力。沟通的方式和方法往往比技术问题更重要。
王建新:通过个人和团队的努力,我们主要取得以下几个方面的成果:
首先是转转 RPC 框架的稳定性获得了不小的提升。这体现在底层通信 Netty 化、服务端优雅关闭、连接数量优化和服务端线程模型优化、日志规范化,以及心跳保活、权重、预热与动态权重这些方面。同时在过程中也修复了一些隐藏的多线程安全问题。
其次,转转 RPC 框架的易用性和扩展性的提升。我们在协议兼容的基础上,对 RPC 框架的整体架构和 API 进行了重构。整体架构上,我们抽象出核心逻辑,开放扩展接口,增强了健壮性、扩展性,使架构层次更加分明;API 上,从面向过程重构到面向对象,增强了 RPC 框架的易用性。
再就是实现了转转全链路标签路由功能。标签路由功能的推出需要架构部、工程效率和运维三部门联合才能完成,开发完成后的推广还需要业务部门的配合,可以说是多部门协作项目的成功典范。转转的全链路标签路由不仅可以在 RPC 调用之间进行路由,同样可以在 MQ 的生产和消费时进行路由。在标签路由项目完成后,转转测试环境的搭建耗时从 1~2 天缩减至 5 分钟。
同样借助于标签路由功能,我们完成了线上压测流量的隔离。在标签路由功能上线之后,链路变得更加复杂了,排查问题的难度也提高了。基于这个问题,我们又开发了分布式全链路跟踪系统,实现了调用链路的可视化,大大降低了排查问题的难度。
最后,我们对转转注册中心也进行了重构。重构后的转转注册中心逻辑更简单,链路更短,健壮性更强,能满足转转在未来相当长一段时间内的业务需求。
这里为大家简单介绍一下转转的标签路由功能:
在没有标签路由功能之前,搭建一个测试环境,我们需要部署从第一个被测的服务到最后一个被测服务链路中的所有服务,哪怕中间所经过的服务并未修改。服务之间的 RPC 调用并没有通过注册中心进行注册与发现,而是通过修改 Host 文件的方式将服务域名指向 127.0.0.1。MQ 则通过添加 topic 前缀的方式与稳定环境的 topic 进行区分。
如下图所示的动态环境,需要修改 Host 文件,将 A.zhuaninc.com,B.zhuaninc.com,C.zhuaninc.com,D.zhuaninc.com 指向 127.0.0.1。此种部署方式搭建一个测试环境需要 1~2 天,而且需要一个超大内存(100GB)以上的虚拟机。
第一个版本的标签路由以 IP 为标签,服务间的 RPC 调用使用注册中心,而 MQ 客户端则自动检测环境为 group 添加前缀,MQ 的路由发生在 Broker 内部,由 Broker 判断动态环境 group 的存在决定消息的路由。这种方式只需部署调用链路上的被测服务,没有修改的服务则使用稳定环境。
优势在于用户无任何感知,标签的获取是自动化的,无需手动为流量打标。缺点在于动态环境的虚拟机无法进行内存扩容,在项目开始时无法预测要修改的服务数量,随着被测服务的增加,需要重新申请虚拟机。此种部署方式搭建一个测试环境需要 30 分钟~1 小时。
第二个版本的标签路由不再使用 IP 作为标签,而是需要在发起请求时通过 HTTP Header 手动传入标签,而 RPC 服务注册时则会将环境的标签注册到注册中心,MQ 客户端则自动检测环境标签, 为 group 添加环境标签作为前缀。动态环境则从虚拟机升级到 Docker,不再有内存限制,一个环境下可以部署任意数量的服务,随时可以从环境内移除服务或者向环境内增加服务。
至此一个测试环境的搭建时间缩短至 5 分钟左右。与第一个版本以 IP 为标签的路由相比,虽然多了手动传入标签的工作量,但带来的收益是巨大的。
标签路由中标签的跨线程 / 线程池传递使用了阿里巴巴开源的 TransmittableThreadLocal,只需要在 JVM 参数中添加 Java Agent 参数即可实现无感知的跨线程 / 跨线程池传递。
王建新:从事 RPC 框架的开发,掌握一些高级技术是必不可少的。例如动态代理、反射、多线程、线程安全、TCP/IP 协议等,这些都是必须要掌握的。除此之外,对设计模式掌握也十分重要。设计模式掌握程度的高低决定了框架架构的合理性、可扩展性以及 API 易用性的优劣。不合理的架构设计会严重影响未来的升级迭代,而不易用的 API 也会带来糟糕的用户体验。
另外,阅读开源代码是个收益率非常高的自我提升路径,站在巨人的肩膀上更容易取得成果。大量阅读开源代码,学习和吸收开源中间件的优秀设计思路,可以让我们少走许多弯路,也更容易设计出架构合理的 RPC 框架。
多“造轮子”也是提升能力的重要方法。模仿成熟的开源中间件自己造个 Hello World 级别的中间件,实现其中的核心功能。在“造轮子”的过程中,我们可以领悟到其中某些只可意会不可言传的“套路”。造得轮子多了,就可以尝试开发生产级别的中间件了。
王建新:在服务治理方向上,当下最新的热点就是 Service Mesh。Service Mesh 通过代理模式将服务发现、路由、负载均衡、监控等功能下沉到 Side Car 代理中。业务服务依赖的 lib 库只有薄薄的一层功能。Service Mesh 技术使得框架的升级在大部分情况下对服务透明,只需要统一升级 Side Car 即可。
但是转转并没有紧跟潮流引入 Service Mesh,因为 Servie Mesh 有它的缺点。通过 Side Car 代理之后,调用链路更长了,会造成性能的损失。相对于应用来说,Side Car 是个黑盒,系统复杂度提高了,出现问题时排查变得更加困难,这对 Service Mesh 的运维和管理是一个相当大的挑战。
是否要紧跟潮流技术,需要从公司的实际情况出发。就转转目前的情况,无论从人力资源配置上还是服务规模上来说都还不适合引入 Service Mesh。
王建新:我的自学之路走得很曲折。刚开始我用一周左右的时间学完了 C 语言,学起来感觉并不难,但接下来的路程严重打击了我的自信。我学习了很多计算机的基础课程,像数据结构、操作系统、计算机网络、C++、汇编语言、Intel 汇编、AT&T 汇编等,但是仍然写不出什么有用的程序,沮丧到了极点,感觉自己到了要放弃的边缘。
有朋友建议我学点实用的技术,比如 Java web。我从网上下载的 Java 基础教程开始学习,又下载了培训班的免费视频教程,终于我在浏览器里看到了“Hello, world”,还填写了表单提交到后端应用。到这里我终于感觉自己好像能写点“有用的”代码了。
但是找工作的历程还是很艰难,所有的招聘都要求 2~3 年工作经验,由于没有相关经验,我的简历初筛可能都过不了。听说 Java 培训班的老师都会教怎么修饰简历,在要不要修饰简历这件事上,我内心十分纠结和忐忑,最终还是没有修饰我的简历。简陋到不足一页纸的简历,实在没有亮点,根本过不了 HR 的法眼。投递了几百份简历都最终石沉大海,偶尔的一两次面试机会也都以失败告终。不过最后还好有贵人相助,我拿到了第一份写代码的工作。
工作之后的经历相对要顺利得多了。既然起点低,那我就付出更多的时间和努力,平时比别人早来一两个小时,周末也经常独自一人在公司学习。在两段小公司工作的经历之后,我来到了 58,一年之后又来到了转转做中间件,在转转拿了一次季度优秀员工和年度优秀员工。
关于工作和学习,我有几点心得分享给大家:
兴趣是最好的老师:自学转行最需要的就是内驱力,我的内驱力来源于兴趣,写代码并不仅仅是为了挣钱。写了这么多年代码,我依然兴趣不减,写出优雅的代码是一种享受,学到新的知识令我兴奋。
书读百遍,其义自见:有很多书,有很多源码,我都读过多遍。一开始读不懂、读不完,没关系,放那儿,过一段时间回头再来一次,如此往复终有一天你会茅塞顿开。但是这并不代表每次遇到困难不用思考直接放弃,而是在充分思考想到脑袋都要炸了依然没有理解的情况下,先放下,下次再来。大家知道冲击钻和普通电钻的区别吗?仔细琢磨一下。
基础很重要:我仍然不后悔当初没有直接学 Java web 开发,而先去学了很多“无聊”的计算机基础知识,至今我仍然会去翻那些书。
目标要明确:我们要充分了解自己,知道自己喜欢什么,知道自己擅长什么,明确学习目标,知道该往哪里走。跳槽目标也要明确,不要为了跳槽而跳槽,也不要仅仅为了工资而跳槽。当然如果现在就差钱了,那就为了工资而跳槽。
王建新
转转 架构部 服务治理负责人
资深后端开发,主要从事服务治理、RPC 框架、网关、分布式调用跟踪、监控、分布式锁等系统开发。从业 6 年,从做业务成长起来,到做架构走向成熟,实战经验丰富。能很好地理解业务开发痛点,开发好用的中间件解决开发难题。
在 4 月 24-25 日,ArchSummit 全球架构师峰会即将落地上海,数字化转型是大趋势,不管是金融转型,还是汽车产业数字化转型,制造业数字化转型,一定会涉及到企业的产品形态,这里面包括市场定位和可行性的诸多因素,还有 ROI 评估模型。
除了在业务上的转型,在技术上也需要底层技术支持,例如微服务架构实践,数据库管理,前端开发,质量提升等等,共同保证项目落地。基于这些需求,ArchSummit 架构师峰会上邀请了国内较有经验的专家来分享各自的案例,帮助大家少踩坑,多复用,快速解决工作中的问题,了解更多请点击阅读原文或扫描下方二维码即可参与。
点个在看少个 bug 👇