silky微服務的應用服務和服務條目


服務的定義

服務接口是微服務定義服務的基本單位,定義的應用服務接口可以被其他微服務引用,其他微服務通過rpc框架與該微服務進行通信。

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

例如:

[ServiceRoute]
public interface ITestAppService
{
}

雖然我們通過使用[ServiceRoute]特性可以對任何一個接口標識為一個服務,服務定義的方法會通過應用服務的模板和方法特性的模板生成對應的webapi(該方法沒有服務特性標識為禁用外網)。但是良好的命名規范可以為我們構建服務省去很多不必要的麻煩(通俗的說就是:約定大約配置)。

一般地,我們推薦使用AppService作為定義的服務的后綴。即推薦使用IXxxxAppService作為應用服務接口名稱,默認生成的服務模板為:api/{appservice},使用XxxxAppService作為應用服務實現類的名稱。

路由特性(ServiceRouteAttribute)可以通過template對服務路由模板進行設置。路由模板可以通過{appservice=templateName}設置服務的名稱。

屬性名稱 說明 缺省值
template 在對服務接口標識為服務路由時,可以通過[ServiceRoute("{appservice=templateName}")]指定應用服務接口的路由模板。templateName的缺省值名稱為對應服務的名稱 api/{appservice}

服務條目

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

根據服務條目生成WebAPI

應用接口被web主機應用或是網關引用后,會根據服務應用接口的路由模板和服務條目方法的Http動詞特性指定的路由信息或是方法名稱生成相應的webapi,服務條目生成的WebAPI支持restfulAPI風格。

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

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

  2. 可以通過 [ServiceRoute("{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]
        Task<string> UpdatePart(TestInput input);
    }

服務的實現

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

一個服務接口可以有一個或多個實現類。只有應用接口在當前微服務應用中存在實現類,該微服務應用接口對應的服務條目才會生成相應的服務路由,並將服務路由信息注冊到服務注冊中心,同時其他微服務應用的實例會訂閱到微服務集群的路由信息。

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

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

實例:

/// 應用服務接口(如:可定義在ITestApplication.csproj項目)
[ServiceRoute]
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文檔如下:

在rpc通信過程中,可以通過IServiceKeyExecutor的實例設置要請求的應用接口的serviceKey

private readonly IServiceKeyExecutor _serviceKeyExecutor;

public TestProxyAppService(ITestAppService testAppService,
    IServiceKeyExecutor serviceKeyExecutor)
{
    _testAppService = testAppService;
    _serviceKeyExecutor = serviceKeyExecutor;
}

public async Task<string> CreateProxy(TestInput testInput)
{
   return await _serviceKeyExecutor.Execute(() => _testAppService.Create(testInput), "v2");
}

開源地址

在線文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM