本次最佳實踐,分析傳統HSF應用的劣勢及更新至Pandora Boot開發的優勢。将結合HSF代碼和Pandora Boot代碼進行詳解傳統HSF應用如何更新至Pandora Boot開發。
背景資訊
HSF開發應用的劣勢在于HSF要使用指定的Ali-Tomcat容器,還需要在Ali-Tomcat等容器中加入sar包擴充,對使用者運作環境的侵入性大。而HSF開發的應用程式最終以WAR包的形式運作,不符合微服務設計的輕量應用理念。并且開發過程注入Bean需要編寫較多的xml檔案配置。
而Pandora Boot開發應用的優勢就在于依賴容器Pandora,不需要Ali-Tomcat。而且Pandora Boot開發也相容了完整的HSF功能,同時與Spring Boot無縫內建。是以使用Pandora Boot的同時也可以引入Spring Boot提供開箱即用的依賴模闆。快速、靈活的開發Spring架構的應用程式,享受開發的便利。
Pandora Boot開發的應用最終以FatJar包的形式運作,并且Pandora環境也可以直接在IDE中啟動,開發調試等效率将得到極大的提高。而Pandora Boot注入Bean通過注解的方式,也減少了繁雜的xml配置檔案編寫,提高開發效率。
因為在傳統客戶中有HSF應用更新至Pandora Boot開發的需求,是以針對客戶的需要,本次最佳實踐将較長的描述傳統HSF應用和PandoraBoot應用的開發,使單體微服務應用的靈活開發及拓展性有所提高。
HSF環境介紹及準備
産品元件
- Ali-Tomcat是EDAS中的服務運作依賴的一個容器,支援Servlet 3.0規範,支援WebSocket。包含服務的釋出、訂閱、調用鍊追蹤等一系列的核心功能。
- Pandora是一個輕量級的隔離容器-taobao-hsf.sar,它用來隔離Webapp和中間件的依賴,也用來隔離中間件之間的依賴,并實作部署與應用分離。
- 輕量級配置中心(Diamond)是淘寶内部廣泛使用的配置中心,提供持久化管理和動态配置推送服務。應用方釋出的配置會通過持久化存儲儲存,與釋出者的生命周期無關。
- 動态配置推送是Diamond的核心功能,在淘寶内部有很多應用場景,如資料庫動态切換和擴容,業務系統開關配置運作時變更等。
- 輕量級注冊中心(ConfigServer):主要用于非持久資料的釋出和訂閱、資料的生命周期和TCP連接配接生命周期綁定、産品架構基于釋出訂閱模型和去中心無master設計,保證了系統的可擴充性、高可用。在集團内部主要場景為分布式消息系統Notify、分布式RPC架構HSF提供位址發現服務。
基本結構
HSF結構分為6個部分,共同組合在一起可以提供全功能的分布式服務,分别是:
- 服務消費方:消費服務提供方提供的服務,服務消費者通過位址注冊中心訂閱服務,根據訂閱到的位址資訊發起調用,位址注冊中心作為旁路不參與調用。
- 服務提供方: 在服務架構中真正提供服務功能實作的應用執行個體,為了保障服務提供的高可用性,一般均是叢集部署,同時将位址資訊釋出到位址注冊中心。
- 位址注冊中心:接受服務提供方釋出的位址,當服務消費方根據服務進行訂閱時,會将位址資訊推送給服務消費方,注冊中心就是服務資訊的中介,提供服務發現的能力
- 持久化配置中心:持久化的配置中心用于存儲 HSF 服務的各種治理規則,HSF 用戶端在啟動的過程中會向持久化配置中心訂閱各種服務治理規則,如路由規則、歸組規則、權重規則等,進而根據規則對調用過程的選址邏輯進行幹預。
- 中繼資料存儲中心:中繼資料是指 HSF 服務對應的方法清單以及參數結構等資訊,中繼資料不會對 HSF 的調用過程産生影響,是以中繼資料存儲中心也并不是必須的。但考慮到服務運維的便捷性,HSF用戶端在啟動時會将中繼資料上報到中繼資料存儲中心,以便提供給服務運維使用
- HSF運維平台(HSF控制台):HSF 控制台通過打通位址注冊中心 ConfigServer、持久化配置中心 Diamond、中繼資料存儲中心Redis,為使用者提供了一些列服務運維功能,包括服務查詢、服務治理規則管理、服務測試、服務 Mock、單機運維等,旨在提高 HSF 服務研發的效率、運維的便捷性。
環境準備流程步驟
在進行開發前,需要準備以下基本内容:
- JDK基礎運作環境:正确安裝JDK 7+,正确配置JAVA_HOME環境變量。
- MAVEN環境及建構HSF MAVEN工程:添加打war包與HSF開發編譯依賴。
- 開發IDE:推薦Eclipse或IDEA。
- Eclipse配置:Tomcat4e插件+Pandora配置。
- IDEA:配置AliTomcat+Pandora。
- 輕量級配置中心:HSF服務的釋出與訂閱。
Pandora Boot環境介紹及準備
- Pandora:一個輕量級的隔離容器-taobao-hsf.sar,它用來隔離Webapp和中間件的依賴,也用來隔離中間件之間的依賴,也實作部署與應用分離。
- 輕量級配置及注冊中心:對于開發者可以在本地使用輕量級配置及注冊中心實作應用的注冊、發現與配置管理,完成應用的開發和測試。本地開發完應用托管到EDAS服務上,EDAS内置注冊及配置中心,是以注冊及配置功能仍然可以正常使用。
在進行開發前,需要準備一下基本内容:
- MAVEN環境及建構Pandora Boot MAVEN工程:添加打FatJar包與Pandora Boot開發編譯依賴。
- Eclipse配置:啟動Pandora Boot應用主函數自動加載Pandora容器。
- IDEA配置:啟動Pandora Boot應用主函數自動加載Pandora容器。
- 輕量級注冊及配置中心:Pandora Boot應用的注冊、發現及配置管理。
HSF開發流程及流程圖
開發流程節點:
- 服務接口:HSF的服務基于接口實作,接口需在服務設計時預先定義設計好,生産者将實作該接口以提供具體的實作來提供服務,消費者也是基于此接口作為服務去訂閱。
- 服務生産者:生産者将實作之前定義的服務接口以提供具體實作,除了代碼實作的工作之外,由于HSF是基于Spring架構來實作的,是以還需要再定義服務釋出的XML檔案。
- 服務消費者:消費者基于接口使用服務,具體調用時需要做兩個步驟
-
- Pandora Boot 的配置檔案使用注解@HSFConsumer定義好一個 Bean。
- 在使用的時候從 Spring 的 context 中将 Bean 取出。
圖 1. 流程圖
開發HSF應用
- 無縫相容:HSF與Spring無縫相容,标準用法使用Spring的xml配置。
- 标準schema:提供hsf:provider,hsf:consumer兩個标準的xml格式。
- 代碼無侵入:使用xml方式開發時,代碼不需要感覺HSF架構,而且POM中隻需要引入edas-sdk。
- 架構與WAR包分離:最終輸入的war,不需要包含HSF架構,HSF架構依賴AliTomcat+Pandora方式提供。
您需要按以下步驟建構HSF maven工程:
- 建構maven工程,建立完maven工程後,在pom.xml檔案中定義添加edas-sdk和spring的依賴。
<dependencies>
<!-- 添加 servlet 的依賴 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<!-- 添加 edas-sdk 的依賴 -->
<dependency>
<groupId>com.alibaba.edas</groupId>
<artifactId>edas-sdk</artifactId>
<version>1.5.4</version>
</dependency>
</dependencies>
- 在pom.xml檔案中添加HSF應用的maven打包插件。
<build>
<finalName>itemcenter</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
定義服務接口
HSF服務基于接口實作調用,定義好接口Sayhello後,生産者将使用該接口實作具體的服務,消費者也基于此接口訂閱服務,是以一般會将接口定義在一個工程中,它會打成一個jar包,釋出到maven倉庫中。下面是公共子產品edasdemo-api接口定義代碼:
package com.edas.demo;
public interface Sayhello {
public String sayHello();
public String sayHello(String name);
}
編寫HSF服務提供者
- 編寫HSF提供者服務除建構HSF maven工程外,服務提供者需要在pom.xml檔案中引入公共子產品工程的依賴。
<dependency>
<groupId>com.edas.demo</groupId>
<artifactId>edasdemo-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
- 服務提供者實作接口提供具體的服務,然後釋出對應的服務。
package com.edas.demo;
public class SayhelloImpl implements Sayhello {
public String sayHello() {
System.out.println("INFO:執行一次Hello");
return "Hello!";
}
public String sayHello(String name) {
return "你好"+name;
}
}
- 在hsf-provider-beans.xml中Spring配置HSF服務。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:hsf="http://www.taobao.com/hsf"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.taobao.com/hsf
http://www.taobao.com/hsf/hsf.xsd" default-autowire="byName">
<bean id="sayHello" class="com.edas.demo.SayhelloImpl" />
<!-- 提供一個服務示例 -->
<hsf:provider id="sayHelloProvider" interface="com.edas.demo.Sayhello"
ref="sayHello" version="1.0.0">
</hsf:provider>
</beans>
編寫HSF服務消費者
- 編寫消費者服務除建構HSF maven工程外,服務消費者需要在pom.xml檔案中引入公共子產品工程的依賴。
<dependency>
<groupId>com.edas.demo</groupId>
<artifactId>edasdemo-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
- 在hsf-consumer-beans.xml中Spring配置HSF服務。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:hsf="http://www.taobao.com/hsf"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.taobao.com/hsf
http://www.taobao.com/hsf/hsf.xsd" default-autowire="byName">
<!-- 消費一個服務示例 -->
<hsf:consumer id="sayHello" interface="com.edas.demo.Sayhello"
version="1.0.0">
</hsf:consumer>
</beans>
- 編寫服務消費者基于接口調用服務提供者的代碼SayhelloServlet。
public class SayhelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public SayhelloServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
// 根據 Spring 配置中的 Bean ID "item" 擷取訂閱到的服務
final Sayhello sayHello = (Sayhello) ctx.getBean("sayHello");
Thread thread = new Thread( new Runnable() {
@Override
public void run() {
while ( true ) {
try {
Thread.sleep(500l);
System.out.println(sayHello.sayHello());
System.out.println(sayHello.sayHello("tom"));
} catch ( Throwable e ) {
e.printStackTrace();
}
}
}
});
thread.start();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
開發HSF異步調用
異步調用
對于用戶端來說,并不是所有的HSF服務都需要同步等待服務端傳回結果,對于這些場景,HSF提供異步調用的方式,讓用戶端不必同步阻塞在HSF操作上。在HSF服務發起異步調用,調用結果都是傳回的預設值。而真正的結果要在HSFResponseFuture或者回調函數callback中擷取。
Futrue異步調用
HSF與Spring架構無縫內建,可以使用Spring XML的方式進行Future異步調用配置。
<bean id="orderService"
class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
<property name="interfaceName"
value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
<property name="version"
value="1.0.0"/>
<property name="group"
value="HSF"/>
<!--[設定] 訂閱服務的接口-->
<property name="asyncallMethods">
<list>
<value>name:queryOrder;type:future</value>
</list>
</property>
</bean>
Callback異步調用
使用Spring XML進行Callback異步調用配置。
<bean id="CallHelloWorld"
class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
<property name="interfaceName"
value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
<property name="version" value="1.0.0"/>
<property name="group" value="HSF"/>
<property name="asyncallMethods">
<list>
<!--name:methodName;type:future|callback-->
<value>name:queryOrder;type:callback;listener:com.alibaba.middleware.hsf.CallbackHandler</value>
</list>
</property>
</bean>
開發HSF泛化調用
相對于依賴業務用戶端Jar包的正常調用,需要依賴二方包,使用特定的GenericService接口,而傳入需要調用的方法名,方法簽名和參數值進行調用服務。泛化調用更适用于一些網關應用,其中hsfops服務測試也是依賴泛化調用功能。
API形式配置HSF服務
将HSFConsumerBean,配置generic為true,辨別用戶端忽略加載不到接口的異常。
HSFApiConsumerBean hsfApiConsumerBean = new
HSFApiConsumerBean();
hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
hsfApiConsumerBean.setVersion("1.0.0");
hsfApiConsumerBean.setGroup("HSF");
// [設定] 泛化配置
hsfApiConsumerBean.setGeneric("true");
hsfApiConsumerBean.init(true);
//使用泛化接口擷取代理
GenericService genericOrderService = (GenericService)
hsfApiConsumerBean.getObject();
// ---------------------- 調用 -----------------------//
// [調用] 發起HSF泛化調用, 傳回map類型的result。
Map orderModelMap = (Map) genericOrderService.$invoke("queryOrder",
new String[] { Long.class.getName() },
new Object[] { 1L });
GenericService提供的$invoke方法包含了真實調用的方法名、入參類型和參數,以便服務端找到改方法。由于沒有依賴服務端的API jar包,傳入的參數如果是自定義的DTO,需要轉成用戶端可以序列化的Map類型。
Spring配置HSF服務
上面描述了通過API配置HSF服務,也可以通過Spring XML配置HSF服務。
<bean id="CallHelloWorld"
class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
<!--[設定] 訂閱服務的接口-->
<property name="interfaceName"
value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
<!--[設定] 服務的版本-->
<property name="version" value="1.0.0"/>
<!--[設定] 服務的歸組-->
<property name="group" value="HSF"/>
<property name="generic" value="true"/>
</bean>
調用上下文
請求上下文包括一次性調用相關的屬性,比如調用的位址,調用方的應用名,逾時時間等屬性和使用者在接口定義的參數之外傳遞自定義的資料。
設定和擷取本次調用上下文
com.taobao.hsf.util.RequestCtxUtil提供設定和擷取調用上下文的靜态方法,基于ThreadLocal工作,getXXX操作會将XXX屬性從目前ThreadLocal變量中remove掉,僅作用于目前線程的單次調用。
表1. 用戶端
名稱 | 描述 |
setRequestTimeout() | 設定單次調用的逾時時間 |
setUserId() | 設定本次調用的單元化服務的userId(泛化調用中需要通過此方法配置) |
getProviderIp() | 擷取【最近一次】調用的服務端的IP |
setTargetServerIp(String ip) | 設定目前線程下一次調用的目标伺服器IP(此IP必須包含在記憶體已提供服務的位址清單裡) |
setDirectTargetServerIp(String targetIp) | 設定目前線程下一次調用的目标伺服器IP(繞過注冊中心,忽略記憶體裡的位址清單) |
表 2. 服務端
getClientIp() | 服務端擷取調用方IP |
getAppNameOfClient() | 服務端擷取調用方的應用名 |
isHttpRequest() | 是否是http調用 |
getHttpHeader(String key) | 擷取http請求的header屬性 |
傳遞自定義請求上下文
RpcContext提供一種不修改接口,向服務端額外傳遞資料的方式。參數可以是自定義的DO或者基本類型。要保證對端也有該對應的類型,并且可以能夠被序列化。
- 傳遞自定義上下文開發示例 ,在建構的Maven項目中導入下面依賴。
<dependency>
<groupId>com.taobao.hsf</groupId>
<artifactId>hsf-feature-context</artifactId>
</dependency>
- 用戶端發起調用前,設定上下文。
RPCContext rpcContext = RPCContext.getClientContext();
rpcContext.putAttachment("tetantId", "123");
orderService.queryOrder(1L);
- 服務端業務方法内,擷取上下文。
RPCContext rpcContext = RPCContext.getServerContext();
String myContext = (String)rpcContext.getAttachment("tetantId");
配置序列化
序列化的過程是将JAVA對象轉成byte數組在網絡中傳輸,反序列化會将byte數組轉成JAVA對象。
序列化方式配置
序列化的選擇需要考慮相容性,性能等因素,HSF的序列化方式支援JAVA、hessian、hessian2、JSON、kyro,預設是hessian2。這些序列化方式的對比和配置(隻在服務端配置HSFApiProviderBean)如下表所示:
序列化方式 | maven依賴 | 配置 | 相容性 | 性能 |
hessian2 | <artifactId>hsf-io-serialize-hessian2</artifactId> | setPreferSerializeType("hessian2") | 好 | |
java | <artifactId>hsf-io-serialize-java</artifactId> | setPreferSerializeType("java") | 最好 | 一般 |
fastjson | <artifactId>hsf-io-serialize-json</artifactId> | setPreferSerializeType("json") | ||
kryo | <artifactId>hsf-io-serialize-kryo</artifactId> | setPreferSerializeType("kryo") |
HSFApiProviderBean hsfApiProviderBean = new HSFApiProviderBean();
hsfApiProviderBean.setPreferSerializeType("hessian2");
Spring架構是在應用中廣泛使用的元件,如果不想通過API的形式配置HSF服務,可以使用Spring XML的形式進行配置,上述例子中的API配置等同于如下XML配置:
<bean class="com.taobao.hsf.app.spring.util.HSFSpringProviderBean" init-method="init">
<!--[設定] 釋出服務的接口-->
<property name="serviceInterface" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
<!--[設定] 服務的實作對象 target必須配置[ref],為需要釋出為HSF服務的spring bean id-->
<property name="target" ref="引用的BeanId"/>
<!--[設定] 服務的版本-->
<property name="serviceVersion" value="1.0.0"/>
<!--[設定] 服務的歸組-->
<property name="serviceGroup" value="HSF"/>
<!--[設定] 服務的響應時間-->
<property name="clientTimeout" value="3000"/>
<!--[設定] 服務傳輸業務對象時的序列化類型-->
<property name="preferSerializeType" value="hessian2"/>
</bean>
逾時設定
有關網絡調用的請求,都需要配置逾時,HSF的預設逾時時間是3000ms。用戶端和服務端都可以設定逾時,預設優先采用用戶端的配置,如果用戶端沒有配置,使用服務端的逾時配置。在服務端設定逾時時,需要考慮到業務本身的執行耗時,加上序列化和網絡通訊的時間。是以推薦服務端給每個服務都配置個預設的時間。當然用戶端也可以根據自己的業務場景配置逾時時間,比如一些前端應用,需要使用者快速看到結果,可以把逾時時間設定小一些。
用戶端逾時配置
用戶端逾時配置有以下兩種方式:
- API形式配置HSF服務,配置HSFApiConsumerBean的clientTimeout屬性,機關是ms,我們把接口的逾時配置為1000ms,方法queryOrder配置為100ms,代碼如下:
HSFApiConsumerBean consumerBean = new HSFApiConsumerBean();
//接口級别逾時配置
consumerBean.setClientTimeout(1000);
//xxx
MethodSpecial methodSpecial = new MethodSpecial();
methodSpecial.setMethodName("queryOrder");
//方法級别逾時配置,優先于接口逾時配置
methodSpecial.setClientTimeout(100);
consumerBean.setMethodSpecials(new MethodSpecial[]{methodSpecial});
- Spring配置HSF服務,上述例子中的API配置等同于如下XML配置:
<bean id="CallHelloWorld" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
...
<property name="clientTimeout" value="1000" />
<property name="methodSpecials">
<list>
<bean class="com.taobao.hsf.model.metadata.MethodSpecial">
<property name="methodName" value="queryOrder" />
<property name="clientTimeout" value="100" />
</bean>
</list>
</property>
...
</bean>
服務端逾時配置
服務端逾時配置也有兩種不同的方式:
- API形式配置HSF服務,配置HSFApiProviderBean的clientTimeout屬性,機關是ms,代碼如下:
HSFApiProviderBean providerBean = new HSFApiProviderBean();
//接口級别逾時配置
providerBean.setClientTimeout(1000);
//xxx
MethodSpecial methodSpecial = new MethodSpecial();
methodSpecial.setMethodName("queryOrder");
//方法級别逾時配置,優先于接口逾時配置
methodSpecial.setClientTimeout(100);
providerBean.setMethodSpecials(new MethodSpecial[]{methodSpecial});
<bean class="com.taobao.hsf.app.spring.util.HSFSpringProviderBean" init-method="init">
...
<property name="clientTimeout" value="1000" />
<property name="methodSpecials">
<list>
<bean class="com.taobao.hsf.model.metadata.MethodSpecial">
<property name="methodName" value="queryOrder" />
<property name="clientTimeout" value="2000" />
</bean>
</list>
</property>
...
</bean>
服務端線程池配置
HSF服務端線程池主要分為IO線程和業務線程,其中IO線程模型就是netty reactor網絡模型中使用的。
預設線程池配置
服務端線程池是用來執行業務邏輯的線程池,線程池預設的core size是50,max size是720,keepAliveTime 500s。隊列使用的是SynchronousQueue,沒有緩存隊列,不會堆積使用者請求。 當服務端線程池所有線程(720)都在處理請求時,對于新的請求,會立即拒絕,傳回Thread pool is full異常。可以使用下面VM參數(-D參數)進行配置。
- 線程池最小配置:-Dhsf.server.min.poolsize。
- 線程池最大的配置:-Dhsf.server.max.poolsize。
- 線程收斂的存活時間:-Dhsf.server.thread.keepalive。
服務線程池配置
對于一些慢服務、并發高,可以為其單獨配置線程池,以免占用過多的業務線程,影響應用的其他服務的調用,你可以通過以下兩種方式配置HSF應用:
- API形式配置HSF應用:
HSFApiProviderBean hsfApiProviderBean = new HSFApiProviderBean();
//...
hsfApiProviderBean.setCorePoolSize("50");
hsfApiProviderBean.setMaxPoolSize("200");
- Spring配置HSF應用:
<bean class="com.taobao.hsf.app.spring.util.HSFSpringProviderBean" init-method="init">
<!--[設定] 釋出服務的接口-->
<property name="serviceInterface" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
<property name="corePoolSize" value="50" />
<property name="maxPoolSize" value="200" />
</bean>
Pandora Boot開發流程
- 服務接口:Pandora Boot 的服務基于接口實作,接口需在服務設計時預先定義設計好,生産者将實作該接口以提供具體的實作來提供服務,消費者也是基于此接口作為服務去訂閱。
- 服務生産者:生産者将實作之前定義的服務接口以提供具體實作,除了代碼實作的工作之外,是以還需要再定義服務釋出的注解@HSFProvider配置辨別為服務提供者。
- 服務消費者:消費者基于接口使用服務,具體調用時需要做兩個步驟:
-
- Spring 的配置檔案使用标簽定義好一個Bean。
- 在使用的時候從Spring的context中将Bean取出。
開發Pandora Boot應用
建構Pandora Boot maven工程
- 建構maven工程,建立完maven工程後,在pom.xml檔案中配置EDAS的私服庫位址和插件私服庫位址。
<repositories>
<repository>
<id>central</id>
<url>http://repo1.maven.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>edas-oss-central</id>
<name>taobao mirror central</name>
<url>http://edas-public.oss-example.aliyuncs.com/repository</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://repo1.maven.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>edas-oss-plugin-central</id>
<url>http://edas-public.oss-example.aliyuncs.com/repository</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
- 建構maven工程,建立完maven工程後,在pom.xml檔案中定義添加pandora boot和spring boot依賴的版本。
<properties>
<spring-boot.version>2.1.6.RELEASE</spring-boot.version>
<pandora-boot.version>2019-06-stable</pandora-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.taobao.pandora</groupId>
<artifactId>pandora-boot-starter-bom</artifactId>
<version>${pandora-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
- 在pom.xml添加pandora boot 和spring boot開發的依賴。
<dependencies>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>pandora-hsf-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>com.taobao.pandora</groupId>
<artifactId>pandora-boot-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Use Swagger UI for REST API test -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
</dependencies>
- 在pom.xml檔案中添加pandora boot應用的maven打包插件。
<build>
<plugins>
<plugin>
<groupId>com.taobao.pandora</groupId>
<artifactId>pandora-boot-maven-plugin</artifactId>
<version>2.1.11.8</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
- 在工程目錄resources下的application.properties檔案中配置應用名和監聽端口号。
spring.application.name=hsf-provider
server.port=8082
spring.hsf.version=1.0.0
spring.hsf.timeout=1000
- 添加服務啟動的main函數入口。
@SpringBootApplication
public class HSFProviderApplication {
public static void main(String[] args) {
// 啟動 Pandora Boot 用于加載 Pandora 容器
PandoraBootstrap.run(args);
SpringApplication.run(HSFProviderApplication.class, args);
// 标記服務啟動完成,并設定線程 wait。防止使用者業務代碼運作完畢退出後,導緻容器退出。
PandoraBootstrap.markStartupAndWait();
}
}
編寫Pandora Boot服務提供者
- 對于服務提供者,其建立過程與以上建構Pandora Boot maven工程步驟一緻外,需要定義服務接口EchoService提供給消費者訂閱。
public interface EchoService {
String echo(String string);
String echoFuture(String str);
String echoCallback(String str);
String echoHSFMix(int id);
String echoMQConsumer(String str);
}
- 建立服務提供者的具體實作類EchoServiceImpl,并通過注解方式釋出服務。
@HSFProvider(serviceInterface = EchoService.class, serviceGroup = "HSF", serviceVersion = "1.0.0")
public class EchoServiceImpl implements EchoService {
@Autowired
private EchoDao echoDao;
@Autowired
private SimpleMQConsumer simpleMQConsumer;
@Autowired
private SimpleMQProduce simpleMQProduce;
public String echo(String str) {
return "hello --" + str;
}
public String echoFuture(String str) {
return "welcome --" + str;
}
public String echoCallback(String str) {
return "welcome --" + str;
}
public String echoHSFMix(int id) {
//寫消息
simpleMQProduce.sendMsg(id+"");
return echoDao.findById(id).getUserName();
}
public String echoMQConsumer(String str) {
//訂閱消息
simpleMQConsumer.receive();
return str;
}
}
編寫Pandora Boot消費者服務
- 對于服務消費者,其建立過程與以上建構Pandora Boot maven工程步驟一緻,添加服務提供者定義的接口複制到消費者服務工程。
public interface EchoService {
String echo(String string);
String echoFuture(String str);
String echoCallback(String str);
String echoHSFMix(int id);
String echoMQConsumer(String str);
}
- 添加消費者服務配置類及業務邏輯代碼。
消費者服務配置類:
@Configuration
public class HsfConfig {
//通過注解的方式将服務消費者的執行個體注入到Spring的Context中,同步調用
@HSFConsumer(clientTimeout = 3000, serviceVersion = "1.0.0")
private EchoService echoService;
}
消費者服務實作調用HSF服務提供者:
@RestController
@RequestMapping(value = "/poc")
@Api(description = "HSF-POC功能測試接口")
public class ConsumerController {
@Autowired
private EchoService echoService;
@NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)
private String useLocalCache;
@ApiOperation(value = "擷取服務者傳回資訊 A->C")
@RequestMapping(value = "/hsf-echo", method = RequestMethod.GET)
public String echo(@RequestParam("str") String str) {
return echoService.echo(str) + "\r\n";
}
@ApiOperation(value = "通過ID查詢資料庫擷取傳回資訊、RocketMQ寫資訊 A->C")
@RequestMapping(value = "/hsf-echo-mix", method = RequestMethod.GET)
public String echoHSFMix(@RequestParam("id") int id) {
return echoService.echoHSFMix(id) + "\r\n";
}
@ApiOperation(value = "RocketMQ訂閱消息 A->C")
@RequestMapping(value = "/hsf-echo-mq", method = RequestMethod.GET)
public String echoMQConsumer(@RequestParam("str") String str) {
return echoService.echoMQConsumer(str) + "\r\n";
}
@ApiOperation(value = "擷取版本資訊")
@RequestMapping(value = "/echo-version", method = RequestMethod.GET)
public String echoVersion() {
return "This is pandora boot version 2" + "\r\n";
}
}
開發Pandora Boot異步調用
對于用戶端來說,并不是所有的HSF服務都是需要同步等待服務端傳回結果的,對于這些場景,HSF提供異步調用的方式,讓用戶端不必同步阻塞在HSF操作上。在HSF服務發起異步調用,調用結果都是傳回的預設值。而真正的結果要在HSFResponseFuture或者回調函數callback中擷取。
- 對于服務提供者,其建立過程與以上建構Pandora Boot maven工程步驟一緻外,需要定義服務接口EchoService提供給消費者訂閱,在EchoService添加Future異步調用的接口及實作類。
public interface EchoService {
String echoFuture(String str);
}
接口實作類:
@HSFProvider(serviceInterface = EchoService.class, serviceGroup = "HSF", serviceVersion =
"1.0.0")
public class EchoServiceImpl implements EchoService {
public String echoFuture(String str) {
return "welcome --" + str;
}
}
- 對于服務消費者,其建立過程與以上建構Pandora Boot maven工程步驟一緻外,編寫Future異步調用代碼。
在配置類通過注解配置HSF接口方法為異步調用:
//Future異步調用
@HSFConsumer(serviceGroup = "HSF", serviceVersion = "1.0.0", futureMethods = "echoFuture")
private EchoService echoService;
編寫對外暴露請求的Future異步調用代碼:
@RequestMapping(value = "/hsf-echo-future", method = RequestMethod.GET)
public String echoFuture(@RequestParam("str") String str) {
String resp = echoService.echoFuture(str) + "\r\n";
System.out.println(resp);
//及時在目前調用上下文中,擷取 future 對象;因為該對象是放在ThreadLocal中,同一線程中後續調用會覆寫future對象,是以要及時取出。
HSFFuture hsfFuture = HSFResponseFuture.getFuture();
//這裡才真正地擷取結果,如果調用還未完成,将阻塞等待結果,5000ms是等待結果的最大時間
try {
System.out.println(hsfFuture.getResponse(5000));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return resp;
}
- 對于服務提供者,其建立過程與以上建構Pandora Boot maven一緻外,需要定義服務接口EchoService提供給消費者訂閱,在EchoService添加Callback異步調用的接口及實作類。
public interface EchoService {
String echoFuture(String str);
}
接口實作類:
@HSFProvider(serviceInterface = EchoService.class, serviceGroup = "HSF", serviceVersion = "1.0.0")
public class EchoServiceImpl implements EchoService {
public String echoFuture(String str) {
return "welcome --" + str;
}
}
- 對于服務消費者,其建立過程與以上建構Pandora Boot maven步驟一緻外,編寫Future異步調用代碼。
注解配置HSF接口方法為callback調用:
@AsyncOn(interfaceName = EchoService.class, methodName = "echoCallback")
public class CallbackHandler implements HSFResponseCallback {
public void onAppException(Throwable t) {
t.printStackTrace();
}
public void onAppResponse(Object result) {
//取 callback 調用時設定的上下文
Object context = CallbackInvocationContext.getContext();
System.out.println(result.toString());
System.out.println(context);
}
public void onHSFException(HSFException e) {
e.printStackTrace();
}
}
編寫對外暴露請求的Callback異步調用代碼:
@RequestMapping(value = "/hsf-echo-callback", method = RequestMethod.GET)
public String echoCallback(@RequestParam("str") String str) {
String resp = echoService.echoCallback(str) + "\r\n";
System.out.println(resp);
return resp;
}
開發Pandora Boot逾時設定
注解配置HSF服務,SpringBoot廣泛使用的今天,使用注解裝配SpringBean也成為一種選擇,HSF也支援使用注解進行配置,用來訂閱服務。
用戶端注解逾時配置
- 首先在Maven項目pom.xml檔案中添加依賴Starter:
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>pandora-hsf-spring-boot-starter</artifactId>
</dependency>
- 通常一個HSF Consumer需要在多個地方使用,但并不需要在每次使用的地方都用@HSFConsumer來标記。隻需要寫一個統一個Config類,然後在其它需要使用的地方,直接@Autowired注入即可上述例子中的API配置等同于如下注解配置:
@HSFConsumer(clientTimeout = 1000, methodSpecials =
@HSFConsumer.ConsumerMethodSpecial(methodName = "queryOrder", clientTimeout = "100"))
private OderService orderService;
服務端注解逾時配置
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>pandora-hsf-spring-boot-starter</artifactId>
</dependency>
- 服務端注解配置HSF服務逾時設定:
@HSFProvider(serviceInterface = OrderService.class, clientTimeout = 3000)
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDAO orderDAO;
@Override
public OrderModel queryOrder(Long id) {
return orderDAO.queryOrder(id);
}
}
開發Pandora Boot服務線程池配置
注解配置HSF服務,SpringBoot被廣泛使用的今天,使用注解裝配SpringBean也成為一種選擇,HSF也支援使用注解進行配置,用來訂閱服務。
- 首先在Maven項目pom.xml檔案中添加依賴Starter。
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>pandora-hsf-spring-boot-starter</artifactId>
</dependency>
- 将@HSFProvider配置到實作的類型上,上述例子中的API配置等同于如下注解配置。
@HSFProvider(serviceInterface = OrderService.class, corePoolSize = 50, maxPoolSize = 200)
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDAO orderDAO;
@Override
public OrderModel queryOrder(Long id) {
return orderDAO.queryOrder(id);
}
}
我們是阿裡雲智能全球技術服務-SRE團隊,我們緻力成為一個以技術為基礎、面向服務、保障業務系統高可用的工程師團隊;提供專業、體系化的SRE服務,幫助廣大客戶更好地使用雲、基于雲建構更加穩定可靠的業務系統,提升業務穩定性。我們期望能夠分享更多幫助企業客戶上雲、用好雲,讓客戶雲上業務運作更加穩定可靠的技術,您可用釘釘掃描下方二維碼,加入阿裡雲SRE技術學院釘釘圈子,和更多雲上人交流關于雲平台的那些事。