Skip to content
Leo的技术分享
Go back

Dubbo 入门应用程序

Dubbo 是一款微服务框架,提供高性能 RPC 通信,服务发现,流量管理等服务治理能力,提供构建大规模微服务集群所需的整套解决方案。

本文讲述如何利用 Dubbo 快速构建一个完整的服务端 - 客户端程序,包括基于 XML,注解和 API 的方式实现一个 Dubbo 的 demo。

配置开发环境

本文使用 IntelliJ IDEA 作为 Dubbo 应用程序开发的 IDE。

安装 ZooKeeper

Dubbo 推荐使用 ZooKeeper (下文简称为 zk)作用注册中心,为简单起见,本文搭建一个单机版的 zk 以供 Dubbo 应用程序使用。有关 zk 集群的搭建步骤,可参考 ZooKeeper的安装与部署

在 zk 官网下载最新版本的 zk 二进制安装包,解压,将 conf 目录下 zoo_sample.cfg 文件复制为 zoo.cfg,然后在 zk 主目录执行命令,将 zk 启动起来。此时 zk 会监听默认的 2181 端口。

bin/zkServer.sh start

下载源代码

本文的 Dubbo 入门应用程序很简单,服务端接收到客户端请求,然后直接将消息返回给客户端。应用程序可以通过 XML、注解和 API 三种方式编写。

本文示例应用程序在 https://github.com/zonghaishang/dubbo-samples 可以下载。

下载后,使用 IDEA 打开,导入 pom.xml 所需的依赖后显示如下:

基于 XML 实现

Echo 服务端

定义服务端接口 EchoService ,暴露出来以供客户端使用:

package com.alibaba.dubbo.samples.echo.api;

public interface EchoService {

    String echo(String message);

}

接下来是接口的实现:

package com.alibaba.dubbo.samples.echo.impl;

import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.samples.echo.api.EchoService;

import java.text.SimpleDateFormat;
import java.util.Date;

public class EchoServiceImpl implements EchoService {

    public String echo(String message) {
        String now = new SimpleDateFormat("HH:mm:ss").format(new Date());
        System.out.println("[" + now + "] Hello " + message
                + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        return message;
    }
}

为了让 EchoService 对外正常提供服务,还需要一些额外的配置,通过 spring 配置声明以暴露服务:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 服务提供方应用名称, 方便用于依赖跟踪 -->
    <dubbo:application name="echo-provider"/>

    <!-- 使用本地zookeeper作为注册中心 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <!-- 只用Dubbo协议并且指定监听端口 20880 -->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- 通过xml方式配置为bean, 让spring托管和实例化 -->
    <bean id="echoService" class="com.alibaba.dubbo.samples.echo.impl.EchoServiceImpl"/>

    <!-- 声明服务暴露的接口,并暴露服务 -->
    <dubbo:service interface="com.alibaba.dubbo.samples.echo.api.EchoService" ref="echoService"/>

</beans>

最后是编写代码加载 spring 配置:

package com.alibaba.dubbo.samples.echo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EchoProvider {

    public static void main(String[] args) throws Exception {
        // #1 指定服务暴露配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/echo-provider.xml"});
        // #2 启动spring容器并暴露服务
        context.start();

        System.in.read();
    }
}

Echo 服务端示例代码,以及各代码所在目录位置如下图所示:

启动 EchoProvider ,控制台输出:

[08/11/20 09:50:38:038 CST] main INFO zookeeper.ZookeeperRegistry: [DUBBO] Notify urls for subscribe url provider://192.168.3.3:20880/com.alibaba.dubbo.samples.echo.api.EchoService

打开一个新的终端,输入命令连接服务端:

telnet localhost 20880

然后输入执行接口调用命令:

dubbo>invoke com.alibaba.dubbo.samples.echo.api.EchoService.echo("leehao.me")

输出:

“leehao.me” elapsed: 0 ms.

说明已正常调用了服务端口的接口。

Echo 客户端

上面我们使用 telnet 命令调用服务端接口,现在我们编写 Dubbo 客户端来调用服务端接口。

客户端通过 spring 配置引用服务端接口:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 服务消费方应用名称, 方便用于依赖跟踪 -->
    <dubbo:application name="echo-consumer"/>

    <!-- 使用本地zookeeper作为注册中心 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <!-- 指定要消费的服务 -->
    <dubbo:reference id="echoService" check="false" interface="com.alibaba.dubbo.samples.echo.api.EchoService"/>

</beans>

主要配置包括,使用 zk 作为注册中心,需要消费的服务接口,等。

然后,编写代码,加载 spring 配置,调用远程接口:

package com.alibaba.dubbo.samples.echo;

import com.alibaba.dubbo.samples.echo.api.EchoService;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EchoConsumer {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/echo-consumer.xml"});
        context.start();
        EchoService echoService = (EchoService) context.getBean("echoService"); // get remote service proxy

        String status = echoService.echo("Hello leehao!");
        System.out.println("echo result: " + status);
    }
}

启动 EchoConsumer,控制台输出:

echo result: Hello leehao!

基于注解实现

Dubbo 支持使用注解的方式来暴露服务接口。

Echo 服务端

在服务接口添加 @Service 注解,即可以暴露服务。

package com.alibaba.dubbo.samples.echo.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.samples.echo.api.EchoService;

import java.text.SimpleDateFormat;
import java.util.Date;

@Service
public class EchoServiceImpl implements EchoService {

    public String echo(String message) {
        String now = new SimpleDateFormat("HH:mm:ss").format(new Date());
        System.out.println("[" + now + "] Hello " + message
                + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        return message;
    }
}

然后,由 dubbo 将这个服务接口提升为 spring 容器的 bean,并负责配置的初始化和服务暴露:

import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class AnnotationProvider {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.in.read();
    }

    @Configuration
    // #1 指定扫描服务的位置
    @EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.echo")
    static class ProviderConfiguration {
        @Bean
        public ProviderConfig providerConfig() {
            return new ProviderConfig();
        }

        @Bean
        public ApplicationConfig applicationConfig() {
            ApplicationConfig applicationConfig = new ApplicationConfig();
            applicationConfig.setName("echo-annotation-provider");
            return applicationConfig;
        }

        @Bean
        public RegistryConfig registryConfig() {
            RegistryConfig registryConfig = new RegistryConfig();
            // #2 使用zookeeper作为注册中心,同时给出注册中心ip和端口
            registryConfig.setProtocol("zookeeper");
            registryConfig.setAddress("localhost");
            registryConfig.setPort(2181);
            return registryConfig;
        }

        @Bean
        public ProtocolConfig protocolConfig() {
            ProtocolConfig protocolConfig = new ProtocolConfig();
            // #3 默认服务使用dubbo协议,在20880监听服务
            protocolConfig.setName("dubbo");
            protocolConfig.setPort(20880);
            return protocolConfig;
        }
    }
}

Echo 客户端

使用 @Reference 注解来标注需要消费的服务:

package com.alibaba.dubbo.samples.echo.refer;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.samples.echo.api.EchoService;

import org.springframework.stereotype.Component;

@Component
public class EchoConsumer {

    @Reference
    private EchoService echoService;

    public String echo(String name) {
        return echoService.echo(name);
    }
}

然后定义基于注解的消费者的启动代码:

package com.alibaba.dubbo.samples.echo;

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import com.alibaba.dubbo.samples.echo.refer.EchoConsumer;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

public class AnnotationConsumer {

    public static void main(String[] args) {
        // #1 基于注解配置初始化spring上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
        context.start();
        // #2 发起服务调用
        EchoConsumer echoService = context.getBean(EchoConsumer.class);
        String hello = echoService.echo("Hello world!");
        System.out.println("result: " + hello);
    }

    @Configuration
    // #3 指定要扫描的消费注解,会触发注入
    @EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.echo")
    @ComponentScan(value = {"com.alibaba.dubbo.samples.echo"})
    static class ConsumerConfiguration {
        @Bean
        public ApplicationConfig applicationConfig() {
            ApplicationConfig applicationConfig = new ApplicationConfig();
            applicationConfig.setName("echo-annotation-consumer");
            return applicationConfig;
        }

        @Bean
        public ConsumerConfig consumerConfig() {
            return new ConsumerConfig();
        }

        @Bean
        public RegistryConfig registryConfig() {
            RegistryConfig registryConfig = new RegistryConfig();
            // #4 使用zookeeper作为注册中心,同时给出注册中心ip和端口
            registryConfig.setProtocol("zookeeper");
            registryConfig.setAddress("localhost");
            registryConfig.setPort(2181);
            return registryConfig;
        }
    }
}

输出:

result: Hello world!

基于 API 实现

Dubbo 框架支持 API 的方式实现暴露服务和消费服务。

Echo 服务端

package com.alibaba.dubbo.samples.echo;

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.ServiceConfig;
import com.alibaba.dubbo.samples.echo.api.EchoService;
import com.alibaba.dubbo.samples.echo.impl.EchoServiceImpl;

import java.io.IOException;

public class EchoProvider {
    public static void main(String[] args) throws IOException {
        ServiceConfig<EchoService> service = new ServiceConfig<>();
        service.setApplication(new ApplicationConfig("java-echo-provider"));
        service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        service.setInterface(EchoService.class);
        service.setRef(new EchoServiceImpl());
        service.export();
        System.out.println("java-echo-provider is running.");
        System.in.read();
    }
}

Echo 客户端

package com.alibaba.dubbo.samples.echo;

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.samples.echo.api.EchoService;

public class EchoConsumer {
    public static void main(String[] args) {
        ReferenceConfig<EchoService> reference = new ReferenceConfig<>();
        // #1 设置消费方应用名称
        reference.setApplication(new ApplicationConfig("java-echo-consumer"));
        // #2 设置注册中心地址和协议
        reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        // #3 指定要消费的服务接口
        reference.setInterface(EchoService.class);
        // #4 创建远程连接并做动态代理转换
        EchoService greetingsService = reference.get();
        String message = greetingsService.echo("Hello world!");
        System.out.println(message);
    }
}

输出:

Hello world!

参考资料


Share this post on:

Previous Post
CMake 语言 15 分钟入门教程
Next Post
k8s 部署 nginx 入门