今天我的主题是在微服务场景下的一个性能问题的定位优化,那么今天会讲一个我们其实出现的一个真实的一个场景,然后其实还是花了蛮长时间,然后把这个东西才定位到一个具体的问题。
现在云原生微服务架构特别的火,有非常多的优势,比如说这里面写的快速迭代,高并发,可维护,可扩展,灰度发布,高可用,这些词大家都耳熟能详,这些就不用细说了。
但是微服务不是没有成本的,如果说单体应用的复杂度大概是10的话,上了微服务可能是变成100,可能是十倍的复杂度提高,需要投入大量的人去做这个事儿,并且需要一定的支撑系统和工具链,才能将这些复杂性降下来。
我这边总结了一下微服务实施之后,会大概率出现以下的痛点:
第一:服务依赖的管理,就是一个服务到底调用了哪些,被哪些服务调用,如果依赖管理比较混乱,就会比较痛苦,比如说你要发布一个应用,你可能不知道这个应用被谁依赖了,有没有有一个特别关键的应用在依赖于我这个应用,会不会我升级了以后是否会引发关键业务的不稳定,是应该白天发布,还是凌晨发布,这个时候我们就特别需要希望有一个系统能够看到任何一个服务都被哪些服务依赖以及依赖于哪些服务。
第二:调用统计问题,对于调用记录有一个统计和告警,例如有没有接口突然调用失败率增高,有没有接口突然时延增长,都应该及早发现,而不能因为因为一次发布引入一个bug,导致时延变长但无人知晓,等到流量一来,直接就可能压挂了。再就是有没有接口再也没有人调用,这个接口是否可以下线,这在接口升级的时候,常常采取先添加接口,再下线接口的方式,就需要这样一个统计系统。
第三:接口规范问题,各个团队开发出来的各个服务的接口是否符合统一的接口规范,有没有统一的地方去看接暴露出来的接口?如果说有的接口不遵守规范,那么是不是时候会在同一个地方能看到,然后去尽早的去发现这个问题。
第四:安全管理,很多企业往往通过白名单通过配置中心配到各个服务里面去的,比如说支付这个服务不是所有服务都能调用的,只有部分服务可以调用他。这些配置原来都是散落在这个服务里面去的,各自为站,有可能一不小心就配置错了或者漏了,应该能访问的访问不了,不该访问的能够访问了,但是没有人察觉。
第五,熔断限流降级这些服务治理能力。虽然有很多开源组件可以做这些事情,但是需要写大量重复代码去做,同样是散落在各个地方。
第六,接口测试问题,我们如何保证在不断的拆合的过程中不会引入新的bug,这其实是比较头疼的一个事情,所以需要一个比较大的一个测试集合,就需要一个测试平台来保证。
第七,灰度发布问题,很多公司做灰度发布,都是通过代码里面写if-else做的,当什么条件满足的时候,走这个逻辑,当时什么条件满足的时候,走那个逻辑,这个时候也是相对比较痛苦的。
第八,压力测试问题,这一般是实施微服务的后期,当需要面对大规模流量的时候,才会引入进来的。一开始线上大促的时候,基本处于这种一脸蒙,靠运气的这种状态,心里压根都没有谱,必须要通过压力测试去做这个事儿。
第九,调用链分析问题,一旦出现慢的时候,相对比较难以发现慢的点,尤其是当服务数目多,调用链长了之后。
第十,测试环境治理。服务数目增多了,大家都用了容器,带来的好处就是部署的特别方便,一个编排就能启动一套系统,但是同时也带来一个痛苦,其实我们从云的时候就有这个痛苦,一旦放给大家的权限让大家可以随时部署,对于资源的使用就控制不住了,大家谁都想启动一个新的环境,自己的测试环境和别人都不在一块。如果说只有几个容器,那么每次都重新部署一个新环境,这没有问题,但是如果服务特别多的时候,例如一百个容器的时候,这时候全量部署比较困难。
为了解决这些问题,需要配备比较复杂的工具集合:容器平台负责声明式部署,持续集成和测试平台负责灰度发布和测试集合的维护,API网关负责入口流量的接入,微服务框架负责微服务之间的相互调用,管理和治理,分布式事物负责拆分后的事务问题,APM性能管理负责调用链分析。我们后面也能看到这些组件在定位问题的过程中都起到了什么样子的作用。
微服务的拆分过程并不是一蹴而就的,我们发现很多公司开始计划实施微服务的时候,往往第一个问题是微服务应该怎么拆,应该拆分到什么粒度?觉得是这是一个最重要的一个维度。后来我们发现其实并不是这个样子的,微服务的拆分只是其中很小的一个方面,需要匹配一套工具链,并且经历十二个过程,逐步完成。
接下来,我们来解析一个微服务场景下的问题定位过程。
为什么说在微服务场景下,我们发现定位问题的时候,我们会觉得它特别的复杂,往往需要架构师牵头协调各个部门一块去定位某个问题。
首先第一个复杂度就是,从顶层到底层这个层次是实在是太多了,我们能想到就是任何一个比如说我们发现请求比较慢的时候,我们首先会想到整个层次中,最上面的应用是不是出了问题,应用这一层本身就比较复杂,图中密密麻麻的线是double服务之间的一个调用关系,它的复杂度已经十分高了,然后中间一旦出现了慢请求,这时候其实很难定位到一哪一个点,哪个服务集群调用哪个服务集群。
应用层下面这就是容器层,容器下面其实是openstack的云网络,我们采取方式是kubernetes和openstack整合的一个方式。因为kubernetes对于容器的发布,运行十分友好,对于网络尤其是多机房高可用横向扩展的vpc网络特性相对弱一些,那么正好云在VPC这方面相对比较强一些,例如浮动ip,跨机房高科用,专线等。容器层或者云网络层,都可能产生问题,例如容器网卡吞吐量受限制,或者ovs吞吐量受限制,都会造成性能问题。
再往下是物理网络,因为牵扯到多机房了,那么可能每机房还有多高可用区,那么整个机房的拓扑结构逻辑也会很复杂。
这时候就可以想象说当一个服务的多个副本构成一个业务集群,当从一个业务集群到另一个业务集群时延高,每个层次都其实都有可能出现问题是吧?然后我们就需要层层的去排查这个事情。
另外一个相对比较头疼的一个事,是从前端到后端的层级也是挺多的。问题往往是在压测的时候会发现,那么压测的时候,我们是在我们公有云上去压真实线上的业务,所以其实是走公有网络的。
我们可以想象,从前端到后端经历的环节实在是太多了,你比较难判断他会慢在什么地方。
一开始进入机房,有边界路由器,然后有核心汇聚交换机,这是网管部去管的。
接下来就进入了虚拟的云网络网关,由云计算部门维护。
云网关进来后,会有一层负载均衡,这个负载均衡是可以适配多机房的负载均衡,可以做跨机房浮动IP漂移。
再往里面会有个API网关作为接入层,然后再往后就是服务层了。服务层分业务服务层和基础服务层,之间会引入注册发现机制,比如说用dubbo管理他们之间的相互调用。由于微服务规模比较大,就像刚才图中展示的一样,复杂度很高。
业务之间调用完毕之后,最终肯定是要落库的,这时候会涉及到缓存的访问,缓存后面是数据库。
有时候还涉及到调用云外的系统,因而需要经过云网络的网关。比如说外部的一些支付系统,安全检测系统,包括大数据等,都是云外的。这两次网关其实是不一样的,前面网关是DNAT网关,后面网关是SNAT网关。
可以想象一旦出现性能问题的时候,经过这么多环节就比较头疼,经常会困惑,这个问题到底出现在哪个环节呢?
一般来说,性能问题往往通过线上性能压测发现的。一般大促之前,提前一段时间,就要开始进行压测。压的时候就会涉及到从前往后,从底到上的所有的系统和部门,都要派代表去参加,哪一块出现了问题,哪一个环节出现了性能瓶颈,哪一块就要改。
线上压力测试需要有一个性能测试的平台,做多种形式的压力测试。例如容量测试,通过梯度的加压,看到什么时候实在不行。摸高测试,测试在最大的限度之上还能承受多大的量,有一定的余量会保险一些,心里相对比较有底。再就是稳定性测试,测试峰值的稳定性,看这个峰值能够撑一分钟,两分钟还是30分钟。还有秒杀场景测试,限流降级演练测试等。
有的时候压测会遇到让人崩溃的事情,例如可能前几天压测的时候,看着吞吐量在向好的方向发展,突然有一天一下子就降下来了,这个时候离大促时间越来越近,心态就会比较崩溃,需要大家一块去看到底是什么问题。
对于测试环境的管理,也是非常关键的。线上压测的时候,为了让数据和正式的线上数据实现隔离,常用的方法是对于消息队列,缓存,数据库,都是使用影子的方式。就需要流量染色的技术,带一个tag进去,说明这个请求是测试数据,还是真实数据。
流量染色的功能,除了压测里面使用,还可以用于测试环境治理。在大规模微服务场景下,不可能每个部门部署一套完整的环境,因为耗费的资源量实在是太大了。
这时候就需要合理规划测试环境。测试环境是应该和持续集成的流程紧密协作的。我们使用分支开发的方式,每个功能的开发在分支上,上线的时候,合并到主分支上来,主分支对应线上环境。
对于测试环境的规划,也是采取类似的思路。我们会有一个基准测试环境,对应master分支,里面部署全量的应用。每一个分支,比如说你修改了五个工程,测试的时候,不需要部署全量的应用,只需要把这五个工程去创建一个Delta测试环境就可以了。当客户端进行测试的时候,带上一个此分支的tag,从API网关开始,微服务框架嵌入的jar会将这个tag一直带下去。当这五个服务之内相互调用的时候,微服务框架就会选择这五个服务的实例进行调研,如果需要调用五个服务之外的其他服务的时候,微服务框架会到master环境里面,选择服务实例进行调用。
有了流量染色的环境治理机制,测试环境数量就会小小特别多。
压测中一旦出现了慢请求,最先从哪里能够看出来呢。在微服务的接入层有API网关这一层。
在API网关中,有一个调用统计功能,可以筛选出慢请求。在压测的时候,每一轮都有一个压测结果,是一个列表,按请求时间排序。通过这个列表,可以筛选出这次压测的时候,最慢的接口都是哪一些,异常的接口(也即请求响应速度有巨大反差)有哪一些? 我们就可以针对于这些接口进行特别的排查。
在服务治理平台中,服务之间的调用也有一个统计,也有个一个排名,同样会列出来哪几个应用需要改进,重构,或者排查。
和压测匹配的一个重要的系统是全方位的性能监控。一般的监控平台仅仅会监控第一项就是服务器的性能,比如cpu,内存,网卡等。其实监控应该是全方位的,对于应用方面的,应该监控堆的内存,GC,线程,cpu利用率等。业务指标应该监控下单数,支付数,购物车请求数等。调用链路监控应该包括RT值,TPS等。应用组件应该监控连接的状态,消息积压,zookeeper节点数等。还应该有一些异常监控,例如异常的流量,Exception,报警等。这些都是在统一的一个平台上去做的。
定位问题需要深度的依赖于这个平台,一般先在这个平台上去找到大概的问题点,如果不能找到真正原因的话,就可以更加细粒度的登到机器上去看。没有统一的监控,会在大规模微服务场景下,有了问题,比较茫然,根本找不到地方。
有了统一监控平台,我们也在压测的时候,在API网关上发现了慢请求,接下来就需要逐层定位了。
高延迟的现象往往会先出现在应用层,外部的现象就是外部请求的时延高,如果顺着调用链定位下来,往往会反应到内部从一个服务集群到另一个服务集群的RT值高或者出现大量超时异常。
我们可以查看对于服务之间相互调用的监控,dubbo和hystrix我们做了集成,右下角的这个图是熔断的基本原理。通过监控我们可以发现有熔断的情况出现,我们就知道这可能是出现了问题。
一旦出现了问题,架构部一步一步来分析分析这个问题。我们的排查思路是从上层往下层,从业务层往底层层层排查,因为如果让物理网络或者虚拟网络直接去定位,会比较迷茫。
第一步:先应用层初步定位。
第一件事情,查看近期是否有新变更的发布上线,例如昨天压测的时候还正常,这次突然出现超时,很可能是新的发布导致的。
我们可以重点查看新发布的应用,甚至可以进行代码review。
如果新发布的应用没有发现问题,就需要在整条调用链路上定位问题了。这个时候应用性能监控APM可以帮上忙,他可以在整条调用链路里面,找到最耗时的那个环节,这是我们应该重点定位问题的地方。
在最耗时的环节上,如果我们发现TPS比较低,相应时间长,但是CPU使用率不高。
我们可以通过服务治理平台,查看这两个服务之间的调用是否开启了足够的线程池,当前的线程池是否已经被打满,如果满了,是否可以提高线程池的数目,这和集群所在的虚拟机和物理的核的分配是有关系的,因为核数用完了,再多的线程徒增调度成本,没有收益。如果可以提供线程池的数目,可以通过配置中心进行统一的配置这个服务集群的线程池数目。
很多情况下,性能的降低的原因在于数据库层,这一点在APM上也能看的出来。这个时候应该DBA去查看问题,可以梳理一下SQL语句,通过慢SQL分析,查看慢的原因,例如索引问题,死锁问题等,这些都可以通过Mysql或者DDB的日志找出来。
另外一个经常出现的问题就是TPS比较低,但是CPU使用率非常高。
这时候我们需要通过监控系统查看线程的状态,查看耗时的方法列表以及调用栈,看哪个线程耗时比较多,是否因为业务逻辑的Bug,是否在一直计算某个东西,或者出现业务层的死锁。
另外就是看是否频繁的FullGC,一旦出现了FullGC就会出现一段时间的业务逻辑暂停,在大促本来压力就很大,而且峰值时间比较短的情况下,一个线程hang就会使得调用链拥塞一片。我们要通过监控系统查看堆的信息,找出耗内存的对象,看是否内存一直往上涨,是否存在泄露。
另外对于缓存的一个监控,应用层的缓存设计一般有多层。我们本来一开始设计的是有三层缓存的,localcache有两层,然后分布式缓存有一层,localcache分主动刷新和被动刷新的两层,被动刷新就是遇到请求了再刷新,主动刷新是主动的去过一段时间去刷新。后来我们又把三层缓存简化成了两层缓存,被动刷新的localcache的命中率已经足够用了,再多一层反而会降低性能。
我们之所以监控缓存,是因为我们发现有时候应用层Key使用的不对,或者是配置的不对,会导致缓存层失效,请求就会直接打到数据库,导致请求相对比较的慢。对于缓存的监控,我们在缓存的客户端的jar,会和配置中心进行联动,监控某些我们认为相对比较重要的Key,当Key出现缓存丢失,写频繁,或者写丢失等类似这种事件的时候,它就会上报到我们的监控系统。这个时候,就会发现导致缓存失效的程序Bug。
第二步:如果应用层没有问题,则检查异常流量
如果我们确认应用层的确没有问题,就需要开始往下层去进行怀疑了。
首先第一个比较怀疑的是,是不是出现异常流量。比如说有外部的异常流量,可以问问网管和安全部是否网站被DDoS,另外入口网关是否接受到了大量的请求,例如有临时的促销或者临时的事件。
如果入口没有问题,则查看集群的监控,要对照着两个指标看,一个是服务端的请求数目的一个统计,看是否有集中的热点,比如说一个集群有多个副本,其中某个副本收到的请求量特别的大,而其他副本收到的请求数相对比较低。二是在云主机里面的网卡虚拟网卡,也是能够看到相应的监控的,看是不是网络流量就集中到某几台机器上。这两个会有一个对照,如果两方面指标能够对照起来,集中热点比较容易定位,也即请求数上去了,同时云网络的网卡流量也上去了。当出现了热点的时候,就需要通过服务治理中心或者配置中心,将请求打散。
如果两个指标不匹配,这时候就有问题了,也即服务端请求的数目其实并没有多,但是云网络发现出现了问题,这时候就就可能是底层基础设施的问题,我们这个例子遇到这个点就是相对比较诡异,还需要接着解析。
如果不是异常流量,就需要从前往后再来梳理这个事,比如说从核心交换机到VPC的网关区域这些是不是出了问题,这时候就要联系机房的网管部门去定位,然后是VPC网关到云的负载均衡,然后是从负载均衡到应用的API网关,然后API网关到应用的Controller层,然后是中间的dubbo调用,然后缓存层到数据库层。接下来要按照整个链路依次的去排查。
这里画了一张经比较经典的机房的一个图,其实任何一家公司机房的样子要比这个图可能要复杂得多,这就需要架构部门对机房的架构相对比较清楚。
我们会分机房,分可用区,还会分二级可用区,他们会走不同的汇聚交换机,在监控系统里面也要标明某个服务器群和另一个服务集群之间的访问到底是哪个物理机集群到哪个物理机集群之间的访问,这时候你心里可能需要清楚他们是否在同一个机架里面,或者是在同一个二级可用区,还是在同一个一级可用区,是跨机房了,或者是甚至到异地了,这时候你要心里有个数,因为他们之间的时延都是不一样的。
第二个就是对于VPC网络的架构是要比较清楚。我们是通过OpenStack Neutron的vxlan去做的这个事情的。横向流量是通过vxlan,基于OVS去做的,纵向的流量,出去的时候会有网关,我们不是用的物理网关,而是虚拟网关,在数据中心里面有一个虚拟网关的网关层,网关层有的时候是挂在汇聚交换机下面的,有的机房要求吞吐量比较大,网关层可以直接挂在核心交换机下面的,很可能网络瓶颈点会发生在这些网关上。
我们的VPC网络是有一定的改进的,因为我们要求一个VPC里面能承载的虚拟机的数量要比开源的OpenStack要多得多,能达到5万台的规模。
限制网络规模的一是广播问题,我们可以事先把整个网络拓扑下发到一个拓扑库上去,每一个节点上的Agent会订阅拓扑库的更新事件,从而更新本地的OVS策略。每个Agent都会看到整个网络的拓朴结构,则ARP的时候,本地就可以拦截来进行返回。
另外的一个改进就是虚拟网络,默认的虚拟网关只能做主备,横向扩展能力没有这么好,不能够承载大的并发量,这其实需要有一排的虚拟网关,全部挂载到汇聚或者核心交换机上。接下来的问题是纵向的流量怎么从这一排网关里面去选择,这里使用了一致性哈希的算法。
另外一个改进是Kubernetes和OpenStack的整合。左面的图是Kubernetes默认创建一个Pod的流程,好在他是基于事件驱动的,使得我们可以定制化右面创建Pod的流程。我们开发了自己的Resource Controller和Scheduler,当发现容器的节点资源不足的情况下,Controller会调用IaaS的接口创建云主机,然后加到Kubernetes集群中。这种模式下,在应用层的角度,只能看到容器,不需要运维Kubernetes,更加看不到OpenStack的接口,使得应用层有统一的编排接口。
另外对于容器和Kubernetes层的整合,网络层也需要整合,容器也要融合VPC网络,只有适配了VPC网络才能实现多机房的高可用性,跨机房的负载均衡,以及跨机房的浮动IP漂移和切换。比如说一个机房挂了,则挂在A机房的浮动IP地址要能够切换到B机房的网关上去,并且和核心交换机有联动,路由也要切过来,这些都只有VPC可以完成这个事情。
我们开发了一个自己的CNI插件去做这个事儿,容器没有使用单独容器网络方案,而是直接用了OVS的网络,也即OpenStack的OVS是可以直接看到容器的IP地址的,容器的IP地址和虚拟机的IP是平的,是属于同一个局域网。
这样其实还有另外一个好处,因为我们还有很多没有容器化的应用,如果部分做容器化,是可以和容器内的应用无缝的打通的。一些有状态的PaaS也是部署在虚拟机里面的,也可以实现在同一个局域网内的互通。
另外一个针对Kubernetes的优化就是规模问题,这是其中的一个优化的例子。
重启一个API server的时候会导致一些问题,尤其是在Pod的数目比较多的时候。通过监控图可以看出,中间会重启时间长达七分钟,中间出现429错误并且飙升,是因为流控。为什么会流控?因为它重启的时候,会有大量的请求重新再连上来,由于并发太多,有可能就把它再冲挂了。
那么应该如何改进呢?方法一就是多级流控,根据UserAgent, Resource, Verb几个参数,对于不同的客户端有不同的流控策略,比如说对于master上面的进程优先级相对高一点,对于kubelet结点上的进程,优先级就低一些。不会所有的kubelet节点一股脑上来进行注册。方法二是拥塞控制,Retry-After参数也即多长时间再重试一次,这个原来是一个默认值,我们改成一个动态的值,根据系统的繁忙程度是调节到1秒到8秒之间,这样不同的客户端重试的时间会错开。使得把整个恢复时间大幅度的降低了,这是我们做的规模化的一个例子。
来我们从接入层开始分析。我们在VPC的网关以及负载均衡层也是做了优化的。
优化之一是基于BGP ECMP的等价路由,也即对于LB集群和前面的核心交换机之间配置等价路由,实现横向扩展和负载均衡。如果使用LVS做这个网关的话,可以使用DPDK技术,也即基于DPVS的负载均衡。
电商请求基本上都是基于HTTPS协议的,SSL往往通过offload的方式通过硬件进行加速,在压力大的时候,优化明显。
对于负载均衡这一层的问题,出问题的点往往在于虚拟网关和负载均衡,
我们要中断看这一层的网卡是不是丢包?
另外就是DPDK的PMD进程的CPU占用率是不是特别的高,因为默认的网卡接收网络包是通过中断通知内核来接收包,但是DPDK进行了优化,它通过主动polling的模式减少中断,它在用户态有一个PMD的进程,他会不断地从网卡里面去polling,会占用大量CPU,如果他CPU特别高就说明接收包的速度本身就很高了。这时候要注意观察流量是否存在热点,流量是否突破单台瓶颈。
对于虚拟网关来说,使用的是一致性哈希的方法选择网关,随着持续的浮动IP的创建和删除,分布不一定均衡,就算IP分布均衡,这些IP对应的业务其实没有那么均衡,例如多个热点请求落到一个网关上。
如果说是存在热点,导致DPDK的PMD进程CPU过高,可以采取的策略是高流量的IP地址可以进一步地打散,重新调整哈希的策略。
另外会观察是否超过单台的瓶颈,我们会测试每台机器的IO的瓶颈,也会进行QoS限流,如果是超过了瓶颈,说明这一台的确压力过大,如果其他的节点网络IO还相对比较低,基本上也是热点问题。如果都高,则说明需要扩容。
另外网关要注意区分走哪种类型的网关,通过测试,我们发现DNAT网关由于是一对一的,性能比较好。SNAT因为共享IP并且要计算哈希,还要做conntrack,吞吐量不太容易优化上去。对于功能需求走SNAT网关比较方便的应用,可以建议用户换成DNAT网关,从而获得高的吞吐量。
如果入口流量没有问题,这时候就要看服务器之间的调用是不是有问题,因为我们的容器是套在虚拟机里面的,主要起编排和发布的作用,这时候就要看云主机的steal是不是特别的高,对于物理机上的cpu,多个虚拟机会竞争排队,排队不上就会等待,等待的时候就会出现CPU steal,网络吞吐量肯定就上不去,甚至会出现丢包,因为收到的网络包来不及处理。
另外就是磁盘的IO utils是不是有异常,虽然容器中多部署无状态服务,对于磁盘的依赖相对比较小,日志也是以异步的方式输出的,但是有时候也会存在IO hang的情况,这时候就要看IO的监控。
如果需要细致的分析,可以登录到宿主机上看KVM,分析KVM的性能的瓶颈在哪里,在虚拟机里面进行网络监控之外,在宿主机上可以对虚拟网络的组件进行分析,测试两个集群之间的虚拟网络组件是否出现了问题。
分析完了网络和存储虚拟化,接下来看计算虚拟化KVM。可以做一些定制化的一些调优,比如说CPU的BIOS中会把C-States和P-States这种节能开关关掉,如果是核心业务集群,不需要打开这些开关。
另外就是物理CPU和vCPU的绑定,这是为了解决steal的问题,如果把核绑到虚拟机上,基本上就不会存在VCPU在物理CPU之间切换的问题,就会把steal解决掉。
另外是NUMA亲和性的问题,在同一个NUMA Node中,CPU对内存的访问就会快一些,跨NUMA node就会慢一点,分配的时候尽量是同样CPU和同样的内存放在同一个NUMA 节点。
虚拟机网卡可以通过SR-IOV的方式来进行优化,云主机绑定核也是在grub中设定好,一台机器上面能起多少个服务,给宿主肌留多少核,给监控留多少核,然后最后给应用留多少核,都是提前规划好的,例如前几个核是给宿主机自己去用的,然后几个核是留给DPDK去用的,最后几个核是留给监控去用的,中间的核留给服务。
对于限流策略问题,基础设施层和业务层有一个配合的问题,底层的网络有QoS流控的,业务其实也有限流的,这两个值要匹配起来,否则会有问题,比如当业务层把线程数调大的时候,结果基础设施层被限流了会出现丢包。
当我们从监控里面看到有丢包事件的时候,业务层会怀疑是虚拟网络的问题,丢包了以后TCP会重传,重传会使得响应比较的慢。
在没有定位到到底哪个环节丢包的时候,可以先做如下的处理。为了使TCP的丢包重传和流控策略不要用默认的策略,也即一旦出现了丢包,内核协议栈就认为网络拥塞,从而减少拥塞窗口,这时候本来就想加速重传,结果窗口下来了,想加速也加不上去。如果切换成BBR,出现丢包的时候就会好很多。
但是我们还是就要看底层的网络,网络包会丢在什么地方。
最后实在没有办法,就需要登到物理机上去debug整条链路。从集群A到集群B中间抽出两台机器进行测试。测试完毕后通过tcpdump进行分析虚拟机和虚拟机之间的情况,通过ovs-tcpdump可以分析两台物理机上的OVS之间的情况,物理机之间的链路可需要进行分析。如果包的数目比较多,需要写程序比对时间戳序列号。
抓包分析之后,我们发现两个集群之间并不是任何两两个服务之间都会丢包,而是发生在两个集群全部在某一批汇聚交换机出现。
原来从来没有怀疑过是物理硬件的问题,物理交换机的监控网粒度往往不会非常细,而且有堆叠这种高可用策略,在监控调整到分钟级别的时候,看流量根本就没有达到上限。
但是一旦我们发现出问题的两个集群总是要过某一台汇聚交换机的时候,我们就建议网管,你能不能把那台的监控配置到秒级,然后就发现问题了,就是分钟级监控没有超过它的瓶颈,但是秒级的监控就会发现它瞬时流量是超过了瓶颈的,因而会丢包。原因是部署缓存集群的时候,实例过于集中了,两个集群之间的交互基本上都集中在同一个汇聚交换机上,导致出了问题,接下来的方式就是把网络优化型的实例打散了以后,只要流量瞬时不会超过物理交换机峰值,基本上就没有问题了。
这个例子最后有一点狗血,其实这是一个分析思路,从应用层逐渐的去分析,架构部就要协调各个层次,最后才把这个事情定位到好,我今天的分享就到这里。