SpringBoot下Dubbo的多配置中心使用总结(包括注解使用的坑)
阅读原文时间:2021年04月25日阅读:1

一、前言

在SpringBoot的Dubbo多配置中心遇到了比较多的坑,所以做个总结,对遇到此问题的人,可能会有很大的帮助。
我在项目服务消费方的模块中声明了一个接口,我希望我只做一个类似网关(可以想象成这样)的服务,将获取的消息,通过接口传递到另一个微服务(服务提供方可能有多个,但是接口只有一个),这样其他服务围绕我的服务作为入口,调用服务只需要通过我的路由配置,就可以调用不同服务的相同接口的实现。

二、配置文件具体实现

提一点,通过配置文件的方式非常清楚,能够清晰地明白多注册中心的使用。

要点:

(1)register group,仅仅用来区分注册中心,如果注册中心不同,就新配置一个。

(2)service group是服务的(也用其他属性区分,比如配置文件中可以用id区分),不管注册中心的group是相同还是不同,这个必须存在。这个group是dubbo判断一个接口的依据,如果group不同,那么就是不同的接口,dubbo会生成一个新的代理。这个group必须全局唯一。

1、服务提供者配置文件

provider1.xml: 

   <dubbo:application name="dubbo-provider1" owner="u"/>
   <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" check="false"
                    group="provider1"/>
   <dubbo:protocol name="dubbo" port="20880" host="127.0.0.1"/>

   <dubbo:service interface="com.example.demo.service.DemoService" ref="providerService" protocol="dubbo"/>
   <bean id="providerService" class="com.example.demo.service.impl.ProviderServiceImpl"/>

provider2.xml:

   <dubbo:application name="dubbo-provider2" owner="l"/>
   <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" check="false"
                    group="provider2"/>
   <dubbo:protocol name="dubbo" port="20881" host="127.0.0.1" />

   <dubbo:service interface="com.example.demo.service.DemoService" ref="provider2Service" protocol="dubbo"/>
   <bean id="provider2Service"class="com.example.demo.service.impl.Provider2ServiceImpl"/>

2、服务消费者配置文件

consumer.xml:

    <dubbo:application name="dubbo-consumer"/>
    <dubbo:registry id="p1" check="false" protocol="zookeeper" address="127.0.0.1:2181" group="provider1"/>
    <dubbo:registry id="p2" check="false" protocol="zookeeper" address="127.0.0.1:2181" group="provider2"/>
    <dubbo:protocol name="dubbo" port="20883" host="127.0.0.1"/>

    <dubbo:reference interface="com.example.demo.service.DemoService" id="providerService" registry="p1"/>
    <dubbo:reference interface="com.example.demo.service.DemoService" id="provider2Service" registry="p2"/>

从服务提供者和消费者可以看出:服务提供方注册到了zookeeper不同的配置中心上,通过group来区分不同的注册中心,而不同的服务方提供了不同的Id来区分。(提问:明显不同的服务注册到不同的注册中心上,为什么要用不同Id来区分?)

从服务消费者配置中心上,我们注册了2个配置中心p1和p2。而同一个接口所提供的服务service是通过id来区分,并且到不同registry来进行消费,毕竟要在同一个注册中心中才能找到服务提供者。

3、其他Java类

我这里使用的是SpringBoot的方式启动。
DemoApplication.Java

微服务提供者1
@SpringBootApplication
@ImportResource(value = {"classpath:providers1.xml"})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

微服务提供者2
@SpringBootApplication
@ImportResource(value = {"classpath:providers2.xml"})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

服务消费者
@SpringBootApplication
@ImportResource(value = {"classpath:comsumers.xml"})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

DemoService.java

public interface DemoService {
    String sayHello(String name);
}

public class Provider1ServiceImpl implements DemoService {
    @Override
    public String sayHello(String name) {
        return "hello world ! ";
    }
}

public class Provider2ServiceImpl implements DemoService {
    @Override
    public String sayHello(String name) {
        return "hello java!!!";
    }
}

DemoController.java

@Controller
public class DemoController {
    @Resource
    private DemoService provider2Service;
    @Resource
    private DemoService providerService;

    @RequestMapping(value = "/service")
    @ResponseBody
    public String demo() {
        return providerService.sayHello("");
    }

    @RequestMapping(value = "/service2")
    @ResponseBody
    public String demo2() {
        return provider2Service.sayHello("");
    }

}

4、启动运行查看效果

先启动provider查看效果,我这里使用的ZooInspector,有没有大佬推荐点其他好用的管理界面,这个管理界面毕竟慢,而且连接看不全,如下图,我们可以看到2个提供者分别在provider1和provider2下面。

启动消费者,启动后如下:

下面我调用下controller测试一下。

配置文件的dubbo多配置中心到这里就基本成功了,下面我们来看基于Reference注解的配置实现,这里面非常坑。

三、Reference注解并基于Springboot的yml文件

重要重要重要!!!。反正我被坑了。

consumer配置

#dubbo通用配置
dubbo:
  #多注册中心的配置
  registries:
    provider1:
      group: p1
      protocol: zookeeper
      address: 127.0.0.1:2181
    provider2:
      group: p2
      protocol: zookeeper
      address: 127.0.0.1:2181
  application:
    name: consumer
  config:
    #配置多个
    multiple: true
  protocol:
    id: dubbo
    name: dubbo
    # dubbo端口
    port: 20882
    threadpool: fixed
    threads: 200
  consumer:
    filter: tracing
    timeout: 2000
    check: false
spring:
  application:
    name: consumer




DemoController.java

@Controller
public class DemoController {

    @Reference(registry = "provider1")
    private DemoService providerService;
    @Reference(registry = "provider2")
    private DemoService provider2Service;

   @RequestMapping(value = "/service")
    @ResponseBody
    public String demo() {
        System.out.println("providerService: "+providerService.toString());
        return providerService.sayHello("");
    }

    @RequestMapping(value = "/service2")
    @ResponseBody
    public String demo2() {
        System.out.println("provider2Service: "+provider2Service.toString()+"");
        return provider2Service.sayHello("");
    }

}

从消费者的配置可以看到,我们这里使用了2个配置中心provider1和provider2(为了使registry的使用看起来更明显我改为了p1和p2),在controller中引用了provider1和provider2注册中心,细心的朋友,可能发现我添加了一段代码,将service打印出来,并且加了一个信息的字符串,为什么加这个,肯定是debug用。那么配置这样感觉是没有问题,但是一运行后,问题就来了(为了暴露这个问题,我的服务提供方的代码不变),我们先启动consumer。

启动结果如上图所示,服务提供者和消费者都有,看起来是没有问题的,那么调用试试。

如图所示,分别调用了service和service2,发现结果一样,what?why?。那我们在通过打印的日志来看看。

从打印的日志来看,我们的service是一个对象,也就是说都指向了一个服务提供者,但是我们明明使用了不同的配置中心,为什么会这样呢!这个bug该怎么改呢!debug吧。我们知道至少我的hello world是成功的提供了服务。那么我们将hello world的服务提供者关闭后再次试试。

我们现在看到只有provider2有提供者,再次打开消费者试试。

从上图,我们看到消费者启动成功了,并且成功的注册了服务,但是为什么provider1也有消费者呢,服务提供者在那,继续看看hashcode

显示是同一个对象,调用后的结果都是hello java。
看到这里好像还是有点迷惑了,2个都开启的时候,调用结果为hello world,但是关闭hello world调用结果是hello java,也就是说再2个提供者都提供服务时,hello world 会覆盖hello java?其实不是这样,service并没有被registry区分,链接认为就应该消费第一个对象,再看看Reference注解的源码,我们可以使用什么参数来进行区分。

public @interface Reference {
    Class<?> interfaceClass() default void.class;

    String interfaceName() default "";

    String version() default "";

    String group() default "";

    String url() default "";

    String client() default "";

    boolean generic() default false;

    boolean injvm() default false;

    boolean check() default true;

    boolean init() default false;

    boolean lazy() default false;

    boolean stubevent() default false;

    String reconnect() default "";

    boolean sticky() default false;

    String proxy() default "";

    String stub() default "";

    String cluster() default "";

    int connections() default 0;

    int callbacks() default 0;

    String onconnect() default "";

    String ondisconnect() default "";

    String owner() default "";

    String layer() default "";

    int retries() default 0;

    String loadbalance() default "";

    boolean async() default false;

    int actives() default 0;

    boolean sent() default false;

    String mock() default "";

    String validation() default "";

    int timeout() default 0;

    String cache() default "";

    String[] filter() default {};

    String[] listener() default {};

    String[] parameters() default {};

    String application() default "";

    String module() default "";

    String consumer() default "";

    String monitor() default "";

    String[] registry() default {};
}

从上面可以看出,还是有不少可以用的参数,比如group和version,我们尝试一下group。

 @Reference(registry = "provider1",group = "p1")
    private DemoService providerService;
    @Reference(registry = "provider2",group = "p2")
    private DemoService provider2Service;

添加group ,重启consumer后,结果如下图。


consumer和providers都有,我们调用试试,结果如下图,2个service都一样,为什么就找不到了呢。

consule查看到错误,没有提供者。从url中我们看到group=p1 ,其实这个就相当于一个id,那么我们需要做的就是让提供者的连接变得跟下面的连接相同。

No provider available in [invoker :interface com.example.demo.service.DemoService
zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?
application=consumer&default.check=false&default.timeout=2000&dubbo=2.6.2&group=p1&interface=com.example.demo.service.DemoService&methods=sayHello&pid=52809&re
gister.ip=127.0.0.1&side=consumer&timestamp=1552618215486, 

提供者配置文件需要添加的代码,在service中添加group(如果使用version需要加version):

<dubbo:service interface="com.example.demo.service.DemoService" ref="providerService" protocol="dubbo" 
group="p1"/>
    <bean id="providerService" class="com.example.demo.service.impl.ProviderServiceImpl"/>


 <dubbo:service interface="com.example.demo.service.DemoService" ref="provider2Service" protocol="dubbo" 
group="p2"/>
    <bean id="provider2Service" class="com.example.demo.service.impl.Provider2ServiceImpl"/>

重启提供者服务,调用服务,从下图,我们可以看到,服务成功区分开来。

其他区分方法类似。总的来说,还是对SpringBoot的dubbo注解不太了解,导致踩坑太多,希望对遇到此情况的人有所帮助。

Tips:
1. 认真检查配置文件,看看配置文件是否出了问题。
2. 如果找不到提供者,可以多分析分析dubbo的连接(我贴出来的那个)是否出了问题,跟提供者有什么区别。
3. 多借助外部工具,碰到问题能够帮助分析。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器