Dubbo
2023-12-27 15:04:58 20 举报
AI智能生成
Dubbo
作者其他创作
大纲/内容
RPC
优点
简单、直接、开发方便。
长链接,不必每次通信都要像http一样去3次握手什么的,减少了网络开销
RPC框架一般都有注册中心,有丰富的监控管理
发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作
RPC一般配合netty框架、spring自定义注解来编写轻量级框架,
较新的jdk的IO一般是NIO,即非阻塞IO,在高并发网站中,RPC的优势会很明显
较新的jdk的IO一般是NIO,即非阻塞IO,在高并发网站中,RPC的优势会很明显
组件
客户端(Client)
客户端存根(Client Stub)
存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端
服务端存根(Server Stub)
接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理
服务端(Server)
具体调用过程
- 服务消费者(client客户端)通过调用本地服务的方式调用需要消费的服务;
- 客户端存根(client stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体;
- 客户端存根(client stub)找到远程的服务地址,并且将消息通过网络发送给服务端;、
- 服务端存根(server stub)收到消息后进行解码(反序列化操作);
- 服务端存根(server stub)根据解码结果调用本地的服务进行相关处理;
- 本地服务执行具体业务逻辑并将处理结果返回给服务端存根(server stub);
- 服务端存根(server stub)将返回结果重新打包成消息(序列化)并通过网络发送至消费方;
- 客户端存根(client stub)接收到消息,并进行解码(反序列化);
- 服务消费方得到最终结果;
目标
RPC框架的实现目标则是将上面的第2-10步完好地封装起来,
也就是把调用、编码/解码的过程给封装起来,让用户感觉上像调用本地服务一样的调用远程服务
也就是把调用、编码/解码的过程给封装起来,让用户感觉上像调用本地服务一样的调用远程服务
其他相关概念
REST
SOAP
SOA
需要解决的问题
1、如何确定客户端和服务端之间的通信协议?
2、如何更高效地进行网络通信?
3、服务端提供的服务如何暴露给客户端?
4、客户端如何发现这些暴露的服务?
5、如何更高效地对请求对象和响应结果进行序列化和反序列化操作?
2、如何更高效地进行网络通信?
3、服务端提供的服务如何暴露给客户端?
4、客户端如何发现这些暴露的服务?
5、如何更高效地对请求对象和响应结果进行序列化和反序列化操作?
1、需要有非常高效的网络通信,比如一般选择Netty作为网络通信框架;
2、需要有比较高效的序列化框架,比如谷歌的Protobuf序列化框架;
3、可靠的寻址方式(主要是提供服务的发现),比如可以使用Zookeeper来注册服务等等;
4、如果是带会话(状态)的RPC调用,还需要有会话和状态保持的功能;
2、需要有比较高效的序列化框架,比如谷歌的Protobuf序列化框架;
3、可靠的寻址方式(主要是提供服务的发现),比如可以使用Zookeeper来注册服务等等;
4、如果是带会话(状态)的RPC调用,还需要有会话和状态保持的功能;
关键技术
动态代理
序列化与反序列化
NIO通信
注册中心
其他主流RPC框架
RMI
Hessian
potobuf-rpc-pro
Thrift
Finagle
Avro
Dubbo
实现
建立通信
是通过在客户端和服务器之间建立TCP连接
连接分类
按需连接
需要的时候建立连接,用后关闭
长连接
客户端和服务器建立起连接之后保持长期持有,
不管此时有无数据包的发送,可以配合心跳检测机制定期检测建立的连接是否存活有效;
多个远程过程调用共享同一个连接。
不管此时有无数据包的发送,可以配合心跳检测机制定期检测建立的连接是否存活有效;
多个远程过程调用共享同一个连接。
服务寻址
采用Redis或者Zookeeper来注册服务
服务提供者角度
将自己提供的服务注册到指定的注册中心
各种原因致使提供的服务停止时,需要向注册中心注销停止的服务;
服务的提供者需要定期向服务注册中心发送心跳检测,
服务注册中心如果一段时间未收到来自服务提供者的心跳后,认为该服务提供者已经停止服务,则将该服务从注册中心上去掉
服务注册中心如果一段时间未收到来自服务提供者的心跳后,认为该服务提供者已经停止服务,则将该服务从注册中心上去掉
调用者角度
消费的服务上线或者下线的时候,注册中心会告知该服务的调用者
服务调用者下线的时候,则取消订阅
网络传输
序列化
传输的参数数据都需要先进行序列化(Serialize)或者编组(marshal)成二进制的形式才能在网络中进行传输
反序列化
将二进制信息恢复为内存中的表达方式,
然后再找到对应的方法(寻址的一部分)进行本地调用(一般是通过生成代理Proxy去调用,通常会有JDK动态代理、CGLIB动态代理、Javassist生成字节码技术等),之后得到调用的返回值。
然后再找到对应的方法(寻址的一部分)进行本地调用(一般是通过生成代理Proxy去调用,通常会有JDK动态代理、CGLIB动态代理、Javassist生成字节码技术等),之后得到调用的返回值。
服务调用
SPI
Dubbo SPI
- 对 Dubbo 进行扩展,不需要改动 Dubbo 的源码
- 按需加载:延迟加载,可以一次只加载自己想要加载的扩展实现。
- 增加了对扩展点 IOC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
- Dubbo 的扩展机制能很好的支持第三方 IoC 容器,默认支持 Spring Bean。
依靠SPI机制实现插件化功能
Java SPI
JDK 标准的 SPI 会一次性加载所有的扩展实现,
如果有的扩展很耗时,但也没用上,很浪费资源。
所以只希望加载某个的实现,就不现实了
如果有的扩展很耗时,但也没用上,很浪费资源。
所以只希望加载某个的实现,就不现实了
实现方法
需要一个目录META/services 在classpath下面
目录下放置配置文件
文件名为要扩展的接口全名
文件内容为要实现的接口的实现方法
文件必须为UTF-8编码
使用方法
ServiceLoader<接口> searchServiceLoader = ServiceLoader.load(接口.class);
Iterator<Search> iterable = searchServiceLoader.iterator();
Iterator<Search> iterable = searchServiceLoader.iterator();
解释:SPI ,全称为 Service Provider Interface,是一种服务发现机制
运维管理
通过接口版本号兼容老接口
可以利用 telnet 命令进行调试、管理。
Dubbo2.0.5 以上版本服务提供端口支持 telnet 命令
Dubbo2.0.5 以上版本服务提供端口支持 telnet 命令
参考资料:
https://www.cnblogs.com/suger43894/p/9372123.html
https://www.jianshu.com/p/bbb1c92cb113
https://www.cnblogs.com/suger43894/p/9372123.html
https://www.jianshu.com/p/bbb1c92cb113
服务降级
1.在监控管理界面处理
2.通过代码
mock=force:return+null
不发起远程调用看,直接放回null。用来屏蔽不重要服务不可用时对调用方的影响
mock=fail:return+null
在远程调用失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
mock=“return null”
dubbo:reference 中设置 mock=“return null”。
mock 的值也可以修改为 true,然后再跟接口同一个路径下实现一个 Mock 类,
命名规则是 “接口名称+Mock” 后缀。mock实现需要保证有无参的构造方法。
然后在 Mock 类里实现自己的降级逻辑
mock 的值也可以修改为 true,然后再跟接口同一个路径下实现一个 Mock 类,
命名规则是 “接口名称+Mock” 后缀。mock实现需要保证有无参的构造方法。
然后在 Mock 类里实现自己的降级逻辑
3.集成Hystric
优雅停机
通过 JDK 的 ShutdownHook 来完成优雅停机的,
如果使用kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。
如果使用kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。
通信协议
通信框架
默认netty
支持的协议
Dubbo
单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用,以及消费者远大于提供者。
传输协议 TCP,
异步 Hessian 序列化。
传输协议 TCP,
异步 Hessian 序列化。
RMI
JDK 标准的 RMI 协议实现,
传输参数和返回参数对象需要实现 Serializable 接口使用 Java 标准序列化机制,
使用阻塞式短连接,传输数据包大小混合,
消费者和提供者个数差不多,可传文件,
传输协议 TCP。 多个短连接 TCP 协议传输,同步传输,适用常规的远程服务调用和 RMI 互操作。
在依赖低版本的 Common-Collections 包,Java 序列化存在安全漏洞。
传输参数和返回参数对象需要实现 Serializable 接口使用 Java 标准序列化机制,
使用阻塞式短连接,传输数据包大小混合,
消费者和提供者个数差不多,可传文件,
传输协议 TCP。 多个短连接 TCP 协议传输,同步传输,适用常规的远程服务调用和 RMI 互操作。
在依赖低版本的 Common-Collections 包,Java 序列化存在安全漏洞。
WebService
集成 CXF 实现,提供和原生 WebService 的互操作。
多个短连接,基于 HTTP 传输,同步传输,
适用系统集成和跨语言调用。
多个短连接,基于 HTTP 传输,同步传输,
适用系统集成和跨语言调用。
Http
使用 Spring 的 HttpInvoke 实现。
多个短连接,传输协议 HTTP,传入参数大小混合,
提供者个数多于消费者,需要给应用程序和浏览器 JS 调用。
多个短连接,传输协议 HTTP,传入参数大小混合,
提供者个数多于消费者,需要给应用程序和浏览器 JS 调用。
Hessian
集成 Hessian 服务,基于 HTTP 通讯,采用 Servlet 暴露服务,
Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与 Hession 服务互操作。
多个短连接,同步 HTTP 传输,
Hessian 序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件。
Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与 Hession 服务互操作。
多个短连接,同步 HTTP 传输,
Hessian 序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件。
Memcache
基于 Memcache实现的 RPC 协议。
Redis
基于 Redis 实现的RPC协议。
Thrift
其他
调用失败默认重试两次
不支持事务,需要集成第三方
缓存
为了提高数据访问的速度。Dubbo 提供了声明式缓存,以减少用户加缓存的工作量<dubbo:reference cache=“true” />
支持的序列化方式
Hessian(默认)
Dubbo序列化
FastJson
Java自带序列化
安全措施
通过 Token 令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。
提供服务黑白名单,来控制服务所允许的调用方。
Zookeeper 的注册可以添加用户权限认证
是否阻塞调用
默认是阻塞的,可以异步调用,没有返回值的可以这么做。
Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。
Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。
服务时效踢出原理
基于 zookeeper 的临时节点原理。
Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。
当一个接口有多种实现时,可以用 group 属性来分组,服务提供方和消费方都指定同一个 group 即可。
可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用。这个和服务分组的概念有一点类似。
Dubbo 的设计目的是为了满足高并发小数据量的 rpc 调用,在大数据量下的性能表现并不好,建议使用 rmi 或 http 协议。
管理控制台主要包含:路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等管理功能。
Dubbo 默认使用 Netty 框架,也是推荐的选择,另外内容还集成有Mina、Grizzly。
基础知识
使用场景
透明化的远程调用
像掉本地方法一样调用远程方法
软负载均衡及容错机制
服务自动注册与发现
不需要写死服务提供方地址,而是通过注册中心管理服务提供方
核心功能
Remoting(网络通信框架)
提供对多种NIO框架抽象封装,包括“同步转异步”和“请求-响应”模式的信息交换方式。
Cluster(集群框架)
提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
Registred(服务注册中心)
基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
注册中心
Multicast 注册中心
Multicast 注册中心不需要任何中心节点,只要广播地址,就能进行服务注册和发现,基于网络中组播传输实现。
组播受网络结构限制,只适合小规模应用或开发阶段使用
Provider 和 Consumer 和 Registry 不能跨机房(路由)
Zookeeper注册中心
基于分布式协调系统 Zookeeper 实现,采用 Zookeeper 的 watch 机制实现数据变更。
Redis注册中心
基于 Redis 实现,采用 key/map 存储,
key 存储服务名和类型,
map 中 key 存储服务 url,value 服务过期时间。基于 Redis 的发布/订阅模式通知数据变更。
key 存储服务名和类型,
map 中 key 存储服务 url,value 服务过期时间。基于 Redis 的发布/订阅模式通知数据变更。
通过心跳的方式检测脏数据,服务器时间必须相同
脏数据由监控中心删除。(注意:服务器时间必需同步,否则过期检测会不准确)
脏数据由监控中心删除。(注意:服务器时间必需同步,否则过期检测会不准确)
Simple注册中心
注册中心本身就是一个普通的Dubbo服务,可以减少第三方依赖,使整体通讯方式一致。
SimpleRegistryService只是简单实现,不支持集群,可作为自定义注册中心的参考,但不适合直接用于生产环境
SimpleRegistryService只是简单实现,不支持集群,可作为自定义注册中心的参考,但不适合直接用于生产环境
参考:
https://zhuanlan.zhihu.com/p/73949200
https://www.cnblogs.com/twoheads/p/10119251.html
https://zhuanlan.zhihu.com/p/73949200
https://www.cnblogs.com/twoheads/p/10119251.html
配置
配置说明
服务配置<dubbo:service/>
用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心
引用配置<dubbo:reference/>
用于创建一个远程服务代理,一个引用可以指向多个注册中心
协议配置<dubbo:protocol/>
用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受
应用配置<dubbo:application/>
用于配置当前应用信息,不管该应用是提供者还是消费者
模块配置<dubbo:module/>(可选)
用于配置当前模块信息
注册中心配置<dubbo:registry/>
用于配置连接注册中心相关信息
监控中心配置<dubbo:monitor/>(可选)
用于配置连接监控中心相关信息
提供方配置<dubbo:provider/>(可选)
当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值
消费方配置<dubbo:consumer/>(可选)
当 ReferenceConfig 某属性没有配置时,采用此缺省值
方法配置<dubbo:method/>
用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息
参数配置<dubbo:argument/>
用于指定方法参数配置
超时设置方式
服务提供者端设置超时(如果消费端配置则以消费端配置为主,如果消费方超时,服务端线程不会定制,会产生警告)
推荐使用
服务消费者端设置超时
不建议使用
分布式框架
类似框架
spring cloud
dubbox
dubbo的扩展,增加了restful调用,增加了开源组件等
与springCloud区别
dubbo
主要关注服务调用,流量分发,流量监控和熔断,服务的方方面面
底层使用Netty这样的NIO框架,是基于TCP传输协议,配合Hession序列化完成RPC调用
springCloud
微服务的方方面面,打造一个生态
基于Http协议Rest远程调用过程的协议;报文更大;占用带宽更多;但更灵活,只通过契约约束,不存在代码依赖
架构设计
核心组件
Provider
Consumer
Registry
Monitor
注册发现流程
1.服务提供者启动时,向注册中心注册自己提供的服务;
2.Consumer在启动时,向注册中心获取自己所需的服务;
3.注册中心返回提供者注测的消费者所需接口给消费者;如果有变更,基于长连接通知消费者;
4.消费者从服务提供者地址列表中,基于软负载均衡,先取一个进行调用,如果失败再调用另一台;
2.Consumer在启动时,向注册中心获取自己所需的服务;
3.注册中心返回提供者注测的消费者所需接口给消费者;如果有变更,基于长连接通知消费者;
4.消费者从服务提供者地址列表中,基于软负载均衡,先取一个进行调用,如果失败再调用另一台;
服务提供者和消费者在内存中记录调用次数和时间,定时每分钟发送一次统计数据到监控中心Monitor;
框架设计分层
接口服务层(Service)
开发的业务逻辑层
配置层(Config)
用于扩展实现consumer/provider配置信息,以ServiceConfig和ReferenceConfig为中心,可以直接new配置类,也可以通过Sping解析配置生成配置类
服务代理层(Proxy)
使用动态代理的方式为接口创建代理类,Proxy 层最主要的接口就是 ProxyFactory。其默认的扩展点有:stub、jdk、javassist。jdk 使用反射的方式创建代理类,javassist 通过拼接字符串然后编译的方式创建代理类。
对于服务提供者,代理的对象是接口的真实实现。
对于服务消费者,代理的对象是远程服务的 invoker 对象。
对于服务提供者,代理的对象是接口的真实实现。
对于服务消费者,代理的对象是远程服务的 invoker 对象。
服务注册层(Registry)
注册层主要负责的就是服务的注册发现。这层的主要接口就是 RegistryFactory,默认的扩展实现有:
- zookeeper
- redis
- multicast(广播模式)
- 内存
集群层(Cluster)
对多提供者调用场景的抽象,是 Dubbo 整个集群容错的抽象层。主要的扩展点有:
- 容错(Cluster)、
- 路由(RouterFactory)、
- 负载均衡(LoadBalance)。
监控层(Monitor)
RPC调用次数和调用时间监控,以Statistics为中心,扩展接口为MonitorFactory,Monitor和MonitorService
远程调用层(Protocol)
在这一层发起服务暴露(protocol.export)和服务引用(protocol.refer)。
Dubbo 提供的协议有:
Dubbo 提供的协议有:
- Dubbo (默认);
- injvm;
- rmi;
- http;
- hessian;
- thrift。
信息交换层(Exchange)
封装 Request/Response 对象。
网络传输层(Transport)
数据传输就是在传输层发生的,所以这层包含 Transport(传输)、Dispatcher(分派)、Codec2(编解码)、ThreadPool(线程池),这几个接口。
数据传输发生在传输层,所以传输层一定需要具备数据传输的能力,也就是 Transport 和 Codec2,其中 Transport 就是 Netty 等网络传输的接口,编解码不必说了,传输肯定需要;
除了传输能力,数据接收之后的处理,Dispatcher 和 ThreadPool,也就是分派任务和任务提交给线程池处理,也是必不可少的。
数据传输发生在传输层,所以传输层一定需要具备数据传输的能力,也就是 Transport 和 Codec2,其中 Transport 就是 Netty 等网络传输的接口,编解码不必说了,传输肯定需要;
除了传输能力,数据接收之后的处理,Dispatcher 和 ThreadPool,也就是分派任务和任务提交给线程池处理,也是必不可少的。
数据序列化层(Serialize)
序列化的作用是把对象转化为二进制流,然后在网络中传输。Dubbo 提供的序列化方式有:
Hessian2(默认);
Fastjson;
fst;
Java;
Kayo;
protobuff。
Hessian2(默认);
Fastjson;
fst;
Java;
Kayo;
protobuff。
Monitor实现原理
Consumer 端在发起调用之前会先走 filter 链;
provider 端在接收到请求时也是先走 filter 链,
然后才进行真正的业务逻辑处理。默认情况下,在 consumer 和provider 的 filter 链中都会有 Monitorfilter。
provider 端在接收到请求时也是先走 filter 链,
然后才进行真正的业务逻辑处理。默认情况下,在 consumer 和provider 的 filter 链中都会有 Monitorfilter。
流程
1、MonitorFilter 向 DubboMonitor 发送数据
2、DubboMonitor 将数据进行聚合后(默认聚合 1min 中的统计数据)暂存到ConcurrentMap<Statistics, AtomicReference> statisticsMap,然后使用一个含有 3 个线程(线程名字:DubboMonitorSendTimer)的线程池每隔 1min 钟,调用 SimpleMonitorService 遍历发送 statisticsMap 中的统计数据,每发送完毕一个,就重置当前的 Statistics 的 AtomicReference
3、SimpleMonitorService 将这些聚合数据塞入 BlockingQueue queue 中(队列大写为 100000)
4、SimpleMonitorService 使用一个后台线程(线程名为:DubboMonitorAsyncWriteLogThread)将 queue 中的数据写入文件(该线程以死循环的形式来写)
5、SimpleMonitorService 还会使用一个含有 1 个线程(线程名字:DubboMonitorTimer)的线程池每隔 5min 钟,将文件中的统计数据画成图表
集群
负载均衡策略
随机选取提供策略(Random loadBalance)(默认)
有利于动态调整提供者权重。截面碰撞率高,调用次数越多,分布越均匀。
循环选取提供策略(RoundRobin loadBalance)
平均分布,但是存在请求累积的问题。
量少活跃调用策略(LeastActive loadBalance)
解决慢提供者接收更少的请求。
一致性哈希策略(Constant loadBalance)
使相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者,避免引起提
供者的剧烈变动。
供者的剧烈变动。
容错策略
(消费者,cluster属性设置)
(消费者,cluster属性设置)
失败切换(Failover)
当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。
快速失败(FailFaster)
只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
失败安全(FailSafe)
出现异常时,直接忽略。通常用于写入审计日志等操作。
失败自动恢复(FailBack)
后台记录失败请求,定时重发。通常用于消息通知操作。
并行调用多个服务器(Forking)
只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
可通过 forks=”2″ 来设置最大并行数。
可通过 forks=”2″ 来设置最大并行数。
广播调用所有提供者(Broadcast)
逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。
路由
条件路由
文件路由
脚本路由
0 条评论
下一页