本文来自OPPO互联网基础技术团队,转载请注名作者。同时欢迎关注我们的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及活动。

1. 背景

Dubbo是一款高性能、轻量级的开源Java RPC框架,诞生于2012年,2015年停止研发,后来重启并发布了2.7及连续多个版本。Dubbo自开源以来,许多大公司都以此为微服务架构基石,甚至在官方停止维护的几年中,热度依然不减。

但最近几年云原生技术开始成为主流,与Dubbo框架的核心设计理念有不相容之处,再加上公司安全治理的需求,OPPO互联网技术团队开发了面向云原生、 Mesh友好的ESA RPC框架。

2.Dubbo协议解析

协议是两个网络实体进行通信的基础,数据在网络上从一个实体传输到另一个实体,以字节流的形式传递到对端。Dubbo协议由服务提供者与消费者双端约定,需要确定的是一次有意义的传输内容在读到何时结束,因为一个一个byte传输过来,需要有一个结束。而且数据在网络上的传输,存在粘包和半包的情况,能够应对这个问题的办法就是协议能够准确的识别,当粘包发生时不会多读,当半包发生时会继续读取。

2.1 Dubbo Header内容

Dubbo header的长度总共16字节,128位,如下图所示:

  • Magic(16 bits) : 协议魔数,标识Dubbo 数据包。
  • Req/Res(1 bit) : 标识请求或者相应。请求:1,相应:0。
  • Two Way(1 bit) : 仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。
  • Event(1 bit) : 标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。
  • SerializationId(5 bits) : 序列化id。
  • Status(8 bits) : 仅在 Req/Res 为0(响应)时有用,用于标识响应的状态。
  • RequstId(64 bits) : 标识唯一请求。类型为long。
  • Data length(32 bits) : 序列化后的内容长度(变长部分,即不包含header),按字节计数。通过payload参数指定,默认为8M。

2.2 Dubbo body内容

Dubbo 数据包的body 部分内容,分为请求包与响应包。

如果是请求包,则包含的部分有:

  • dubbo协议版本号(2.0.2);
  • 接口名;
  • 接口版本号;
  • 方法名;
  • 方法参数类型;
  • 方法参数;
  • 附件(Attachment):
    • 接口分组(group);
    • 接口版本号(version);
    • 接口名;
    • 自定义附件参数;

如果是响应包,则包含的内容有:

  • 返回值类型(byte):

    • 返回空值(2);
    • 正常返回值(1);
    • 异常(0);
  • 返回值;

通过对dubbo协议的解析,我们可以知道,dubbo协议是一个Header定长的变长协议。这也在我们ESA RPC实践过程中提供了一些思路。

2.3 Dubbo协议优缺点

2.3.1 优点

Dubbo协议的设计非常紧凑、简单,尽可能的减少传输包大小,能用一个bit表示的字段,不会用一个byte。

2.3.2 不足

  • 请求body中某些字段重复传递(如接口名,接口版本号),即body内容与附件attachment 中存在重复字段,增大传输数据包大小;
  • 对于ServiceMesh 场景很不友好。在ServiceMesh 场景中,会将原sdk中的大部分功能迁移至SideCar 中实现,这里以服务发现为例。Dubbo 中的服务发现,是通过接口名
    (interfaceName)、接口分组(group)、接口版本号(version)三者定位一个唯一服务,也是服务发现的关键要素,但是我们从dubbo body内容可知,必须要将完整的数据包全部解析(attachment位于body末),才能获取到这三个要素,这是完全没必要的。
  • 没有预留字段,扩展性不足。

3. Dubbo的现状

Dubbo自开源以来,在业内造成了巨大的影响,许多公司甚至大厂都以此为微服务架构基石,甚至在Dubbo官方停止维护的几年中,热度依然不减,足以证明其本身的优秀。

在这过程中,Dubbo协议的内容一直没有太大变化,主要是为了兼容性考虑,但其他内容,随着Dubbo的发展变化却是很大。这里我们主要聊一聊dubbo从2.7.0版本以后的情况。

3.1 Dubbo 2.7.x版本总览

这是dubbo自2.7.0版本以来,各个版本的简要功能说明,以及升级建议。可以看到dubbo官方推荐生产使用的只有2.7.3 和2.7.4.1两个版本。但这两个推荐版本,也有不能满足需求的地方。

由于dubbo在2.7.3 和2.7.4.1 这两个版本中改动巨大,使得这两个版本无法向下兼容,这让基于其他版本做的一些dubbo扩展几乎无法使用。升级dubbo的同时,还需要将以前的扩展全部检查修改一遍,这带来很大工作量。而且除了我们自身团队的一些公共扩展外,全公司其他业务团队很可能还有自己的一些扩展,这无疑增大了我们升级dubbo的成本。

4. ESA RPC最佳实践

最近几年云原生技术开始成为主流,与Dubbo框架的核心设计理念也有不相容之处,再加上公司安全治理的需求,我们需要一款面向云原生、 Mesh友好的RPC框架。

在这个背景下,OPPO互联网技术团队从2019年下半年开始动手设计开发ESA RPC,到2020年一季度,ESA RPC 第一版成功发布。下面我们简单介绍下ESA RPC的一些主要功能。

4.1 实例级服务注册与发现

ESA RPC通过深度整合发布平台,实现实例级服务注册与发现,如图所示:

应用发布时,相应的发布平台会将实例信息注册到OPPO自研的注册中心ESA Registry(应用本身则不再进行注册),注册信息包含应用名、ip、端口、实例编号等等,消费者启动时只需通过应用编号订阅相关提供者即可。

既然服务注册部分是由发布平台完成,开发者在发布应用时,就需要填写相关信息,即相关的暴露协议以及对应的端口,这样发布平台才可以正确注册提供者信息。

4.2 客户端线程模型优化

ESA RPC全面拥抱java8的CompletableFuture ,我们将同步和异步的请求统一处理,认为同步是特殊的异步。而Dubbo,由于历史原因,最初dubbo使用的jdk版本还是1.7,所以在客户端的线程模型中,为了不阻塞IO线程,dubbo增加了一个Cached线程池,所有的IO消息统一都通知到这个Cached线程池中,然后再切换回相应的业务线程,这样可能会造成当请求并发较高时,客户端线程暴涨问题,进而导致客户端性能低下。

所以我们在ESA RPC客户端优化了线程模型,将原有的dubbo客户端cached线程池取消,改为如下图模型:

具体做法:

  • 当前业务线程发出远程调用请求后,生成CompletableFuture 对象,并传递至IO线程,等待返
    回;
  • IO线程收到返回内容后,找到与之对应的CompletableFuture 对象,直接赋予其返回内容;
  • 业务线程通过自己生成的CompletableFuture 对象获取返回值;

4.3 智能Failover

对于一些高并发的服务,可能会因传统Failover 中的重试而导致服务雪崩。ESA RPC对此进行优化,采用基于请求失败率的Failover ,即当请求失败率低于相应阈值时,执行正常的failover重试策略,而当失败率超过阈值时,则停止进行重试,直到失败率低于阈值再恢复重试功能。

ESA RPC采用RingBuffer 的数据结构记录请求状态,成功为0,失败为1。用户可通过配置的方式指定该RingBuffer 的长度,以及请求失败率阈值。

4.4 ServiceKeeper

ESA ServiceKeeper (以下简称ServiceKeeper ),属于OPPO自研的基础框架技术栈ESA Stack系列的一员。ServiceKeeper 是一款轻量级的服务治理框架,通过拦截并代理原始方法的方式织入限流、并发数限制、熔断、降级等功能。

ServiceKeeper 支持方法和参数级的服务治理以及动态动态更新配置等功能,包括:

  • 方法隔离
  • 方法限流
  • 方法熔断
  • 方法降级
  • 参数级隔离、限流、熔断
  • 方法重试
  • 接口分组
  • 动态更新配置,实时生效

ESA RPC中默认使用ServiceKeeper 来实现相关服务治理内容,使用起来也相对简单。

Step 1

application.properties 文件中开启ServiceKeeper 功能。

# 开启服务端
esa.rpc.provider.parameter.enable-service-keeper=true# 开启客户端
esa.rpc.consumer.parameter.enable-service-keeper=true

Step 2

新增service-keeper.properties 配置文件,并按照如下规则进行配置:

# 接口级配置规则:{interfaceName}/{version}/{group}.{serviceKeeper params},示例:
com.oppo.dubbo.demo.DemoService/0.0.1/group1.maxConcurrentLimit=20
com.oppo.dubbo.demo.DemoService/0.0.1/group1.failureRateThreshold=55.5
com.oppo.dubbo.demo.DemoService/0.0.1/group1.forcedOpen=55.5
...#方法级动态配置规则:{interfaceName}/{version}/{group}.{methodName}.{serviceKeeper params},示例:
com.oppo.dubbo.demo.DemoService/0.0.1/group1.sayHello.maxConcurrentLimit=20
com.oppo.dubbo.demo.DemoService/0.0.1/group1.sayHello.maxConcurrentLimit=20
com.oppo.dubbo.demo.DemoService/0.0.1/group1.sayHello.failureRateThreshold=55.5
com.oppo.dubbo.demo.DemoService/0.0.1/group1.sayHello.forcedOpen=false
com.oppo.dubbo.demo.DemoService/0.0.1/group1.sayHello.limitForPeriod=600
...#参数级动态配置规则:{interfaceName}/{version}/{group}.{methodName}.参数别名.配置名称=配置值列表,示例:
com.oppo.dubbo.demo.DemoService/0.0.1/group1.sayHello.arg0.limitForPeriod={LiSi:20,ZhangSan:50}
...

4.5 连接管理

ESA RPC中,一个消费者与一个提供者,默认只会创建一个连接,但是允许用户通过配置创建多个,配置项为connections (与dubbo保持一致)。ESA RPC的连接池通过公司内部一个全异步对象池管理库commons pool来达到对连接的管理,其中连接的创建、销毁等操作均为异步执行,避免阻塞线程,提升框架整体性能。

需要注意的是,这里的建连过程,有一个并发问题要解决: 当客户端在高并发的调用建连方法时,如何保证建立的连接刚好是所设定的个数呢?为了配合 Netty 的无锁理念,我们也采用一个无锁化的建连过程来实现,利用 ConcurrentHashMap 的putIfAbsent 方法:

AcquireTask acquireTask = this.pool.get(idx);
if (acquireTask == null) {acquireTask = new AcquireTask();AcquireTask tmpTask = this.pool.putIfAbsent(idx, acquireTask);if (tmpTask == null) {acquireTask.create(); //执行真正的建连操作}
}

4.6 gRPC协议支持

由于ESA RPC默认使用ESA Regsitry 作为注册中心,由上述实例注册部分可知,服务注册通过发布平
台来完成,所以ESA RPC对于gRPC协议的支持具有天然的优势,即服务的提供者可以不接入任何sdk,甚至可以是其他非java语言,只需要通过公司发布平台发布应用后,就可以注册至注册中心,消费者也就可以进行订阅消费。

这里我们以消费端为例,来介绍ESA RPC客户端如何请求gRPC服务端。

proto文件定义:

syntax = "proto3";option java_multiple_files = false;
option java_outer_classname = "HelloWorld";
option objc_class_prefix = "HLW";package esa.rpc.grpc.test.service;// The greeting service definition.
service GreeterService {// Sends a greetingrpc sayHello (HelloRequest) returns (HelloReply) {}
}service DemoService {// Sends a greetingrpc sayHello (HelloRequest) returns (HelloReply) {}
}// The request message containing the user's name.
message HelloRequest {string name = 1;
}// The response message containing the greetings
message HelloReply {string message = 1;
}

然后maven中添加proto代码生成插件:

<build><extensions><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>1.5.0.Final</version></extension></extensions><plugins><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.5.0</version><configuration><protocArtifact>com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier}
</protocArtifact><pluginId>grpc-java</pluginId><pluginArtifact>esa.rpc:protoc-gen-grpc-java:1.0.0-
SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact></configuration><executions><execution><goals><goal>compile</goal><goal>compile-custom</goal></goals></execution></executions></plugin></plugins></build>

如上proto定义文件,通过protobuf:compile和protobuf:compile-custom则会生成如下代码:

可以看到,自动生成的代码中我们额外生成了相应的java接口。

在dubbo客户端我们就可以直接使用这个接口进行远程调用,使用方式:

@Reference(...,protocol="grpc")
private DemoService demoService;

4.7 ESA RPC性能

这里仅举一例,展示ESA RPC性能。

5. ESA RPC未来规划

5.1 ESA RPC如何进行平滑迁移?

由于历史原因,现公司内部大量使用的是Dubbo作为RPC框架,以及zookeeper注册中心,如何能够保证业务的平滑迁移,一直是我们在思考的问题。这个问题想要解答,主要分为以下两点。

5.1.1 代码层面

在代码层面,ESA RPC考虑到这个历史原因,尽可能的兼容dubbo,尽可能降低迁移成本。但ESA RPC毕竟作为一款新的RPC框架,想要零成本零改动迁移是不可能的,但在没有dubbo扩展的情况下,改动很小。

5.1.2 整体架构

这一点我们举例说明,当业务方迁移某一应用至ESA RPC框架时,该应用中消费ABCD四个接口,但这些接口的服务提供者应用并未升级至ESA RPC,接口元数据信息均保存至zookeeper注册中心当中,而ESA RPC推荐使用的ESA Registry注册中心中没有这些提供者信息,这就导致了消费者无法消费这些老的提供者信息。

针对这一问题,后续我们ESA Stack系列会提供相应的数据同步工具,将原zookeeper注册中心中的服务元数据信息同步到我们ESA Registry中,而zookeeper中的这些信息暂时不删除(以便老的接口消费者能够消费),等待均升级完成后,即可停用zookeeper注册中心。

5.2 自研RPC协议

在上面Dubbo协议解析过程中,我们分析了Dubbo协议的优缺点,了解了Dubbo协议的不足。所以后续的版本升级过程中,自研RPC协议是一个不可忽视的内容。自研RPC协议需要充分考虑安全、性能、Mesh支持、可扩展、兼容性等因素,相信通过自研RPC协议可以使我们的ESA RPC更上一层楼。

5.3 其他

  • 多协议暴露
  • 同机房优先路由
  • 类隔离
  • ...

在这篇文章中,我们主要分享了Dubbo协议的分析以及ESA RPC的实践内容,后续OPPO互联网技术团队会继续分享更多ESA RPC的动态。

dubbo协议_Dubbo协议解析与OPPO自研ESA RPC框架实践相关推荐

  1. java dubbo协议_dubbo协议端口

    dubbo框架一共支持9种协议.其中,默认的,也是最常用的,那就是dubbo协议. 既然是协议,那么,就需要有端口. 所以,在使用dubbo协议暴露服务时,需要配置dubbo端口. dubbo协议的默 ...

  2. 服务化实战之 dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型

    原文:http://blog.csdn.net/liubenlong007/article/details/54692241

  3. dubbo协议_Dubbo框架支持多少种协议?各有什么特点?文中一一为你揭晓

    概述 Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议. 除了dubbo协议外, Dubbo框架还支持另外8种服务暴露协议,如rmi协议. hessian协议. ht ...

  4. 生产队上线丨千锋索尔《阿里RPC框架Dubbo源码级讲解》重磅来袭

    命运就算颠沛流离 命运就算曲折离奇 命运就算恐吓着你做人没趣味 别流泪心酸更不应舍弃 愿千锋能一生永远陪伴你 一生之中弯弯曲曲我也要走过 从何时有你有你伴我给我热烈地拍和 像Java语言陪伴真的我 结 ...

  5. RPC 框架 Dubbo 从理解到使用(一)

    技术架构演变 学习 Dubbo 之前我们有必要先来了解一下互联网技术架构的演变过程及通信方式,方便我们搞清楚为什么需要使用基于 RPC 思想的系列框架. 单一应用架构 通俗地讲,"单体应用( ...

  6. 中dubbo所需要的依赖_Dubbo介绍、dubbo架构图、dubbo运行和执行流程、dubbo支持的协议以及支持的注册中心...

    第67次(Dubbo) 学习主题:Dubbo 学习目标: 掌握Dubbo的作用,理解Dubbo的架构图 掌握Dubbo支持的协议以及支持的注册中心 1. Dubbo介绍 1.1 什么是Dubbo? D ...

  7. java dubbo协议_Dubbo同时支持多种协议(以dubbo和rest为例)

    一. 背景知识 有时候微服务需要提供给多个消费者, 而不通过的消费者可能希望依据自身情况使用不同的协议. 另一方面, 有时候如果原本服务以 dubbo 协议提供服务, 但是为了调试或者监控方便, 我们 ...

  8. dubbo优势_Dubbo 迈出云原生重要一步 应用级服务发现解析

    作者 | 刘军(陆龟)  Apache Dubbo PMC 概述 社区版本 Dubbo 从 2.7.5 版本开始,新引入了一种基于实例(应用)粒度的服务发现机制,这是我们为 Dubbo 适配云原生基础 ...

  9. 为什么都说Dubbo不适合传输大文件?Dubbo支持的协议

    背景 之前公司有一个 Dubbo 服务,内部封装了腾讯云的对象存储服务 SDK,是为了统一管理这种三方服务的SDK,其他系统直接调用这个对象存储的 Dubbo 服务.用来避免因平台 SDK 出现不兼容 ...

最新文章

  1. python编写爬虫的步骤-python网络爬虫(二)编写第一个爬虫
  2. 关于ORM中只有XML没有映射实体的分析
  3. sqlyog设置自动补全_sqlyog智能提示如何设置?sqlyog智能提示设置流程介绍
  4. wordpress备份方法比较
  5. 对勾函数_对勾函数?2020福建省中考压轴题分析
  6. linux缓存代码,Linux使用的缓存
  7. Linux下Java环境变量配置
  8. Linq常用List操作总结,ForEach、分页、交并集、去重、SelectMany等
  9. [教程]博客园插入视频教程
  10. python爬虫天气实例scrapy_2017.08.04 Python网络爬虫之Scrapy爬虫实战二 天气预报...
  11. PAT 1064 朋友数
  12. 在ubuntu10.10下安装Google谷歌拼音输入法
  13. django1.2中将ManyToManyField呈现为checkbox
  14. 关于Android Gradle你需要知道这些(2)
  15. C# 使用SkinSharp皮肤库
  16. 遥感图像计算机自动分类原理,遥感原理与应用_第5章_2遥感影像解译-遥感影像计算机自动分类讲义.ppt...
  17. 手游实时阴影方案之Projector Shadow
  18. 计算机硬盘用u盘维修,电脑维修:U盘和移动硬盘及固态硬盘的区别
  19. 高数 | 函数可导和函数连续可导
  20. php 商铺附近定位,php 地图标注百度地图添加商铺

热门文章

  1. java日期处理总结
  2. 基于visual Studio2013解决面试题之0307最后谁剩下
  3. Leetcode--120. 三角形最小路径和
  4. 吕述望 计算机网络专家,特稿: 中科院吕述望教授:互联网名不符实
  5. linux内核等价多路径路由,Linux内核分析 - 网络[四]:路由表
  6. param注解报错_mybatis不加@Parm注解报错的解决方案
  7. python ftp上传_Python FTP传输的简单示例
  8. Python判断变量类型
  9. 图像处理 --- 三、图像变换 3.2 图像的几何变换
  10. mysql为什么选错索引?怎么解决?