一文搞懂微服务架构演进( 五 )


 
使用网关有一个问题就是要决定在多大粒度上使用:最粗粒度的方案是整个微服务一个网关,微服务外部通过网关访问微服务,微服务内部则直接调用;最细粒度则是所有调用,不管是微服务内部调用或者来自外部的调用,都必须通过网关 。折中的方案是按照业务领域将微服务分成几个区 , 区内直接调用,区间通过网关调用 。
 
由于整个网上超市的服务数量还不算特别多,小明采用的最粗粒度的方案:
 

一文搞懂微服务架构演进

文章插图
图片
 
09 服务注册与发现 - 动态扩容前面的组件,都是旨在降低故障发生的可能性 。然而故障总是会发生的 , 所以另一个需要研究的是如何降低故障产生的影响 。
 
最粗暴的(也是最常用的)故障处理策略就是冗余 。一般来说,一个服务都会部署多个实例,这样一来能够分担压力提高性能 , 二来即使一个实例挂了其他实例还能响应 。
 
冗余的一个问题是使用几个冗余?这个问题在时间轴上并没有一个切确的答案 。根据服务功能、时间段的不同,需要不同数量的实例 。比如在平日里,可能4个实例已经够用;而在促销活动时 , 流量大增,可能需要40个实例 。因此冗余数量并不是一个固定的值,而是根据需要实时调整的 。
 
一般来说新增实例的操作为:
  • 部署新实例
  • 将新实例注册到负载均衡或DNS上
 
操作只有两步 , 但如果注册到负载均衡或DNS的操作为人工操作的话,那事情就不简单了 。想想新增40个实例后,要手工输入40个IP的感觉……
 
解决这个问题的方案是服务自动注册与发现 。首先,需要部署一个服务发现服务,它提供所有已注册服务的地址信息的服务 。DNS也算是一种服务发现服务 。然后各个应用服务在启动时自动将自己注册到服务发现服务上 。并且应用服务启动后会实时(定期)从服务发现服务同步各个应用服务的地址列表到本地 。服务发现服务也会定期检查应用服务的健康状态,去掉不健康的实例地址 。这样新增实例时只需要部署新实例,实例下线时直接关停服务即可 , 服务发现会自动检查服务实例的增减 。
 
一文搞懂微服务架构演进

文章插图
图片
 
服务发现还会跟客户端负载均衡配合使用 。由于应用服务已经同步服务地址列表在本地了,所以访问微服务时,可以自己决定负载策略 。甚至可以在服务注册时加入一些元数据(服务版本等信息),客户端负载则根据这些元数据进行流量控制,实现A/B测试、蓝绿发布等功能 。
 
服务发现有很多组件可以选择,比如说Zookeeper 、Eureka、Consul、Etcd等 。不过小明觉得自己水平不错,想炫技,于是基于Redis自己写了一个……
10 熔断、服务降级、限流 
熔断当一个服务因为各种原因停止响应时 , 调用方通常会等待一段时间,然后超时或者收到错误返回 。如果调用链路比较长,可能会导致请求堆积 , 整条链路占用大量资源一直在等待下游响应 。所以当多次访问一个服务失败时,应熔断,标记该服务已停止工作,直接返回错误 。直至该服务恢复正常后再重新建立连接 。
 
一文搞懂微服务架构演进

文章插图
图片
图片来自《微服务设计》
 
服务降级当下游服务停止工作后,如果该服务并非核心业务,则上游服务应该降级,以保证核心业务不中断 。比如网上超市下单界面有一个推荐商品凑单的功能,当推荐模块挂了后,下单功能不能一起挂掉,只需要暂时关闭推荐功能即可 。
 
限流一个服务挂掉后,上游服务或者用户一般会习惯性地重试访问 。这导致一旦服务恢复正常 , 很可能因为瞬间网络流量过大又立刻挂掉,在棺材里重复着仰卧起坐 。因此服务需要能够自我保护——限流 。限流策略有很多,最简单的比如当单位时间内请求数过多时,丢弃多余的请求 。另外,也可以考虑分区限流 。仅拒绝来自产生大量请求的服务的请求 。例如商品服务和订单服务都需要访问促销服务,商品服务由于代码问题发起了大量请求,促销服务则只限制来自商品服务的请求,来自订单服务的请求则正常响应 。


推荐阅读