有時候微服務須要提供給多個消費者, 而不經過的消費者可能但願依據自身狀況使用不一樣的協議. 另外一方面, 有時候若是本來服務以 dubbo 協議提供服務, 可是爲了調試或者監控方便, 咱們也提供 rest 協議.java
本文示例服務者同時提供 dubbo 和 rest 協議. 使用的 dubbo 版本爲 2.7.1, springboot 版本爲 2.1.5.spring
爲了真實地模擬不一樣微服務之間的調用, 本文將服務者和消費分開. 對應的項目有兩個, 分別爲dubboshop-inventory(服務者)和dubboshop-order(消費者). 其結構以下:apache
dubboshop-inventory (庫存微服務. 這個項目主要演示服務提供者角色) |- dubbo-api: 包含服務接口和 DTO 對象. 打包成 `dubboshop-inventory:1.0.0-snapshot.jar`, 經過 `mvn install`到本地或者`mvn deploy`部署到私服, 在下面`dubboshop-order`項目中引用依賴. |- fun.faceless.dubboshop.comms.entity.Result.java |- fun.faceless.dubboshop.comms.entity.CommRetCode.java |- dubbo-provider: 服務接口具體實現類 |- fun.faceless.dubboshop.inventory.dubboprovider.DubboApplication.java |- fun.faceless.dubboshop.inventory.dubboprovider.impl.InventoryProviderImpl.java dubboshop-order (訂單微服務) |- dubbo-provider: 訂單服務的提供者, 同時是dubboshop-inventory服務的消費者. 這裏主要演示其做爲消費者的角色. |- fun.faceless.dubboshop.order.dubboprovider.impl.OrderProviderImpl.java
本文兩個項目都做爲服務者, 也都支持dubbo和rest協議, 因此二者dubbo相關的依賴都包含如下幾部分.api
1) 首先引入通用 dubbo 依賴緩存
<!-- Dubbo dependencies --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-bom</artifactId> <type>pom</type> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> <exclusions> <exclusion> <groupId>org.apache.thrift</groupId> <artifactId>libthrift</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <!-- dubbo registry: zookeeper dependencies--> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-dependencies-zookeeper</artifactId> <version>2.7.1</version> <type>pom</type> <exclusions> <exclusion> <artifactId>log4j</artifactId> <groupId>log4j</groupId> </exclusion> <exclusion> <artifactId>slf4j-log4j12</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency>
2) dubbo rest 依賴tomcat
<!-- for dubbo rest protocol --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-rpc-rest</artifactId> </dependency> <!-- for dubbo rest protocol with tomcat server --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> </dependency>
1) 數據傳輸對象定義 Result.java:springboot
注意必定要定義默認構造函數和實現Serializable接口.mybatis
@Data @NoArgsConstructor @AllArgsConstructor public class Result implements Serializable { private static final long serialVersionUID = 1L; public static String SUCCESS_MSG = "SUCC"; public static String DEFAULT_FAIL_MSG = "FAIL"; private String code; private String msg; private String subCode; private String subMsg; private String sign; private Object data; private Result(String code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; this.subCode = ""; this.subMsg = ""; this.sign = ""; } /** * Return succ result with given data. * @return */ public static Result succ() { return new Result(CommRetCode.OK, SUCCESS_MSG, null); } /** * Return succ result with given data. * @param msg * @return */ public static Result succ(String msg) { return new Result(CommRetCode.OK, msg, null); } /** * Return failed result with given code, msg, and data * @param code * @param msg * @param data * @return */ public static Result fail(String code, String msg, Object data) { return new Result(code, msg, data); } // 其餘省略... }
2) 通用返回值 CommRetCode.javaapp
package fun.faceless.dubboshop.comms.entity; public interface CommRetCode { /** 一切 ok */ public final static String OK = "00000"; // 其餘省略... }
3) 定義服務提供者接口API: InventoryProvider.javaless
注意 REST 相關的註解都放在接口類中.
package fun.faceless.dubboshop.inventory.dubboprovider; import javax.ws.rs.*; import fun.faceless.dubboshop.comms.entity.Result; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.rpc.protocol.rest.support.ContentType; /** * Order Dubbo Service */ @Service @Path("inventory") @Produces({ContentType.APPLICATION_JSON_UTF_8}) @Consumes({ContentType.APPLICATION_JSON_UTF_8}) public interface InventoryProvider { @GET @Path("hello") Result hello(); /** * 減扣商品庫存 */ @POST @Path("debit") Result debit(@QueryParam("goodsId") int goodsId, @QueryParam("amount") int amount); }
本節示例 Inventory 微服務即庫存服務提供者.
1) 提供的服務的具體實現類 InventoryProviderImpl.java
package fun.faceless.dubboshop.inventory.dubboprovider.impl; import fun.faceless.dubboshop.comms.entity.Result; import fun.faceless.dubboshop.inventory.dao.InventoryDao; import fun.faceless.dubboshop.inventory.dubboprovider.InventoryProvider; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.Service; import org.springframework.beans.factory.annotation.Autowired; @Slf4j @Service public class InventoryProviderImpl implements InventoryProvider { @Autowired InventoryDao inventoryDao; @Override public Result hello() { return Result.succ("hello from inventory center"); } @Override public Result debit(int goodsId, int amount) { log.debug("debit() goodsId: {}", goodsId); log.debug("debit() amount: {}", amount); inventoryDao.debit(goodsId, amount); return Result.succ(); } }
2) 服務啓動類 DubboApplication.java
package fun.faceless.dubboshop.inventory.dubboprovider; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @EnableDubbo @ComponentScan("fun.faceless.dubboshop.inventory") @MapperScan("fun.faceless.dubboshop.inventory.dao.mapper") public class DubboApplication { public static void main(String[] args) { SpringApplication.run(DubboApplication.class, args); } }
3) 模塊配置文件 application-dev.yaml.
多協議支持的配置主要包含:
dubbo.config.multiple = true
: 開啓多個 protocol 配置綁定.dubbo.protocols
: 支持的 prottocols 列表.# 此處省略項目其餘配置... # ========= Dubbo Provider ============== dubbo: config: # 開啓多個protocol配置綁定 multiple: true # 註冊中心配置 registry: id: dubboshop-registry address: zookeeper://yyadmin:2181 group: dubboshop simplified: true application: name: dubboshop-inventory id: dubboshop-inventory logger: slf4j qos-enable: false qos-accept-foreign-ip: true qos-port: 22223 protocols: dubbo: name: dubbo port: 20882 server: netty4 rest: name: rest server: tomcat port: 8082 scan: # dubbo 服務提供者實現類所在包 base-packages: fun.faceless.dubboshop.inventory.dubboprovider
本節示例 Order 微服務即做爲訂單的模塊的服務者, 同時做爲上面庫存(Inventory)的消費者. 本例注重消費者.
1) 在pom.xml中添加服務者api的依賴:
<!-- inter-project dependencies --> <dependency> <groupId>fun.faceless.dubboshop.inventory</groupId> <artifactId>dubboapi</artifactId> </dependency>
2) 在 OderProviderImpl.java 中消費接口.
package fun.faceless.dubboshop.order.dubboprovider.impl; public class OrderProviderImpl implements OrderProvider { @Autowired private OrderDao orderDao; // 經過 protocol="dubbo" 或者 protocol="rest" 指定使用的協議 @Reference(protocol="dubbo") private InventoryProvider inventoryProvider; @Override public int createOrder(int userId, int goodsId, int orderCount) { // 調用服務接口 Result debitResult = inventoryProvider.debit(goodsId, orderCount); return this.saveOrder(userId, goodsId, orderCount); } // 省略其餘代碼... }
Serializiable
接口.@Data @NoArgsConstructor @AllArgsConstructor public class Result implements Serializable { private static final long serialVersionUID = 1L; // ...
若是沒有默認構造函數, 則在使用 rest
protocol 時, 返回值沒法消費端反序列化. 消費端會報相似以下異常:
org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class fun.faceless.xxx.Result]: can not instantiate from JSON object (need to add/enable type information?)
若是沒有實現 Serializiable
接口, 則在使用 dubbo
protocol 時, 服務提供者沒法正常序列化返回值. 服務 (provider) 端會報相似以下異常:
Caused by: org.apache.dubbo.remoting.RemotingException: Failed to send response: Response [id=2, version=2.0.2, status=20, event=false, error=null, result=RpcResult [result=Result(code=00000, msg=SUCC, subCode=, subMsg=, sign=, data=null), exception=null]], cause: java.lang.IllegalStateException: Serialized class fun.faceless.dubboshop.comms.entity.Result must implement java.io.Serializable java.lang.IllegalStateException: Serialized class fun.faceless.dubboshop.comms.entity.Result must implement java.io.Serializable
若是修改公共項目, 或者服務提供者接口所在的module, 在 mvn install
或 mvn deploy
以後, 必定也要記得再調用這些 module 的項目從新導入依賴, 不然會由於緩存, 即使重啓消費者服務, 也無濟於事.
對於 rest
protocol, 若是是將接口單獨打包 (即不帶實現類在包內) 提供給消費者. 那麼須要將 JAX-RS 相關的註解放到接口上. 不然會報以下錯誤:
RESTEASY004600: You must use at least one, but no more than one http method annotation on XXX
這是由於 resteasy jax-rs 2客戶端彷佛不直接接受實現類。要使其工做,必須建立一個正確註釋的接口。示例接口註解:
// InventoryProvider.java, 在 `dubboshop-inventory`項目`dubbo-api`模塊中, 單獨發佈給消費者使用. @Service @Path("inventory") @Produces({ContentType.APPLICATION_JSON_UTF_8}) @Consumes({ContentType.APPLICATION_JSON_UTF_8}) public interface InventoryProvider { @GET @Path("hello") Result hello(); /** * 減扣商品庫存 */ @POST @Path("debit") Result debit(@QueryParam("goodsId") int goodsId, @QueryParam("amount") int amount); }
對應的實現類以下:
// InventoryProviderImpl.java, 在 `dubboshop-inventory`項目`dubbo-provider`模塊中, 與消費者無關. @Slf4j @Service public class InventoryProviderImpl implements InventoryProvider { @Autowired InventoryDao inventoryDao; @Override public Result hello() { return Result.succ("hello from inventory center"); } @Override public Result debit(int goodsId, int amount) { inventoryDao.debit(goodsId, amount); return Result.succ(); } }
@Reference(protocol="dubbo") // 或 @Reference(protocol="rest") private InventoryProvider inventoryProvider;