微服务理论知识汇总-dubbo

Dubbo

Dubbo架构图

Dubbo层次结构图

第一层:service层,接口层,给服务提供者和消费者来实现的
第二层:config层,配置层,主要是对dubbo进行各种配置的
第三层:proxy层,服务代理层,透明生成客户端的stub和服务单的skeleton
第四层:registry层,服务注册层,负责服务的注册与发现
第五层:cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
第六层:monitor层,监控层,对rpc接口的调用次数和调用时间进行监控
第七层:protocol层,远程调用层,封装rpc调用
第八层:exchange层,信息交换层,封装请求响应模式,同步转异步
第九层:transport层,网络传输层,抽象mina和netty为统一接口
第十层:serialize层,数据序列化层

Dubbo支持的通信协议

协议名 序列化 连接 适用场景
Dubbo协议 hessian2 单一长连接,NIO异步通信 传输数据量很小(每次请求在100kb以内),但是并发量很高,消费者远大于提供者
rmi协议 java二进制序列化 多个短连接 消费者和提供者数量差不多,文件的传输
hessian协议 hessian序列化协议 多个短连接 传输数据量较大,提供者数量比消费者数量还多,文件的传输
http协议 json序列化 多个短连接 需同时给应用程序和浏览器 JS 使用的服务
webservice SOAP文本序列化 多个短连接 系统集成,跨语言调用

Dubbo不好用的地方

不好用的地方 说明 原因 解决方案
参数及返回值需要实现Serializable接口 默认协议是Dubbo协议,通过netty传输,需要转换成二机制数据 实现Serializable接口
参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口 - 默认协议是Dubbo协议,采用的是hessian序列化&反序列化方式,遇上以上接口时会做特殊处理,自定义实现类中的属性值都会丢失 使用java原生接口
父子类有相同属性时值丢失 - 默认采用hessian序列化,获取属性时,采用了map去重,但是读值时,根据序列化顺序,对于同名字段,子类的该字段值会被赋值两次,总是被父类的值覆盖,导致子类的字段值丢失 不要重名
自定义异常被包装成RuntimeException - dubbo提供端对异常进行了统一封装导致的 将自定义异常和接口类放到一个包中/方法签名上申明自定义异常
IP暴露问题 Docker、双网卡、虚拟机等环境下,Dubbo默认绑定的IP可能并不是我们期望的正确IP 跟Dubbo绑定IP默认行为有关 通过添加启动参数和配置解决
Data length too large 请求或者响应的报文体长度超过了8k dubbo协议适合小数据传输,建议更改协议
线程耗尽 Dubbo提供者线程池为fix类型,默认线程数为200,队列为0 解决业务耗时原因/扩容

dubbo存在的坑

Dubbo服务提供端异常统一处理逻辑

目的是为了保证抛出的异常,消费端都能识别,具体逻辑如下:

  1. 如果是checked异常(不是RuntimeException但是是Exception.java类型的异常),直接抛出;
  2. 在方法签名上有声明(例如String saySomething()throws MyException ),直接抛出;
  3. 异常类和接口类在同一jar包里,直接抛出;
  4. 是JDK自带的异常(全类名以java或者javax开头,例如java.lang.IllegalStateException),直接抛出;
  5. 是Dubbo本身的异常RpcException,直接抛出;
  6. 否则,Dubbo通过如下代码将异常包装成RuntimeException抛给客户端

Dubbo常用性能调优属性

C90DBB86-152E-4D54-A388-106EB095A321.jpeg
image.png

注意表中参数与图中的对应关系:

    1、当consumer发起一个请求时,首先经过active limit(参数actives)进行方法级别的限制,其实现方式为CHM中存放计数器(AtomicInteger),请求时加1,请求完成(包括异常)减1,如果超过actives则等待有其他请求完成后重试或者超时后失败;

    2、从多个连接(connections)中选择一个连接发送数据,对于默认的netty实现来说,由于可以复用连接,默认一个连接就可以。不过如果你在压测,且只有一个consumer,一个provider,此时适当的加大connections确实能够增强网络传输能力。但线上业务由于有多个consumer多个provider,因此不建议增加connections参数;

    3、连接到达provider时(如dubbo的初次连接),首先会判断总连接数是否超限(acceps),超过限制连接将被拒绝;

    4、连接成功后,具体的请求交给io thread处理。io threads虽然是处理数据的读写,但io部分为异步,更多的消耗的是cpu,因此iothreads默认cpu个数+1是比较合理的设置,不建议调整此参数;

    5、数据读取并反序列化以后,交给业务线程池处理,默认情况下线程池为fixed,且排队队列为0(queues),这种情况下,最大并发等于业务线程池大小(threads),如果希望有请求的堆积能力,可以调整queues参数。如果希望快速失败由其他节点处理(官方推荐方式),则不修改queues,只调整threads;

    6、execute limit(参数executes)是方法级别的并发限制,原理与actives类似,只是少了等待的过程,即受限后立即失败;

Dubbo负载均衡策略

策略名 实现逻辑
随机 生成随机值,判断落在哪个权重范围内
轮询 维护一个总调用次数,对总权重取模,判断落在哪个权重范围内
最少活跃数 维护每个服务器的活跃数,取最小值
一致性Hash

Dubbo的集群及容错策略

容错策略名 说明 适用场景
failover 失败自动切换 读操作
failfast 失败立即返回 写操作
failsafe 失败则忽略 允许丢失的操作,例如日志
failback 失败定时重发 消息队列
forking 并行调用多个provider,某个成功就返回
broadcast 广播,逐个调用所有的provider

Dubbo的动态代理

  • 默认使用javassist动态字节码生成,创建代理类

java的spi机制的原理(ServiceLoader)

  • 获取classloader
  • 读取META-INF/services/+全限定接口名的文件内容
  • 循环创建实现类对象并加入缓存
  • 返回实现类对象集合

springboot的spi机制

  • spring.factories文件

Dubbo的spi机制的使用

  • 三个路径都可以放以接口全限定类为名的文件, META-INF/services/ 、META-INF/dubbo/和META-INF/dubbo/internal/
  • 文件内容是键值对格式
  • 接口需要用@SPI注解修饰
  • 通过接口类找到一个 ExtensionLoader
  • 通过ExtensionLoader.getExtension(key) 得到指定key的实现类实例

Dubbo的spi机制的原理

image.png
  • 通过接口类找到一个 ExtensionLoader
  • 通过定义key到实例缓存中查找是否存在该实例,如果存在则返回
  • 实现类缓存中查找是否存在该类,没有则扫描约定的三个目录并加载
  • 放射创建指定实现类对象
  • 经过注入和包装后返回

Dubbo的服务降级

通过mock属性实现服务降级

Dubbo的服务熔断

无熔断机制,可通过hystrix实现。

Dubbo的服务限流

限流方式 使用客户端 标签 含义
executes 提供端 service、method 并发执行不能超过指定数
accepts 提供端 protocol 指定协议的连接数不能超过指定数
actives 提供端/消费端 service、reference、method 长链接时:最多可以处理的请求个数;短链接时:可以同时处理的短连接数量
connections 提供端/消费端 service、reference、method 最大连接数

spring与Dubbo搭配使用时,在什么时候开始服务注册

  • spring启动过程中刷新上下文操作时
  • bean实例化结束后会发送上下文刷新完成事件
  • Dubbo的ServiceBean类实现了ApplicationListener接口监听了该事件
  • 故此时会去执行onApplicationEvent方法,也是在此时进行了服务注册

Dubbo 服务暴露的流程

服务暴露

服务暴露的大致流程
  1. 根据配置得到 URL
  2. 通过 javassist 动态封装服务实现类,统一暴露出 Invoker 使得调用方便,屏蔽底层实现细节(ProxyFactory)
  3. 利用 Dubbo SPI 机制根据 URL 的参数选择对应的插件实现类(例如protocol)
  4. 然后将invoker封装成 exporter 存储起来,等待消费者的调用
  5. 最后将URL 注册到注册中心,使得消费者可以获取服务提供者的信息(ExporterListener)

Dubbo服务引用的过程(注册中心方式举例)

服务引用详细流程

服务引用大致流程
  1. 通过配置组成URL
  2. 向注册中心注册消费者,订阅节点(Directory)
  3. 监听注册中心获取提供者信息(Directory实现了NotifyListener接口)
  4. 通过SPI机制获取对应协议,生成Invoker(Directory.refer)
  5. Invoker中加入负载,容错等处理 (RegistryProtocol.merge)
  6. 最后通过动态代理封装得到代理类(ProxyFactory)

如何实现链路追踪

  1. 通过brave生成和传递traceId
  2. zipkin汇总traceId
  3. Logstash采集日志
  4. ElasticSearch+Kibana 可视化分析日志

dubbo的线程模型

  • all 所有消息都派发到线程池
  • direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行
  • message 只有请求、响应消息派发到业务线程池,其他连接断开事件/心跳等消息,直接在IO线程上执行
  • execution 只请求消息派发到业务线程池,响应和连接、断开事件、心跳等消息,直接在 IO 线程上执行
  • connection 在 IO 线程上,将连接、断开事件放入队列,有序逐个执行,其它消息派发到线程池

dubbo的线程模型图

dubbo的线程模型图

dubbo停机的过程

dubbo停机的过程
  1. 收到 kill PID 进程退出信号,Spring 容器会触发容器销毁事件。
  2. provider 端会注销服务元数据信息(删除ZK节点)。
  3. consumer 会拉取最新服务提供者列表。
  4. provider 会发送 readonly 事件报文通知 consumer 服务不可用。
  5. 服务端等待已经执行的任务结束并拒绝新任务执行。
  6. TCP连接断开

dubbo优雅停机方案

你可能感兴趣的:(微服务理论知识汇总-dubbo)