lms框架應用服務介面和服務條目詳解

懶小蟲 發表於 2021-06-08

應用介面的定義

服務應用介面是微服務定義webAPI的基本單位,可以被其他微服務應用引用,其他微服務可以通過rpc通訊與該微服務進行通訊。應用介面如果被閘道器應用引用,閘道器可以通過服務應用介面生成swagger文件。

通過ServiceRouteAttribute特性對一個介面進行標識即可成為一個服務應用介面。

例如:


[ServiceRoute]
public interface ITestAppService
{
}

服務路由特性

開發者對應用介面標識路由(ServiceRouteAttribute)時,可以通過路由特性的屬性對生成的路由模板、應用介面請求頭是否支援serviceKey進行配置。

屬性名稱 說明 預設值
template 在對服務應用介面標識為服務路由時,可以通過[ServiceRoute(template: "test/{appservice=templateName}")]指定應用服務介面的路由模板。 api/{appservice}
multipleServiceKey 閘道器生成的webapi請求頭是否支援serviceKey請求頭 false

服務條目

服務條目(ServiceEntry): 服務應用介面中定義的每一個方法都會生成微服務叢集的一個服務條目。對微服務應用本身而言,服務條目就相當於MVC中的Action,服務應用介面就相當於Controller

根據服務條目生成webAPI

應用介面被閘道器引用後,會根據服務應用介面的路由模板和服務條目方法的Http動詞特性指定的路由資訊或是方法名稱生成相應的webapi,服務條目生成的WebAPI支援restfulAPI風格。

服務條目生成webapi的規則為:

  1. 禁止叢集外部訪問的服務條目([Governance(ProhibitExtranet = true)])不會生成webapi;

  2. 可以通過[ServiceRoute(template: "test/{appservice=templateName}")]為應用介面指定統一的路由模板;

  3. 如果服務條目方法沒有被http謂詞特性標識,那麼生成的webapi的http請求動詞會根據服務條目的方法名稱生成,如果沒有匹配到相應的服務條目方法,則會根據服務條目的方法引數;

  4. 服務條目方法可以通過http謂詞特性進行標識,並且http謂詞特性還支援對服務條目指定路由模板,路由模板的指定還支援對路由引數進行約束;


服務條目生成的webAPI = 應用介面條目路由模板 + “方法名稱||Http特性指定的路由特性”

如果不存在Http謂詞特性標識情況下,生成的webapi路由說明(例如,應用介面名稱為:ITestAppService,路由模板未被改寫):

方法名稱 生成的webAPI路徑 Http請求動詞
GetXXX /api/test get
SearchXXX /api/test/search get
CreateXXX /api/test post
UpdateXXX /api/test put
DeleteXXX /api/test delete

存在Http謂詞情況下,生成的webapi的請求動詞會根據服務條目標識的http謂詞特性來決定,開發者還可以通過http謂詞特性為服務條目的的路由進行調整,並且支援路由引數的形式,例如:

方法名稱 生成的webAPI路徑 http謂詞特性 Http請求動詞
GetXXX /api/test/{id} [HttpGet("{id:strig}")] get
DeleteXXX /api/test/name/{name} [HttpDelete("name/{name:strig}")] delete
UpdateXXX /api/test/email [HttpPatch("email")] patch
CreateXXX /api/test/user [HttpPost("user")] post

服務條目的治理特性

開發者可以通過配置檔案對服務條目的治理進行統一配置,除此之外可以通過Governance特性為服務條目方法進行標識,通過其屬性對服務條目進行治理。通過Governance特性對服務條目方法註解後,服務條目的治理屬性將會被該特性重寫。

服務條目治理的屬性請參考服務條目治理屬性配置

快取攔截

在服務應用介面被其他微服務應用引用後,可以通過快取攔截特性(GetCachingIntercept)在rpc通訊過程中,直接從分散式快取中讀取資料,避免通過網路通訊,而從提高系統效能。

更多關於快取攔截請參考快取攔截

服務條目的例子

    [ServiceRoute]
    public interface ITestAppService
    {
        ///  新增介面測試([post]/api/test)
        [GetCachingIntercept("name:{0}")]
        Task<TestOut> Create(TestInput input);

        ///  更新介面測試([put]/api/test)
        Task<string> Update(TestInput input);

        /// 刪除介面測試([delete]/api/test)
        [RemoveCachingIntercept("ITestApplication.Test.Dtos.TestOut", "name:{0}")]
        [Transaction]
        Task<string> Delete([CacheKey(0)] string name);

        /// 查詢介面測試([get]/api/test/search)
        [HttpGet]
        Task<string> Search([FromQuery] TestInput query);

        /// 以表單格式提交資料([post]/api/test/form)
        [HttpPost]
        string Form([FromForm] TestInput query);

        /// 通過name獲取單條資料([get]/api/test/{name:string},以path引數傳參,並約束引數型別為string)
        [HttpGet("{name:string}")]
        [Governance(ShuntStrategy = AddressSelectorMode.HashAlgorithm)]
        [GetCachingIntercept("name:{0}")]
        Task<TestOut> Get([HashKey] [CacheKey(0)] string name);

        /// 通過id獲取單條資料([get]/api/test/{id:long},以path引數傳參,並約束引數型別為long)
        [HttpGet("{id:long}")]
        [Governance(ShuntStrategy = AddressSelectorMode.HashAlgorithm)]
        [GetCachingIntercept("id:{0}")]
        Task<TestOut> GetById([HashKey] [CacheKey(0)] long id);

        ///更新部分資料,使用patch請求 ([patch]/api/test)
        [HttpPatch]
        [Governance(FallBackType = typeof(UpdatePartFallBack))]
        Task<string> UpdatePart(TestInput input);
    }

應用介面的實現

一般地,開發者應當將應用服務介面和應用服務介面的實現分開定義在不同的程式集。應用介面程式集可以被打包成Nuget包或是以專案的形式被其他微服務應用引用,這樣其他微服務就可以通過rpc代理的方式與該微服務應用進行通訊。更多RPC通訊方面的文件請參考

一個應用介面可以有一個或多個實現類。只有應用介面在當前微服務應用中存在實現類,該微服務應用介面對應的服務條目才會生成相應的服務路由,並將服務路由資訊註冊到服務註冊中心,同時其他微服務應用的例項會訂閱到微服務叢集的路由資訊。

如果服務應用介面存在多個實現類,那麼服務應用介面的路由特性應的multipleServiceKey的引數值應當被設定為true([ServiceRoute(multipleServiceKey: true)])。這樣,在閘道器應用引用該微服務的應用介面程式集生成的swagger文件才會有serviceKey請求頭。

應用介面如果存在多個實現類的情況下,那麼應用介面的實現類,需要通過ServiceKeyAttribute特性進行標識。ServiceKeyAttribute存在兩個引數(屬性)。

屬性名稱 說明 備註
name 服務實現類的名稱 通過webapi請求時,通過請求頭serviceKey進行設定;rpc通訊中,可以通過ICurrentServiceKey的例項調整要請求的應用介面實現。
weight 權重 如果通訊過程中,未指定serviceKey,那麼,會請求權重最高的應用介面的實現類

例項:


/// 應用服務介面(如:可定義在ITestApplication.csproj專案)
[ServiceRoute(multipleServiceKey: true)]
public interface ITestAppService
{
    Task<string> Create(TestInput input);
   // 其他服務條目方法略
}


//------------------------------------//
/// 應用服務例項類1 (如:可定義在TestApplication.csproj專案)
[ServiceKey("v1", 3)]
public class TestAppService : ITestAppService
{
  public Task<string> Create(TestInput input)
  {
      return Task.FromResult("create v1")
  }
  // 其他介面實現方法略
}

//------------------------------------//
/// 應用服務例項類2  (如:可定義在TestApplication.csproj專案)
[ServiceKey("v2", 1)]
public class TestV2AppService : ITestAppService
{
  public Task<string> Create(TestInput input)
  {
      return Task.FromResult("create v2")
  }
   // 其他介面實現方法略
}

生成的swagger文件如下:

image

在rpc通訊過程中,可以通過ICurrentServiceKey的例項設定要請求的應用介面的serviceKey


private readonly ICurrentServiceKey _currentServiceKey;

public TestProxyAppService(ITestAppService testAppService,
    ICurrentServiceKey currentServiceKey)
{
    _testAppService = testAppService;
    _currentServiceKey = currentServiceKey;
}

public async Task<string> CreateProxy(TestInput testInput)
{
    _currentServiceKey.Change("v2"); //在rpc代理請求之前設定`serviceKey`;
    return await _testAppService.Create(testInput);
}

開源地址與文件

github: https://github.com/liuhll/lms

gitee: https://gitee.com/liuhll2/lms

開發者文件: http://docs.lms-fk.com/

相關文章