0 准备
- 安装注册中心:Zookeeper、Dubbox自带的dubbo-registry-simple;
- 安装DubboKeeper监控:https://github.com/dubboclub/dubbokeeper;
以上两点准备,不是本文重点,不做详细介绍,安装比较简单,自行查阅相关资料安装学习。
1 服务端
1.2 接口定义
-
创建Maven模块:msa-demo-api
-
msa-demo-api:配置pom.xml
com.alibaba dubbo 2.8.4 org.projectlombok lombok org.jboss.resteasy resteasy-jaxrs 3.0.7.Final org.jboss.resteasy resteasy-client 3.0.7.Final javax.validation validation-api 1.0.0.GA org.jboss.resteasy resteasy-jackson-provider 3.0.7.Final org.jboss.resteasy resteasy-jaxb-provider 3.0.7.Final org.jboss.resteasy resteasy-netty 3.0.7.Final org.jboss.resteasy resteasy-jdk-http 3.0.7.Final org.apache.tomcat.embed tomcat-embed-core 8.0.11 org.apache.tomcat.embed tomcat-embed-logging-juli 8.0.11 com.esotericsoftware.kryo kryo 2.24.0 de.javakaffee kryo-serializers 0.26 de.ruedigermoeller fst 1.55 com.fasterxml.jackson.core jackson-core 2.3.3 com.fasterxml.jackson.core jackson-databind 2.3.3 以上POM配置,从dubbox-2.8.4开始,所有依赖库的使用方式将和dubbo原来的一样:即如果要使用REST、Kyro、FST、Jackson等功能,需要用户自行手工添加相关的依赖。
-
定义接口:UserService.java
/** * @author TaoBangren * @version 1.0 * @since 2017/5/17 上午9:26 */ public interface UserService { User getUser(Long id); Long registerUser(User user); }
-
定义REST接口:AnotherUserRestService.java
package com.alibaba.dubbo.demo.user.facade; import com.alibaba.dubbo.demo.user.User; import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType; import javax.validation.constraints.Min; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; /** * @author TaoBangren * @version 1.0 * @since 2017/5/17 上午9:26 */ // 在Dubbo中开发REST服务主要都是通过JAX-RS的annotation来完成配置的, // 在上面的示例中,我们都是将annotation放在服务的实现类中。但其实,我 // 们完全也可以将annotation放到服务的接口上,这两种方式是完全等价的. // // 在一般应用中,我们建议将annotation放到服务实现类,这样annotation和 // java实现代码位置更接近,更便于开发和维护。另外更重要的是,我们一般倾向 // 于避免对接口的污染,保持接口的纯净性和广泛适用性。 // 但是,如后文所述,如果我们要用dubbo直接开发的消费端来访问此服务,则annotation必须放到接口上。 // 如果接口和实现类都同时添加了annotation,则实现类的annotation配置会生效,接口上的annotation被直接忽略。 @Path("u") @Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) @Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8}) public interface AnotherUserRestService { @GET @Path("{id : \\d+}") // 在一个REST服务同时对多种数据格式支持的情况下,根据JAX-RS标准, // 一般是通过HTTP中的MIME header(content-type和accept)来指定当前想用的是哪种格式的数据。 // @Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8}) // 但是在dubbo中,我们还自动支持目前业界普遍使用的方式,即用一个URL后缀(.json和.xml)来指定 // 想用的数据格式。例如,在添加上述annotation后,直接访问http://localhost:8888/users/1001.json // 则表示用json格式,直接访问http://localhost:8888/users/1002.xml则表示用xml格式, // 比用HTTP Header更简单直观。Twitter、微博等的REST API都是采用这种方式。 // 如果你既不加HTTP header,也不加后缀,则dubbo的REST会优先启用在以上annotation定义中排位最靠前的那种数据格式。 // 注意:这里要支持XML格式数据,在annotation中既可以用MediaType.TEXT_XML,也可以用MediaType.APPLICATION_XML, // 但是TEXT_XML是更常用的,并且如果要利用上述的URL后缀方式来指定数据格式,只能配置为TEXT_XML才能生效。 User getUser(@PathParam("id") @Min(1L) Long id); @POST @Path("register") RegistrationResult registerUser(User user); }
-
定义实体:User.java
package com.alibaba.dubbo.demo.user; import lombok.Data; import org.codehaus.jackson.annotate.JsonProperty; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; /** * @author TaoBangren * @version 1.0 * @since 2017/5/17 上午9:26 */ // 由于JAX-RS的实现一般都用标准的JAXB(Java API for XML Binding)来序列化和反序列化XML格式数据, // 所以我们需要为每一个要用XML传输的对象添加一个类级别的JAXB annotation(@XmlRootElement) ,否则序列化将报错。 @Data @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class User implements Serializable { @NotNull @Min(1L) private Long id; // REST的底层实现会在service的对象和JSON/XML数据格式之间自动做序列化/反序列化。 // 但有些场景下,如果觉得这种自动转换不满足要求,可以对其做定制。 // Dubbo中的REST实现是用JAXB做XML序列化,用Jackson做JSON序列化, // 所以在对象上添加JAXB或Jackson的annotation即可以定制映射。 @JsonProperty("username") @XmlElement(name = "username") @NotNull @Size(min = 6, max = 50) private String name; }
-
定义REST响应结果实体:RegistrationResult.java
package com.alibaba.dubbo.demo.user.facade; import lombok.Data; import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; /** * @author TaoBangren * @version 1.0 * @since 2017/5/17 上午9:26 */ // 此外,如果service方法中的返回值是Java的 primitive类型(如int,long,float,double等), // 最好为它们添加一层wrapper对象,因为JAXB不能直接序列化primitive类型。这样不但能够解决XML序列化的问题, // 而且使得返回的数据都符合XML和JSON的规范。 // 这种wrapper对象其实利用所谓Data Transfer Object(DTO)模式,采用DTO还能对传输数据做更多有用的定制。 @Data @XmlRootElement public class RegistrationResult implements Serializable { private Long id; }
1.3 服务实现
-
创建Maven模块:msa-demo-provider
-
msa-demo-provider:配置pom.xml
com.alibaba msa-demo-api 1.0-SNAPSHOT -
实现UserService接口:UserServiceImpl.java
package com.alibaba.dubbo.demo.user; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.atomic.AtomicLong; /** * @author TaoBangren * @version 1.0 * @since 2017/5/17 上午9:26 */ @Slf4j public class UserServiceImpl implements UserService { private final AtomicLong idGen = new AtomicLong(); public User getUser(Long id) { User user = new User(); user.setId(id); user.setName("username" + id); return user; } public Long registerUser(User user) { // System.out.println("Username is " + user.getName()); return idGen.incrementAndGet(); } }
-
实现REST接口AnotherUserRestService:AnotherUserRestServiceImpl.java
package com.alibaba.dubbo.demo.user.facade; import com.alibaba.dubbo.demo.user.User; import com.alibaba.dubbo.demo.user.UserService; import com.alibaba.dubbo.rpc.RpcContext; import lombok.extern.slf4j.Slf4j; /** * @author TaoBangren * @version 1.0 * @since 2017/5/17 上午9:26 */ @Slf4j public class AnotherUserRestServiceImpl implements AnotherUserRestService { private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public User getUser(Long id) { System.out.println("Client name is " + RpcContext.getContext().getAttachment("clientName")); System.out.println("Client impl is " + RpcContext.getContext().getAttachment("clientImpl")); return userService.getUser(id); } public RegistrationResult registerUser(User user) { Long id = userService.registerUser(user); RegistrationResult registrationResult = new RegistrationResult(); registrationResult.setId(id); return registrationResult; } }
-
Dubbox与Spring集成配置:msa-demo-provider.xml
-
配置dubbo.properties
#dubbo.container=log4j,spring #dubbo.application.name=demo-provider #dubbo.application.owner= #dubbo.registry.address=multicast://224.5.6.7:1234 #dubbo.registry.address=zookeeper://127.0.0.1:2181 #dubbo.registry.address=redis://127.0.0.1:6379 #dubbo.registry.address=dubbo://127.0.0.1:9090 #dubbo.monitor.protocol=registry #dubbo.protocol.name=dubbo #dubbo.protocol.port=20880 #dubbo.service.loadbalance=roundrobin #dubbo.log4j.file=logs/msa-demo-provider.log #dubbo.log4j.level=INFO #dubbo.log4j.subdirectory=20880 dubbo.application.logger=slf4j dubbo.spring.config=classpath*:msa-*.xml
1.4 服务启动
定义服务启动类
package com.alibaba.dubbo.demo.provider;
/**
* @author TaoBangren
* @version 1.0
* @since 2017/5/17 上午9:26
*/
public class DemoProvider {
public static void main(String[] args) {
com.alibaba.dubbo.container.Main.main(args);
}
}
执行main方法启动,看到以下日志输出时,msa-demo-provider启动成功:
查看DubboKeeper监控大盘,msa-demo-provider发布服务成功,可以看到我们发布的两个接口:
2. 客户端
-
创建Maven模块:msa-demo-client
-
msa-demo-client:配置pom.xml
com.alibaba msa-demo-api 1.0-SNAPSHOT -
Dubbox与Spring集成配置:msa-demo-client.xml
3. 消费端
3.1 消费端实现
-
创建Maven模块:msa-demo-consumer
-
msa-demo-consumer:配置pom.xml
com.jeasy msa-demo-client 1.0-SNAPSHOT -
创建消费端测试类:DemoAction.java
package com.alibaba.dubbo.demo; import com.alibaba.dubbo.rpc.RpcContext; import com.alibaba.dubbo.demo.user.User; import com.alibaba.dubbo.demo.user.UserService; import com.alibaba.dubbo.demo.user.facade.AnotherUserRestService; /** * @author TaoBangren * @version 1.0 * @since 2017/5/17 上午9:26 */ public class DemoAction { private UserService userService; private AnotherUserRestService anotherUserRestService; public void setUserService(final UserService userService) { this.userService = userService; } public void setAnotherUserRestService(final AnotherUserRestService anotherUserRestService) { this.anotherUserRestService = anotherUserRestService; } public void start() throws Exception { User user = new User(); user.setId(1L); user.setName("larrypage"); System.out.println("SUCCESS: registered user with id by rest" + anotherUserRestService.registerUser(user).getId()); System.out.println("SUCCESS: registered user with id " + userService.registerUser(user)); RpcContext.getContext().setAttachment("clientName", "demo"); RpcContext.getContext().setAttachment("clientImpl", "dubbox rest"); System.out.println("SUCCESS: got user by rest" + anotherUserRestService.getUser(1L)); System.out.println("SUCCESS: got user " + userService.getUser(1L)); } }
-
Dubbox与Spring集成配置:msa-demo-consumer.xml
-
配置dubbo.properties
#dubbo.container=log4j,spring #dubbo.application.name=demo-consumer #dubbo.application.owner= #dubbo.registry.address=multicast://224.5.6.7:1234 #dubbo.registry.address=zookeeper://127.0.0.1:2181 #dubbo.registry.address=redis://127.0.0.1:6379 #dubbo.registry.address=dubbo://127.0.0.1:9090 #dubbo.monitor.protocol=registry #dubbo.log4j.file=logs/msa-demo-consumer.log #dubbo.log4j.level=INFO dubbo.application.logger=slf4j dubbo.spring.config=classpath*:msa-*.xml
3.2 消费端测试
定义消费启动类:
package com.jeasy;
/**
* @author TaoBangren
* @version 1.0
* @since 2017/5/17 上午9:26
*/
public class DemoConsumer {
public static void main(String[] args) {
com.alibaba.dubbo.container.Main.main(args);
}
}
执行main方法启动,看到以下日志输出时,msa-demo-consumer启动成功:
同时服务端会输出服务调用日志信息,并调用成功,如下:
4. 规范使用
模块 | 描述 | 是否必须 |
---|---|---|
msa-xxx-api | 定义接口&实体 | 必须 |
msa-xxx-provider | 依赖api模块,实现服务接口,提供服务 | 必须 |
msa-xxx-client | 依赖api模块,Spring配置文件&测试用例,提供给第三方调用服务使用 | 必须 |
msa-xxx-consumer | 依赖client模块,建议保留该模块,避免client模块直接与应用方紧耦合 | 可选 |
5. 推荐阅读
5.1 Dubbox相关资源
- 源码地址 : https://github.com/dangdangdotcom/dubbox
- 在Dubbo中开发REST风格的远程调用 : https://dangdangdotcom.github.io/dubbox/rest.html
- 在Dubbo中使用高效的Java序列化 : https://dangdangdotcom.github.io/dubbox/serialization.html
- 使用JavaConfig方式配置dubbox : https://dangdangdotcom.github.io/dubbox/java-config.html
- Dubbo Jackson序列化使用说明 : https://dangdangdotcom.github.io/dubbox/jackson.html
- Demo : https://dangdangdotcom.github.io/dubbox/demo.html
- 当当网开源Dubbox,扩展Dubbo服务框架支持REST风格远程调用 : http://www.infoq.com/cn/news/2014/10/dubbox-open-source
- Dubbox Wiki : https://github.com/dangdangdotcom/dubbox/wiki
5.2 Dubbo相关资源
- 源码地址 : https://github.com/alibaba/dubbo
- Dubbo Wiki : https://github.com/alibaba/dubbo/wiki
- http://dubbo.io/