- 安裝註冊中心:Zookeeper、Dubbox自帶的dubbo-registry-simple;
- 安裝DubboKeeper監控:https://github.com/dubboclub/dubbokeeper;
以上兩點準備,不是本文重點,不作詳細介紹,安裝比較簡單,自行查閱相關資料安裝學習。php
建立Maven模塊:msa-demo-apihtml
msa-demo-api:配置pom.xmljava
<!-- Dubbox依賴 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.8.4</version>
</dependency>
<!-- END -->
<!-- 若是要使用lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- END -->
<!-- 若是要使用REST風格遠程調用 -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.0.7.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>3.0.7.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<!-- END -->
<!-- 若是要使用json序列化 -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>3.0.7.Final</version>
</dependency>
<!-- END -->
<!-- 若是要使用xml序列化 -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>3.0.7.Final</version>
</dependency>
<!-- END -->
<!-- 若是要使用netty server -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-netty</artifactId>
<version>3.0.7.Final</version>
</dependency>
<!-- END -->
<!-- 若是要使用Sun HTTP server -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jdk-http</artifactId>
<version>3.0.7.Final</version>
</dependency>
<!-- END -->
<!-- 若是要使用tomcat server -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<version>8.0.11</version>
</dependency>
<!-- END -->
<!-- 若是要使用Kyro序列化 -->
<dependency>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo</artifactId>
<version>2.24.0</version>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
<version>0.26</version>
</dependency>
<!-- END -->
<!-- 若是要使用FST序列化 -->
<dependency>
<groupId>de.ruedigermoeller</groupId>
<artifactId>fst</artifactId>
<version>1.55</version>
</dependency>
<!-- END -->
<!-- 若是要使用Jackson序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>
<!-- END -->
複製代碼
以上POM配置,從dubbox-2.8.4開始,全部依賴庫的使用方式將和dubbo原來的同樣:即若是要使用REST、Kyro、FST、Jackson等功能,須要用戶自行手工添加相關的依賴。git
定義接口:UserService.javagithub
/** * @author TaoBangren * @version 1.0 * @since 2017/5/17 上午9:26 */
public interface UserService {
User getUser(Long id);
Long registerUser(User user);
}
複製代碼
定義REST接口:AnotherUserRestService.javaweb
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.javaredis
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.javaspring
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;
}
複製代碼
建立Maven模塊:msa-demo-providerapache
msa-demo-provider:配置pom.xmljson
<!-- Module依賴 START -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>msa-demo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Module依賴 END -->
複製代碼
實現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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 當前應用信息配置 -->
<dubbo:application name="msa-demo-provider" owner="tbr" organization="tbr"/>
<dubbo:monitor address="x.x.x.x:20884"/>
<!-- 多註冊中心配置,豎號分隔表示同時鏈接多個不一樣註冊中心,同一註冊中心的多個集羣地址用逗號分隔 -->
<dubbo:registry protocol="zookeeper" address="x.x.x.x:2181,x.x.x.x:2181,x.x.x.x:2181"/>
<dubbo:protocol name="dubbo" port="20880" serialization="kryo"/>
<!-- 1. 選用了嵌入式的jetty來作rest server,同時,若是不配置server屬性,rest協議默認也是選用jetty。 jetty是很是成熟的java servlet容器,並和dubbo已經有較好的集成(目前5種嵌入式server中只有jetty 和後面所述的tomcat、tjws,與dubbo監控系統等完成了無縫的集成),因此,若是你的dubbo系統是單獨啓動的進程, 你能夠直接默認採用jetty便可。dubbo中的rest協議默認將採用80端口. <dubbo:protocol name="rest" server="jetty"/> 2. 配置選用了嵌入式的tomcat來作rest server。在嵌入式tomcat上,REST的性能比jetty上要好得多(參見後面的基準測試), 建議在須要高性能的場景下采用tomcat。 <dubbo:protocol name="rest" server="tomcat"/> 3. 配置選用嵌入式的netty來作rest server。 <dubbo:protocol name="rest" server="netty"/> 4. 配置選用嵌入式的tjws或Sun HTTP server來作rest server。這兩個server實現很是輕量級, 很是方便在集成測試中快速啓動使用,固然也能夠在負荷不高的生產環境中使用。 注:tjws目前已經 被deprecated掉了,由於它不能很好的和servlet 3.1 API工做。 <dubbo:protocol name="rest" server="tjws"/> <dubbo:protocol name="rest" server="sunhttp"/> 5. 若是你的dubbo系統不是單獨啓動的進程,而是部署到了Java應用服務器中,則建議你採用如下配置: <dubbo:protocol name="rest" server="servlet"/> 6. 經過將server設置爲servlet,dubbo將採用外部應用服務器的servlet容器來作rest server。同時,還要在dubbo系統的web.xml中添加以下配置: <web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/META-INF/spring/dubbo-demo-provider.xml</param-value> </context-param> <listener> <listener-class>com.alibaba.dubbo.remoting.http.servlet.BootstrapListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> 即必須將dubbo的BootstrapListener和DispatherServlet添加到web.xml,以完成dubbo的REST功能與外部servlet容器的集成。 其實,這種場景下你依然能夠堅持用嵌入式server,但外部應用服務器的servlet容器每每比嵌入式server更增強大 (特別是若是你是部署到更健壯更可伸縮的WebLogic,WebSphere等),另外有時也便於在應用服務器作統一管理、監控等等。 若是將dubbo REST部署到外部Tomcat上,並配置server="servlet",即啓用外部的tomcat來作爲rest server的底層實現, 則最好在tomcat上添加以下配置: <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" redirectPort="8443" minSpareThreads="20" enableLookups="false" maxThreads="100" maxKeepAliveRequests="-1" keepAliveTimeout="60000"/> 特別是maxKeepAliveRequests="-1",這個配置主要是保證tomcat一直啓用http長鏈接,以提升REST調用性能。 可是請注意,若是REST消費端不是持續的調用REST服務,則一直啓用長鏈接未必是最好的作法。另外,一直啓用長連 接的方式通常不適合針對普通webapp,更適合這種相似rpc的場景。因此爲了高性能,在tomcat中,dubbo REST應 用和普通web應用最好不要混合部署,而應該用單獨的實例。 7. 注意:若是你是用spring的ContextLoaderListener來加載spring, 則必須保證BootstrapListener配置在ContextLoaderListener以前,不然dubbo初始化會出錯。 -->
<!-- 1. 設置一個全部rest服務都適用的基礎相對路徑,即java web應用中常說的context path。只須要添加以下contextpath屬性便可. <dubbo:protocol name="rest" port="8888" keepalive="true" server="netty" iothreads="5" threads="100" contextpath="services"/> 2. 能夠爲rest服務配置線程池大小: <dubbo:protocol name="rest" threads="500"/> 注意:目前線程池的設置只有當server="netty"或者server="jetty"或者server="tomcat"的時候才能生效。另外,若是server="servlet",因爲這時候啓用 的是外部應用服務器作rest server,不受dubbo控制,因此這裏的線程池設置也無效。 若是是選用netty server,還能夠配置Netty的IO worker線程數: <dubbo:protocol name="rest" iothreads="5" threads="100"/> 3. 注意:若是你是選用外部應用服務器作rest server, 即配置: <dubbo:protocol name="rest" port="8888" contextpath="services" server="servlet"/> 則必須保證這裏設置的port、contextpath,與外部應用服務器的端口、DispatcherServlet的上下文路徑(即webapp path加上servlet url pattern)保持一致。 4. Dubbo中的rest服務默認都是採用http長鏈接來訪問,若是想切換爲短鏈接,直接配置: <dubbo:protocol name="rest" keepalive="false"/> 注意:這個配置目前只對server="netty"和server="tomcat"才能生效。 5. 配置服務器提供端所能同時接收的最大HTTP鏈接數,防止REST server被過多鏈接撐爆,以做爲一種最基本的自我保護機制: <dubbo:protocol name="rest" accepts="500" server="tomcat/> 注意:這個配置目前只對server="tomcat"才能生效。 6. 若是rest服務的消費端也是dubbo系統,能夠像其餘dubbo RPC機制同樣,配置消費端調用此rest服務的最大超時時間以及每一個消費端所能啓動的最大HTTP鏈接數。 <dubbo:service interface="xxx" ref="xxx" protocol="rest" timeout="2000" connections="10"/> 固然,因爲這個配置針對消費端生效的,因此也能夠在消費端配置: <dubbo:reference id="xxx" interface="xxx" timeout="2000" connections="10"/> 可是,一般咱們建議配置在服務提供端提供此類配置。按照dubbo官方文檔的說法:「Provider上儘可能多配置Consumer端的屬性,讓Provider實現者一開始就思考Provider服務特色、服務質量的問題。」 注意:若是dubbo的REST服務是發佈給非dubbo的客戶端使用,則這裏<dubbo:service/>上的配置徹底無效,由於這種客戶端不受dubbo控制。 7. Dubbo的REST支持用GZIP壓縮請求和響應的數據,以減小網絡傳輸時間和帶寬佔用,但這種方式會也增長CPU開銷。 -->
<!-- 1. use tomcat server 2. 用rest協議在8888端口暴露服務 3. Dubbo的REST也支持JAX-RS標準的Filter和Interceptor,以方便對REST的請求與響應過程作定製化的攔截處理。 其中,Filter主要用於訪問和設置HTTP請求和響應的參數、URI等等。如:CacheControlFilter.java Interceptor主要用於訪問和修改輸入與輸出字節流,例如,手動添加GZIP壓縮.如:GZIPWriterInterceptor.java 4. 在標準JAX-RS應用中,咱們通常是爲Filter和Interceptor添加@Provider annotation,而後JAX-RS runtime會 自動發現並啓用它們。而在dubbo中,咱們是經過添加XML配置的方式來註冊Filter和Interceptor. 5. 在此,咱們能夠將Filter、Interceptor和DynamicFuture這三種類型的對象都添加到extension屬性上,多個之間用逗號分隔。(DynamicFuture是另外一個接口,能夠方便咱們更動態的啓用Filter和Interceptor,感興趣請自行google。) 6. 固然,dubbo自身也支持Filter的概念,但咱們這裏討論的Filter和Interceptor更加接近協議實現的底層, 相比dubbo的filter,能夠作更底層的定製化。 注:這裏的XML屬性叫extension,而不是叫interceptor或者filter,是由於除了Interceptor和Filter,將來咱們 還會添加更多的擴展類型。 7. 若是REST的消費端也是dubbo系統(參見下文的討論),則也能夠用相似方式爲消費端配置Interceptor和Filter。但注 意,JAX-RS中消費端的Filter和提供端的Filter是兩種不一樣的接口。例如前面例子中服務端是ContainerResponseFilter接口, 而消費端對應的是ClientResponseFilter. 8. Dubbo的REST也支持JAX-RS標準的ExceptionMapper,能夠用來定製特定exception發生後應該返回的HTTP響應。 9. Dubbo rest支持輸出全部HTTP請求/響應中的header字段和body消息體。LoggingFilter -->
<dubbo:protocol name="rest" port="8888" threads="500" contextpath="services" server="tomcat" accepts="500" extension="com.alibaba.dubbo.demo.extension.TraceInterceptor, com.alibaba.dubbo.demo.extension.TraceFilter, com.alibaba.dubbo.demo.extension.ClientTraceFilter, com.alibaba.dubbo.demo.extension.DynamicTraceBinding, com.alibaba.dubbo.demo.extension.CustomExceptionMapper, com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter"/>
<!-- use the external tomcat or other server with the servlet approach; the port and contextpath must be exactly the same as those in external server <dubbo:protocol name="rest" port="8888" contextpath="services" server="servlet"/> -->
<dubbo:protocol name="http" port="8889"/>
<dubbo:protocol name="hessian" port="8890"/>
<dubbo:protocol name="webservice" port="8892"/>
<!-- 聲明須要暴露的服務接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.user.UserService" ref="userService" protocol="dubbo" group="xmlConfig"/>
<!-- 1. 爲了和其餘dubbo遠程調用協議保持一致,在rest中做校驗的annotation必須放在服務的接口上, 把annotation放在接口上至少有一個好處是,dubbo的客戶端能夠共享這個接口的信息,dubbo甚 至不須要作遠程調用,在本地就能夠完成輸入校驗。 而後按照dubbo的標準方式在XML配置中打開驗證: <dubbo:service interface=xxx.UserService" ref="userService" protocol="rest" validation="true"/> 2. 在dubbo的其餘不少遠程調用協議中,若是輸入驗證出錯,是直接將RpcException拋向客戶端,而在rest中因爲客戶端常常是非dubbo,甚至非java的系統,因此不便直接拋出Java異常。所以,目前咱們將校驗錯誤以XML的格式返回: <violationReport> <constraintViolations> <path>getUserArgument0</path> <message>User ID must be greater than 1</message> <value>0</value> </constraintViolations> </violationReport> 若是你認爲默認的校驗錯誤返回格式不符合你的要求,能夠如上面章節所述,添加自定義的ExceptionMapper來自由的定製錯誤返回格式。 須要注意的是,這個ExceptionMapper必須用泛型聲明來捕獲dubbo的RpcException,才能成功覆蓋dubbo rest默認的異常處理策略。 爲了簡化操做,其實這裏最簡單的方式是直接繼承dubbo rest的RpcExceptionMapper,並覆蓋其中處理校驗異常的方法便可. -->
<dubbo:service interface="com.alibaba.dubbo.demo.user.facade.AnotherUserRestService" ref="anotherUserRestService" protocol="rest" timeout="2000" connections="100" validation="true"/>
<bean id="userService" class="com.alibaba.dubbo.demo.user.UserServiceImpl"/>
<bean id="anotherUserRestService" class="com.alibaba.dubbo.demo.user.facade.AnotherUserRestServiceImpl">
<property name="userService" ref="userService"/>
</bean>
<!-- 對於jax-rs和spring mvc,其實我對spring mvc的rest支持尚未太深刻的看過,說點初步想法,請你們指正: spring mvc也支持annotation的配置,其實和jax-rs看起來是很是很是相似的。 我我的認爲spring mvc相對更適合於面向web應用的restful服務,好比被AJAX調用,也可能輸出HTML之類的,應用中還 有頁面跳轉流程之類,spring mvc既能夠作好正常的web頁面請求也能夠同時處理rest請求。但總的來講這個restful服務 是在展示層或者叫web層之類實現的 而jax-rs相對更適合純粹的服務化應用,也就是傳統Java EE中所說的中間層服務,好比它能夠把傳統的EJB發佈成restful 服務。在spring應用中,也就把spring中充當service之類的bean直接發佈成restful服務。總的來講這個restful服務是 在業務、應用層或者facade層。而MVC層次和概念在這種作好比(後臺)服務化的應用中一般是沒有多大價值的。 固然jax-rs的有些實現好比jersey,也試圖提供mvc支持,以更好的適應上面所說的web應用,但應該是不如spring mvc。 在dubbo應用中,我想不少人都比較喜歡直接將一個本地的spring service bean(或者叫manager之類的)徹底透明的發佈 成遠程服務,則這裏用JAX-RS是更天然更直接的,沒必要額外的引入MVC概念。固然,先不討論透明發布遠程服務是否是最佳實踐, 要不要添加facade之類。 固然,我知道在dubbo不支持rest的狀況下,不少朋友採用的架構是spring mvc restful調用dubbo (spring) service 來發布restful服務的。這種方式我以爲也很是好,只是若是不修改spring mvc並將其與dubbo深度集成,restful服務不能 像dubbo中的其餘遠程調用協議好比webservices、dubbo rpc、hessian等等那樣,享受諸多高級的服務治理的功能,好比: 註冊到dubbo的服務註冊中心,經過dubbo監控中心監控其調用次數、TPS、響應時間之類,經過dubbo的統一的配置方式控制其 好比線程池大小、最大鏈接數等等,經過dubbo統一方式作服務流量控制、權限控制、頻次控制。另外spring mvc僅僅負責服務 端,而在消費端,一般是用spring restTemplate,若是restTemplate不和dubbo集成,有可能像dubbo服務客戶端那樣自動 或者人工干預作服務降級。若是服務端消費端都是dubbo系統,經過spring的rest交互,若是spring rest不深度整合dubbo, 則不能用dubbo統一的路由分流等功能。 固然,其實我我的認爲這些東西沒必要要非此即彼的。我據說spring創始人rod johnson老是愛說一句話, the customer is always right,其實與其非要探討哪一種方式更好,不如同時支持兩種方式就是了, 因此原來在文檔中也寫過計劃支持spring rest annoation,只是不知道具體可行性有多高。 1. JAX-RS中重載的方法可以映射到同一URL地址嗎? http://stackoverflow.com/questions/17196766/can-resteasy-choose-method-based-on-query-params 2. JAX-RS中做POST的方法可以接收多個參數嗎? http://stackoverflow.com/questions/5553218/jax-rs-post-multiple-objects 注:以上備註,均來自:https://dangdangdotcom.github.io/dubbox/rest.html -->
</beans>
複製代碼
配置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
複製代碼
定義服務啓動類
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發佈服務成功,能夠看到咱們發佈的兩個接口:
建立Maven模塊:msa-demo-client
msa-demo-client:配置pom.xml
<!-- Module依賴 START -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>msa-demo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Module依賴 END -->
複製代碼
Dubbox與Spring集成配置:msa-demo-client.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 當前應用信息配置 -->
<!--<dubbo:application name="msa-demo-client" owner="shark" organization="shark"/>-->
<!-- 多註冊中心配置,豎號分隔表示同時鏈接多個不一樣註冊中心,同一註冊中心的多個集羣地址用逗號分隔 -->
<!--<dubbo:registry protocol="zookeeper" address="x.x.x.x:2181,x.x.x.x:2181,x.x.x.x:2181"/>-->
<!--<dubbo:monitor address="x.x.x.x:20884"/>-->
<dubbo:reference id="userService" interface="com.alibaba.dubbo.demo.user.UserService" group="xmlConfig"/>
<dubbo:reference id="anotherUserRestService" interface="com.alibaba.dubbo.demo.user.facade.AnotherUserRestService"/>
<!-- directly connect to provider to simulate the access to non-dubbo rest services <dubbo:reference id="anotherUserRestService" interface="com.alibaba.dubbo.demo.user.facade.AnotherUserRestService" url="rest://localhost:8888/services/"/> -->
</beans>
複製代碼
建立Maven模塊:msa-demo-consumer
msa-demo-consumer:配置pom.xml
<!-- Module依賴 START -->
<dependency>
<groupId>com.jeasy</groupId>
<artifactId>msa-demo-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Module依賴 END -->
複製代碼
建立消費端測試類: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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 當前應用信息配置 -->
<dubbo:application name="msa-demo-consumer" owner="tbr" organization="tbr"/>
<!-- 多註冊中心配置,豎號分隔表示同時鏈接多個不一樣註冊中心,同一註冊中心的多個集羣地址用逗號分隔 -->
<dubbo:registry protocol="zookeeper" address="x.x.x.x:2181,x.x.x.x:2181,x.x.x.x:2181"/>
<dubbo:monitor address="x.x.x.x:20884"/>
<bean class="com.alibaba.dubbo.demo.DemoAction" init-method="start">
<property name="userService" ref="userService"/>
<property name="anotherUserRestService" ref="anotherUserRestService"/>
</bean>
</beans>
複製代碼
配置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
複製代碼
定義消費啓動類:
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啓動成功:
同時服務端會輸出服務調用日誌信息,並調用成功,以下:
模塊 | 描述 | 是否必須 |
---|---|---|
msa-xxx-api | 定義接口&實體 | 必須 |
msa-xxx-provider | 依賴api模塊,實現服務接口,提供服務 | 必須 |
msa-xxx-client | 依賴api模塊,Spring配置文件&測試用例,提供給第三方調用服務使用 | 必須 |
msa-xxx-consumer | 依賴client模塊,建議保留該模塊,避免client模塊直接與應用方緊耦合 | 可選 |