一、基础知识
1.Dubbo是什么?他的核心功能是什么?
dubbo 阿里开源的一个SOA服务治理框架,从目前来看把它称作是一个RCP远程调用框架更为贴切。单从RPC框架来说,功能较完善,支持多种传输和序列化方案。所以想必大家已经知道他的核心功能了:就是远程调用。
2.Dubbo常规配置说明
配置分布图:
定位属性:
定位属性是指消费者匹配提供者的全部标识,总共有如下属性
interface: 接口名称
group: 接口分组
version: 接口版本
标签 |
属性 |
描述 |
必填 |
application |
name |
应用名称 |
是 |
registry |
protocol |
注册中心协议 |
是 |
|
address |
注册中心地址 |
是 |
provider |
threadpool |
线程模型 fixed/cache |
否 |
|
threads |
|
否 |
service |
interface |
|
是 |
|
ref |
|
是 |
|
group |
|
否 |
|
version |
|
否 |
|
timeout |
重试次数 |
否 |
|
retries |
重试次数 |
否 |
consumer |
timeout |
|
是 |
|
retries |
|
是 |
reference |
id |
|
否 |
|
interface |
|
否 |
|
version |
|
否 |
|
check |
|
否 |
|
retries |
重试次数 |
否 |
属性覆盖:
二、RPC 底层协议原理与实现
RPC 协议名词解释
在一个典型RPC的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中RPC协议就指明了程序如何进行网络传输和序列化 。
也就是说一个RPC协议的实现就等于一个非透明的RPC调用,如何做到的的呢?
协议基本组成:
1.地址:服务提供者地址
2.端口:协议指定开放的端口
3.运行服务:
1.netty
2.mina
3.RMI 服务
4.servlet 容器(jetty、Tomcat、Jboss)
4.报文编码:协议报文编码 。注①:http 报文编码 。注②:Dubbo 报文编码
5.序列化方式:
1.Hessian2Serialization、
2.DubboSerialization、
3.JavaSerialization
4.JsonSerialization
RPC协议报文编码与实现详解
注②Dubbo 协议报文编码:
(注:相关源码参见 com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec)
协议的编解码过程:
Dubbo 协议编解码实现过程 (源码来源于dubbo2.5.8 )
1、DubboCodec.encodeRequestData() 116L // 编码request
2、DecodeableRpcInvocation.decode() 89L // 解码request
3、DubboCodec.encodeResponseData() 184L // 编码response
4、DecodeableRpcResult.decode() 73L // 解码response
dubbo 支持的RPC协议列表
|
|
|
|
|
|
|
|
名称 |
实现描述 |
连接描述 |
适用场景 |
dubbo |
传输服务: mina, netty(默认), grizzy |
单个长连接 |
1、常规RPC调用 |
rmi |
传输:java rmi 服务 |
多个短连接 |
1、常规RPC调用 |
hessian |
传输服务:servlet容器 |
基于Http 协议传输, |
1、提供者多于消费者 |
http |
传输服务:servlet容器 |
依懒servlet容器配置 |
1、数据包大小混合 |
thrift |
与thrift RPC 实现集成,并在其基础上修改了报文头 |
长连接、NIO异步传输 |
关于RMI不支持防火墙穿透的补充说明:
原因在于RMI 底层实现中会有两个端口,一个是固定的用于服务发现的注册端口,另外会生成一个随机端口用于网络传输。
这个随机端口就不能在防火墙中提前设置开放开。所以存在防火墙穿透问题
dubbo 协议采用 json 进行序列化 (源码参见:com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol)
采用RMI协议 (源码参见:com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol)
采用Http协议 (源码参见:com.alibaba.dubbo.rpc.protocol.http.HttpProtocol.InternalHandler)
采用Heason协议 (源码参见:com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol.HessianHandler)
三、调用模块详解
dubbo调用模块核心功能是发起一个远程方法的调用并顺利拿到返回结果,其体系组成如下:
1.透明代理:通过动态代理技术,屏蔽远程调用细节以提高编程友好性。
2.负载均衡:当有多个提供者是,如何选择哪个进行调用的负载算法。
3.容错机制:当服务调用失败时采取的策略
4.调用方式:支持同步调用、异步调用
5.结果获取:指同步等待结果返回,还是异步通过回调通知获取结果。
负载均衡
Dubbo 目前官方支持以下负载均衡策略:
1.随机(random):按权重设置随机概率。此为默认算法.
2.轮循 (roundrobin):按公约后的权重设置轮循比率。
3.最少活跃调用数(leastactive):相同活跃数的随机,活跃数指调用前后计数差。
4.一致性Hash(consistenthash ):相同的参数总是发到同一台机器
容错
Dubbo 官方目前支持以下容错策略:
1.失败自动切换:调用失败后基于retries=“2” 属性重试其它服务器
2.快速失败:快速失败,只发起一次调用,失败立即报错。
3.勿略失败:失败后勿略,不抛出异常给客户端。
4.失败重试:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作
5.并行调用: 只要一个成功即返回,并行调用指定数量机器,可通过 forks="2" 来设置最大并行数。
6.广播调用:广播调用所有提供者,逐个调用,任意一台报错则报错
异步调用
异步调用是指发起远程调用之后获取结果的方式。
1.同步等待结果返回
2.异步等待结果返回
3.不需要返回结果
Dubbo 中关于异步等待结果返回的实现流程如下图:
异步调用配置:
异步获取结果演示:
编写异步调用代码
编写同步调用代码
分别演示同步调用与异步调用耗时
异步调用结果获取Demo
demoService.sayHello1("han");
Future
泛化提供&引用
泛化提供
是指不通过接口的方式直接将服务暴露出去。通常用于Mock框架或服务降级框架实现。
#TODO 示例演示
public static void doExportGenericService() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("demo-provider");
// 注册中心
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("192.168.0.147:2181");
ProtocolConfig protocol=new ProtocolConfig();
protocol.setPort(-1);
protocol.setName("dubbo");
GenericService demoService = new MyGenericService();
ServiceConfig service = new ServiceConfig();
// 弱类型接口名
service.setInterface("com.tuling.teach.service.DemoService");
// 指向一个通用服务实现
service.setRef(demoService);
service.setApplication(applicationConfig);
service.setRegistry(registryConfig);
service.setProtocol(protocol);
// 暴露及注册服务
service.export();
}
泛化引用
是指不通过常规接口的方式去引用服务,通常用于测试框架。
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("demo-provider");
// 注册中心
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("192.168.0.147:2181");
// 引用远程服务
ReferenceConfig reference = new ReferenceConfig();
// 弱类型接口名
reference.setInterface("com.tuling.teach.service.DemoService");
// 声明为泛化接口
reference.setGeneric(true);
reference.setApplication(applicationConfig);
reference.setRegistry(registryConfig);
// 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用
GenericService genericService = reference.get();
Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
隐示传参
是指通过非常方法参数传递参数,类似于http 调用当中添加cookie值。通常用于分布式追踪框架的实现。使用方式如下 :
//客户端隐示设置值
RpcContext.getContext().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐
//服务端隐示获取值
String index = RpcContext.getContext().getAttachment("index");
令牌验证
通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者
使用:
开发联调:
在项目开发过程当中,一个开发或测试环境的注册中心很有可能会同时承载着多个服务,如果两组服务正在联调,如何保证调用的是目标服务呢?
1、基于临时分组联调
group 分组
在reference 和server 当中采用相同的临时组 ,通过group 进行设置
2、直连提供者:
在reference 中指定提供者的url即可做到直连
五、dubbo 并发链接底层探究
网络传输的实现组成
1.IO模型:
1.BIO 同步阻塞
2.NIO 同步非阻塞
3.AIO 异步非阻塞
2.连接模型:
1.长连接
2.短连接
3.线程分类:
1.IO线程
2.服务端业务线程
3.客户端调度线程
4.客户端结果exchange线程。
5.保活心跳线程
6.重连线程
4.线程池模型:
1.固定数量线程池
2.缓存线程池
3.有限线程池
二、长连接的创建与维护
Dubbo 长连接实现与配置
初始连接:
引用服务|增加提供者==>获取连接===》是否获取共享连接==>创建连接客户端==》开启心跳检测状态检查定时任务===》开启连接状态检测
源码见:com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getClients
心跳发送:
在创建一个连接客户端同时也会创建一个心跳客户端,客户端默认基于60秒发送一次心跳来保持连接的存活,可通过 heartbeat 设置。
源码见:com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient#startHeatbeatTimer
断线重连:
每创建一个客户端连接都会启动一个定时任务每两秒中检测一次当前连接状态,如果断线则自动重连。
源码见:com.alibaba.dubbo.remoting.transport.AbstractClient#initConnectStatusCheckCommand
连接销毁:
基于注册中心通知,服务端断开后销毁
源码见:com.alibaba.dubbo.remoting.transport.AbstractClient#close()
dubbo传输uml类图:
Dubbo 传输协作线程
1.客户端调度线程:用于发起远程方法调用的线程。
2.客户端结果Exchange线程:当远程方法返回response后由该线程填充至指定ResponseFuture,并叫醒等待的调度线程。
3.客户端IO线程:由传输框架实现,用于request 消息流发送、response 消息流读取与解码等操作。
4.服务端IO线程:由传输框架实现,用于request消息流读取与解码 和response编码与发送。
5.业务执行线程:服务端具体执行业务方法的线程
客户端线程协作流程:
1.调度线程
1.调用远程方法
2.对request 进行协议编码
3.发送request 消息至IO线程
4.等待结果的获取
2.IO线程
1.读取response流
2.response 解码
3.提交Exchange 任务
3.Exchange线程
1.填写response值 至 ResponseFuture
2.唤醒调度线程,通知其获取结果
服务端线程协作:
1.IO线程:
1.request 流读取
2.request 解码
3.提交业务处理任务
2.业务线程:
1.业务方法执行
2.response 解码
3.回写结果至channel
线程池
1.fixed:固定线程池,此线程池启动时即创建固定大小的线程数,不做任何伸缩,
2.cached:缓存线程池,此线程池可伸缩,线程空闲一分钟后回收,新请求重新创建线程
3.Limited:有限线程池,此线程池一直增长,直到上限,增长后不收缩。