You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

58 lines
8.3 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 24 | 如何在线上环境里兼容多种RPC协议
你好我是何小锋。上一讲我们学习了如何在没有接口的情况下完成RPC调用其关键在于你要理解接口定义在RPC里面的作用。除了我们前面说的动态代理生成的过程中需要用到接口定义剩余的其它过程中接口的定义只是被当作元数据来使用而动态代理在RPC中并不是一个必须的环节所以在没有接口定义的情况下我们同样也是可以完成RPC调用的。
回顾完上一讲的重点咱们就言归正传切入今天的主题一起看看如何在线上环境里兼容多种RPC协议。
看到这个问题后可能你的第一反应就是在真实环境中为什么会存在多个协议呢我们说过RPC是能够帮助我们屏蔽网络编程细节实现调用远程方法就跟调用本地一样的体验。大白话说就是RPC是能够帮助我们在开发过程中完成应用之间的通信而又不需要我们关心具体通信细节的工具。
## 为什么要支持多协议?
既然应用之间的通信都是通过RPC来完成的而能够完成RPC通信的工具有很多比如像Web Service、Hessian、gRPC等都可以用来充当RPC使用。这些不同的RPC框架都是随着互联网技术的发展而慢慢涌现出来的而这些RPC框架可能在不同时期会被我们引入到不同的项目中解决当时应用之间的通信问题这样就导致我们线上的生成环境中存在各种各样的RPC框架。
很显然这种混乱使用RPC框架的方式肯定不利于公司技术栈的管理最明显的一个特点就是我们维护RPC框架的成本越来越高因为每种RPC框架都需要有专人去负责升级维护。
为了解决早期遗留的一些技术负债我们通常会去选择更高级的、更好用的工具来解决治理RPC框架混乱的问题也是一样。为了解决同时维护多个RPC框架的困难我们肯定希望能够用统一用一种RPC框架来替代线上所有的RPC框架这样不仅能降低我们的维护成本而且还可以让我们在一种RPC上面去精进。
**既然目标明确后,我们该如何实施呢?**
可能你会说这很简单啊我们只要把所有的应用都改造成新RPC的使用方式然后同时上线所有改造后的应用就可以了。如果在团队比较小的情况下这种断崖式的更新可能确实是最快的方法但如果是在团队比较大的情况下要想做到同时上线所有改造后的应用暂且不讨论这种方式是否存在风险光从多个团队同一时间上线所有应用来看这也几乎是一件不可能做到的事儿。
那对于多人团队来说有什么办法可以让其把多个RPC框架统一到一个工具上呢我们先看下多人团队在升级过程中所要面临的困难人数多就意味着要维护的应用会比较多应用多了之后线上应用之间的调用关系就会相对比较复杂。那这时候如果单纯地把任意一个应用目前使用的RPC框架换成新的RPC框架的话就需要让所有调用这个应用的调用方去改成新的调用方式。
通过这种自下而上的滚动升级方式最终是可以让所有的应用都切换到统一的RPC框架上但是这种升级方式存在一定的局限性首先要求我们能够清楚地梳理出各个应用之间的调用关系只有这样我们才能按部就班地把所有应用都升级到新的RPC框架上其次要求应用之间的关系不能存在互相调用的情况最好的情况就是应用之间的调用关系像一颗树有一定的层次关系。但实际上我们应用的调用关系可能已经变成了网状结构这时候想再按照这种方式去推进升级的话就可能寸步难行了。
为了解决上面升级过程中遇到的问题你可能还会想到另外一个方案那就是在应用升级的过程中先不移除原有的RPC框架但同时接入新的RPC框架让两种RPC同时提供服务然后等所有的应用都接入完新的RPC以后再让所有的应用逐步接入到新的RPC上。这样既解决了上面存在的问题同时也可以让所有的应用都能无序地升级到统一的RPC框架上。
在保持原有RPC使用方式不变的情况下同时引入新的RPC框架的思路是可以让所有的应用最终都能升级到我们想要升级的RPC上但对于开发人员来说这样切换成本还是有点儿高整个过程最少需要两次上线才能彻底地把应用里面的旧RPC都切换成新RPC。
那有没有更好的方式可以让应用上线一次就可以完成新老RPC的切换呢关键就在于要让新的RPC能同时支持多种RPC调用当一个调用方切换到新的RPC之后调用方和服务提供方之间就可以用新的协议完成调用当调用方还是用老的RPC进行调用的话调用方和服务提供方之间就继续沿用老的协议完成调用。对于服务提供方来说所要处理的请求关系如下图所示
![](https://static001.geekbang.org/resource/image/c6/87/c6e87eea6d8f312e949af71b3e1eea87.jpg "调用关系")
## 怎么优雅处理多协议?
要让新的RPC同时支持多种RPC调用关键就在于要让新的RPC能够原地支持多种协议的请求。怎么才能做到在[\[第 02 讲\]](https://time.geekbang.org/column/article/199651) 我们说过协议的作用就是用于分割二进制数据流。每种协议约定的数据包格式是不一样的而且每种协议开头都有一个协议编码我们一般叫做magic number。
当RPC收到了数据包后我们可以先解析出magic number来。获取到magic number后我们就很容易地找到对应协议的数据格式然后用对应协议的数据格式去解析收到的二进制数据包。
协议解析过程就是把一连串的二进制数据变成一个RPC内部对象但这个对象一般是跟协议相关的所以为了能让RPC内部处理起来更加方便我们一般都会把这个协议相关的对象转成一个跟协议无关的RPC对象。这是因为在RPC流程中当服务提供方收到反序列化后的请求的时候我们需要根据当前请求的参数找到对应接口的实现类去完成真正的方法调用。如果这个请求参数是跟协议相关的话那后续RPC的整个处理逻辑就会变得很复杂。
当完成了真正的方法调用以后RPC返回的也是一个跟协议无关的通用对象所以在真正往调用方写回数据的时候我们同样需要完成一个对象转换的逻辑只不过这时候是把通用对象转成协议相关的对象。
在收发数据包的时候我们通过两次转换实现RPC内部的处理逻辑跟协议无关同时保证调用方收到的数据格式跟调用请求过来的数据格式是一样的。整个流程如下图所示
![](https://static001.geekbang.org/resource/image/43/37/43451aea86fef673c3928230191fac37.jpg "多协议处理流程")
## 总结
在我们日常开发的过程中最难的环节不是从0到1完成一个新应用的开发而是把一个老应用通过架构升级完成从70分到80分的跳跃。因为在老应用升级的过程中我们不仅需要考虑既有的功能逻辑也需要考虑切换到新架构上的成本这就要求我们在设计新架构的时候要考虑如何让老应用能够平滑地升级就像在RPC里面支持多协议一样。
在RPC里面支持多协议不仅能让我们更从容地推进应用RPC的升级还能为未来在RPC里面扩展新协议奠定一个良好的基础。所以我们平时在设计应用架构的时候不仅要考虑应用自身功能的完整性还需要考虑应用的可运维性以及是否能平滑升级等一些软性能力。
## 课后思考
在RPC里面支持多协议的时候有一个关键点就是能够识别出不同的协议并且根据不同的magic number找到不同协议的解析逻辑。如果线上协议存在很多种的话就需要我们事先在RPC里面内置各种协议但通过枚举的方式可能会遗漏不知道针对这种问题你有什么好的办法吗
欢迎留言和我分享你的答案,也欢迎你把文章分享给你的朋友,邀请他加入学习。我们下节课再见!