Dubbo常用配置
使用Dubbo作为项目的RPC框架,可以根据项目实际需要,选择Dubbo提供的自定义配置,本文介绍了Dubbo常用的配置选项
启动时检查
默认情况下,消费者服务启动时,会检查注册中心是否有启用的提供者服务,如果没有,会造成检查不通过,启动失败

有时候在开发过程中,需要绕过启动检查,可以使用check属性,将值设置为false既可
@Service
public class OrderServiceImpl implements OrderService {
@DubboReference(check = false)
UserService userService;
}
或者在配置文件中添加以下配置
dubbo:
consumer:
check: false
或者添加以下配置,效果相同
dubbo:
registry:
check: false
超时失败
在进行远程调用时,可以配置调用的超时时间,在超时时间内没有受到返回时,则视为调用失败,抛出异常或者执行容错逻辑,配置调用的超时时间,可以使用timeout属性,单位为毫秒
我们创建一个服务提供者,在执行过程中阻塞3秒,以模拟执行时间超时的情况
@Service
@DubboService
public class UserServiceImpl implements UserService {
@Override
public List<UserAddress> getUserAddressList() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
UserAddress userAddress1 = new UserAddress(1, "HZ", "00000", "小王", "15652211111", "1");
UserAddress userAddress2 = new UserAddress(2, "BJ", "00001", "小张", "17911111111", "1");
return Arrays.asList(userAddress1, userAddress2);
}
}
在服务消费者通过timeout属性配置超时时间为1秒
@Service
public class OrderServiceImpl implements OrderService {
@DubboReference(timeout = 1000)
UserService userService;
@Override
public UserAddress initOrder(String userId) {
List<UserAddress> userAddressList = userService.getUserAddressList();
for (UserAddress userAddress : userAddressList) {
if (userAddress.getUserId().equals(userId)) {
return userAddress;
}
}
return null;
}
}
执行远程调用,发现过了1秒后,消费者服务抛出超时异常

超时配置也可以在配置文件中设置,在消费者端使用以下的配置
dubbo:
consumer:
timeout: 1000
或者在提供者端使用dubbo.provider.timeout配置或者@DubboService注解的timeout属性
失败重试
当服务调用失败时,可以通过配置来指定失败后重试的次数,默认的重试次数为两次
使用之前服务超时的情景来模拟调用失败,并打印服务被调用的次数
@Service
@Slf4j
@DubboService
public class UserServiceImpl implements UserService {
private static int count = 0;
@Override
public List<UserAddress> getUserAddressList() {
count++;
log.info("调用次数:{}", count);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
UserAddress userAddress1 = new UserAddress(1, "HZ", "00000", "小王", "15652211111", "1");
UserAddress userAddress2 = new UserAddress(2, "BJ", "00001", "小张", "17911111111", "1");
return Arrays.asList(userAddress1, userAddress2);
}
}
在消费者端使用retries属性设置重试次数,注意这个重试次数指的是失败后重试的次数,例如以下配置为3,那么还会重试3次,一共就会进行4次调用
@Service
public class OrderServiceImpl implements OrderService {
@DubboReference(timeout = 1000,retries = 3)
UserService userService;
@Override
public UserAddress initOrder(String userId) {
List<UserAddress> userAddressList = userService.getUserAddressList();
for (UserAddress userAddress : userAddressList) {
if (userAddress.getUserId().equals(userId)) {
return userAddress;
}
}
return null;
}
}
在服务端查看后台日志,发现被调用了4次

在多个服务提供者的情况下默认使用轮询的方式重试
多版本
Dubbo提供了对api添加版本号,进行版本控制和灰度发布的功能,使用这个功能,可以在服务提供者端使用version属性设置Service类下的api的版本号
为了测试,我们编写一个服务的实现类,设置版本号为1.0.0
@Service
@DubboService(version = "1.0.0")
public class UserServiceImpl implements UserService {
@Override
public List<UserAddress> getUserAddressList() {
UserAddress userAddress1 = new UserAddress(1, "HZ", "00000", "小王", "15652211111", "1");
UserAddress userAddress2 = new UserAddress(2, "BJ", "00001", "小张", "17911111111", "1");
return Arrays.asList(userAddress1, userAddress2);
}
}
然后再编写一个不同返回的服务实现类,设置版本号为2.0.0
@Service
@DubboService(version = "2.0.0")
public class NewUserServiceImpl implements UserService {
@Override
public List<UserAddress> getUserAddressList() {
UserAddress userAddress1 = new UserAddress(1, "LA", "00000", "Tim", "15652211111", "1");
UserAddress userAddress2 = new UserAddress(2, "NY", "00001", "John", "17911111111", "1");
return Arrays.asList(userAddress1, userAddress2);
}
}
在消费者端,也使用version属性,来选择调用时所选择的api版本号
@Service
public class OrderServiceImpl implements OrderService {
@DubboReference(version = "1.0.0")
UserService userService;
@Override
public UserAddress initOrder(String userId) {
List<UserAddress> userAddressList = userService.getUserAddressList();
for (UserAddress userAddress : userAddressList) {
if (userAddress.getUserId().equals(userId)) {
return userAddress;
}
}
return null;
}
}
测试可以发现,调用了1.0.0版本的api

将消费者端指定调用的版本号改为2.0.0,可以发现调用了不同的版本

Dubbo还可以实现简单的灰度发布,只要将消费者调用的接口版本号设置为*,Dubbo就会随机地从现有的版本中选取一个版本进行调用
本地存根
类似Spring框架中AOP的作用,有时候调用方需要在远程调用的前后实现一些增强逻辑,类似可以做一些前置的参数验证,或者查询数据缓存,调用失败容错数据等等,但是每处调用的地方都需要写一次额外的代码太麻烦了,而且这样调用增强的逻辑和本地的业务逻辑耦合性太强,这时候可以使用本地存根功能
使用本地存根,需要实现一个自定义的Stub类,实现服务提供者的服务接口,在Stub类中使用构造器注入一个服务接口,并且在接口的实现方法中增加增强逻辑,这就实现了代理功能
@Slf4j
public class UserServiceStub implements UserService {
private final UserService userService;
public UserServiceStub(UserService userService) {
this.userService = userService;
}
@Override
public List<UserAddress> getUserAddressList() {
log.info("调用本地存根");
return userService.getUserAddressList();
}
}
在调用时使用stub属性绑定自定义的Stub类,既可使用存根功能
@Service
public class OrderServiceImpl implements OrderService {
@DubboReference(version = "*",stub = "com.blackball.service.stub.UserServiceStub")
UserService userService;
}
每次调用前会先执行存根中的日志打印增强逻辑

配置优先级
以上Dubbo的配置,都可以从消费者服务的@DubboReference注解,提供者服务的@DubboService注解中的属性做局部配置,也可以从消费者服务的dubbo.consumer配置下和提供者服务的dubbo.provider配置下做全局配置,那么如果多处都做了同样的配置,哪个配置优先生效呢?
首先按照精确度匹配,局部配置优先级高于全局配置,其次在精确度一样的情况下,消费者的配置优先级高于提供者的配置