为啥需要更换RPC?

很多小伙伴都遇到过需要为分布式系统调用更换RPC的问题,为什么会遇到这种事呢?其实,在系统搭建初期,需求简单,架构简单,最重要的是请求量也少,所以很多系统都采用快速原型开发模式,对rpc的要求不高,随便找一个顺手的或者熟悉的rpc框架套进系统中即可。但是随着业务复杂度增高,系统承载的请求量增高,可能一开始所采用的RPC框架显现出一些致命的问题,比如大扇出问题。我们以Thrift为例。例如随着业务复杂度的增长,我们面临着如下的需求。

如图所示,每一次请求,上游服务都要获取下游A~Z一共26个服务的结果,然后把这26个服务的结果拼装返回给前端服务。有人说,26个服务是不是有些夸张了,我的系统中根本没有遇到过这个情况。这实际一点不夸张,一个业务复杂的系统经过服务拆分,最后拆成一些高内聚低耦合的独立服务,非常容易达到这样一个服务种类数,而且26还远远不是很多。那么遇到这种问题,传统的同步的RPC怎么解决这个问题呢?

以Thrift为例,如果需要访问26个服务,为了保证请求处理速度,必须要并行访问各个下游服务(不能串行请求,因为这将导致 一次请求的响应时间至少为timeA + timeB + ...... + timeZ),那么我们只能通过多线程进行并发。

通过多线程并发请求,我们基本能够达到处理一次请求至多需要 max(timeA, timeB, ......, timeZ),但是实际上要比这个稍多。看样子我们必须弄一个请求线程池,可是这个池子要多大呢?假如现在前端请求速率为 P,那么为了保证每个请求处理时间都尽可能快,我们需要一个大小为 26 * P的线程池。虽然,初看起来可能还可以应付,毕竟请求线程在发送网络请求后,会阻塞在IO,它会放弃CPU,从而使得计算线程获得CPU,不会浪费多少CPU的资源,但是当P太大就不好了。比如P为100或者1000,这个时候线程数过多可能就会造成CPU调度开销增大,因为它会增加CPU的线程切换负担。

所以,我们更换RPC,当且仅当,当前的RPC已经造成了系统负担,对于业务量不大的系统,RPC的更换并没有必要,但是为了技术提升你也可以更换RPC,只不过收益可能不大。

需要什么样的RPC?

考虑到Thrift对于大扇出并不合适,我们可能需要下面这样工作模式的RPC。

这种反应器模型(只是简单举例子)可以减少请求线程数。这种RPC使用系统的Epoll进行后端服务的请求以及数据的接收,这样无论多少请求,只使用一个线程完成,通过Epoll的机制在数据到来或者可发送的情况下通知用户进程,只不过最后需要把接收到的数据返回给计算线程使用。这种模型其实要比Thrift那种那好一些。我自己也在业余时间实现了一个简单的RPC框架:http://www.cnblogs.com/haolujun/p/7527313.html ,比较粗糙但是足够小。
还有有很多开源的RPC框架,fbthrift,GRPC都可以应对大扇出,找到适合你的系统,并且改动量和后期维护成本最低的那个。

如何迁移到新的RPC?

把系统迁移到新的RPC上,除了改动代码外,就是要做到兼容,系统在迁移过程中可能需要在两套RPC框架上运行,并且必须做到平滑迁移。例如,一般的分布式系统可能会长成如下的样子。

服务B1~B4把自己的地址写入到ETCD中,但是由于我们一开始并未考虑到RPC的迁移,所以value对应的是服务的地址,没有服务使用的rpc类型等等。

方案1 添加新key

对于A1~A2,B1~B4,可以先选择一部分进行平滑过渡,例如我们选择A1,B1~B2进行迁移。

上线步骤如下:

  • 下线A1,B1,B2。
  • 更新A1配置,使其从新的key:service_new_rpc中读取后端服务列表。
  • 更新B1,B2配置,使其在新的key:service_new_rpc中注册自己。
  • 启动B1,B2。
  • 启动A1。
  • 对于A2,B3,B4重复如上步骤。

通过这种方式,我们可以平滑的进行服务迁移。但是它的缺点很明显,需要一个新的key,而且后期还需要一点点把服务挪回到旧的key上。

方案2 代码兼容

这个方案必须更改一些解析代码,使其能够兼容新的ETCD中value的格式,如下图。

  • 首先改造A代码,使其能够兼容新地址解析格式。新地址格式在每个地址后加上RPC类型标识:T(Thrift),G(GRPC),新格式和旧格式的兼容很容易,只需在解析的时候找一下分割符,并判断分隔符最后一部分是T是G还是什么都没有,没有就默认为T。
  • 改造A代码,使其能够根据后端服务在ETCD中的RPC类型使用不同的RPC框架调用后端。
  • 改造B1~B4的配置,在ETCD中注册自己的时候把RPC类型顺便加上。
  • 改造B1~B2,使用新RPC作为服务端,并且在注册的时候把RPC类型设置为G。
  • 改造B3~B4,使用新RPC作为服务端,并且在注册的时候把RPC类型设置为G。

通过这个步骤,我们就能做到RPC的平滑迁移。这个方式的缺点也有:需要同时维护两套RPC框架,直到其中一种RPC彻底下线。但是优点也有,没有增加新key。

总结

更换RPC并不像想象中的那样困难,只要理清前后逻辑,一点点的迁移,最终你的服务会全部搞定。最重要的问题是你的系统真的达到了非得换RPC的地步了么?

如何为分布式系统优雅的更换RPC的更多相关文章

  1. 分布式系统间通信之RPC简单Demo(七)

    看似终点,回到起点.第一次接触C#,编写的第一个真正的Demo是基于Socket的简单通信,现在JAVA开始的第一个RPC的Demo也是基于Socket.. 下面通过java原生的序列化,Socket ...

  2. 分布式系统间通信之RPC的基本概念(六)

    RPC(Remote Procedure Call Protocol)远程过程调用协议.一个通俗的描述是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象 ...

  3. 分布式系统中的必备良药 —— RPC

    阅读目录 前言 成熟的解决方案 剖析 性能测试 结语 一.前言 在上一篇分布式系统系列中<分布式系统中的必备良药 —— 服务治理>中阐述了服务治理的一些概念,那么与服务治理配套的必然会涉及 ...

  4. 10本Java架构师必读书籍

    1.大型网站系统与JAVA中间件实践 本书围绕大型网站和支撑大型网站架构的Java中间件的实践展开介绍. 从分布式系统的知识切入,让读者对分布式系统有基本的了解:然后介绍大型网站随着数据量.访问量增长 ...

  5. Java架构师必看的10本书

    1.大型网站系统与JAVA中间件实践 本书围绕大型网站和支撑大型网站架构的Java中间件的实践展开介绍. 从分布式系统的知识切入,让读者对分布式系统有基本的了解:然后介绍大型网站随着数据量.访问量增长 ...

  6. Java开发者入职必备条件

    01.基础技术体系 我认为知识技能体系化是判断技术是否过关的第一步.知识体系化包含两层含义: 1. 能够知道技术知识图谱(高清版图谱扫文末二维码)的内容 比如分布式系统中常用的RPC技术,其背后就涉及 ...

  7. Go 微服务架构Micro相关概念理解

    Micro是一个微服务框架(或者说是工具集):提供了各类组件,解决微服务架构中的不同问题,服务监控.服务发现.熔断机制,负载均衡等等,自己一个个解决这些问题几乎不可能,这时候就需要借助go-micro ...

  8. java大框架

    本文章,列出了一些程序员需要学习的技术和知识点,有些技术和知识点没有写道,欢迎大家进行修改和补充,有些技术公司用到,大家需要先学习,有些技术和知识点过时,大家可以了解.本人笔记连接[[http://2 ...

  9. 353 stars Java项目!Java小白必看!austin介绍 【第一话】

    有好几个群友问我为什么最近更新变慢了.工作忙是一方面,另一方面是我更新文章的动力确实下降了.近大半年一直在更新的<对线面试官>系列,到现在已经40篇了. 说实话,当时我更新该系列有很大一部 ...

随机推荐

  1. 初学深度学习(TensorFlow框架的心得and经验总结)自用环境的总结

    初学者的时间大部分浪费在了环境上了: 建议直接上Linux系统,我推荐国产的深度系统,deepin这几年一直在不断的发展,现在15.4已经很不错了 1,图形化界面很漂亮,内置正版crossover,并 ...

  2. nodejs 使用CAS 实现 单点登录(SSO) 【开源库实现,简单】

    大部分企业使用 java 开发业务系统, 针对java cas的认证 demo 比较多 ,还有PHPCAS ,标准的参考这里: phpCAS 的使用 整理登录流程如下图,图片来自网络 找了不少资料,n ...

  3. OAuth2.0学习(1-10)新浪开放平台微博认证-手机应用授权和refresh_token刷新access_token

    1.当你是使用微博官方移动SDK的移动应用时,授权返回access_token的同时,还会多返回一个refresh_token: JSON 1 2 3 4 5 6 {     "access ...

  4. python/零起点(一、列表)

    python/零起点(一.列表) 列表(list)list()可以强行转换数据类型为列表,列表是可迭代对象 列表是有序的,且列表是可变的数据类型 列表中的元素可以是(字符串.整型.元祖.列表.字典.集 ...

  5. Codeforces Round #436 (Div. 2) D. Make a Permutation!

    http://codeforces.com/contest/864/problem/D 题意: 给出n和n个数(ai <= n),要求改变其中某些数,使得这n个数为1到n的一个排列,首先保证修改 ...

  6. Linux查看文件指定某些行的内容

    查看从第5行开始的100行内容,并把结果重定向到一个新的文件 cat file | tail -n +5 | head -n 100 > newfile.txt 查看第5行到100行的内容,并把 ...

  7. java8接口定义增强

    java1.7之前,接口中只允许有全局常量和抽象方法,而1.8之后允许在接口中扩充default修饰的普通方法和static修饰的静态方法 其目的是在修改接口中方法的时候,子类就不必去一一修改 pac ...

  8. LoggerOne

    LoggerOne 一个高效.简约.灵活高性能的遵循 PSR-3 的 PHP 日志类库实现. 特性 天然的缓存特性(Logger实例属性),延迟批量写入. 安装&使用 Install $ co ...

  9. Vue2.0父子组件之间的双向数据绑定问题解决方案

    对于vue 1.0项目代码,如果把vue换成vue 2.0,那么之后项目代码就完全奔溃不能运行,vue 2.0在父子组件数据绑定的变化(不再支持双向绑定)颠覆了1.0的约定,很遗憾. 解决方案只有两种 ...

  10. [HNOI 2008]GT考试

    Description 题库链接 问你长度为 \(n\) 的可含前导零的数字串中,不含长度为 \(m\) 的子串 \(X\) 有多少个,取模. \(1\leq n\leq 10^9,1\leq m\l ...