使用注解方式构建dubbo服务

前言

Dubbo是阿里巴巴开源的一个高性能优秀的服务框架,通过使用RPC实现服务调用。在业界尤其国内使用广泛。下面就从头开始构建dubbo的简单demo,配置使用注释方式完成,以zookeeper为注册中心。

构建项目

以 IntelliJ IDEA 为例,创建一个多模块的项目,项目结构如下图所示。

dubbo注解 发布服务 dubbo通过注解注册服务_dubbo

其中,dubbo-demo是父项目,其下有3个子项目,分别是:

  1. dubbo-server 服务提供者,提供服务接口具体实现,对外提供服务。
  2. dubbo-client 服务调用者,充当客户端角色,调用服务。
  3. dubbo-api 定义接口,为以上两者充当桥梁作用,目的解耦。

需要注意的是,dubbo-demo作为父项目,它的依赖可用以子项目,且其packaging的值为pom

dubbo注解 发布服务 dubbo通过注解注册服务_dubbo注解 发布服务_02

其他3个项目即为普通的maven项目,打包方式为jar(packaging),dubbo-server及dubbo-client均依赖dubbo-api。

以dubbo-server为例,其pom.xml(部分)为

dubbo注解 发布服务 dubbo通过注解注册服务_zookeeper_03

标注第一个框指明其继承的父项目,第二个框表示依赖了dubbo-api。
dubbo-client与此相似。

前期准备

本文使用zookeeper作为服务注册中心,首先要保证zookeeper服务可以正常运行。这里使用本地zookeeper为例。

进入ZOOKEEPER_HOME/bin 目录,执行

sudo ./zkServer.sh start

命令即可。默认zookeeper端口为2181,不必修改。

定义接口

为解耦以及架构清晰,于是有了dubbo-api子项目。当然这个项目不是必须的,接口的定义也可以放在dubbo-server(即服务提供方),但为以后扩展方便,对外接口统一由dubbo-api定义。

package com.wthfeng.dubboapi.service;

/**
 * @author wangtonghe
 * @since 2018/9/6 09:57
 */
public interface HelloService {

    String sayHello(String name);
}

很简单,定义一个需要实现的接口即可。

提供服务

dubbo-server负责暴露并提供服务。

1. 首先,编写一个定义dubbo配置类。
@Configuration
public class DubboConfig {

    @Resource
    private DubboProperties dubboProperties;

    /**
     * 应用名配置,等同于 <dubbo:application name="xxx"  />
     *
     * @return ApplicationConfig
     */
    @Bean
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName(dubboProperties.getName());
        return applicationConfig;
    }

    /**
     * 注册中心配置,等同于 <dubbo:registry address="url" />
     *
     * @return RegistryConfig
     */
    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress(dubboProperties.getAddress());
        registryConfig.setClient(dubboProperties.getClient());
        return registryConfig;
    }

    /**
     * 协议配置,等同于 <dubbo:protocol name="dubbo" port="20880" />
     *
     * @return ProtocolConfig
     */
    @Bean
    public ProtocolConfig protocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName(dubboProperties.getProtocolName());
        protocolConfig.setPort(dubboProperties.getProtocolPort());
        return protocolConfig;
    }

注释写的很清楚了,DubboProperties 是加载的一个配置类,内容如下:

application.dubbo.demo.server.name=dubbo-server
application.dubbo.demo.server.address=zookeeper://127.0.0.1:2181
application.dubbo.demo.server.client=zkclient
application.dubbo.demo.server.protocolName=dubbo
application.dubbo.demo.server.protocolPort=20880

从配置类可以看出,注册中心使用的是zookeeper,这个稍后再说,先这样写着。下面看看暴露服务的设置。

2. 编写一个实现类实现要暴露的接口。
package com.wthfeng.dubboserver.service;

import com.alibaba.dubbo.config.annotation.Service;
import com.wthfeng.dubboapi.service.HelloService;

/**
 * @author wangtonghe
 * @since 2018/9/5 17:46
 */
@Service(timeout = 5000, version = "1.0", group = "demo-dubbo")
public class HelloServiceImpl implements HelloService {


    @Override
    public String sayHello(String name) {
        String value = "Hello " + name + " !";
        return value;
    }
}

注意:@Service 这个注解是dubbo的用于暴露服务的注解。而不是spring的那个注解!其中timeout为调用该服务的超时时间,version为版本号,group为分组。interface这里指HelloService,

interface、group、version三者确定一个服务。

3. 最后,需要在spring boot 启动类中配置dubbo的暴露服务的包的扫描路径,即将HelloServiceImpl这样的类放于一个包中使其能扫描到。
@SpringBootApplication
@DubboComponentScan(value = "com.wthfeng.dubboserver.service")
public class DemoServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoServerApplication.class, args);
    }
}

至此,服务提供者的配置就完成了。下面看看服务消费者的配置。

消费服务

1. 设置配置文件

配置文件和服务提供者的类似,直接贴出了。

@Configuration
public class DubboClientConfig {

    @Resource
    private DubboProperties dubboProperties;

    /**
     * 应用名配置,等同于 <dubbo:application name="xxx"  />
     *
     * @return ApplicationConfig
     */
    @Bean
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName(dubboProperties.getName());
        return applicationConfig;
    }

    /**
     * 注册中心配置,等同于 <dubbo:registry address="url" />
     *
     * @return RegistryConfig
     */
    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress(dubboProperties.getAddress());
        registryConfig.setClient(dubboProperties.getClient());
        return registryConfig;
    }

    /**
     * 协议配置,等同于 <dubbo:protocol name="dubbo" port="20880" />
     *
     * @return ProtocolConfig
     */
    @Bean
    public ProtocolConfig protocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName(dubboProperties.getProtocolName());
        protocolConfig.setPort(dubboProperties.getProtocolPort());
        return protocolConfig;
    }
}
2. 引用服务
@Component
public class BusinessService {

    /**
     * 引用服务,与 <dubbo:reference/> 等同。注意这里的version和group要和暴露服务的一致
     */
    @Reference(version = "1.0", group = "demo-dubbo")
    private HelloService helloService;


    public void testHello(String name) {
        String str = helloService.sayHello(name);
        System.out.println("调用结果:" + str);
    }
}

@Reference 注解为dubbo服务调用的注解,和dubbo中@Service 注解对应,尤其是version和group以及interface字段两者一定要一致。

3. 设置扫描包地址
@SpringBootApplication
@DubboComponentScan(value = "com.wthfeng.democlient.service")
public class DemoClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoClientApplication.class, args);
    }
}

在dubbo-client项目启动类中,添加dubbo包扫描地址。

项目启动

据此,dubbo项目构建完毕。为测试方便,在dubbo-client的测试包下添加一个测试类,如下

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoClientApplicationTests {

    @Resource
    private BusinessService businessService;

    @Test
    public void contextLoads() {
        businessService.testHello("dubbo");

    }
}

在zookeeper正常运行的前提下,先启动dubbo-server,再运行此测试用例。输出:

调用结果:Hello dubbo !

表示构建成功。

项目架构

目前,我们可以把上述项目及组件整理分类一下。可分为服务调用房,服务提供者,注册中心,(dubbo-api为系统解耦,不作为系统部分)。

根据dubbo文档及资料描述,流程如下:

  1. 启动zookeeper及服务提供方(Provider)
  2. Provider 向注册中心注册服务(告诉zookeeper我都有什么服务)
  3. Consumer(消费方)订阅需要的服务(告诉zookeeper我需要什么服务)
  4. 注册中心告知消费者服务所在地址(zookeeper告知消费者其需要服务的地址列表)
  5. 消费者调用服务

流程图如下(来自官网):

dubbo注解 发布服务 dubbo通过注解注册服务_zookeeper_04

其他补充

  1. 图中5表示,消费者和提供服务者,需要定时向监控中心发送调用次数等的数据,便于监控中心统计,此处不是服务所必须的
  2. 注册中心还需时刻保持与服务提供者的联系(通过心跳包),以确定提供者在线,若其下线,需及时告知消费者。
  3. 图中3也表示,当服务列表变化时,向消费者推送变更通知。