查看Dubbo完整配置示例,参考官方文档。
【1】启动时检查
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"
。
可以通过 check="false"
关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check="false"
,总是会返回引用,当服务恢复时,能自动连上。
① 通过 spring 配置文件
关闭某个服务的启动时检查 (没有提供者时报错):
<dubbo:reference interface="com.foo.BarService" check="false" />
<dubbo:consumer check="false" />
关闭注册中心启动时检查 (注册订阅失败时报错):
<dubbo:registry check="false" />
② 通过 dubbo.properties
dubbo.reference.com.foo.BarService.check=false dubbo.reference.check=false dubbo.consumer.check=false dubbo.registry.check=false
③ 通过 -D 参数
java -Ddubbo.reference.com.foo.BarService.check=false java -Ddubbo.reference.check=false java -Ddubbo.consumer.check=false java -Ddubbo.registry.check=false
④ 配置的含义
dubbo.reference.check=false
,强制改变所有 reference 的 check 值,就算配置中有声明,也会被覆盖。如<dubbo:reference interface="com.foo.BarService" check="true" />
虽然设置了true,同样按照false处理。
dubbo.consumer.check=false
,是设置 check 的缺省值,如果配置中有显式的声明,如:<dubbo:reference interface="com.foo.BarService" check="true" />
,则不会受影响。那些没有显示设置check的reference 将会受到影响。
dubbo.registry.check=false
,前面两个都是指订阅成功,但提供者列表是否为空是否报错,如果注册订阅失败时,也允许启动,需使用此选项,将在后台定时重试。
⑤ Dubbo的配置
Dubbo的配置有xml配置、属性配置、API配置和注解配置。XML配置和注解配置就是常规项目中用到的两种方式,一种是完全使用xml,如传统项目;一种似乎使用注解代替,如SpringBoot。详情参考官方文档。
这里说一下属性配置,如上面看到的①②③。
如果公共配置很简单,没有多注册中心,多协议等情况,或者想多个 Spring 容器想共享配置,可以使用 dubbo.properties 作为缺省配置。
Dubbo 将自动加载 classpath 根目录下的 dubbo.properties,可以通过JVM启动参数-Ddubbo.properties.file=xxx.properties
改变缺省配置位置。
覆盖策略
JVM 启动 -D 参数优先
,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。
XML 次之,
如果在 XML 中有配置,则 dubbo.properties 中的相应配置项无效。
Properties 最后
,相当于缺省值,只有 XML 没有配置时,dubbo.properties 的相应配置项才会生效,通常用于共享公共配置,比如应用名。
【2】服务调用超时设置和重试次数
① 超时timeout属性设置
当调用一个服务长时间得不到响应的时候,并不会让其一直阻塞等待下去。大量线程阻塞等待响应会引起性能下降,这时就需要设置超时属性-timeout。
dubbo:reference
的timeout属性缺省使用<dubbo:consumer>
的timeout属性,而后者的timeout属性缺省值为1000。参考schema配置参考手册。
服务消费者配置实例如下:
<dubbo:reference interface="com.web.gmall.service.UserService" id="userService" timeout="5000" > <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method> </dubbo:reference> <!-- 配置当前消费者的统一规则:所有的服务都不检查 --> <dubbo:consumer check="false" timeout="5000"></dubbo:consumer>
服务提供者配置实例如下:
<!-- 暴露服务 ref:指向服务的真正的实现对象 --> <dubbo:service interface="com.web.gmall.service.UserService" ref="userServiceImpl" timeout="1000" > <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method> </dubbo:service> <!--统一设置服务提供方的规则 --> <dubbo:provider timeout="1000"></dubbo:provider>
这时候就有有个问题,哪个配置的timeout属性生效?
在官方文档xml配置中,说明了该问题。
② xml配置覆盖关系
以 timeout 为例,显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似:
- 方法级优先,接口级次之,全局配置再次之。
- 如果级别一样,则消费方优先,提供方次之。
其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。
建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置。
理论上 ReferenceConfig 的非服务标识配置,在 ConsumerConfig,ServiceConfig, ProviderConfig 均可以缺省配置。
③ 重试次数retries属性
为了提高可用性和冗错率,可以设置retries属性。dubbo:reference
的retries属性缺省使用<dubbo:consumer>
的retries属性。而<dubbo:consumer>
的retries属性默认值为2。
消费者实例设置如下:
<dubbo:reference interface="com.web.gmall.service.UserService" id="userService" timeout="5000" retries="3" > <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method> </dubbo:reference> //设置retries="3"表示除第一次调用外再尝试调用三次,总共调用四次。
如果服务提供者如UserService有多个实例,消费者重试的时候还会尝试调用其他UserService实例。
那么是否所有情况下都应该设置重试次数?答案当然是否定的!
如果每次的操作结果都一样,那么应该可以进行重试,这种情况称之为幂等
。如果每次操作结果都不一样,那么不应该能够重试,这种情况称之为非幂等
。幂等操作如查询、删除和修改,非幂等操作如新增。
举个例子,如消费者调用提供者进行数据库新增,在提供者插入数据库过程中,消费者超时了,这时就不应该再次重试。
禁止重试,只需要将retries设置为0即可。如下所示:
<dubbo:reference interface="com.web.gmall.service.UserService" id="userService" timeout="5000" retries="3" version="*"> <dubbo:method name="insertUser" timeout="1000" retries="0"></dubbo:method>
接口级别可以重试三次,insertUser该方法禁止重试。
故而在系统设计的时候重试次数一定要额外注意,该值默认值为2。
【3】多版本
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
- 0.在低压力时间段,先升级一半提供者为新版本
- 1.再将所有消费者升级为新版本
- 2.然后将剩下的一半提供者升级为新版本
老版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
新版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="2.0.0" />
老版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
新版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />
如果不需要区分版本,可以按照以下的方式配置 :
<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
这样就可以从旧版本平滑过渡到新版本,也就是Dubbo官网介绍的灰度发布。
【4】本地存根Stub
远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。
dubbo:service的stub属性
值类型为class/boolean
,默认值为false。设为true,表示使用缺省代理类名,即:接口名 + Local后缀,服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)
实例
如下所示,这里将接口抽取到了gmall-interface项目中,order-service-consumer和user-service-provider均依赖该项目。故而在gmall-interface项目中,添加UserServiceStub即可。
UserServiceStub实例如下:
public class UserServiceStub implements UserService { private final UserService userService; /** * 传入的是userService远程的代理对象 * @param userService */ public UserServiceStub(UserService userService) { super(); this.userService = userService; } @Override public List<UserAddress> getUserAddressList(String userId) { // TODO Auto-generated method stub System.out.println("UserServiceStub....."); if(userId!=null&&userId!="") { return userService.getUserAddressList(userId); } return null; } }
在user-service-provider项目中配置如下:
<dubbo:service interface="com.web.gmall.service.UserService" ref="userServiceImpl" timeout="1000" stub="com.web.gmall.service.stub.UserServiceStub" > <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method> </dubbo:service>
分别启动provider和consumer,测试如下:
用户id:1 UserServiceStub..... 北京市昌平区宏福科技园综合楼3层 深圳市宝安区西部硅谷大厦B座3层(深圳分校) 调用完成...
可以看到在发起远程调用之前,首先调用了UserServiceStub 。