原创

Dubbo 配置


一、覆盖策略

JVM 启动 -D 参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。

XML 次之,如果在 XML 中有配置,则 dubbo.properties 中的相应配置项无效。

Properties 最后,相当于缺省值,只有 XML 没有配置时,dubbo.properties 的相应配置项才会生效,通常用于共享公共配置,比如应用名。

优先权:dubbo.properties < dubbo.xml < -D 参数

我们复制一节的项目,来演示。

1. 我们在resources 目录下创建 dubbo.propertites ,内容如下

dubbo.protocol.port=20882

2. application.properties 内容

#就是服务名,不能跟别的dubbo提供端重复
dubbo.application.name=gmall-user-provider

#是指定注册中心协议
dubbo.registry.protocol=zookeeper

#注册中心的地址加端口号
dubbo.registry.address=127.0.0.1:2181

#注解方式要扫描的包

dubbo.scan.base-package=com.gf

#是分布式固定是dubbo,不要改
dubbo.protocol.name=dubbo

#服务暴露端口
dubbo.protocol.port=20881

#表示从注册中心发现监控中心地址
dubbo.monitor.protocol=registry

3. 设置VM参数

我们运行项目测试结果如下:

这说明VM参数配置的优先权最高。

我们去掉VM参数测试如下:

我们最后再去掉application.properties中的 服务暴露端口配置,测试如下:

所以我得出dubbo的配置优先权:dubbo.properties < dubbo.xml < -D 参数

二、启动时检查

当我们先启动 服务消费者,没有先启动 服务提供者进服务注册到注册中心时,是会报错的。报错信息 为没有可用的服务提供者

我们可以通过设置来关闭 服务消费者 ,对远程服务的检查:

方式1
注解 @Reference 中有个属性 check 我为设置为 false ,就可以关闭启动是时对该服务的检查,到真正使用时才检查是否可用:

@Service
public class OrderServiceImpl implements OrderService {

    @Reference(check = false)
    UserService userService;


    @Override
    public List<UserAddress> initOrder(String userId) {
        System.out.println( "用户id : " + userId );
        //查询用户地址
        List<UserAddress> addressList = userService.getUserAddressList( userId );
        return addressList;
    }
}

我重启动消费者 ,发现已经正常了。

我们的演示项目中远程服务少 ,如果服务远程服务比较多,又想去掉启东时检测,我们一个个的设置 ,比较麻烦 ,Dubbo为我提供了统一配置:

dubbo.consumer.check=false

三、配置覆盖关系

1. timeout

以 timeout 为例,显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似:

  • 方法级优先,接口级次之,全局配置再次之。
  • 如果级别一样,则消费方优先,提供方次之。 其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。

Dubbo官方建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置。

服务端 通过 Dubbo 的@Service 注解设置实例:

@Service(timeout = 3000)
@Component
public class UserServiceImpl implements UserService{


    @Override
    public List<UserAddress> getUserAddressList(String userId) {

        List<UserAddress> list = new ArrayList<>();
        UserAddress address1 = new UserAddress(1 , "上海市杨浦区xxx路xxx号" , "1" , "王某某","0");
        UserAddress address2 = new UserAddress(2 , "上海市徐汇区xxx路xxx号" , "1" , "张某某","1");

        list.add( address1 );
        list.add( address2 );

        try {
            Thread.sleep( 4000 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return list;
    }
}

我们远程访问接口测试结果如下,出现了等待服务器端响应超时的错误。

2. retries

timeout 一般配合 retries(重试次数,不包含第一次调用)使用

@Service(timeout = 3000 , retries = 3)
@Component
public class UserServiceImpl implements UserService{


    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println("UserServiceImpl 被调用...");
        List<UserAddress> list = new ArrayList<>();
        UserAddress address1 = new UserAddress(1 , "上海市杨浦区xxx路xxx号" , "1" , "王某某","0");
        UserAddress address2 = new UserAddress(2 , "上海市徐汇区xxx路xxx号" , "1" , "张某某","1");

        list.add( address1 );
        list.add( address2 );

        try {
            Thread.sleep( 4000 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return list;
    }
}

访问远程接口 测试结果如下,“UserServiceImpl 被调用...” 会打印 4 次

通常正式环境,我们的服务提供者会部署多个 ,那么一般是调用失败后,会轮询重试各个服务,本例如果我们部署3台机器,访问的是server1 ,那么server1 会先打印1次 ,然后重试 server1、server2、server3 会各打印1次。

retries 应该在合适的时候使用,一般如果方法的访问是幂等 的,我们可以使用(一般情况下我们应该把 查询、删除、修改的方法 设计成幂等的)。

四、多版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

可以按照以下的步骤进行版本迁移:

    1. 在低压力时间段,先升级一半提供者为新版本
    1. 再将所有消费者升级为新版本
    1. 然后将剩下的一半提供者升级为新版本

实例

我们在服务提供方再创建一个UserService的实现类 UserServiceImpl2

1. UserServiceImpl

@Service(version = "1.0.0")
@Component
public class UserServiceImpl implements UserService{


    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println("UserServiceImpl 旧版本...");
        List<UserAddress> list = new ArrayList<>();
        UserAddress address1 = new UserAddress(1 , "上海市杨浦区xxx路xxx号" , "1" , "王某某","0");
        UserAddress address2 = new UserAddress(2 , "上海市徐汇区xxx路xxx号" , "1" , "张某某","1");

        list.add( address1 );
        list.add( address2 );

        return list;
    }
}

2. UserServiceImpl

@Service(version = "2.0.0")
@Component
public class UserServiceImpl2 implements UserService {


    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println( "UserServiceImpl 新版本..." );
        List<UserAddress> list = new ArrayList<>();
        UserAddress address1 = new UserAddress( 1, "上海市杨浦区xxx路xxx号", "1", "王某某", "0" );
        UserAddress address2 = new UserAddress( 2, "上海市徐汇区xxx路xxx号", "1", "张某某", "1" );

        list.add( address1 );
        list.add( address2 );

        return list;
    }
}

3. OrderServiceImpl

我们之后就可以在服务消费方的 @Reference 设置调用的服务版本。

@Service
public class OrderServiceImpl implements OrderService {

    @Reference(version = "2.0.0")
    UserService userService;


    @Override
    public List<UserAddress> initOrder(String userId) {
        System.out.println( "用户id : " + userId );
        //查询用户地址
        List<UserAddress> addressList = userService.getUserAddressList( userId );
        return addressList;
    }
}

启动服务提供者和消费者 ,访问远程接口测试结果如下,打印的是 “新版本” 说明我们调用的就是 “2.0.0” 版本的服务。这也就实现Dubbo官方说的 灰度发布

五、本地存根

远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub ,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。

1. 创建UserServiceSub

我们在服务消费方创建 UserServiceSub 并实现 UserService 接口

public class UserServiceStub implements UserService{

    private final UserService userService;

    // 构造函数传入真正的远程代理对象
    public UserServiceStub(UserService userService) {
        this.userService = userService;
    }

    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println("UserServiceStub ...");
        // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
        if (!StringUtils.isEmpty( userId )) {
            return userService.getUserAddressList( userId );
        }

        // 你可以容错,可以做任何AOP拦截事项
        return null;

    }
}

2. OrderServiceImpl

通过Dubbo 的**@Reference** 的 stub 属性 使用本地存根

@Service
public class OrderServiceImpl implements OrderService {

    @Reference(stub = "com.gf.service.impl.UserServiceStub")
    UserService userService;


    @Override
    public List<UserAddress> initOrder(String userId) {
        System.out.println( "用户id : " + userId );
        //查询用户地址
        List<UserAddress> addressList = userService.getUserAddressList( userId );
        return addressList;
    }
}

我们访问远程接口时,服务消费者会先走存根 ,验证参数userId是否为空:

dubbo
  • 作者:程序员果果
  • 发表时间:2018-11-20 15:51
  • 版权声明:自由转载-非商用-非衍生-保持署名 (创意共享4.0许可证)
  • 公众号转载:请在文末添加作者公众号二维码
  • 评论

      微信扫一扫,关注公众号 『 程序员果果 』