设为首页 加入收藏

TOP

Go微服务容错与韧性(Service Resilience)(四)
2019-09-26 18:14:08 】 浏览:156
Tags:服务 容错 韧性 Service Resilience
BreakerCallGet) CallGet(ctx context.Context, key string, c pb.CacheServiceClient) ( []byte, error) { var err error var value []byte var serviceUp bool log.Printf("state:%v", cb.State().String()) _, err = cb.Execute(func() (interface{}, error) { value, err = tcg.Next.CallGet(ctx, key, c) if err != nil { return nil, err } serviceUp = true return value, nil }) if !serviceUp { //return a default value here. You can also run a downgrade function here log.Printf("circuit breaker return error:%v\n", err) return []byte{0}, nil } return value, nil }

本示例中熔断器是设置在客户端的。从本质上来讲它是针对客户端的功能,当客户端察觉要调用的服务失效时,它暂时屏蔽掉这个服务。但我觉得它其实设置在服务端更有优势。 设想一下当有很多不同节点访问一个服务时,当然也有可能别人都能访问,只有我不能访问。这基本可以肯定是我的节点和服务端节点之间的链接出了问题,根本不需要熔断器来处理(容器就可以处理了)。因此,熔断器要处理的大部分问题是某个微服务宕机了,这时监测服务端更有效,而不是监测客户端。当然最后的效果还是要屏蔽客户端请求,这样才能有效减少网络负载。这就需要服务端和客户端之间进行协调,因此有一定难度。

另外还有一个问题就是如何判断服务宕机了,这个是整个熔断器的关键。如果服务返回错误结果,那么是否意味着服务失效呢?这会有很多不同的情况,熔断器几乎不可能做出完全准确的判断,从这点上来讲,熔断器还是有瑕疵的。我觉得要想做出准确的判断,必须网络,容器和Service Mesh进行联合诊断才行。

故障注入(Fault Injection)

故障注入通过人为地注入错误来模拟生产环境中的各种故障和不稳定性。你可以模拟10%的错误率,或服务响应延迟。使用的技术跟上面讲的差不多,可以通过修饰模式来对服务请求进行监控和控制,来达到模拟错误的目的。故障注入既可以在服务端也可以在客户端。

舱壁隔离技术(Bulkhead)

舱壁隔离技术指的是对系统资源进行隔离,这样当一个请求出现问题时不会导致整个系统的瘫痪。比较常用的是Thread pool和Connection pool。比如系统里有数据库的Connection Pool,它一般都有一个上限值,如果请求超出,多余的请求就只能处于等待状态。如果你的系统中既有运行很慢的请求(例如报表),也有运行很快的请求(例如修改一个数据库字段),那么一个好的办法就是设立两个Connection Pool, 一个给快的一个给慢的。这样当慢的请求很多时,占用了所有Connection Pool,但不会影响到快的请求。下面是它的示意图,它用的是Thread Pool。

file

图片来源

Netflix的Hystrix同时集成了舱壁隔离技术和熔断器,它通过限制访问一个服务的资源(一般是Thread)来达到隔离的目的。它有两种方式,第一种是通过Thread Pool, 第二种是信号隔离(Semaphore Isolation)。也就是每个请求都要先得到授权才能访问资源,详细情况请参阅这里.

舱壁隔离的实际应用方式要比Hystrix的广泛得多,它不是一种单一的技术,而是可以应用在许多不同的方向(详细情况请参阅这里) 。

新一代技术--自适应并发限制(Adaptive Concurreny Limit)

上面讲的技术都是基于静态阀值的,多数都是每秒多少请求。但是对于拥有自动伸缩(Auto-scaling)的大型分布式系统,这种方式并不适用。自动伸缩的云系统会根据服务负载来调整服务器的个数,这样服务的阀值就变成动态的,而不是静态的。Netflix的新一代技术可以建立动态阀值,它叫自适应并发限制(Adaptive Concurrency Limit)。它的原理是根据服务的延迟来计算负载,从而动态地找出服务的阀值。一旦找出动态阀值,这项技术是很容易执行的,困难的地方是如何找出阀值。这项技术可以应用在下面几个方向:

  • RPC(gRPC):既可以应用在客户端,也可以应用在服务端。可以使用拦截器(Interceptor)来实现
  • Servlet: 可以使用过滤器来实现(Filter)
  • Thread Pool:这是一种更通用的方式。它可以根据服务延迟来自动调节Thread Pool的大小,从而达到并发限制。

详细情况请参见Netflix/concurrency-limits.

Service Mesh实现:

从上面的程序实现可以看出,它们的每个功能并不复杂,而且不会对业务逻辑进行侵入。但上面只是实现了一个微服务调用的一个函数,如果你有很多微服务,每个微服务又有不少函数的话,那它的工作量还是相当大的。有没有更好的办法呢?当我接触服务韧性时,就觉得直接把功能放到程序里对业务逻辑侵入太大,就用了拦截器(Interceptor),但它不够灵活,也不方便。后来终于找到了比较灵活的修饰模式的实现方式,这个问题终于解决了。但工作量还是太大。后来看到了Service Mesh才发现问题的根源。因为服务韧性本来就不是应用程序应该解决的问题,
而是基础设施或中间件的主场。这里面涉及到的许多数据都和网络和基础设施相关,应用程序本来就不掌握这些信息,因此处理起来就束手束脚。应用程序还是应该主要关注业务逻辑,而把这些跨领域的问题交给基础设施来处理。

我们知道容器技术(Docker和Kubernetes)的出现大大简化了程序的部署,特别是对微服务而言。但一开始服务韧性这部分还是由应用程序来做,最有名的应该是“Netflix OSS”。现在我们把这部分功能抽出来, 就是Service Mesh, 比较有名的是IstioLinkerd。当然Service Mesh还有其它功能,包括网路流量控制,权限控制与授权,应用程序监测等。它是在容器的基础上,加强了对应用程序的管理并提供了更多的服务。

当用Service Mesh来实现服务韧性时,你基本不用编程,只需要写些配置文件,这样更加彻底地把它与业务逻辑分开了,也减轻了码农的负担。但它也不是没有缺点的,编写配置文件实际上是另一种变向的编程,当文件大了之后很容易出错。现在已经有了比较好的支持容器的IDE,但对Service Mesh的支持还不是太理想。另外,就是这个技术还比较新,有很多人都在测试它,但在生产环境中应用的好像不是特别多。如果想要在生产环境中使用,需要做好准备去应对各种问题。当然Service Mesh是一个不可阻挡的趋势,就像容器技术一样,也许将来它会融入到容器中,成为容器的一部分。有关生产环境中使用Service Mesh请参阅“下一代的微服务架构基础是ServiceMesh?”

Service Mesh的另一个好处是

首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇[系列] go-gin-api 路由中间件 - .. 下一篇阿里云CentOS服务器下安装Golang1..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目