如何基於ServiceComb做微服務能力開放
大量的微服務能力需要通過閘道器開放給使用者、其他外部系統訪問。閘道器一方面扮演著彙集使用者請求的作用,同時還會扮演認證、鑑權、流量控制、防攻擊的用途。同時,由於閘道器是一個匯聚點,容易形成業務的瓶頸,通常還會採用多級閘道器機制,最外層的閘道器提供主備以及簡單的請求轉發功能,中間層的實現鑑權等功能,多例項部署。常見的可以用於閘道器的技術和服務包括LVS、Nginx、Zuul等。
ServiceComb 也提供了自己的閘道器服務Edge Service。Edge Service內建了強大的路由策略,支援介面級別的相容性轉發(灰度釋出),內嵌ServiceComb治理能力,並支援非常靈活的擴充套件機制。
使用Edge Service做邊緣服務
Edge Service是ServiceComb提供的JAVA閘道器服務。Edge Service作為整個微服務系統對外的介面,向終端使用者提供服務,接入RESTful請求,轉發給內部微服務。Edge Service以開發框架的形式提供,開發者可以非常簡單的搭建一個Edge Service服務,通過簡單的配置就可以定義路由轉發規則。同時Edge Service支援強大的擴充套件能力,服務對映、請求解析、加密解密、鑑權等邏輯都可以通過擴充套件實現。
Edge Service本身也是一個微服務,需遵守所有微服務開發的規則。其本身可以部署為多例項,前端使用負載均衡裝置進行負載分發;也可以部署為主備,直接接入使用者請求。開發者可以根據Edge Service承載的邏輯和業務訪問量、組網情況來規劃。
開發Edge Service服務
開發Edge Service和開發一個普通的微服務步驟差不多,開發者可以從匯入ServiceComb Edge Service Demo入手。從頭搭建專案包含如下幾個步驟:
- 配置依賴關係
在專案中加入edge-core的依賴,就可以啟動Edge Service的功能。Edge Service在請求轉發的時候,會經過處理鏈,因此還可以加入相關的處理鏈的模組的依賴,下面的例項增加的負載均衡的處理鏈,這個是必須的。
<dependency> <groupId>org.apache.servicecomb</groupId> <artifactId>edge-core</artifactId> </dependency> <dependency> <groupId>org.apache.servicecomb</groupId> <artifactId>handler-loadbalance</artifactId> </dependency>
- 定義啟動類
和開發普通微服務一樣,可以通過載入Spring的方式將服務拉起來。
public class EdgeMain {
public static void main(String[] args) throws Exception {
Log4jUtils.init();
BeanUtils.init();
}
}
- 增加配置檔案microservie.yaml
Edge Service本身也是一個微服務,遵循微服務查詢的規則,自己也會進行註冊。注意APPLICAIONT_ID與需要轉發的微服務相同。在下面的配置中,指定了Edge Service監聽的地址,處理鏈等資訊。其中auth處理鏈是DEMO專案中自定義的處理鏈,用於實現認證。同時auth服務本身,不經過這個處理鏈,相當於不鑑權。
APPLICATION_ID: edge
service_description:
name: edge
version: 0.0.1
servicecomb:
service:
registry:
address: http://127.0.0.1:30100
rest:
address: 127.0.0.1:18080
handler:
chain:
Consumer:
default: auth,loadbalance
service:
auth: loadbalance
工作流程
Edge Service的工作流程如下,藍色背景部分在Eventloop執行緒中執行,黃色背景部分:
- 如果工作於reactive模式,則直接在Eventloop執行緒執行
- 如果工作於執行緒池模式,則線上程池的執行緒中執行
定製路由規則
使用Edge Service的核心工作是配置路由規則。場景不同,規則也不同。 路由規則由一系列AbstractEdgeDispatcher組成。Edge Service提供了幾個常見的Dispatcher,通過配置即可啟用,如果這些Dispatcher不滿足業務場景需要,還可以自定義。
使用DefaultEdgeDispatcher
DefaultEdgeDispatcher是一個非常簡單、容易管理的Dispatcher,使用這個Dispatcher,使用者不用動態管理轉發規則,應用於實際的業務場景非常方便,這個也是推薦的一種管理機制。它包含如下幾個配置項:
servicecomb:
http:
dispatcher:
edge:
default:
enabled: true
prefix: rest
withVersion: true
prefixSegmentCount: 1
常見的這些配置項的示例及含義如下:
- [prefix=rest;withVersion=true;prefixSegmentCount=1]微服務xService提供的URL為: /xService/v1/abc,通過Edge訪問的地址為/rest/xService/v1/abc,請求只轉發到[1.0.0-2.0.0)版本的微服務例項。
- [prefix=rest;withVersion=true;prefixSegmentCount=2]微服務xService提供的URL為: /v1/abc,通過Edge訪問的地址為/rest/xService/v1/abc,請求只轉發到[1.0.0-2.0.0)版本的微服務例項。
- [prefix=rest;withVersion=true;prefixSegmentCount=3]微服務xService提供的URL為: /abc,通過Edge訪問的地址為/rest/xService/v1/abc,請求只轉發到[1.0.0-2.0.0)版本的微服務例項。
- [prefix=rest;withVersion=false;prefixSegmentCount=1]微服務xService提供的URL為: /xService/v1/abc,通過Edge訪問的地址為/rest/xService/v1/abc,請求可能轉發到任意微服務例項。
- [prefix=rest;withVersion=false;prefixSegmentCount=2]微服務xService提供的URL為: /v1/abc,通過Edge訪問的地址為/rest/xService/v1/abc,,請求可能轉發到任意微服務例項。
- [prefix=rest;withVersion=false;prefixSegmentCount=2]微服務xService提供的URL為: /abc,通過Edge訪問的地址為/rest/xService/abc,,請求可能轉發到任意微服務例項。
withVersion配置項提供了客戶端灰度規則,可以讓客戶端指定訪問的服務端版本。Edge Service還包含根據介面相容性自動路由的功能,請求會轉發到包含了該介面的例項。假設某微服務,相容規劃為所有高版本必須相容低版本,部署了以下版本例項:
-
1.0.0,提供了operation1
-
1.1.0,提供了operation1、operation2
Edge Service在轉發operation1時,會自動使用1.0.0+的規則來過濾例項
Edge Service在轉發operation2時,會自動使用1.1.0+的規則來過濾例項
以上過程使用者不必做任何干預,全自動完成,以避免將新版本的operation轉發到舊版本的例項中去。
使用URLMappedEdgeDispatcher
URLMappedEdgeDispatcher允許使用者配置URL和微服務的對映關係。使用它可以非常靈活的定義哪些URL轉發到哪些微服務。它包含如下幾個配置項:
servicecomb:
http:
dispatcher:
edge:
url:
enabled: true
mappings:
businessV1:
prefixSegmentCount: 1
path: "/url/business/v1/.*"
microserviceName: business
versionRule: 1.0.0-2.0.0
businessV2:
prefixSegmentCount: 1
path: "/url/business/v2/.*"
microserviceName: business
versionRule: 2.0.0-3.0.0
businessV1配置項表示的含義是將請求路徑為/usr/business/v1/.的請求,轉發到business這個微服務,並且只轉發到版本號為1.0.0-2.0.0的例項(不含2.0.0)。轉發的時候URL為/business/v1/.。path使用的是JDK的正則表示式,可以檢視Pattern類的說明。prefixSegmentCount表示字首的URL Segment數量,字首不包含在轉發的URL路徑中。有三種形式的versionRule可以指定。2.0.0-3.0.0表示版本範圍,含2.0.0,但不含3.0.0;2.0.0+表示大於2.0.0的版本,含2.0.0;2.0.0表示只轉發到2.0.0版本。2,2.0等價於2.0.0。
從上面的配置可以看出,URLMappedEdgeDispatcher也支援客戶端灰度。當然配置項會比DefaultEdgeDispatcher多。URLMappedEdgeDispatcher支援通過配置中心動態的修改配置,調整路由規則。
自定義Dispatcher
自定義Dispatcher包含兩個步驟:
- 實現AbstractEdgeDispatcher
- 通過SPI釋出:增加檔案META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher,並寫入實現類
詳細的程式碼細節可以參考下面的章節"DEMO功能說明"。開發者也可以參考DefaultEdgeDispatcher等程式碼來定義自己的Dispatcher。
進行認證鑑權和其他業務處理
通過Edge Servie工作流程可以看出,可以通過多種方式來擴充套件Edge Service的功能,包括Dispatcher、HttpServerFilter、Handler、HttpClientFilter等。比較常用和簡單的是通過Handler來擴充套件。DEMO裡面展示瞭如何通過Handler擴充套件來實現鑑權。詳細的程式碼細節可以參考下面的章節"DEMO功能說明"。
部署示例
工作模式
reactive (預設)
Edge Service預設工作於高效能的reactive模式,此模式要求工作於Edge Service轉發流程中的業務程式碼不能有任何的阻塞操作,包括不限於:
-
遠端同步呼叫,比如同步查詢資料庫、同步呼叫微服務,或是同步查詢遠端快取等等
-
任何的sleep呼叫
-
任何的wait呼叫
-
超大的迴圈
Edge Service的底層是基於netty的vertx,以上約束即是netty的reactive模式約束。
執行緒池
如果業務模型無法滿足reactive要求,則需要使用執行緒池模式。
此時需要在Edge Service的microservice.yaml中配置:
servicecomb:
executors:
default: servicecomb.executor.groupThreadPool
這裡的servicecomb.executor.groupThreadPool是ServiceComb內建的預設執行緒池對應的spring bean的beanId;業務可以定製自己的執行緒池,並宣告為一個bean,其beanId也可以配置到這裡。
DEMO功能說明
請參考github上的edge service demo:
該demo包含以下工程:
- authentication:微服務:鑑權伺服器
- edge-service
- hiboard-business-1.0.0微服務:business,1.0.0版本,operation add
- hiboard-business-1.1.0微服務:business,1.1.0版本,operation add/dec
- hiboard-business-2.0.0微服務:business,2.0.0版本,operation add/dec
- hiboard-consumer作為一個普通的httpclient,而不是servicecomb consumer
- hiboard-model非微服務,僅僅是一些公共的model
通過edge-service訪問微服務business的不同版本,並確認是由正確的例項處理的。
1.註冊Dispatcher
實現介面org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher,或從org.apache.servicecomb.edge.core.AbstractEdgeDispatcher繼承,實現自己的dispatcher功能。
實現類通過java標準的SPI機制註冊到系統中去。
Dispatcher需要實現2個方法:
-
getOrder
Dispatcher需要向vertx注入路由規則,路由規則之間是有優先順序順序關係的。
系統中所有的Dispatcher按照getOrder的返回值按從小到大的方式排序,按順序初始化。
如果2個Dispatcher的getOrder返回值相同,則2者的順序不可預知。
-
init
init方法入參為vertx框架中的io.vertx.ext.web.Router,需要通過該物件實現路由規則的定製。
可以指定滿足要求的url,是否需要處理cookie、是否需要處理body、使用哪個自定義方法處理收到的請求等等
更多路由規則細節請參考vertx官方文件:vertx路由機制
提示:
多個Dispatcher可以設定路由規則,覆蓋到相同的url。
假設Dispatcher A和B都可以處理同一個url,並且A優先順序更高,則:
-
如果A處理完,既沒應答,也沒有呼叫RoutingContext.next(),則屬於bug,本次請求掛死了
-
如果A處理完,然後呼叫了RoutingContext.next(),則會將請求轉移給B處理
2.轉發請求
註冊路由時,指定了使用哪個方法來處理請求(下面使用onRequest來指代該方法),在onRequest中實現轉發邏輯。
方法原型為:
void onRequest(RoutingContext context)
系統封裝了org.apache.servicecomb.edge.core.EdgeInvocation來實現轉發功能,至少需要準備以下引數:
-
microserviceName,業務自行制定規則,可以在url傳入,或是根據url查詢等等
-
context,即onRequest的入參
-
path,轉發目標的url
-
httpServerFilters,Dispatcher父類已經初始化好的成員變數
EdgeInvocation edgeInvocation = new EdgeInvocation();
edgeInvocation.init(microserviceName, context, path, httpServerFilters);
edgeInvocation.edgeInvoke();
edgeInvoke呼叫內部,會作為ServiceComb標準consumer去轉發呼叫。
作為標準consumer,意味著ServiceComb所有標準的治理能力在這裡都是生效的。
3.設定相容規則
不同的業務可能有不同的相容規劃,servicecomb預設的相容規則,要求所有新版本相容舊版本。如果滿足這個要求,則不必做任何特殊的設定。
還有一種典型的規劃:
-
1.0.0-2.0.0內部相容,url為/microserviceName/v1/….的形式
-
2.0.0-3.0.0內部相容,url為/microserviceName/v2/….的形式
……
各大版本之間不相容
此時,開發人員需要針對EdgeInvocation設定相容規則:
private CompatiblePathVersionMapper versionMapper = new CompatiblePathVersionMapper();
……
edgeInvocation.setVersionRule(versionMapper.getOrCreate(pathVersion).getVersionRule());
versionMapper的作用是將v1或是v2這樣的串,轉為1.0.0-2.0.0或2.0.0-3.0.0這樣的相容規則。
注意:
介面不相容會導致非常多的問題。java chassis要求高版本服務相容低版本服務,只允許增加介面不允許刪除介面。在增加介面後,必須增加微服務的版本號。在開發階段,介面變更頻繁,開發者往往忘記這個規則。當這個約束被打破的時候,需要清理服務中心微服務的資訊,並重啟微服務和Edge Service(以及依賴於該微服務的其他服務)。否則可能出現請求轉發失敗等情況。
4.鑑權
Edge Service是系統的邊界,對於很多請求需要執行鑑權邏輯。
基於標準的ServiceComb機制,可以通過handler來實現這個功能。
最簡單的示意程式碼如下:
public class AuthHandler implements Handler {
private Auth auth;
public AuthHandler() {
auth = Invoker.createProxy("auth", "auth", Auth.class);
}
……
@Override
public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
if (!auth.auth("")) {
asyncResp.consumerFail(new InvocationException(Status.UNAUTHORIZED, (Object) "auth failed"));
return;
}
LOGGER.debug("auth success.");
invocation.next(asyncResp);
}
}
Auth表示是鑑權微服務提供的介面,Invoker.createProxy("auth", "auth", Auth.class)是透明RPC開發模式中consumer的底層api,與@ReferenceRpc是等效,只不過不需要依賴spring bean機制。
Auth介面完全由業務定義,這裡只是一個示例。
Handler開發完成後,配置到edge service的microservice.yaml中:
servicecomb:
handler:
chain:
Consumer:
default: auth,……
service:
auth: ……
這個例子,表示轉發請求給所有的微服務都必須經過鑑權,但是呼叫鑑權微服務時不需要鑑權。