Silky微服务框架在线文档Silky微服务框架在线文档
首页
文档
配置
源码解析
博文
github
gitee
  • 简体中文
  • English
首页
文档
配置
源码解析
博文
github
gitee
  • 简体中文
  • English
  • 简介

    • silky 框架介绍
  • 入门

    • 名词解释
    • 快速开始
    • 脚手架
    • 微服务模块化架构的最佳实践 & 约定
    • 示例
  • 主机与模块

    • 主机
    • 网关
    • 模块
    • 插件
  • 网关与 HTTP

    • Swagger 文档
    • 性能分析(MiniProfiler)
    • 跨域(CORS)
    • 审计日志
  • 服务与 RPC

    • 应用服务和服务条目
    • rpc通信
    • websocket通信
    •  服务注册中心
    • 服务治理
  • 数据与缓存

    • EFCore 数据访问
    • 缓存
    • 分布式锁
  • 安全与认证

    • 身份认证与授权
    • 分布式事务
  • 基础设施

    • 依赖注入
    • 对象到对象的映射
    • 参数验证
    • 链路跟踪
    • 日志(Serilog)
    • 健康检查
    • 消息总线(MassTransit)
    • 单元测试与集成测试

服务的定义

服务接口是微服务应用定义服务的基本单位,定义的应用服务接口可以被其他微服务引用,其他微服务通过 RPC 框架与该微服务进行通信。这里的服务也就是 RPC 框架中所指的服务提供者。

通过 [ServiceRoute](即 ServiceRouteAttribute)特性对一个接口进行标识即可将其定义为一个应用服务接口。

例如:

namespace Test;

[ServiceRoute]
public interface ITestAppService
{
   Task<string> Echo(string ping);
}

我们通过使用[ServiceRoute]特性可以对任何一个接口标识为一个服务,服务定义的方法会通过应用服务的模板和方法特性的模板生成对应的webapi(该方法没有服务特性标识为禁止外网)。良好的命名规范可以为我们构建服务省去很多不必要的麻烦(通俓的说就是:约定大于配置)。

一般地,我们推荐使用AppService作为定义的服务的后缀。即推荐使用IXxxxAppService作为应用服务接口名称,默认生成的服务模板为:api/{appservice},使用XxxxAppService作为应用服务实现类的名称。

路由特性(ServiceRouteAttribute)可以通过template对服务路由模板进行设置。路由模板可以通过{appservice=templateName}设置服务的名称。

属性名称说明缺省值
template在对服务接口标识为服务路由时,可以通过[ServiceRoute("{appservice=templateName}")]指定应用服务接口的路由模板。templateName的缺省值名称为对应服务的名称api/{appservice}

服务条目

服务条目(ServiceEntry): 服务接口中定义的每一个方法都会生成微服务集群的一个服务条目。对微服务应用本身而言,服务条目就相当于MVC中的Action,应用服务就相当于Controller。

每个服务条目都会在运行时生成一个相应的服务条目信息,在rpc通信过程中,可以通过服务条目Id或是其对应的WebAPI + Http谓词来定位到相应的服务条目。

服务条目Id

每个微服务条目都会生成唯一的服务条目Id,他将会用于定位(路由)相应的服务条目,服务条目Id的生成规则如下:


服务条目Id = 方法的完全限定名 + 参数名 + _ + Http谓词

例如如下的Echo方法生成的服务条目Id为:Test.ITestAppService.Echo.ping_Get

namespace Test;

[ServiceRoute]
public interface ITestAppService
{
   [HttpGet]
   Task<string> Echo(string ping);
}

开发者可以通过网关开启微服务集群的管理端(Dashboard),然后登录管理端后,查询各个微服务应用生成的服务以及相应的服务条目,并查询该服务条目的配置参数,他将会影响服务之间的RPC通信行为。

根据服务条目生成WebAPI

应用接口被web主机应用或是网关引用后,会根据服务应用接口的路由模板和服务条目方法的Http动词特性指定的路由信息或是方法名称生成相应的webapi,服务条目生成的WebAPI支持restfulAPI风格。

服务条目生成webapi的规则为:

  1. 通过特性[ProhibitExtranet](或是[Governance(ProhibitExtranet = true)])可以禁止生成的服务条目被外网访问;

  2. 可以通过 [ServiceRoute("{appservice=templateName}")]为应用接口指定统一的路由模板;

  3. 如果服务条目方法没有被http谓词特性标识,那么生成的webapi的http请求动词会根据服务条目的方法名称生成,如果没有匹配到相应的服务条目方法,则会根据服务条目的方法参数;

  4. 服务条目方法可以通过http谓词特性进行标识,并且http谓词特性还支持对服务条目指定路由模板,路由模板的指定还支持对路由参数进行约束;


服务条目生成的webAPI = 应用接口条目路由模板 + “方法名称||Http特性指定的路由特性”

如果不存在Http谓词特性标识情况下,生成的webapi路由说明(例如,应用接口名称为:ITestAppService,路由模板未被改写):

方法名称生成的 WebAPI 路径HTTP 请求动词
GetXXX/api/testGET
SearchXXX/api/test/searchGET
CreateXXX/api/testPOST
UpdateXXX/api/testPUT
DeleteXXX/api/testDELETE

存在 HTTP 谓词特性时,生成的 WebAPI 请求动词由服务条目标识的 HTTP 谓词特性决定,开发者还可以通过 HTTP 谓词特性调整路由,支持路由参数约束:

方法名称生成的 WebAPI 路径HTTP 谓词特性HTTP 请求动词
GetXXX/api/test/{id}[HttpGet("{id:long}")]GET
DeleteXXX/api/test/name/{name}[HttpDelete("name/{name:string}")]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:{Name}")]
        Task<TestOut> Create(TestInput input);

        ///  更新接口测试([put]/api/test)
        Task<string> Update(TestInput input);

        /// 删除接口测试([delete]/api/test)
        [RemoveCachingIntercept("ITestApplication.Test.Dtos.TestOut", "name:{name}")]
        [Transaction]
        Task<string> Delete(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 = ShuntStrategy.HashAlgorithm)]
        [GetCachingIntercept("name:{name}")]
        Task<TestOut> Get(string name);

        /// 通过id获取单条数据([get]/api/test/{id:long},以path参数传参,并约束参数类型为long)
        [HttpGet("{id:long}")]
        [Governance(ShuntStrategy = ShuntStrategy.HashAlgorithm)]
        [GetCachingIntercept("id:{id}")]
        Task<TestOut> GetById(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文档如下:

appservice-and-serviceentry1.jpg

在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");
}

RPC 过滤器

silky 框架提供了与 ASP.NET Core MVC 过滤器类似的 RPC 过滤器机制,允许开发者在 RPC 调用的不同阶段插入自定义逻辑。框架提供了两类过滤器:

  • 服务端过滤器(ServerFilterAttribute):在服务提供者执行服务条目前后触发,用于在服务端进行鉴权、日志、数据处理等操作。
  • 客户端过滤器(ClientFilterAttribute):在服务消费者发起 RPC 调用前后触发,用于在调用端进行参数修改、结果转换、错误处理等操作。

两类过滤器均可标注在应用服务接口或服务条目方法上,支持 AllowMultiple = true(同一目标允许多个),并通过 Order 属性控制执行顺序。

服务端过滤器

继承 ServerFilterAttribute 并重写所需方法即可自定义服务端过滤器:

方法触发时机
OnActionExecuting(ServerInvokeExecutingContext)服务条目方法执行前
OnActionExecuted(ServerInvokeExecutedContext)服务条目方法执行后
OnActionExecutionAsync(...)执行前后的异步包装,覆盖此方法可完全控制执行流程
OnResultExecuting(ServerResultExecutingContext)结果写回前
OnResultExecuted(ServerResultExecutedContext)结果写回后

ServerInvokeExecutingContext 的关键属性:

属性类型说明
ServiceEntryServiceEntry当前服务条目元数据
ServiceInstanceobject当前服务实现类实例
ServiceEntryArgumentsIDictionary<string, object>服务条目方法的入参
Resultobject设置此属性可短路后续执行并直接返回该值
// 自定义服务端过滤器示例:记录服务条目执行耗时
public class ServerTimingFilterAttribute : ServerFilterAttribute
{
    private readonly ILogger<ServerTimingFilterAttribute> _logger;
    private Stopwatch _stopwatch;

    public ServerTimingFilterAttribute(ILogger<ServerTimingFilterAttribute> logger)
    {
        _logger = logger;
    }

    public override void OnActionExecuting(ServerInvokeExecutingContext context)
    {
        _stopwatch = Stopwatch.StartNew();
    }

    public override void OnActionExecuted(ServerInvokeExecutedContext context)
    {
        _stopwatch.Stop();
        _logger.LogInformation("ServiceEntry {Id} executed in {Elapsed}ms",
            context.ServiceEntry.Id, _stopwatch.ElapsedMilliseconds);
    }
}

// 标注到应用服务接口或特定方法上
[ServiceRoute]
[ServerTiming]  // 作用于整个服务接口的所有方法
public interface IOrderAppService
{
    Task<OrderOutput> GetOrderAsync(long id);
}

客户端过滤器

继承 ClientFilterAttribute 并重写所需方法即可自定义客户端过滤器:

方法触发时机
OnActionExecuting(ClientInvokeExecutingContext)发起 RPC 调用前
OnActionExecuted(ClientInvokeExecutedContext)收到 RPC 响应后
OnResultExecuting(ClientResultExecutingContext)结果处理前
OnResultExecuted(ClientResultExecutedContext)结果处理后

ClientInvokeExecutingContext 的关键属性:

属性类型说明
ServiceEntryServiceEntry当前服务条目元数据
ResultRemoteResultMessage?设置此属性可短路 RPC 调用,直接返回指定结果
// 自定义客户端过滤器示例:在调用前附加自定义请求头
public class ClientCorrelationFilterAttribute : ClientFilterAttribute
{
    public override void OnActionExecuting(ClientInvokeExecutingContext context)
    {
        // 在发起 RPC 调用前,向 RpcContext 附件中写入自定义信息
        RpcContext.Context.SetInvokeAttachment("x-correlation-id", Guid.NewGuid().ToString());
    }
}

// 标注到应用服务接口或特定方法上
[ServiceRoute]
public interface IInventoryAppService
{
    [ClientCorrelationFilter]  // 仅作用于此方法
    Task<int> GetStockAsync(long productId);
}

提示

  • 服务端过滤器在被调用方(服务提供者)执行,客户端过滤器在调用方(服务消费者)执行。
  • 两类过滤器均可通过依赖注入获取服务(构造器注入)。
  • 可使用 Order 属性控制多个过滤器的执行顺序,值越小越先执行。
编辑当前页
Next
rpc通信