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!