Dubbo(应用篇)

注册中心

注册中心通过registry标签配置,可配置多个。

  • 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  • 注册中心不做请求转发,相当于目录服务,存储地址和地址变更通知
  • 注册中心与消费端、服务端均为长连接,与服务端保持长连接可以检测服务提供端是否异常,与消费端保持长连接可以通知服务端地址变动。
  • 注册中心异常不会影响已经运行中的消费者和服务提供者,消费者缓存了服务提供端地址。
  • 注册中心非必须,可以消费端直接连接服务提供方,一般只有测试时使用这种方式,通过jvm参数或配置文件或reference标签可以指定某个服务消费哪个地址。
  • 服务提供方宕机,消费端会无限次重连
  • 支持一个服务注册注册多个服务中心,service标签的registry参数可以通过逗号分隔配置多个注册中心名
  • 支持Multicast、

负载均衡策略

  • 通过protocol标签、service标签、reference标签rloadbalance参数配置权重则通过管理后台可以配置

Random LoadBalance(默认策略)
随机。按权重设置随机概率,在一个截面上碰撞的概率高,重试时,可能出现瞬间压力不均
RoundRobin LoadBalance
轮询。按公约后的权重设置轮询比率,存在慢的机器累积请求问题,极端情况可能产生雪崩。
LeastActive LoadBalance
最少活跃调用数。相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少请求。不支持权重,在容量规划时,不能通过权重把压力导向一台机器压测容量
ConsistentHash LoadBalance
一致性Hash。相同参数的请求总是发到同一提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。默认虚拟节点是160个虚拟节点,根据服务存活数分摊。存在压力分摊不均问题。默认通过第一个参数进行hash,可以通过配置修改。

扩展负载均衡算法
基于spi,实现LoadBalance类,在指定目录文件配置,META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance,负载均衡策略名字=权限定名

调用监控

  • 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

协议对比

  • 通过protocol配置协议,指定端口,可以配置多个协议。service标签可以通过protocol参数指定协议,通过逗号分隔可配置多个。
 

Dubbo协议(推荐使用)
采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好。在大文件传输时,单一连接会成为瓶
Rmi协议。
可与原生RMI互操作,基于TCP协议,偶尔会连接失败,需重建Stub
Hessian协议
可与原生Hessian互操作,基于HTTP协议,需hessian.jar支持。http短连接的开销大,设置传输大文件、大数据

调用失败策略

  • 通过服务提供service标签和消费reference标签的cluster参数设置

Failover Cluster(默认策略)
失败自动切换,当出现失败,重试其它服务器,通常用于读操作.重试会带来更长延迟,默认重试3次。
Failfast Cluster
快速失败,只发起一次调用,失败立即报错,通常用于非幂等写操作,如果有机器正在重启,可能会出现调用失败。
Failsafe Cluster
失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作,调用信息丢失
Failback Cluster
失败立即返回,后台记录失败请求,定时重试,通常用于消息通知操作。不可靠,重启丢失
Forking Cluster
并行调用多个服务器,只要一个成功即返回,通常用于实时性要求较高的读操作,需要浪费更多服务资源
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错,通常用于更新提供方本地状态,速度慢,任意一台报错则报错

配置优先级

dubbo支持动态配置,可以配置dubbo从配置中心获取配置,只要指定配置中心,支持apollo、zookeeper、nacos等等,通过dubbo:config-center标签或者ConfigCenterConfig类配置。
配置优先级,从上到下依次降低。

  • JVM System Properties,-D参数
  • Externalized Configuration,外部化配置
  • ServiceConfig、ReferenceConfig等编程接口采集的配置
  • 本地配置文件dubbo.properties

线程模型

  • 通过protocol标签,dispatcher参数配置线程池使用范围,threads配置线程数,threadpool配置线程池类型。
  • 如果服务处理时,只是纯内存操作,则直接用io线程处理即可(io线程即服务端与客户端建立连接后,处理连接的线程),如果还有耗时的io操作(比如数据库操作),则需要派发到线程池,否则无法接受新的io请求。

Dispatcher配置

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

ThreadPool

  • fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(默认策略)
  • cached 缓存线程池,空闲一分钟自动删除,需要时重建。
  • limited 可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题。
  • eager 优先创建Worker线程池。在任务数量大于corePoolSize但是小于maximumPoolSize时,优先创建Worker来处理任务。当任务数量大于maximumPoolSize时,将任务放入阻塞队列中。阻塞队列充满时抛出RejectedExecutionException。(相比于cached:cached在任务数量超过maximumPoolSize时直接抛出异常而不是将任务放入阻塞队列)

服务分组

可以通过provider标签和consumer标签的group参数,为所有服务提供者和消费者配置分组。也可以通过service标签和reference标签为某个服务注册或消费指定分组。
基于环境变量,可以实现共用一个注册中心,而互不影响。

支持分组聚合,如果一个接口有不同实现,可以指定不同分组,通过merger参数,可以聚合不同分组的返回接口。

多版本

如果服务进行了不兼容旧版的升级,服务端和消费端可以通过version参数指定版本号,消费端只消费对应版本号的服务提供者,这样旧服务还是消费旧的,新升级的升级新的,就不用停止服务,无感发布。

参数校验

服务提供validation 参数设为true可以开启dubbo方法参数校验

缓存

参数cache 可以缓存查询接口,当数据不会变动时,由dubbo缓存结果。
策略

  • lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。
  • threadlocal 当前线程缓存
  • jcache 与JSR107集成,可以桥接各种缓存实现。

回声测试 用于检测某个服务是否可用

强转成EchoService类并调用 e c h o 即 可 , 每 个 服 务 都 会 被 添 加 该 接 口 , 代 理 中 判 断 echo即可,每个服务都会被添加该接口,代理中判断 echoecho方法则会被代理,直接返回第一个参数值。

隐式传参

通过RpcContext的setAttachment方法和getAttachment方法可以设置和获取参数。
类似cookie穿参,需要注意的是每次发起新的dubbo调用RocCOntent都会更新,变成新的调用上下文。setAttachment方法每调用一次远程服务,就要重新设置才有效。

异步调用

两种方式:

  1. 服务提供方方法返回值设置为CompletableFuture,通过CompletableFuture.supplyAsync进行执行业务代码。调用方通过CompletableFuture的whenComplete可以设置回调方法。
  2. 通过RpcContext调用,调用方需要在reference标签配置async参数,然后直接调用。如果需要拿到返回值, RpcContext.getContext().getCompletableFuture()可以获取到获取到CompletableFuture,添加回调。

标签上的sent参数可以设置是否等待消息发出,如果设置false则不等待,消息进入io队列就返回。如果不需要返回值,可以设置return参数为false,则会忽略返回值,减少CompletableFuture管理。

代理客户端调用,在客户端执行额外的逻辑

<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />

package com.foo;
public class BarServiceStub implements BarService {
     
    private final BarService barService;
    
    // 构造函数传入真正的远程代理对象
    public BarServiceStub(BarService barService){
     
        this.barService = barService;
    }
 
    public String sayHello(String name) {
     
        // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
        try {
     
            return barService.sayHello(name);
        } catch (Exception e) {
     
            // 你可以容错,可以做任何AOP拦截事项
            return "容错数据";
        }
    }
}

异常处理

服务方调用提供方如果异常了,不想报错,可以用reference的mock参数设置处理方案。

  1. return null,报错时就不会抛出异常,而是直接返回空。还可以支持empty基本数据类型默认值、true、false。或者是
  2. throw,默认抛出RPCException,或者throw com.foo.MockException 抛出指定的异常

延迟暴露

2.6.5以前的版本不会在spring初始化之后再暴露服务,之后的版本则会。通过service标签的delay可以指定延迟时间,单位时毫秒,如果写入-1则表示在spring初始化后暴露服务。

死锁问题
在spring初始化未结束前暴露服务,如果服务中使用了applactionContent.getBean去获取对象,在这个时候接受到请求可能会导致死锁。因为getBean方法会先判断bean是否存在,会先同步singletonObjects去获取bean,获取不到再同步beanDefinitionMap进行初始化,并再同步singletonObjects写入bean。而spring初始化时是不用先获取singletonObjects同步的,因为一开始是肯定没有的,所以初始化是直接先同步beanDefinitionMap再去同步singletonObjects。这样两边获取锁的顺序刚好相反,就可能触发死锁。

避免死锁
避免用applactionContent.getBean获取对象,或让dubbo延迟暴露,

控制方法的并发执行数

dubbo:service标签,executes参数限制服务提供方的并发数,也就是各客户端并发执行总数的限制。actives参数限制单个客户端的并发执行数

控制方法的连接数

accepts参数限制服务端连接数,connections参数限制客户端连接数

dubbo协议 长连接的延迟连接

使用dubbo协议时,通过lazy="true"可以让长连接在发起调用时才创建。

粘滞连接 默认开启

默认同一客户端会一直往同一提供者发起调用,除非该提供者挂了,再连另一台。减少长连接创建。通过sticky参数设置

token验证

token参数设置为true,token由注册中心,可以客户端避免绕过注册中心,直接返回服务提供者

服务降级

通过mock参数,fail:return+null,可以在失败时返回null,而不报错

平滑退出

java支持注册退出钩子,在jvm退出时执行钩子,通过Runtime.addShutdownHook(Thread hook)注册钩子,退出时jvm会执行集合所有hook并调用。dubbo也是通过这种形式,因此通过非强制kill、正常退出、System.exit都会调用钩子。

日志适配

  • 命令行 java -Ddubbo.application.logger=log4j
  • 在 dubbo.properties 中指定 dubbo.application.logger=log4j
  • dubbo.xml 中配置

输出dubbo调用日志


容器类型

支持Spring、Jetty、Log4j
默认只加载spring
通过 main 函数参数传入要加载的容器

  • java org.apache.dubbo.container.Main spring jetty log4j
  • 通过 JVM 启动参数传入要加载的容器java org.apache.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j
  • 通过 classpath 下的 dubbo.properties 配置传入要加载的容器dubbo.container=spring,jetty,log4j

ReferenceConfig缓存

ReferenceConfig 可以通过 ReferenceConfigCache获取,避免重复生成

dubbo协议对比

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uU0bztOb-1585713006969)(evernotecid://EA56B0F8-13B5-4B11-8191-1E827D9FC2A6/appyinxiangcom/22897236/ENResource/p18)]

怎么抛出自定义异常

  • 抛出的是一个Exception 而非 RuntimeException
  • 在方法签名上有声明
  • 异常类和接口声明在同一个包里
  • 是JDK 自身异常
  • 是dubbo 自身异常

其他情况会变成RuntimeException抛到客户端


内容参考dubbo官网

你可能感兴趣的:(rpc)