近年来随着互联网的快速崛起,为更好让技术与业务更好融合,快速解决用户和业务复杂需求,软件开发模式也得到日新月异的发展,应用架构由最初传统单体应用、SOA到现在的微服务分布式架构。从搜索指数来看,微服务的热度在进入 2017 年后突然爆发,国内各大会议和论坛的相关讨论也如雨后春笋般层出不穷,对于微服务到底是银弹还是焦油弹在网上也是各抒己见,各大一线互联网公司也纷纷将这一技术引入并在实际业务中落地,而开源微服务框架越来越多,比如阿里的Dubbo、唯品会OSP、微博Motan、腾讯的Tars,spring cloud等等,而随着微服务发展,也带动了容器化技术、devops的快速发展。
新一代微服务架构确实解决了很多问题,使得部署、管理和服务功能交付变得更加简单,但对于自动化提出了更高的要求,而要真正自动化,那么微服务的基础配套建设显得非常重要,以下是基础配套体系建设的图,也可以参考https://www.jianshu.com/p/a2f86e24610b:
而今天主要从服务路由和服务容错两个方面来简单聊聊。
为了应用的稳定性,我们线上应用往往多机部署,特别大厂的核心应用,为用户的体验以及容灾方面考虑,高可用架构基本都是异地多活,即同一个应用会在全国的多个机房部署,消费者从服务列表中选择合适的服务进行调用,这就涉及到服务路由,而服务路由需要从两个方面来考虑,即负载均衡和路由规则两个方面来权衡:
负载均衡我们常见的有3 种:DNS 负载均衡、硬件负载均衡和软件负载均衡,在微服务体系下,软件负载均衡通常有随机算法、轮询算法、加权轮询算法、最少活跃连接算法、一致性 hash 算法:
1)、随机算法
随机算法就是每次请求从可用服务中随机挑一个节点来调用,比如有5个节点,会生成1-5的随机数,采用Random方式随机挑取一个节点,在对等集群组网中,采用随机算法进行负债均衡,流量较高的情况下,随机路由算法消息分发还是比较均匀的。
2)、轮询/加权轮询算法
按照公约后的权重设置轮询比率,到达边界之后,继续绕接,顾名思义就是按照固定的顺序,把可用的服务节点,挨个访问一次。它的缺点:存在慢的提供者累积请求问题。如果集群中有一台机器很慢,但没挂,请就会到此机器时就卡住,久而久之,所有请求都卡在该机器上。而加权轮询算法是在此基础上,给每个节点赋予一个权重,从而使每个节点被访问到的概率不同,权重大的节点被访问的概率就高,权重小的节点被访问的概率就小,从一定程度上解决卡住的问题。
3)、最少活跃连接数
最少活跃连接算法,顾名思义就是每一次访问都选择连接数最少的节点。因为不同节点处理请求的速度不同,使得同一个服务消费者同每一个节点的连接数都不相同。连接数大的节点,可以认为是处理请求慢,而连接数小的节点,可以认为是处理请求快。所以在挑选节点时,可以以连接数为依据,选择连接数最少的节点访问。
4)、一致性 hash
一致性 hash 算法,是通过某个 hash 函数,把同一个来源的请求都映射到同一个节点上。一致性 hash 算法最大的特点就是同一个来源的请求,只会映射到同一个节点上,可以说是具有记忆功能。只有当这个节点不可用时,请求才会被分配到相邻的可用节点上。
负载均衡算法解决了服务消费者如何从众多可用的服务节点中选取一个最合适的节点发起调用,但是在我们实际线上运维过程中,往往需要灰度发布、切流、多机房本地路由优先策略、读写分离等场景,那么需要按某些特定的规则来进行路由,一般通常是按条件路由和脚本路由两种。
条件路由是指按照按照条件表达式进行路由,比如:排除法、黑白名单、读写分离等,而脚本路由则是利用脚本语言,比如groovy,ruby等脚语言编写进行路由。
在实际生产中,路由规则获取的方式主要是本地化配置和配置中心管理并配置下发,本地化配置就是路由规则存储在服务消费者本地上,当服务消费者发起调用时,从本地固定位置读取路由规则,然后按照路由规则选取一个服务节点发起。而配置中心管理,无论是服务提供者还是消费者,本地配置的路由策略统一注册到注册中心,进行集中化配置管理。
本地化配置管理适合规模较小的集群,随着业务规模的扩大、服务节点增多,尤其是涉及多数据中心部署的情况,则需要通过配置中心进行管理和下发。
在集群中难免会出现服务超时,单机节点负载高,服务异常等,又或在异地多活跨机房部署中机房故障、网络故障等问题,或在双十一期间保证核心应用不受影响,需要对非核心服务进行降级,确保购物主流程不受影响等,那么就需要对服务进行降或容错,从而降低影响范围并及时止损。
从宏观层面来看,出现故障主要有三种故障:集群故障、单 IDC 故障、单机故障;而从微观层面来看,主要是接口级别故障,在实际业务运行过程中,接口级别故障发生的概率较高。
宏观层面,应付集群故障的思路,主要有两种:降级、限流。
降级就是通过停止系统中的某些功能,来保证系统整体的可用性,降级可以说是一种被动防御的措施,它的核心思想就是丢车保帅,优先保证核心业务。降级一般在大促期间用得非常多,比如日志采集频率、商品评价等功能降级。在实际业务应用的时候,降级要按照对业务的影响程度进行分级,一般分为三级:一级降级是对业务影响最小的降级,在故障的情况下,首先执行一级降级,所以一级降级也可以设置成自动降级,不需要人为干预;二级降级是对业务有一定影响的降级,在故障的情况下,如果一级降级起不到多大作用的时候,可以人为采取措施,执行二级降级;三级降级是对业务有较大影响的降级,这种降级要么是对商业收入有重大影响,要么是对用户体验有重大影响,所以操作起来要非常谨慎,不在最后时刻一般不予采用。
降级针对功能层面,而限流主要从用户流量的角度考虑如何对故障,通常情况下,系统能够承载的流量根据集群规模的大小是固定的,可以称之为系统的最大容量。当真实流量超过了系统的最大容量后,就会导致系统响应变慢,服务调用出现大量超时,反映给用户的感觉就是卡顿、无响应。所以,应该根据系统的最大容量,给系统设置一个阈值,超过这个阈值的请求会被自动抛弃,这样的话可以最大限度地保证系统提供的服务正常。
接口级故障的典型表现就是系统并没有宕机,网络也没有中断,但业务却出现问题了。例如,业务响应缓慢、大量访问超时、大量访问出现异常,比如mysql连接数耗尽,程序BUG引起报错、单节点负载高导致响应慢等,而面对接口级故障,通常的应对措施有降级、限流、熔断、排队、重试等。
与宏观层面相比,限流降级思想是一致的,但维度更细一些,限流一般基于用户请求限流和基于资源限流。
基于请求:一般限制总量、限制时间量,比如API每秒最多允许调用量1W;抢购活动商品数量只有 100 个,限制参与抢购的用户上限为 1 万个,1 万以后的用户直接拒绝;无论是限制总量还是限制时间量,共同的特点都是实现简单,但缺点比较难以找到对应的阀值,一般要线上压测。
基于资源:基于请求限流是从系统外部考虑的,而基于资源限流是从系统内部考虑的,即:找到系统内部影响性能的关键资源,对其使用上限进行限制。常见的内部资源有:连接数、文件句柄、线程数、请求队列等。
电路熔断是对于后端服务的保护,当错误、超时、负载上升到一定的高度,那么负载再高下去,对后端来说肯定是无法承受,好像和电路熔断一样,这三个因素超过了阈值,客户端就可以停止对后端服务的调用,这个保护的措施,帮助了运维人员能迅速通过增加机器和优化系统,帮助系统度过难关。
排队理解非常简单,国内大家所熟知12306购票网站,购票是基于排队来实现的。排队需要临时缓存大量的业务请求,单个系统内部无法缓存这么多数据,一般情况下,排队需要用独立的系统去实现,例如使用 Kafka、redis来缓存用户请求。
本文从服务路由和服务降级容错两个方面简单进了小结,一般大厂的微服架构也都是基于这些思想来实现。微服务崛起,生态圈也得到了快速成长,那它到底是银弹还是焦油弹,大家欢迎大家发表想法。