Dubbo
1、定义
- Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。简单的说,Dubbo 就是个服务框架,说白了就是个远程服务调用的分布式框架。
2、Dubbo架构层次:
2.1 十层架构
- 第一层:service层,接口层,给服务提供者和消费者来实现的。
- 第二层:config层,配置层,主要是对dubbo进行各种配置的。
- 第三层:proxy层,服务代理层,透明生成客户端的stub和服务单的skeleton。
- 第四层:registry层,服务注册层,负责服务的注册与发现。
- 第五层:cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务。
- 第六层:monitor层,监控层,对rpc接口的调用次数和调用时间进行监控
- 第七层:protocol层,远程调用层,封装rpc调用。
- 第八层:exchange层,信息交换层,封装请求响应模式,同步转异步。
- 第九层:transport层,网络传输层,抽象mina和netty为统一接口。
- 第十层:serialize层,数据序列化层。
2.2 工作流程:
1)第一步,provider向注册中心去注册
2)第二步,consumer从注册中心订阅服务,注册中心会通知consumer注册好的服务
3)第三步,consumer调用provider
4)第四步,consumer和provider都异步的通知监控中心
2.3 通信协议、序列化协议、远程调用的方式
2.3.1:Dubbo通信协议
2.3.1.1 默认协议
- 默认就是走dubbo协议的,单一长连接,NIO异步通信,基于hessian作为序列化协议。
- 适用的场景就是:传输数据量很小(每次请求在100kb以内),但是并发量很高。
- 为了要支持高并发场景,一般是服务提供者就几台机器,但是服务消费者有上百台,可能每天调用量达到上亿次!此时用长连接是最合适的,就是跟每个服务消费者维持一个长连接就可以,可能总共就100个连接。然后后面直接基于长连接NIO异步通信,可以支撑高并发请求。
- 否则如果上亿次请求每次都是短连接的话,服务提供者会扛不住。
- 而且因为走的是单一长连接,所以传输数据量太大的话,会导致并发能力降低。所以一般建议是传输数据量很小,支撑高并发访问。
2.3.1.2 其他协议:
- Dubbo 默认协议:议采用单一长连接和 NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。
- REST 协议: 基于标准的Java REST API实现的REST调用支持
- Hessian 协议:Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用
Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现 - HTTP 协议:需同时给应用程序和浏览器 JS 使用的服务。
- dubbo rpc jsonrpc
- WebService 协议:系统集成,跨语言调用
- RMI 协议:RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。
- Thrift 协议:使用Thrift实现PRC协议
- Redis 协议:基于redis实现RPC协议
- Memcached 协议:基于Memcached实现RPC协议
2.3.2:序列化协议
-
序列化:序列化,是一种把对像流化的机制,即把对像写入二进制数组(byte[]),传输到另一个地方,将其读出来之后,进行反序列化,得到对象含状态(状态就是属性的值)。
-
为什么要序列化。方便在网络间进行传输,并且可以把对象持久化到数据库或者文件中。
-
序列化协议(6种)
- dubbo-hessian-lite 是官方 hessian 的一个 Apache Dubbo 私有版本。
- Apache Avro™ 是一个数据序列化系统
- Jdk 序列化
- JSON - fastjson:一个Java版本内的 JSON 解析器/生成器
- FST: 一个快速的Java序列化工具
- Kryo是一个Java版本的快速有效的二进制对象序列化框架
2.3.2.1 hessian序列化协议
序列化参考:https://www.jb51.net/article/123555.htm
- Dubbo默认的序列化协议。
- 比java原生的序列化更快,序列化出来的数据更小。
- Java序列化会把要序列化的对象类的元数据和业务数据全部序列化从字节流,而且是把整个继承关系上的东西全部序列化了。它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息,因此效率较低而且字节流比较大。但是由于确实是序列化了所有内容,所以可以说什么都可以传输,因此也更可用和可靠。
2.3.3 远程调用的方式(4种)
- 1.RPC:采用C/S方式,跨语言跨平台,Apache Thrift是RPC的经典框架
- 2.webservice:请求应答机制,跨系统跨平台
- 3.RMI: 降低客户端与服务器的耦合性,java远程接口调用;跨虚拟机
- 4.JMS:JAVA消息服务,点对点和发布订阅模型 如ActiveMQ
2.4 负载均衡:
2.4.1 负载均衡策略
-
1)random(随机) load balance
- 默认情况下,dubbo是random(随机) load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。
-
2)roundrobin(轮询调度) loadbalance
- 还有roundrobin loadbalance,这个的话默认就是均匀地将流量打到各个机器上去,但是如果各个机器的性能不一样,容易导致性能差的机器负载过高。所以此时需要调整权重,让性能差的机器承载权重小一些,流量少一些。
-
3)leastactive loadbalance
- 这个就是自动感知一下,如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会给不活跃的性能差的机器更少的请求
-
4)consistanthash loadbalance
- 一致性Hash算法,相同参数的请求一定分发到一个provider上去,provider挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。如果你需要的不是随机负载均衡,是要一类请求都到一个节点,那就走这个一致性hash策略。
2.4.2 负载均衡策略配置:
- 由@SPI注解可以看到,dubbo默认的负载均衡策略是随机调用法
- 如何改变dubbo的负载均衡策略?
- 1:如果是springboot项目,直接注解在@Reference中引用,然后注明loadblance=“xx”.其中xx为每个实现类中的name的值
- 2:xml配置的方式
- 1:如果是springboot项目,直接注解在@Reference中引用,然后注明loadblance=“xx”.其中xx为每个实现类中的name的值
<dubbo:serviceinterface="..."loadbalance="roundrobin"/>
2.5 集群容错策略:
-
1)failover cluster模式
- 失败自动切换,自动重试其他机器,默认就是这个,常见于读操作
-
2)failfast cluster模式
- 一次调用失败就立即失败,常见于写操作
-
3)failsafe cluster模式
- 出现异常时忽略掉,常用于不重要的接口调用,比如记录日志
-
4)failbackc cluster模式
- 失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种
-
5)forking cluster
- 并行调用多个provider,只要一个成功就立即返回
-
6)broadcacst cluster
- 逐个调用所有的provider
2.6 SPI(Service provide interface)机制
2.6.1 定义:
- SPI,Service Provider Interface,主要是被框架的开发人员使用,比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,mysql和postgresql都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。
- SPI是JDK内置的一种服务提供发现机制。目前市面上很多框架都用它来做服务的扩展发现。简单的说,它是一种动态替换发现的机制。
- 举个简单的例子,我们想在运行时动态给它添加实现,你只需要添加一个实现,然后把新的实现描述给JDK知道就行了。大家耳熟能详的如JDBC,日志框架都有用到。
- 当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java.util.ServiceLoader。
2.6.2 加载机制:
- dubbo的spi实现原理和java spi相似,只不过增强了一些功能和优化。java spi的是把所有的spi都加载到内存,但对于dubbo来说可能只需要加载用户指定的实现方式,而不需要全部加载进来,全部加载也会有性能问题,所以dubbo实现的是在有用到的时候去加载这些扩展组件。
2.7 服务治理、服务降级、失败重试以及超时重试
2.7.1 服务治理
-
1)调用链路自动生成
-
一个大型的分布式系统,或者说是用现在流行的微服务架构来说吧,分布式系统由大量的服务组成。那么这些服务之间互相是如何调用的?调用链路是啥?说实话,几乎到后面没人搞的清楚了,因为服务实在太多了,可能几百个甚至几千个服务。
-
那就需要基于dubbo做的分布式系统中,对各个服务之间的调用自动记录下来,然后自动将各个服务之间的依赖关系和调用链路生成出来,做成一张图,显示出来,大家才可以看到对吧。
服务A -> 服务B -> 服务C -> 服务E -> 服务D -> 服务F -> 服务W
-
2)服务访问压力以及时长统计
-
需要自动统计各个接口和服务之间的调用次数以及访问延时,而且要分成两个级别。一个级别是接口粒度,就是每个服务的每个接口每天被调用多少次,TP50,TP90,TP99,三个档次的请求延时分别是多少;第二个级别是从源头入口开始,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延时的TP50,TP90,TP99,分别是多少。
-
这些东西都搞定了之后,后面才可以来看当前系统的压力主要在哪里,如何来扩容和优化啊
-
3)其他的
-
服务分层(避免循环依赖),调用链路失败监控和报警,服务鉴权,每个服务的可用性的监控(接口调用成功率?几个9?)99.99%,99.9%,99%
2.7.2 服务降级
- 比如说服务A调用服务B,结果服务B挂掉了,服务A重试几次调用服务B,还是不行,直接降级,走一个备用的逻辑,给用户返回响应。
2.7.3 失败重试和超时重试
- 所谓失败重试,就是consumer调用provider要是失败了,比如抛异常了,此时应该是可以重试的,或者调用超时了也可以重试。
- 如果是超时了,timeout就会设置超时时间;如果是调用失败了自动就会重试指定的次数。
你就结合你们公司的具体的场景来说说你是怎么设置这些参数的,timeout,一般设置为200ms,我们认为不能超过200ms还没返回。 - retries,3次,设置retries,还一般是在读请求的时候,比如你要查询个数据,你可以设置个retries,如果第一次没读到,报错,重试指定的次数,尝试再次读取2次。
X、常见面试题
1、注册中心挂了可以继续通信吗?
- 可以,因为刚开始初始化的时候,消费者consumer会将提供者provider的地址等信息拉取到本地缓存,所以注册中心挂了可以继续通信。