Dubbo是⼀个支持远程调用的分布式服务框架,提供⾼性能和透明化的RPC远程服务调⽤⽅案,以及SOA服务治理方案。常见的重点问题,
- 透明远程调用 就像调用本地方法一样调用远程方法;只需简单配置,没有任何API侵入;
- 负载均衡机制 客户端(client)loadbalance;
- 容错重试机制 服务Mock数据,重试次数、超时机制等;
- 自动注册发现 注册中心基于接口名查询服务提 供者的IP地址,并且能够平滑添加或删除服务提供者;
- 性能日志监控 Monitor统计服务的调用次调和调用时间的监控中心;
- 服务治理中心 路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等手动配置。
- 自动治理中心 无,比如:熔断限流机制、自动权重调整等。
一、Dubbo 的工作流程
角色:
- Provider: 暴露服务的服务提供方。
- Consumer: 调用远程服务的服务消费方。
- Registry: 服务注册与发现的注册中心。
- Monitor: 统计服务的调用次调和调用时间的监控中心。
- Container: 服务运行容器。
流程:
- 0. 服务容器负责启动,加载,运行服务提供者。
- 1. 服务提供者在启动时,向注册中心注册自己提供的服务。
- 2. 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
二、Dubbo 的十层架构
Dubbo框架设计一共划分了10个层,最上面的Service层是留给实际想要使用Dubbo开发分布式服务的开发者实现业务逻辑的接口层。图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口, 位于中轴线上的为双方都用到的接口。
- 服务接口层(Service):实际业务逻辑,服务提供方和服务消费方对应的接口和实现。
- 配置层(Config):对外配置接口。
- 服务代理层(Proxy):服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton。
- 服务注册层(Registry):负责服务地址的注册与发现,以服务URL为中心。
- 集群层(Cluster):封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务。
- 监控层(Monitor):RPC调用次数和调用时间监控。
- 远程调用层(Protocol):封将RPC的远程调用。
- 信息交换层(Exchange):封装请求响应模式,同步转异步。
- 网络传输层(Transport):抽象mina和netty为统一接口。
- 数据序列化层(Serialize):可复用的一些工具,扩展接口为Serialization等接口。
关于dubbo十层架构的一些理解,
- 在RPC中,Protocol是核心层,也就是只要有Protocol + Invoker + Exporter就可以完成非透明的RPC调用,然后在Invoker的主过程上Filter拦截点。
- 而Cluster是外围概念,所以Cluster的目的是将多个Invoker伪装成一个Invoker,这样其它人只要关注Protocol层Invoker即可,加上Cluster或者去掉Cluster对其它层都不会造成影响,因为只有一个提供者时,是不需要Cluster的。
- Proxy层封装了所有接口的透明化代理,而在其它层都以Invoker为中心,只有到了暴露给用户使用时,才用Proxy将Invoker转成接口,或将接口实现转成Invoker,也就是去掉Proxy层RPC是可以Run的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。
- 而Remoting实现是Dubbo协议的实现,如果你选择RMI协议,整个Remoting都不会用上,Remoting内部再划为Transport传输层和Exchange信息交换层,Transport层只负责单向消息传输,是对Mina、Netty、Grizzly的抽象,它也可以扩展UDP传输,而Exchange层是在传输层之上封装了Request-Response语义。
- Registry和Monitor实际上不算一层,而是一个独立的节点,只是为了全局概览,用层的方式画在一起。
三、服务提供方暴露一个服务的过程
- provider启动时,把想要提供的服务暴露在本地。
- 把服务暴露到远程。
- 启动netty服务,建立长连接。
- 连接到注册中心zk上。
- 监控zk上的消费服务。
注,本地暴露于远程暴露的区别:
- 本地暴露是暴露在本机JVM中,调用本地服务不需要网络通信。
- 远程暴露是将ip,端口等信息暴露给远程客户端,调用远程服务时需要网络通信。
四、服务消费者消费一个服务的过程
首先ReferenceConfig类的init方法调用Protocol的refer方法生成Invoker实例。然后把Invoker转为客户端需要的接口。
五、Dubbo 配置示例
provider.xml 示例,
consumer.xml示例,
六、Dubbo 的负载均衡机制
1、Random LoadBalance,随机(默认的负载均衡策略)
RandomLoadBalance 是加权随机算法的具体实现,可以完全随机,也可以按权重设置随机概率。
2、RoundRobin LoadBalance,轮循
可以轮询和加权轮询。存在响应慢的提供者会累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
3、LeastActive LoadBalance,最少活跃调用数
活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求。此时应优先将请求分配给该服务提供者。
4、ConsistentHash LoadBalance,一致性Hash
一致性Hash算法,相同参数的请求一定分发到一个provider上去。provider挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。
七、Dubbo 的容错机制
容错机制指的是某中系统控制在一定范围的一种允许或包容犯错情况的发生,为了保证集群的整体可用,我们需要建立容错机制。
1、Failover cluster(默认)
失败自动切换,调用失败时,自动重试其他机器。通常用于读操作,但重试会带来更长延迟。
2、Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
3、Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
4、Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
5、Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
八、Dubbo 的服务降级
降级的目的是为了保证核心服务可用。降级可以有几个层面的分类:自动降级,人工降级;按照功能可以分为:读服务降级和写服务降级。
- 对一些非核心服务进行人工降级,在大促之前通过降级开关关闭那些推荐内容,评价等对主流程序没有影响的功能
- 故障降级,比如调用的远程服务挂了,网络故障,或者RPC服务返回异常。那么可以直接降级,降级的方案比如设置默认值,采用兜底数据(系统推荐的行为广告挂了,可以提前准备静态页面做返回)等等
- 限流降级,在秒杀这种流量比较集中并且流量特别大的情况下,因为突发访问量特别大可能导致系统支撑不了。这个时候可以采用限流来限制访问量。当达到阈值时,后续的请求被降级,比如进入排队页面,比如跳转到错误页面(活动火爆,请稍后重试)。
Dubbo的降级方式:Mock,实现步骤如下,
- 在client端创建一个testmock类,实现对应的IGphello的接口(需要对哪个接口进行mock,就实现哪个)名称必须以mock结尾。
- 在client端的xml配置文件中,添加如下配置,增加一个mock属性指向创建的testmock。
- 模拟错误(设置timeout)模拟超时异常,运行测试代码即可访问到testmock这个类,当服务端故障解除以后,调用过程将恢复正常。
当服务器的压力比较大的时候,我们可以通过服务降级功能 临时屏蔽某个出错的非关键服务,并定义降级后的返回策略,屏蔽掉不重要的服务如广告服务等,来降低核心业务的压力。
mock=force:return+null
表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
- 还可以改为
mock=fail:return+null
表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
我们可以直接在Admin控制台来操作服务降级,服务消费者中的屏蔽相当于不发起远程调用。容错相当于对该服务的方法调用在失败后,再返回 null 值。
九、Dubbo 的动态代理策略
默认使用javassist动态字节码生成,创建代理类。也可以通过spi扩展机制配置自己的动态代理策略。
十、Dubbo 的SPI机制
SPI(Service Provider Interface),是一种服务发现机制。Dubbo SPI 源于 JDK SPI,并做了增强处理。简单理解:定义一个接口,并不用关心具体实现。其他人按照这个接口去实现内容。当需要接入别人的实现时,需要一种机制来保证他的内容被正确的加载运行,这就是 SPI。同时,可以在运行时,动态为接口替换实现类,增强了扩展性。
相比JDK 标准的 SPI,Dubbo SPI 主要增强了以下三点,
- jdk SPI仅通过接口类名获取所有实现,但是Duboo SPI可以根据接口类名和key值获取具体一个实现,不会一次性实例化所有实现,而只在需要时实例化。
- 扩展点加载失败的原因有更清楚的展示。
- 增加了 IoC 和 AOP 的支持。
十一、Dubbo 的序列化协议
具体可以参照 Dubbo的序列化协议 。Dubbo 支持9中序列化协议,
-
dubbo://(推荐)
-
rmi://
-
hessian://
-
http://
-
webservice://
-
thrift://
-
memcached://
-
redis://
-
rest://
默认的是 dubbo 协议,使用单一长连接,NIO 异步通信,基于 hessian 作为序列化协议。适用于:传输数据量小,并发量高的场景(一般消费者服务远多于提供者服务)。
- 连接个数:单连接
- 连接方式:长连接
- 传输协议:TCP
- 传输方式:NIO异步传输
- 序列化:Hessian 二进制序列化
- 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
- 适用场景:常规远程服务方法调用
十二、Dubbo内置了哪几种服务容器
-
Spring Container
-
Jetty Container
-
Log4j Container
Dubbo 的服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。
十三、Dubbo 和 Spring Cloud的区别
1、通信方式不同
Dubbo 使用的是 RPC 通信,而 Spring Cloud 使用的是 HTTP RESTFul 方式。
2、组件生态不同