AnthonyZero's Bolg

Dubbo日常使用总结

dubbo

check启动检查

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=”true”。

可以通过 check=”false” 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。

如果容器懒加载,则需要关闭check以保证应用启动时对可能被懒加载引用的dubbo服务bean不要被检查

设置为false则可以不影响应用的启动,后续服务初始化完成后依然可以正常连上。也就是说check=false的配置本身可以解决循环依赖的问题,只是如有加载了dubbo服务引用的bean要应用懒加载策略,需要check设为false来防止出现未完全装配的bean影响应用的正常运行。

超时(timeout)与重试机制(retires)

dubbo通过设置短的超时机制+重试次数,马上将当前超时服务提供者provider切换到其他可用的provider,通过这种思路实现快速响应。但对于一些新增大量记录的操作来说,有可能因为设定时间内,provider无法响应完成,客户端执行n次重试,导致新增记录操作的请求发了n次,数据库里这时候保存了n多条记录数据。暂时解决方式是将那些响应时间相对久一些的服务,它的超时时间设置长一点,并将重试次数retries设置为0

超时机制的规则是如果在一定的时间内,provider没有返回,则认为本次调用失败
重试机制在出现调用失败时,会再次调用。如果在配置的调用次数内都失败,则认为此次请求异常,抛出异常。(dubbo默认重试2次)

1
<dubbo:service interface="com.anthony.DempApiService" ref="dempApiService" timeout="12000" retries="0"/>

属性配置的优先级

我们在dubbo的使用过程中会发现,提供者跟消费者中,很多属性是一样的,我们该怎么配呢?在dubbo的文档当中其实有推荐的用法。
在提供者端尽量多提供消费者端的属性。原因如下:

  • 作为服务的提供方,比服务消费方更清楚服务的性能参数,如调用的超时时间、合理的重试次数等
  • 在 Provider 端配置后,Consumer 端不配置则会使用 Provider 端的配置,即 Provider 端的配置可以作为 Consumer 的缺省值 。否则,Consumer 会使用 Consumer端的全局设置,这对于 Provider 是不可控的,并且往往是不合理的.

Provider 端尽量多配置 Consumer 端的属性,让 Provider 的实现者一开始就思考 Provider 端的服务特点和服务质量等问题。

dubbo使用内网ip

正常情况下,我们的服务调用推荐走内网连接的方式,效率是比较高的。但是有些特殊的情况,我们需要dubbo注册服务的时候使用外网ip,该怎么修改呢?这时候就需要修改我们的服务器上 /etc/hosts 文件了,新增一条 “外网ip 主机名”的记录,restart我们的服务即可。

Mock机制

Mock是SOA之中的一个很有用的功能,不仅可以用来进行服务降级,也可以用来在测试中模拟服务调用的各种异常情况。dubbo框架里面的mock是在服务使用者这一端实现的

在reference标签里,有一个参数mock,该参数有四个值,false,default,true,或者Mock类的类名。分别代表如下含义:

  • false,不调用mock服务。
  • true,当服务调用失败时,使用mock服务。
  • default,当服务调用失败时,使用mock服务。
  • force,强制使用Mock服务(不管服务能否调用成功)。(使用xml配置不生效,使用ReferenceConfigAPI可以生效)

使用方法:

  • 将mock参数启用,在dubbo:reference中添加参数项mock=true。
  • 实现需要调用的服务接口, 且命名必须是该接口名+Mock,例如原接口是com.anthony.demo.DemoService 则实现类Class必须是com.anthony.demo.DemoServiceMock, 可以见到接口和Mock实现类在同一个package下面。

version参数:给服务设定版本

1
@Service(version="1.0.0")

一个Api服务可以有多个实现类,不同的版本并注册了多个地址到注册中心,消费者调用时,注入服务接口时加上版本。
如果需要进行灰度发布,一半服务可以使用1.0.0版本的接口,一半可使用2.0.0的接口

如果生产者注册的服务设置了版本,消费者必须指定版本,如果生产者没设置版本,消费者可以不指定版本。官方推荐做法是设置版本

group参数

服务分组,当一个接口有多个实现,可以用分组区分

如果版本号相同,服务注册到注册中心上只有一个地址,并且只代理其中一个实现类。

现在两个实现类改成都是1.0.0版本,如果我们想要区分,必须加上group区分

生产者设置了versiongroup,消费者调用时必须同时指定version和group

1
2
3
@Service(version="1.0.0", group = "group1")

@Reference(version = "1.0.0",group = "group1")

Dubbo自定义异常

项目使用了Dubbo进行不同系统服务间的调用,当服务端发生异常时,我们希望把异常传递给消费端,由消费端对异常进行捕获并处理。但在实际使用中,发现以往的异常处理在dubbo服务中并不能奏效。例如,自定义异常类BizException继承RuntimeException,当服务端抛出这个异常时,消费端并不能捕获它,而是返回了RuntimeException。

通过Dubbo com.alibaba.dubbo.rpc.filter.ExceptionFilter源码了解到:

  1. 如果是checked异常,直接抛出.很明显,我们的BizException继承RuntimeException,是未受检异常不符合要求。
  2. 在方法签名上有声明,直接抛出
  3. 异常类和接口类在同一jar包里,直接抛出.
  4. 是JDK自带的异常,直接抛出.很明显,这个BizException是我们自定义的,不符合
  5. 是Dubbo本身的异常(RpcException),直接抛出.很明显,这个BizException是我们自定义的,和RpcException几乎没有半毛钱关系.
  6. 否则,包装成RuntimeException抛给客户端.
    因为以上6点均不满足,所以该异常会被包装成RuntimeException异常抛出(重要)

如何解决:
一 在方法签名上显式声明异常,但代码侵入性高,基本所有接口都要写,不考虑。
二 将异常类与接口类放在同一个jar包中。本人采用的此方法

注意:本人在使用dubbo2.6.5的时候遇到provider 抛出的异常,consumer 捕获到的总是 java.lang.reflect.UndeclaredThrowableException,这是dubbo的bug已经在2.6.6版本修复。