1. 程式人生 > >如何基於ServiceComb做微服務能力開放

如何基於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包含兩個步驟:

  1. 實現AbstractEdgeDispatcher
  2. 通過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: ……

這個例子,表示轉發請求給所有的微服務都必須經過鑑權,但是呼叫鑑權微服務時不需要鑑權。