服务治理的概念

服务治理是主要针对分布式服务框架、微服务,处理服务调用之间的关系,服务发布和发现(谁是提供者,谁是消费者,要注册到哪里),出了故障谁调用谁,服务的参数都有哪些约束,如何保证服务的质量?如何服务降级和熔断?怎么让服务受到监控,提高机器的利用率?

微服务有哪些问题需要治理?

  1. 服务注册与发现: 单体服务拆分为微服务后,如果微服务之间存在调用依赖,就需要得到目标服务的服务地址,也就是微服务治理的 服务发现 。要完成服务发现,就需要将服务信息存储到某个载体,载体本身即是微服务治理的服务注册中心,而存储到载体的动作即是服务注册

  2. 可观测性: 微服务由于较单体应用有了更多的部署载体,需要对众多服务间的调用关系、状态有清晰的掌控。可观测性就包括了调用拓扑关系、监控(Metrics)、日志(Logging)、调用追踪(Trace)等。

  3. 流量管理: 由于微服务本身存在不同版本,在版本更迭过程中,需要对微服务间调用进行控制,以完成微服务版本更迭的平滑。这一过程中需要根据流量的特征(访问参数等)、百分比向不同版本服务分发,这也孵化出灰度发布、蓝绿发布、A/B测试等服务治理的细分主题。

  4. 服务容错: 任何服务都不能保证100%不出问题,生产环境复杂多变,服务运行过程中不可避免的发生各种故障(宕机、过载等等),工程师能够做的是在故障发生时尽可能降低影响范围、尽快恢复正常服务,需要引入「熔断、隔离、限流和降级、超时机制」等「服务容错」机制来保证服务持续可用性。

  5. 安全: 不同微服务承载自身独有的业务职责,对于业务敏感的微服务,需要对其他服务的访问进行认证与鉴权,也就是安全问题。

  6. 控制: 对服务治理能力充分建设后,就需要有足够的控制能力,能实时进行服务治理策略向微服务分发。

  7. 服务本身的治理: 确保微服务主机的健康,有能力将不健康节点从微服务集群中移除。

服务注册与发现

silky支持服务的自动注册和发现,支持使用 ZookeeperNacosConsul 作为服务注册中心。服务实例上线、下线智能感知。

  1. 当服务实例启动时,会向服务注册中心新增或是更新服务元数据(如果不存在新增服务元数据、如果存在服务元数据则更新);同时,更新服务注册中心该实例的终结点(实例地址信息)。

  2. 使用 Zookeeper 或是 Nacos 作为服务注册中心,会通过 发布-订阅 的方式从服务注册中心获取最新的服务元数据和服务实例的终结点(实例地址)信息,并更新到本地内存;

  3. 如果使用 Consul 作为服务注册中心,则会通过心跳的方式从服务注册中心 拉取 最新的服务元数据和服务实例的终结点(实例地址)信息。当服务注册中心的终结点(地址信息)发生变化,服务实例的内存中服务路由表信息也将得到更新。

  4. 当在RPC通信过程中发生IO异常或是通信异常时,服务实例将会在n(配置属性为:Governance:UnHealthAddressTimesAllowedBeforeRemoving)次后从服务注册中心移除。(UnHealthAddressTimesAllowedBeforeRemoving如果的值等于0,则服务实例将会被立即移除)。

  5. 在RPC通信过程中,采用长链接, 支持心跳检测。在服务之间建立连接后,如果Governance:EnableHeartbeat配置为true,那么会定时(通过配置Governance:HeartbeatWatchIntervalSeconds)发送一个心跳包,从而保证会话链接的可靠性。如果心跳检测到通信异常,则会根据配置属性(Governance:UnHealthAddressTimesAllowedBeforeRemoving)n次后,从服务注册中心移除。

负载均衡

在RPC通信过程中,silky框架支持 轮询(Polling)随机(Random)哈希一致性(HashAlgorithm) 等负载均衡算法。负载均衡的缺省值为 轮询(Polling) ,开发者可以通过配置属性 Governance:ShuntStrategy 来统一指定负载均衡算法。同时,开发者也可以通过GovernanceAttribute特性来重置应用服务方法(服务条目)的负载均衡算法。

例如:

[HttpGet("{name}")]
[Governance(ShuntStrategy = ShuntStrategy.HashAlgorithm)]
Task<TestOut> Get([HashKey]string name);

如果选择使用 哈希一致性(HashAlgorithm) 作为负载均衡算法,则需要使用[HashKey]对某一个参数进行标识,这样,相同参数的请求,在RPC通信过程中,都会被路由到通一个服务实例。

超时

在RPC通信中,如果在给定的配置时长没有返回结果,则会抛出超时异常。一般地,开发者可以通过配置属性Governance:TimeoutMillSeconds来统一的配置RPC调用超时时长,缺省值为5000ms。同样地,开发者也可以通过GovernanceAttribute特性来重置应用服务方法的超时时长。

[HttpGet("{name}")]
[Governance(TimeoutMillSeconds = 1000)]
Task<TestOut> Get([HashKey]string name);

如果将超时时长配置为0,则表示在RPC调用过程中,不会出现超时异常,直到RPC调用返回结果或是抛出RPC调用抛出其他异常。

::: tip 提示

建议在开发环境中, 将配置属性Governance:TimeoutMillSeconds设置为0,方便开发者进行调试。

:::

故障转移(失败重试)

在RPC通信过程中,如果发生IO异常(IOException)、通信异常(CommunicationException)、或是找不到本地服务条目(服务提供者抛出NotFindLocalServiceEntryException异常)、超出服务提供者允许的最大处理并发量(NotFindLocalServiceEntryException),则服务消费者会根据配置的次数选择其他服务实例重新调用。

  1. 如果RPC调用过程中发生的是IO异常(IOException)或是通信异常(CommunicationException)或是服务提供者抛出NotFindLocalServiceEntryException异常,将会把选择的服务实例的状态变更为不可用状态,在Governance:UnHealthAddressTimesAllowedBeforeRemoving次标识后,服务实例将会下线(将服务提供者的实例地址从服务注册中心移除)。

  2. 如果超出服务提供者实例允许的最大并发量,则会选择其他服务实例进行调用,但不会变更服务实例的状态。(换句话说,也就是服务提供者触发了限流保护)

  3. 其他类型的异常不会导致失败重试。

开发者通过Governance:RetryTimes配置项来确定失败重试的次数,缺省值等于3。同样地,开发者也可以通过GovernanceAttribute特性来重置失败重试次数。如果RetryTimes被设置为小于等于0,则不会发生失败重试。通过Governance:RetryIntervalMillSeconds 可以配置失败重试的间隔时间。

[HttpGet("{name}")]
[Governance(RetryTimes = 3)]
Task<TestOut> Get([HashKey]string name);

开发者也可以自定义失败重试策略,只需要继承InvokeFailoverPolicyProviderBase基类,通过重写Create方法构建失败策略。

下面的例子演示了定义超时重试的策略:

public class TimeoutFailoverPolicyProvider : InvokeFailoverPolicyProviderBase
{ private readonly IServerManager _serverManager; public TimeoutFailoverPolicyProvider( IServerManager serverManager)
{
_serverManager = serverManager;
} public override IAsyncPolicy<object> Create(string serviceEntryId, object[] parameters)
{
IAsyncPolicy<object> policy = null;
var serviceEntryDescriptor = _serverManager.GetServiceEntryDescriptor(serviceEntryId); if (serviceEntryDescriptor?.GovernanceOptions.RetryTimes > 0)
{
policy = Policy<object>
.Handle<Timeoutxception>()
.Or<SilkyException>(ex => ex.GetExceptionStatusCode() == StatusCode.Timeout)
.WaitAndRetryAsync(serviceEntryDescriptor.GovernanceOptions.RetryIntervalMillSeconds,
retryAttempt =>
TimeSpan.FromMilliseconds(serviceEntryDescriptor.GovernanceOptions.RetryIntervalMillSeconds),
(outcome, timeSpan, retryNumber, context)
=> OnRetry(retryNumber, outcome, context));
}
return policy;
}
}

熔断保护(断路器)

RPC通信过程中,在开启熔断保护的情况下,如果在连续发生n次 非业务类异常 (非业务类异常包括友好类异常、鉴权类异常、参数校验类异常等),则会触发熔断保护,在一段时间内,该服务条目将不可用。

开发者通过Governance:EnableCircuitBreaker配置项来确定是否要开启熔断保护,通过Governance:ExceptionsAllowedBeforeBreaking配置项来确定在熔断保护触发前允许的异常次数(这里的异常为非业务类异常),通过Governance:BreakerSeconds配置项来确定熔断的时长(单位为:秒)。同样地,熔断保护也可以通过GovernanceAttribute来进行配置。

[HttpGet("{name}")]
[Governance(EnableCircuitBreaker = true,ExceptionsAllowedBeforeBreaking = 2, BreakerSeconds = 120)]
Task<TestOut> Get([HashKey]string name);

限流

限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。

Silky微服务框架的限流分为两部分,一部分是服务内部RPC之间的通信,一部分是对HTTP请求的进行限流。

RPC限流

当服务提供者接收到RPC请求后,如果当前服务实例并发处理量大于配置的Governance:MaxConcurrentHandlingCount,当前实例无法处理该请求,会抛出OverflowMaxServerHandleException异常。服务消费者会根据配置重试该服务的其他实例,可参考故障转移(失败重试)节点。

Governance:MaxConcurrentHandlingCount的配置缺省值为50,如果配置小于等于0,则表示不对rpc通信进行限流。这里的配置针对的是服务实例所有并发处理的能力,并不是针对某个服务条目的并发量配置,所以开发者无法通过GovernanceAttribute特性来修改并发处理量的配置。

HTTP限流

Silky框架除了支持服务内部之间RPC调用的限流之外,还支持通过AspNetCoreRateLimit实现对Http请求的限流。AspNetCoreRateLimit 支持通过Ip或是针对IP进行限流。

下面我们来简述如何使用 AspNetCoreRateLimit 达到对http请求的限流。

1. 添加配置

网关应用新增限流配置文件 ratelimit.json。在RateLimiting:Client配置节点配置针对客户端的限流通用规则,通过RateLimiting:Client:Policies配置节点重写针对特定客户端的限流策略。开发者可以参考ClientRateLimitMiddleware熟悉相关的配置属性。在RateLimiting:Ip配置节点配置针对IP的限流通用规则,通过RateLimiting:Ip:Policies配置节点重写针对特定IP的限流策略。开发者可以参考IpRateLimitMiddleware熟悉相关的配置属性。

如果网关采用分布式部署,可以通过RateLimiting:RedisConfiguration属性配置redis服务作为存储服务。

例如:

{
"RateLimiting": {
"Client": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"EndpointWhitelist": [
"get:/api/license",
"*:/api/status"
],
"ClientWhitelist": [
"dev-id-1",
"dev-id-2"
],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 5
}
],
"QuotaExceededResponse": {
"Content": "{{ \"data\":null,\"errorMessage\": \"Whoa! Calm down, cowboy! Quota exceeded. Maximum allowed: {0} per {1}. Please try again in {2} second(s).\",\"status\":\"514\",\"statusCode\":\"OverflowMaxRequest\" }}",
"ContentType": "application/json",
"StatusCode": 429
},
"Policies": {
"ClientRules": [
]
}
}
},
"RedisConfiguration": "127.0.0.1:6379,defaultDatabase=1"
}

2. 注册服务

Startup 启动类中, 添加 AspNetCoreRateLimit 的相关服务。

public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
var redisOptions = configuration.GetRateLimitRedisOptions();
services.AddClientRateLimit(redisOptions);
services.AddIpRateLimit(redisOptions);
}

当然,如果开发者是通过 AddSilkyHttpServices() 进行服务注册,在这个过程中,已经同时添加了 AspNetCoreRateLimit 的相关服务

3.启用 AspNetCoreRateLimit 的 相关中间件,实现HTTP限流。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseClientRateLimiting(); // 针对客户端进行限流
// app.UseIpRateLimiting(); // 针对IP进行限流 }

服务回退(服务降级)

在RPC调用过程中,如果执行失败,我们通过调用Fallback方法,从而达到服务降级操作。

想要实现服务回退的处理,我们需要在定义服务方法的时候通过FallbackAttribute特性指定的要回退的接口类型。给定的接口需要定义一个与服务方法参数相同的方法。

FallbackAttribute需要指定回退接口接口的类型,方法名称(如果缺省,则回退接口定义的方法名称与服务条目方法一致),如果定义了多个回退方法,还要给出权重配置。

属性 配置 备注
Type 回退接口类型 必须指定,一般与服务接口定义在一起
MethodName 指定的回退方法 如果不配置的话,则与服务条目方法一致,定义的方法的参数必须一致
  [HttpPatch]
[Fallback(typeof(IUpdatePartFallBack))]
Task<string> UpdatePart(TestInput input);

IUpdatePartFallBack需要定义一个与UpdatePart参数相同的方法。

public interface IUpdatePartFallBack
{
Task<string> UpdatePart(TestInput input);
}

定义的回退接口和方法只有在被实现后,才会在RPC调用失败后执行定义的Fallback方法。服务回退的实现方式有两种方式,一种是在服务端实现回退,一种是在客户端实现。

在服务端实现回退方法

如果在服务端实现回退方法,当RPC在服务端执行业务方法失败,如果服务端有存在定义的回退接口的实现,那么会降级执行回退方法。

举一个比较适用的场景,例如: 在一个短信收发的业务场景中,如果使用阿里云作为服务提供商相对更便宜,但是如果欠费后我们希望能够平滑的切换到腾讯云提供商。在这样的业务场景下,如果我们默认选择使用阿里云作为服务提供商, 我们就可以通过在服务端实现一个定义的Fallback方法从而达到平滑的使用备用短信服务提供商的作用。

在消费端实现回退方法

当然, 我们也可以在调用端(消费端)实现定义的Fallback方法,如果RPC调用失败,那么在调用端就会执行实现了的Fallback方法。返回给前端的是降级后的数据,并不会发生异常。

public class TestUpdatePartFallBack : IUpdatePartFallBack, IScopedDependency
{
public async Task<string> UpdatePart(TestInput input)
{
return "this is a fallback method for update part";
}
}

链路跟踪

silky框架使用SkyAPM实现了链路跟踪,开发者通过引入相应的配置和服务,即可实现对http请求、RPC调用、TCC分布式事务执行过程以及EFCore数据访问的调用链路跟踪。

开发者可以通过查看链路跟踪节点熟悉如何进行配置和引入相应的服务以及如何部署 skywalking,并通过 skywalking 查看调用的链路。

安全

在silky框架中,非常重视对安全模块的设计。

  1. 通过rpc:token的配置,保证外部无法通过RPC端口直接访问应用服务。服务内部之间的调用均需要对rpc:token进行校验,如果rpc:token不一致,则不允许进行调用。
  2. 在网关处统一实现身份认证与授权。开发者可以通过查看身份认证与授权节点查看相关文档。
  3. 开发者可以通过GovernanceAttribute特性来禁止外部访问某个应用服务方法。被外部禁止访问的应用服务方法只允许服务内部之间通过RPC方式进行通信。
[Governance(ProhibitExtranet = true)]
Task<string> Delete(string name);

缓存拦截

在RPC通信过程中,通过引入缓存拦截,极大的提高了系统性能。

开发者可以通过缓存文档,熟悉缓存拦截的使用方法。

开源地址

在线文档

在线示例

silky微服务框架的服务治理介绍的更多相关文章

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

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

  2. 使用dubbo分布式服务框架发布服务及消费服务

    什么是DUBBO DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案. 准备工作 安装zookeeper ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服 ...

  3. Windows服务框架与服务的编写

    从NT内核开始,服务程序已经变为一种非常重要的系统进程,一般的驻守进程和普通的程序必须在桌面登录的情况下才能运行,而许多系统的基础程序必须在用户登录桌面之前就要运行起来,而利用服务,可以很方便的实现这 ...

  4. .Net下几个服务框架介绍

    简介 在公司的服务多了以后,为了调用上的方便,同时为了以后的服务治理,一般都会使用一些服务框架,这里主要介绍我知道的几个服务框架,简析一下这些服务框架的基本概念. 可投入生产环境使用的 以下两个服务框 ...

  5. 日调度万亿次,微服务框架TSF大规模应用——云+未来峰会开发者专场回顾

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 演讲者:张浩 腾讯云中间件产品负责人 背景:众多开发者中,一定经历类似的甜蜜烦恼,就是当线上业务规模越来越大,系统分支发展越来越多的时候,初 ...

  6. Taurus.MVC 微服务框架 入门开发教程:项目集成:1、服务端:注册中心、网关(提供可运行程序下载)。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  7. Dubbo(一) 开始认识Dubbo,分布式服务框架

    引言: 以前的车马很慢,一生只够爱一个人以前的网站人很少,一个单应用服务着一个人--------------------现在,动不动就谈什么高并发,千万级访问.单应用?BOOM!分分钟爆炸.于是,技术 ...

  8. 使用“消息服务框架”(MSF)实现分布式事务的三阶段提交协议(电商创建订单的示例)

    1,示例解决方案介绍 在上一篇 <消息服务框架(MSF)应用实例之分布式事务三阶段提交协议的实现>中,我们分析了分布式事务的三阶段提交协议的原理,现在我们来看看如何使用消息服务框架(MSF ...

  9. 分布式服务框架选型:面对Dubbo,阿里巴巴为什么选择了HSF?

    转载:http://www.sohu.com/a/141490021_268033 阿里巴巴集团内部使用的分布式服务框架 HSF(High Speed Framework,也有人戏称“好舒服”)已经被 ...

随机推荐

  1. Spring面试复习整理

    Spring Spring核心分为三方面: 控制反转(IoC): 就是将创建对象的权利交给框架处理/控制,不需要人为创建,有效降低代码的耦合度,降低了开发成本. 依赖注入(DI): 容器动态地将将某种 ...

  2. display:none、visibility:hidden,opacity:0三者区别

    1. display:none 设置display:none,让这个元素消失 消失不占据原本任何位置 连带子元素一起消失 元素显示:display:block 2. visibility:hidden ...

  3. uoj22 外星人(dp)

    题目大意: 给定一个\(n\)个数的序列\(a\),给定一个\(x\),其中\(a\)数组可以进行顺序的调换,每一个\(a_i\)都能使$x=x \mod a_i \(, 求最后经过一系列计算后的\) ...

  4. NX 图标

    vector_on_curve   crosscut_zig_zag_with_lifts vector_along_curve   zlevel_zig add_new_sc   zlevel_zi ...

  5. 前端面试题之手写promise

    前端面试题之Promise问题 前言 在我们日常开发中会遇到很多异步的情况,比如涉及到 网络请求(ajax,axios等),定时器这些,对于这些异步操作我们如果需要拿到他们操作后的结果,就需要使用到回 ...

  6. SpringBoot 01 hello world 01

    hello world项目结构: pom中配置的依赖相当于spring boot的可安装插件,需要下载的依赖直接在里边配置. 目前用到的每个注解: 1.主程序中 @SpringBootApplicat ...

  7. relativeLayout相对布局的嵌套在py中的引用

    from kivy.app import App from kivy.uix.button import Button from kivy.uix.relativelayout import Rela ...

  8. kivy浮点布局

    from kivy.app import App from kivy.uix.floatlayout import FloatLayout class FloatLayoutWidget(FloatL ...

  9. Java:修饰符小记

    Java:修饰符小记 对 Java 中的 修饰符,做一个微不足道的小小小小记 Java 语言提供了很多修饰符,大概分为两类: 访问权限修饰符 非访问权限修饰符 访问权限修饰符 修饰符 说明 publi ...

  10. elasticsearch的索引重建

    我们知道es在字段的mapping建立后就不可再次修改mapping的值.在我们实际的情况下有些时候就是需要修改mapping的值,解决方案就是重新构建索引数据. 方式一 : 使用索引别名,创建另外一 ...