---
layout: post
title: Dubbo的初级使用
category: 技术
tags: Java
keywords:
description: 记录对Dubbo的基本使用。
---
## 1.背景
闲话就不讲了,Dubbo官网[Dubbo.io]已经阐述的很详细了,接下来直接实战应用Dubbo开发分布式服务。我所理解的分布式服务就是调用方和提供方可以不在同一个进程内甚至不在同一台机器,同一网段内,每次服务的调用都会经过网络传输。
## 2.实战
**maven项目模块划分**
以我的demo项目为例:
- 父模块[brief-dubbo]
一些各模块共用的配置(包括资源配置和POM配置)或代码。
- 接口模块[brief-dubbo-api]
提供消费方和提供方共用的接口和POJO类,该模块需要被customer和service模块依赖。
- 服务调用方(消费方)模块[brief-dubbo-customer],以下简称customer模块。
编写调用服务的代码,不关心服务的内部逻辑,只关心接口来处理自己内部的逻辑。
- 服务提供方模块[brief-dubbo-service],以下简称service模块。
编写接口内部实现,为服务调用方提供接口的功能,封装并隐藏内部实现。
### 2.1 brief-dubbo的POM配置
```xml
2.5.3
1.2.17
1.7.2
4.12
1.2.17
1.16.8
4.3.2.RELEASE
0.1
3.4.9
org.projectlombok
lombok
${lombok.version}
org.springframework
spring-test
${spring.version}
org.slf4j
jcl-over-slf4j
${slf4j.version}
org.slf4j
slf4j-api
${slf4j.version}
org.slf4j
slf4j-log4j12
${slf4j.version}
junit
junit
${junit.version}
```
### 2.2 brief-dubbo-api模块配置
#### 2.2.1 POM配置
引入customer和service都会用到的依赖包
```xml
org.springframework
spring-core
org.springframework
spring-aop
org.springframework
spring-context
org.springframework
spring-context-support
org.springframework
spring-web
org.springframework
spring-webmvc
org.springframework
spring-web
com.alibaba
fastjson
com.alibaba
dubbo
org.springframework
spring-core
org.springframework
spring
com.github.sgroschupf
zkclient
log4j
log4j
org.apache.zookeeper
zookeeper
io.netty
netty
log4j
log4j
org.slf4j
slf4j-api
org.slf4j
slf4j-log4j12
org.aspectj
aspectjweaver
```
#### 2.2.2 编写代码
在brief-dubbo-api模块下`src/main/java`目录下创建接口`cn.followtry.dubbo.api.UserService`和POJO类`cn.followtry.dubbo.bean.User`
```java
package cn.followtry.dubbo.api;
import cn.followtry.dubbo.bean.User;
public interface UserService{
User getUserById(String id);
}
```
```java
package cn.followtry.dubbo.bean;
import java.io.Serializable;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class User implements Serializable {
private static final long serialVersionUID = 3899555909604815507L;
private String name;
private String id;
}
```
这样,简单的UserService的API接口就已经定义好了,接下来配置service模块并实现接口逻辑。
### 2.3 service模块配置
#### 2.3.1 POM配置
因前面已经做了工作,所以此处配置较少。
```xml
war
cn.followtry
brief-dubbo-api
${project.version}
src/main/java
${project.build.directory}
**/*.java
src/main/resources
true
```
#### 2.3.2 web.xml配置
在`src/main/webapp/WEB-INF`下创建`web.xml`文件并配置如下内容:
```xml
developer
contextConfigLocation
classpath*:spring/applicationContext.xml
org.springframework.web.context.ContextLoaderListener
characterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/*
```
#### 2.3.3 资源文件创建
在`src/main/resources`目录下创建`spring/applicationContext.xml`文件
```xml
```
在`src/main/resources`目录下创建`log4j.properties`日志配置文件
```property
log4j.rootLogger=info, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
log4j.appender.stdout.Threshold
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p %l - %m%n
```
#### 2.3.4 编写代码
新建类`cn.followtry.dubbo.impl.UserServiceImpl`实现`cn.followtry.dubbo.api.UserService`接口
```java
package cn.followtry.dubbo.impl;
import cn.followtry.dubbo.api.UserService;
import cn.followtry.dubbo.bean.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service("userService")
@Slf4j
public class UserServiceImpl implements UserService {
public UserServiceImpl() {
log.info("cn.followtry.dubbo.impl.UserServiceImpl.UserServiceImpl()");
}
@Override
public User getUserById(String id) {
//demo code
User user = new User();
if (id != null && !"".equals(id)) {
user.setId(id);
user.setName("hello world");
return user;
}
user.setName("user is not found");
return user;
}
}
```
到这最基本最普通的服务端代码就写好了,但是为什么没有看到Dubbo的配置呢,这样就能部署为分布式服务了吗?当然不能,继续天啊及提供方的Dubbo配置。
#### 2.3.5 服务提供方Dubbo配置(xml配置方式)
首先提一下,[注册中心ZooKeeper集群搭建](http://followtry.cn/2015/04/04/ZooKeeper-setup.html)请看这里。
当前ZooKeeper集群有三个节点`192.168.2.203`,`192.168.2.202`,`192.168.2.201`。
在`src/main/resources/spring`目录下创建`dubbo-provider.xml`文件,用来配置Dubbo服务提供方
```xml
```
在`applicationContext.xml`中加一句``,以便引入`dubbo-provider.xml`的配置。
这次Dubbo服务提供方真的可以在注册中心注册了。在Tomcat下部署并启动service模块。部分打印日志如下:
```logger
[DUBBO] Export dubbo service cn.followtry.dubbo.api.UserService to local registry, dubbo version: 2.5.3, current host: 127.0.0.1
[DUBBO] Export dubbo service cn.followtry.dubbo.api.UserService to url dubbo://192.168.31.1:28080/services/cn.followtry.dubbo.api.UserService?accesslog=true&anyhost=true&application=dubbo-provider-demo&dubbo=2.5.3&group=primary&interface=cn.followtry.dubbo.api.UserService&methods=getUserById&organization=followtry&owner=jingzz&pid=213160&revision=0.0.3&side=provider×tamp=1492965068638, dubbo version: 2.5.3, current host: 127.0.0.1
[DUBBO] Register dubbo service cn.followtry.dubbo.api.UserService url dubbo://192.168.31.1:28080/services/cn.followtry.dubbo.api.UserService?accesslog=true&anyhost=true&application=dubbo-provider-demo&dubbo=2.5.3&group=primary&interface=cn.followtry.dubbo.api.UserService&methods=getUserById&organization=followtry&owner=jingzz&pid=213160&revision=0.0.3&side=provider×tamp=1492965068638 to registry registry://192.168.2.203:2181/com.alibaba.dubbo.registry.RegistryService?application=dubbo-provider-demo&backup=192.168.2.201:2181,192.168.2.202:2181&dubbo=2.5.3&organization=followtry&owner=jingzz&pid=213160®istry=zookeeper×tamp=1492965068638, dubbo version: 2.5.3, current host: 127.0.0.1
[DUBBO] Register: dubbo://192.168.31.1:28080/services/cn.followtry.dubbo.api.UserService?accesslog=true&anyhost=true&application=dubbo-provider-demo&dubbo=2.5.3&group=primary&interface=cn.followtry.dubbo.api.UserService&methods=getUserById&organization=followtry&owner=jingzz&pid=213160&revision=0.0.3&side=provider×tamp=1492965068638, dubbo version: 2.5.3, current host: 127.0.0.1
[DUBBO] Subscribe: provider://192.168.31.1:28080/services/cn.followtry.dubbo.api.UserService?accesslog=true&anyhost=true&application=dubbo-provider-demo&category=configurators&check=false&dubbo=2.5.3&group=primary&interface=cn.followtry.dubbo.api.UserService&methods=getUserById&organization=followtry&owner=jingzz&pid=213160&revision=0.0.3&side=provider×tamp=1492965068638, dubbo version: 2.5.3, current host: 127.0.0.1
[DUBBO] Notify urls for subscribe url provider://192.168.31.1:28080/services/cn.followtry.dubbo.api.UserService?accesslog=true&anyhost=true&application=dubbo-provider-demo&category=configurators&check=false&dubbo=2.5.3&group=primary&interface=cn.followtry.dubbo.api.UserService&methods=getUserById&organization=followtry&owner=jingzz&pid=213160&revision=0.0.3&side=provider×tamp=1492965068638, urls: [empty://192.168.31.1:28080/services/cn.followtry.dubbo.api.UserService?accesslog=true&anyhost=true&application=dubbo-provider-demo&category=configurators&check=false&dubbo=2.5.3&group=primary&interface=cn.followtry.dubbo.api.UserService&methods=getUserById&organization=followtry&owner=jingzz&pid=213160&revision=0.0.3&side=provider×tamp=1492965068638], dubbo version: 2.5.3, current host: 127.0.0.1
[DUBBO] The service ready on spring started. service: cn.followtry.dubbo.api.UserService, dubbo version: 2.5.3, current host: 127.0.0.1
```
这样服务已经暴露给调用方,只等调用方调用即可了。那赶紧编写调用方代码测试下服务是否发布成功吧。
### 2.4 customer模块配置
#### 2.4.1 POM配置
```xml
war
cn.followtry
brief-dubbo-api
${project.version}
src/main/java
${project.build.directory}
**/*.java
src/main/resources
true
```
#### 2.4.1 消费方的资源文件配置
`src/main/resources`创建`applicationContext.xml`文件
```xml
```
日志配置参考服务方的2.3.3段
#### 2.4.2 服务调用方Dubbo配置(xml方式)
`src/main/resources`创建`dubbo-consumer.xml`文件
```xml
```
既然配置好了,那就需要写代码调一调测试下服务是否可以被分布式访问了。
#### 2.4.3 编写代码
**调用代码**
```java
package cn.followtry.dubbo.customer;
import cn.followtry.dubbo.api.UserService;
import cn.followtry.dubbo.bean.User;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class ServiceHandler {
@Autowired
@Qualifier("userService")
private UserService userService;
public User getUser(String id) {
return userService.getUserById(id);
}
}
```
**测试代码**
```java
package cn.followtry.dubbo.customer.core;
import cn.followtry.dubbo.bean.User;
import cn.followtry.dubbo.customer.ServiceHandler;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:applicationContext.xml")
public class ApplicationBoot {
@Autowired
private ServiceHandler serviceHandler;
@Test
public void testUser() {
User jingzz = serviceHandler.getUser("zhangsan");
System.out.println(jingzz);
}
}
```
调用后返回结果为:
```
User(name=hello world, id=zhangsan)
```
说明调用正确,服务确实可以远程访问了。那用Spring web方式是否可以访问呢,肯定可以不过限于篇幅。读者只好动手自己试试喽!
问题来了,那如果我有多个服务接口,那么是不是在service和customer模块各添加很多的配置,岂不是配置文件难以控制,有没有可以配置自动扫描指定注解方式替代手工配置方式呢?这个必须有,继续向下看。
### 2.5 优化代码
#### 2.5.1 优化service模块
修改service模块的dubbo配置,将手工注入修改为自动扫描方式。
添加如下配置:
```xml
```
注释掉
```xml
```
代码得做相应调整,加上dubbo规定的注解`@com.alibaba.dubbo.config.annotation.Service(interfaceClass = UserService.class)`
重启服务。
#### 2.5.2 优化customer模块
添加
```xml
```
移除``
代码修改,得调整注解如下
```java
package cn.followtry.dubbo.customer;
import cn.followtry.dubbo.api.UserService;
import cn.followtry.dubbo.bean.User;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
@Service
public class ServiceHandler {
public User getUser(String id) {
return userService.getUserById(id);
}
// @Autowired
// @Qualifier("userService")
//使用dubbo定义的直接Reference解释该属性
@Reference(interfaceClass = UserService.class)
private UserService userService;
}
```
好的,运行下测试代码,应该是能得出正确结果的。到这里以后写暴露服务就省很多事情了。那么问题又来了,如果一个接口有多个实现的时候我怎么调用呢?嗯,这个嘛,有办法,通过为服务指定group属性,来区分同一接口的不同实现。
##### 2.5.4 解决接口多实现的调用问题
因为多实现的调用会有序列化问题,所以接口`UserService`要继承`Serializable`接口
**服务端**
原实现的`Service`注解的group参数指定为"primary",并新创建类
```java
package cn.followtry.dubbo.impl;
import cn.followtry.dubbo.api.UserService;
import cn.followtry.dubbo.bean.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service("customUserService")
//指定group为custom
@com.alibaba.dubbo.config.annotation.Service(interfaceClass = UserService.class,export = true,group = "custom")
@Slf4j
public class CustomUserServiceImpl implements UserService {
public CustomUserServiceImpl() {
log.info("cn.followtry.dubbo.api.UserService.CustomUserServiceImpl.CustomUserServiceImpl()");
}
@Override
public User getUserById(String id) {
return new User() {
{
setId(id);
setName("custom-user-1");
}
};
}
}
```
**消费端**
都不用动配置, 只要在注入属性的地方注解`Reference`添加上相应的`group`属性即可。
调用代码添加
```java
// @Autowired
@Reference(interfaceClass = UserService.class,group = "custom")
private UserService customUserService;
public User getUser2(String id) {
return customUserService.getUserById(id);
}
```
测试代码添加调用
```java
User zhangsan2 = serviceHandler.getUser2("zhangsan2");
System.out.println(zhangsan2);
```
返回结果为
```
User(name=hello world, id=zhangsan)
User(name=custom-user-1, id=zhangsan2)
```
## 总结
好了,dubbo的使用就简单的介绍到这里吧。夜里加班了两个班小时才搞完。这只是dubbo的入门,详细使用说明还是需要参考官网文档,极其详细。