服务的定义

服务接口是微服务定义服务的基本单位,定义的应用服务接口可以被其他微服务引用,其他微服务通过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");
}

开源地址

在线文档

silky微服务的应用服务和服务条目的更多相关文章

  1. silky微服务简介

    代理主机 silky微服务定义了三种类型的代理主机,开发者可以根据需要选择合适的silky代理主机托管微服务应用.代理主机定义了一个Startup模块,该模块给出了使用该种类型主机所必须依赖的模块. ...

  2. silky微服务框架服务注册中心介绍

    目录 服务注册中心简介 服务元数据 主机名称(hostName) 服务列表(services) 终结点 时间戳 使用Zookeeper作为服务注册中心 使用Nacos作为服务注册中心 使用Consul ...

  3. silky微服务框架的服务治理介绍

    目录 服务治理的概念 服务注册与发现 负载均衡 超时 故障转移(失败重试) 熔断保护(断路器) 限流 RPC限流 HTTP限流 1. 添加配置 2. 注册服务 3.启用 AspNetCoreRateL ...

  4. Silky微服务框架之服务引擎

    构建服务引擎 在注册Silky微服务应用一节中,我们了解到在ConfigureServices阶段,通过IServiceCollection的扩展方法AddSilkyServices<T> ...

  5. Silky微服务框架之模块

    模块的定义 Silky是一个包括多个nuget包构成的模块化的框架,每个模块将程序划分为一个个小的结构,在这个结构中有着自己的逻辑代码和自己的作用域,不会影响到其他的结构. 模块类 一般地,一个模块的 ...

  6. silky微服务业务主机简介

    目录 主机的概念 通用主机 web主机 业务主机类型 使用web主机构建微服务应用 使用通用主机构建微服务应用 构建具有websocket能力的微服务应用 构建网关 开源地址 在线文档 主机的概念 s ...

  7. silky微服务模块

    目录 模块的定义和类型 在模块中注册服务 通过ServiceCollection实现服务注册 通过ContainerBuilder实现服务注册 使用模块初始化任务 使用模块释放资源 模块的依赖关系 构 ...

  8. DDD理论学习系列(8)-- 应用服务&领域服务

    DDD理论学习系列--案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可以理解为服务是行为的抽象.从 ...

  9. 应用服务&领域服务

    应用服务&领域服务 DDD理论学习系列——案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可 ...

随机推荐

  1. spring boot处理跨域请求代码

    @Configuration @WebFilter(filterName = "CorsFilte") public class CorsFilter implements Fil ...

  2. Spring技术内幕笔记2--我懒不写了哈哈哈哈。

    目录 1.1 关于IOC容器设计的线路区别 1.1.1 BeanFactory 1.1.2 ApplicationContext 2.1 FileSystemXmlApplicationContext ...

  3. Google Chrome打开权限设置开关(摄像头,录音等)

    在搜索框输入以下字符 chrome://flags/#unsafely-treat-insecure-origin-as-secure

  4. Java泛型中的细节

    Java泛型中的细节 如果没有泛型 学习Java,必不可少的一个过程就是需要掌握泛型.泛型起源于JDK1.5,为什么我们要使用泛型呢?泛型可以使编译器知道一个对象的限定类型是什么,这样编译器就可以在一 ...

  5. VulnHub 实战靶场Breach-1.0

    相比于CTF题目,Vulnhub的靶场更贴近于实际一些,而且更加综合考察了知识.在这里记录以下打这个靶场的过程和心得. 测试环境 Kali linux IP:192.168.110.128 Breac ...

  6. 从零入门 Serverless | Knative 带来的极致 Serverless 体验

    作者 | 冬岛 阿里巴巴高级技术专家 Serverless 公众号后台回复"knative",即可免费下载<Knative 云原生应用开发指南>电子书! 导读:Serv ...

  7. 双指针之滑动窗口(长度最小的子数组 和 和为s的连续正数序列)

    双指针之滑动窗口 (长度最小的子数组:和为s的连续正数序列) 1, 什么时候使用? (与子数组/字符串 有关的题目)~如果给了某个具体值的target,即用滑动窗口 不然就双指针(一般做法,左边< ...

  8. Python基础 | 字符串格式化输出及print()函数介绍

    在写代码时,我们会经常与字符串打交道,Python中控制字符串格式通常有三种形式,分别是使用str%,str.format(),f-str,用法都差不多,但又有一些细微之差. 一起来看看吧~~~ 一. ...

  9. 洛谷3203 弹飞绵羊(LCT)

    据说这个题当年的正解是分块qwq 根据题目所说,对于题目中的弹力系数,就相当于一条边,那么对于"跳出去"这个限制,我们可以选择建造一个新点\(n+1\)表示结束,那么每次,求一个点 ...

  10. GIS应用|快速开发REST数据服务

    随着计算机的快速发展,GIS已经在各大领域得到应用,和我们的生活息息相关, 但是基于GIS几大厂商搭建服务,都会有一定的门槛,尤其是需要server,成本高,难度大,这里介绍一种在线GIS云平台,帮你 ...