一、Dubbo介绍
1、dubbo 是什么
Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。
Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合),我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配置就能够实现分布式服务调用,也就是说服务提供方(Provider)发布的服务可以天然就是集群服务。
Dubbo的产生背景、最初的需求、架构设计 等可以详细看官方的文档: http://dubbo.apache.org/
代码语言:javascript复制在看代码中觉得dubbo使用的主要技术如下:
代理(Proxy:javassist等)
反射(Invoke)
协议(Protocol:DubboProtocl等)
序列化(Hession等)
NIO(netty,mina)
SPI(java spi)
装饰器模式(wrapper)
观察者模式(订阅和监听)
spring自定义标签(容器启动时bean的解析)
先去了解一下以上的技术会对看源码有很大的帮助!!!
2、 dubbo的出现是为了解决什么问题
代码语言:javascript复制(1) 当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。
(2) 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系
(3) 接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?
二、Dubbo原理
1、Dubbo的架构
(1)Dubbo中的角色
节点角色说明:
代码语言:javascript复制Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。
(2)工作过程(Dubbo服务启动,调用,暴露消费的过程): 服务容器负责启动,加载,运行服务提供者。 服务提供者在启动时,向注册中心注册自己提供的服务。 服务消费者在启动时,向注册中心订阅自己所需的服务。 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者(注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外。注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者。注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表。)。 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
(3)Dubbo的分层 Dubbo框架设计一共划分了10个层:
服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提供方和服务消费方的业务设计对应的接口和实现。 配置层(Config):对外配置接口,以ServiceConfig和ReferenceConfig为中心,可以直接new配置类,也可以通过spring解析配置生成配置类。 服务代理层(Proxy):负责服务注册与查询服务,以及注册服务的本地缓存。服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton,以ServiceProxy为中心,扩展接口为ProxyFactory。 服务注册层(Registry):负责生成消费者的代理对象和服务提供方的Invoker。封装服务地址的注册与发现,以服务URL为中心,扩展接口为RegistryFactory、Registry和RegistryService。可能没有服务注册中心,此时服务提供方直接暴露服务。 集群层(Cluster):负责负载均衡的策略,以及失败的策略。封装多个提供者的路由及负载均衡,并桥接注册中心,以Invoker为中心,扩展接口为Cluster、Directory、Router和LoadBalance。将多个服务提供方组合为一个服务提供方,实现对服务消费方来透明,只需要与一个服务提供方进行交互。 监控层(Monitor):RPC调用次数和调用时间监控,以Statistics为中心,扩展接口为MonitorFactory、Monitor和MonitorService。 远程调用层(Protocol):封将RPC调用,支持多种RPC协议(不包含IO通信部分)。以Invocation和Result为中心,扩展接口为Protocol、Invoker和Exporter。Protocol是服务域,它是Invoker暴露和引用的主功能入口,它负责Invoker的生命周期管理。Invoker是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。 信息交换层(Exchange):封装请求响应模式,同步转异步。处理各种协议的通信请求,支持netty、mina、http等,默认采用netty进行通信。以Request和Response为中心,扩展接口为Exchanger、ExchangeChannel、ExchangeClient和ExchangeServer。 网络传输层(Transport):抽象mina和netty为统一接口,以Message为中心,扩展接口为Channel、Transporter、Client、Server和Codec。 数据序列化层(Serialize):数据序列化层和可复用的一些工具,dubbo协议缺省为hessian2,rmi缺省为java,http缺省为json。扩展接口为Serialization、 ObjectInput、ObjectOutput和ThreadPool。
2、启动时检查
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=“true”。
可以通过 check=“false” 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check=“false”,总是会返回引用,当服务恢复时,能自动连上。
代码语言:javascript复制<dubbo:consumer check="false" />
参考:http://dubbo.apache.org/zh-cn/docs/user/demos/preflight-check.html
3、集群容错
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
各节点的关系
- 这里的 Invoker 是 Provider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息。
- Directory 代表多个 Invoker,可以把它看成 List ,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更。
- Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
- Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等。
- LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。
集群容错模式
- Failover Cluster:失败自动切换(默认),当出现失败,重试其它服务器 [1]。通常用于读操作,但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。由FailoverClusterInvoker实现,原理是先获得retries的值,再通过for循环依次调用服务,如果成功则返回,如果失败则循环调用直至循环结束。
参考:https://www.2cto.com/kf/201803/733613.html
- Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。由FailfastClusterInvoker实现,原理是直接调用一次。
- Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。由FailsaveClusterInvoker实现。
- Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。由FailbackClusterInvoker实现,原理是,执行调用,出现异常后把错误放入ConcurrentHashMap,用任务调度线程池延迟5秒来重新执行调用,如果再失败,记录日志,不再调用。
- Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。
- Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错 [2]。通常用于通知所有提供者更新缓存或日志等本地资源信息。
参考:http://dubbo.apache.org/zh-cn/docs/user/demos/fault-tolerent-strategy.html
4、负载均衡
(1)随机(Random LoadBalance)按照权重设置随机概率(默认)。 (2)轮询(RoundRobin LoadBalance) (3)最少活跃调用数(LeastActive LoadBalance),相同活跃调用数的随机 (4)一致性Hash(ConsistencyHash LoadBalance)一致性Hash,相同参数的请求总是发到同一提供者
参考:http://dubbo.apache.org/zh-cn/docs/user/demos/loadbalance.html
5、多协议
Dubbo 允许配置多协议,在不同服务上或者同一服务上同时支持多种协议。 (1)在不同服务上支持不同协议 不同服务在性能上适用不同协议进行传输,比如大数据用短连接协议,小数据大并发用长连接协议
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="world" />
<dubbo:registry id="registry" address="10.20.141.150:9090" username="admin" password="hello1234" />
<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo协议暴露服务 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" />
<!-- 使用rmi协议暴露服务 -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />
</beans>
(2)在同一服务上支持多种协议 需要与 http 客户端互操作
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="world" />
<dubbo:registry id="registry" address="10.20.141.150:9090" username="admin" password="hello1234" />
<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="hessian" port="8080" />
<!-- 使用多个协议暴露服务 -->
<dubbo:service id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" protocol="dubbo,hessian" />
</beans>
协议 | 稳定性 | 优点 | 缺点 | 建议 |
---|---|---|---|---|
Dubbo协议 | Stable | 采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用) | 在大文件传输时,单一连接会成为瓶颈 | 可用于生产环境 |
Rmi协议 | Stable | 可与原生RMI互操作,基于TCP协议 | 偶尔会连接失败,需重建Stub | 可用于生产环境 |
Hessian协议 | Stable | 可与原生Hessian互操作,基于HTTP协议 | 需hessian.jar支持,http短连接的开销大 | 可用于生产环境 |
此外还支持webservice、http、Thrift、memcached、redis、rest等协议。
6、Dubbo序列化
协议 | 稳定性 | 优点 | 缺点 | 建议 |
---|---|---|---|---|
Hessian Serialization | Stable | 性能较好,多语言支持(推荐使用) | Hessian的各版本兼容性不好,可能和应用使用的Hessian冲突,Dubbo内嵌了hessian3.2.1的源码 | 可用于生产环境 |
Dubbo Serialization | Stable | 通过不传送POJO的类元信息,在大量POJO传输时,性能较好 | 当参数对象增加字段时,需外部文件声明 | 试用 |
Json Serialization | Stable | 纯文本,可跨语言解析,缺省采用FastJson解析 | 性能较差 | 试用 |
Java Serialization | Stable | Java原生支持 | 性能较差 | 可用于生产环境 |
7、注册中心
Dubbo 支持同一服务向多注册中心同时注册,或者不同服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。另外,注册中心是支持自定义扩展的 [1]。 参考:http://dubbo.apache.org/zh-cn/docs/user/demos/multi-registry.html
Dubbo的注册中心有哪些,默认的注册中心是什么
协议 | 稳定性 | 优点 | 缺点 | 建议 |
---|---|---|---|---|
Zookeeper注册中心 | Stable | 支持基于网络的集群方式,有广泛周边开源产品,建议使用dubbo-2.3.3以上版本(推荐使用) | 依赖于Zookeeper的稳定性 | 可用于生产环境 |
Redis注册中心 | Stable | 支持基于客户端双写的集群方式,性能高 | 要求服务器时间同步,用于检查心跳过期脏数据 | 可用于生产环境 |
Multicast注册中心 | Tested | 去中心化,不需要安装注册中心 | 依赖于网络拓扑和路由,跨机房有风险 | 小规模应用或开发测试环境 |
Simple注册中心 | Tested | Dogfooding,注册中心本身也是一个标准的RPC服务 | 没有集群支持,可能单点故障 | 试用 |
参考:http://dubbo.apache.org/zh-cn/docs/user/maturity.html
8、Dubbo服务降级
可以通过服务降级功能 临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。 服务降级就是当服务响应超时或连接请求超时,不用继续等下去,而采用降级措施,返回一个我们自己定义好的提示。而为什么要使用服务降级,这是防止分布式服务发生雪崩效应。当一个请求发生超时,一直等待着服务响应,那么在高并发情况下,很多请求都是因为这样一直等着响应,直到服务资源耗尽产生宕机,而宕机之后会导致分布式其他服务调用该宕机的服务也会出现资源耗尽宕机,这样下去将导致整个分布式服务都瘫痪。集群环境下,当一台服务宕机之后,其他流量分发到其他集群机器上,压力也会随之加大,时间久了整个集群也会垮了,这只是个时间问题。为了防止产生了雪崩效应那么就该对服务配置降级,一旦请求超过规定时间立即返回自定义好的提示,无需继续等待。 dubbo中有提供一个叫做mock的配置,mock只在出现非业务异常(比如超时,网络异常等)时执行。mock的配置支持两种: (1)采用return null,返回简单的空 打开项目里的consumer.xml修改dubbo:reference配置即可:
代码语言:javascript复制<dubbo:reference id="userService" check="false" interface="com.cwh.service.UserService" timeout="3000" mock="return null"/>
在服务提供者例如UserServiceImpl中的getUser方法打个断点来模拟请求超时。然后浏览器访问,断点不过,一致等待,当时间超过3秒,直接返回了空,这样就已经实现了一个简单的服务降级了 (2)采用自定义提示
代码语言:javascript复制<dubbo:reference id="userService" check="false" interface="com.cwh.service.UserService" timeout="3000" mock="true"/>
在service包下也就是同UserService目录下新建一个UserServiceMock,注意这里名字一点要是该接口名 Mock:
代码语言:javascript复制public class UserServiceMock implements UserService{
public List<User>getUser(String name){
//throw new RuntimeException("服务降级-----");
User user = new User();
user.setUserName("服务降级啦");
user.setUserAge("500");
List<User> list = new ArrayList<User>();
list.add(user);
return list;
}
}
参考:https://blog.csdn.net/u011320740/article/details/78929066
9、Dubbo灰度发布
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用,用法如下:
代码语言:javascript复制<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" version="1.0.0" />
利用dubbo该特性,我们能够实现一些功能的灰度发布,实现步骤如下:
- 接口旧的实现定义version=“1.0.0”,接口新的实现version=“2.0.0”
- Consumer端定义version="*"
- 这样定义Provider和Consumer后,新旧接口实现各承担50%的流量
- 利用dubbo该特性,还能完成不兼容版本迁移:
在低压力时间段,先升级一半Provider为新版本;
再将所有消费者升级为新版本;
然后将剩下的一半提供者升级为新版本。
参考:https://www.jianshu.com/p/ab2afef2b279
10、Dubbo异步调用
默认是同步等待结果阻塞的,支持异步调用。通过asyanc属性设置异步,如果设置异步,则不需要等待该方法返回值,程序即可往下进行。
代码语言:javascript复制Object o = Methoda();
Sysout(o);
Methodb();
- 打印结果为null
- Timeout不会超时,所以不会重试
Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。异步调用流程图如下:
参考:https://blog.csdn.net/moakun/article/details/82919804