1。背景一、场景某天,某项目服务突然出现异常。我们定位的原因是突然有大量的流量进来,那么我们先采取被动的临时措施来处理当前的故障,然后通过Nginx的限流功能快速止损,防止二次故障.但是Nginx的限流功能比较粗糙,所以我们有一个更好的长效措施,就是项目接入限流功能,实现维度精细化限流,变被动解决为主动防御和主动治理.对于这个项目来说,做到这一步应该是一个不错的结果。那么我们可以更进一步吗?更进一步,我们需要考虑三个方面:经验积累、通用性和综合效益。经验积累:有哪些经验总结可以给其他项目借鉴?通用性:其他项目是否可以直接使用该解决方案?整体收益:团队的整体成本能否降低?2.问题与目标我们的程序设计需要考虑不同项目之间的差异,因为我们需要接入的项目可能有十几个甚至更多,不同项目之间的差异会体现在开发语言、请求类型、生命周期、部署环境、链接节点等。其中,我们需要特别关注生命周期。有的项目可能是十几年前就存在的老项目,有的可能是近两三年比较新的项目,有的可能是未来要成立的全新项目,我们的解决方案需要能够同时匹配这三个项目,需要抹平其他几个差异。此外,我们的解决方案还有一些其他的预期目标,比如低成本、高效率、高质量,以及专业性、稳定性、可扩展性和高性能。成本低:接入所需的成本相对较低,包括开发、运维、硬件等成本。效率高:可快速完成接入和落地工作。高品质:提供整套限流解决方案,接入方在使用前后都能得到有效的技术支持和指导。其他的点比较容易理解,那么摆在我们面前的问题就是项目很多,差异很大,预期的目标和要求也很多,那么这个方案应该怎么设计呢?3、合作方式在介绍具体的技术方案之前,我先简单介绍一下我们的合作方式。在公司内部推广一项技术的落地,我们通常采用单一项目的方式,即单一项目了解需求、设计、开发,然后在项目落地后逐步推广到其他项目。这种方法虽然没有问题,但是有几个缺点:需求的综合性:不能充分考虑到其他项目的需求,方案的设计可能存在差距,导致后期推广困难,因为仅考虑该项目的要求和场景。成本负担:成本由单个项目承担,可能会影响项目团队原有的工作,对项目本身影响比较大。基于以上情况,我们设计了专业组机制,希望让专业的人做专业的事。工作流程如下:首先,从多个有需求的项目组中招募有兴趣或有经验的成员加入专业组,如图中,从项目组1、项目组2、项目组3各选一人加入专业组。组建一个专业的团队,然后针对这些项目设计一个整体方案来满足他们的需求。项目实施后,将逐步推广到其他项目。项目。与单一项目推进相比,专业组机制具有以下优势:充分考虑了典型场景的需求。因为我们会满足原来三个项目组的需求,所以这三个项目组的场景比较清晰。成本分散在多个项目中。每个项目只需1-2人,对项目组原有工作的影响较小。专业团体的进出机制可以充分发挥成员的技术积累优势和主观能动性。能够加入专业组的成员一般在专业问题上有较多的技术积累,或者对问题很感兴趣,会主动去研究这个问题。在解决现有功能的过程中,专业团队在整个过程中发挥了很大的作用。二、技术方案下面介绍一下具体的技术方案。1.限流实现层第一个问题是我们的限流应该在哪一层做?一般来说,我们可以在应用层进行限流,即在API服务节点再增加一个web中间件,在请求进入API服务时判断是否限流。这是一种比较常见的方式。对于单个项目来说,成本也是最低的。除了在应用层实现限流之外,还有一种在接入层实现限流的方法。在原有的LB和API节点之间,我们可以增加一个网关层,在网关层实现限流功能。与这两种方式相比,应用层限流有以下缺点:异常流量仍然落在API服务上。如果突然有大量流量进来,压力还是会落在API服务上,效果会比较差。逻辑耦合,限流功能不能独立改变。责任不明确,增加了服务的复杂性。在多个项目的情况下难以复用,可行性低,不能满足我们的需求。比如我们前面提到的差异,不同项目的开发语言和部署方式是不同的。如果我们在应用层实现限流,需要针对每个项目单独适配,成本比较高。基于以上情况,我们选择了接入层来实现限流。在接入层实现,我们需要一个网关作为统一的记录层。2.KongGateway按照官方的说法,KongGateway是一个轻量级、快速、灵活的云原生API网关。Kong是基于OpenResty和Nginx实现的。当我们解决这个问题的时候,为什么要选择Kong呢?也是基于我们共同的高性能、高可用、灵活、易扩展。其中,我们特别关注以下两点:一是轻巧灵活。在最轻部署的情况下,只需要一个主进程和一个yaml配置文件。二是易于扩展。Kong的插件机制可以在请求生命周期的每个阶段实现自定义逻辑,即我们可以做很多我们需要的工作。3.Kong插件Kong的插件机制可以支持多种语言,如Golang、Lua、Python、JS。我们选择了Lua和Golang作为我们的开发插件。两者的区别在于Lua插件是直接在Kong进程中执行的,即和Kong是同一个进程,而每个Go插件都是一个单独的插件,与Kong主进程的通信需要通过IPC执行。与Lua和Golang相比,Golang在团队技术栈匹配、生态、工程难度、开发维护成本等方面优势明显,而Lua在性能方面会更高。因为Golang每次执行插件都需要进行IPC通信,IPC通信次数多时性能会受到很大影响。但是我们在实践中发现,在现有场景下,IPC通信的次数比较少,对性能的影响也比较小。所以一般来说,我们优先使用Golang来开发插件。如果Golang实现不了,那我们可以用Lua来开发。4、插件的模块化以前我们都是使用Kong作为接入层来解决不同项目环境的差异,然后可以使用插件机制来解决不同项目的需求差异。我们可以将一个项目的限流需求分为业务需求模块和限流功能模块两个部分。业务需求模块迭代更频繁。这方面的需求包括解析用户名等,根据不同的路径、IP、请求方式,每个项目都会有不同的需求。限流功能模块比较稳定。比如我们需要限制短连接的频率,限制并发连接数等。在具体限流算法的实现之上,还有一个策略的应用,就是针对同一个限流方式,比如令牌桶,我们可能会针对应用设计不同的策略。基于以上的模块划分,我们在实际开发中分为三层,从下到上分别是限流算法层、限流插件层、业务插件层。1)限流算法层限流算法层主要是SDK的形式,因为我们的限流场景很多,算法也不同,所以我们会针对每个不同的场景单独开发SDK,比如短连接的频率可能会有令牌桶或者时间窗等限制,短连接和长连接的并发数也有具体的实现。需要特别注意clientthrottling的场景。我们在实际应用中发现,除了访问Kong服务器的性能外,还需要对客户端请求进行限流。客户端限流是指当客户端发起调用第三方频率的请求时,我们也希望能够控制频率来保护第三方服务。限流算法的SDK以代码嵌入的形式嵌入到限流插件层中。2)限流插件层限流插件层会调用SDK,不同的场景需要设置不同的策略。比如我们的令牌桶限制了短连接的频率。一个插件可能会实现token预测功能,而另一个插件可能会做限流配额,动态调节等,也就是说向业务层迈进了一步。3)业务插件层业务插件层会根据项目组的不同需求制定单独的业务场景。例如,不同的项目团队可能有不同的分析限流维度,需要项目团队适配。比如业务组的配置监控等业务定制功能也可以在这一层完成。我们的分层主要分为三层。Kong在实际执行中,会有两个插件,分别是业务插件和限流插件。这里的插件指的是Kong本身自带的执行插件。下面以长轮询场景为例,对需要限制客户端连接数的场景进行说明。首先,在请求进入Kong网关的时候,在业务插件中,我们会分析它的限流维度,比如user,path,method,ip,然后从这些维度生成一个字符串限流KEY,这可能是一个相对较长的字符串。然后它会匹配一个特定的限流策略,如果不匹配,它会进入自下而上的策略,并以此信息生成限流协议数据。业务插件的配置会记录请求的维度,如路径、方法等,匹配限流策略后会生成其限流协议,该协议会设置最大并发请求数,request超时时间等,然后业务插件会把这些信息组装成一个协议。协议的格式在图的右下角。然后通过Kong的context机制将这些数据传递给限流插件,限流插件会对协议数据进行解析,然后执行限流逻辑判断,判断请求是限流还是透传给上游服务。5、对于分工项目组的需求,如果已有的插件可以满足要求,则直接使用;如果没有,您可以定制自己的业务插件。在定制插件时,项目组只需要关注项目需要的业务逻辑即可。相关生态已由专业组提供。专业组负责所有限流插件和部分通用业务插件的开发,网关和插件公共功能的开发,构建安全、稳定保障,限流的设计和实现不同场景下的算法。插件开发完成后提交到插件库。如果其他项目组有相同的项目,也可以直接使用。6.分发和使用接下来我们简单了解一下插件的分发和使用机制。首先,插件开发完成后,我们会在GitLab上打上tag,然后触发一个CI流程。CI过程的工作主要是编译,写入参数信息,压缩打包,将插件打包成tag压缩文件,上传到文件服务,然后发布到gitrelease接口,然后是项目team可以在发布界面使用查看我们可以使用的插件和它们的能力。项目组在实际使用的时候,需要把部署仓库Fork出来,然后设置多个需要使用的插件和版本。然后触发CI流程,我们会重新下载插件,并进行解析、解包等工作,然后将这些插件及其信息写入Kong,构建推送成一个完整的镜像。镜像制作完成后,我们就可以在部署平台上进行部署了,至此项目组就完成了Kong和插件的使用。7.接入方式接下来简单介绍一下项目组的实际接入方式。前面我们提到项目组之间有很多差异,所以我们分为5种情况:1)如果是全新的项目,直接对接。2)如果已有项目,但没有网关,那么主要考虑业务场景,综合评估后再接入。3)已有的项目和已有的Kong网关,这种情况下直接安装使用我们的限流插件即可。因为我们开发的插件也是Kong的标准插件,所以项目组在使用限流插件时只需要连接到它的限流协议即可。4)已有项目,且有其他网关,则不能使用Kong网关,直接连接限流算法的SDK即可。5)客户端限流的情况下,不需要Kong网关,直接连接客户端限流SDK即可。8.整体结构程序整体结构比较简单,容易适应不同的项目。一般来说,LB和API节点之间会加一个Kong网关。首先,我们将为网关插入一个跟踪插件。本插件内嵌了Open-Tracing的实现,项目组根据自己的需要选择业务插件和限流。插入。业务插件的一个功能是动态监控限流配置,需要通过外部配置中心来实现。动态监控主要是考虑到当有异常流量进来的时候,我们可能需要动态调整配额。例如,场景可能是项目最初设定的配额已经不能满足其需求。该项目的用户数量最近急剧增加。在这种情况下,我们不能限制它的流量,所以我们需要增加流量,以免对业务造成不良影响。根据不同限流算法的实现,限流插件也有不同的依赖。比如我们大部分情况下都会做分布式限流。我们选择通过Redis添加一个Lua脚本来实现分布式限流。这种情况下的依赖也需要添加一个Redis。也有少数情况可能作为本地限流器使用,需要根据具体项目组具体实施。3、实施计划实施计划是指项目组从0接触到整个计划完成上线的过程。前期包括需求、评估、开发等步骤。1.需求在需求阶段,项目组会根据我们提供的模板提交一个issue,其中会包含以下内容:限流场景、类型、限流维度、策略。项目团队需要专门的业务需求。现有的插件能否满足项目组的要求?要求二、评价然后进行整体评价工作。评估方面包括:开发、维护、硬件等成本、部署架构性能、链路影响3.开发(如果需要)如果我们现有的插件不能满足项目组的需求,那么就需要进行开发工作。对于开发工作,我们有更详细的指南,因此入门成本相对较低。项目组需要了解以下几个方面:本地环境部署:我们提供了一个docker-compose环境,可以一键拉起本地开发环境。快速了解相关知识:包括Kong的一些概念等。完善的开发文档:包括如何编写具体的插件等。简单来说,我们提供一份文档,教项目组手把手开发。接下来是测试、部署和上线的环节。4.测试在测试过程中,我们的目的是保证接入Kong和插件后的质量。首先我们要保证我们的限流功能是符合预期的。原API服务的功能返回后,需要和之前的保持一致。镜像流量和故障演练对我们来说很重要。镜像流量是指将生产环境的流量导入测试环境,可以提前验证我们的流量在限流后是否异常。也就是我们需要一个单独的环境来镜像流量,在这个环境中提前验证生产环境的流量,因为我们新增了一个网关,变化的影响比较广,可能会遇到奇怪的情况问题。如果我们提前导入生产环境的流量,就可以提前发现这些问题。故障演练还用于提前发现可能存在的问题和风险。关于这两点,我们内部有两个最佳实践指导,分别是《如何做好镜像流量》和《如何做好从场景出发的故障演练》。5.部署接下来是部署环节。我们实现了基于内部一致交付系统的模板部署,即物理机、公有云、私有云等不同的部署平台可以使用一致交付平台实现统一部署,项目组无需部署即可考虑太多细节。其次,我们可以在部署时一键访问监控、日志、告警、链接等功能。我们在部署的时候,有一个很全面的部署指南,里面会介绍各种问题,比如我们接入Kong网关后,如何处理原来的服务拆分问题。6、上线的最后一步是上线。我们还提供了详细的上线步骤,以及checklist和最重要的灰度和回滚技术方案。总的来说,我们这些环节也达到了手把手指导和实现的全过程。4.总结我们前面提到的目标是低成本、高效率、高质量。1、成本低1)开发成本:项目组对接时,成本基本为零,或者只有少量的开发工作。2)维护费用:后续维护升级工作全部由专业团队负责。3)硬件成本:主要是Kong的集群部署。根据项目组自身的流量,可以部署不同数量的副本。如果选择分布式限流,还需要部署一个Redis。我们一般建议部署一个新的Redis,但是为了节省成本,也可以和原项目已有的Redis共享。2、效率高1)存取时间:我们项目实践中,最快的存取时间是三天。三天是项目组没有自己的定制化需求,不需要开发完成我们之前的介绍。环节,即评估、测试、部署、上线。2)硬件配置:我们有一个推荐的配置,就是根据我们不同的流量分别进行测试,我们提供了比较完整的性能测试数据作为参考。3)高效部署:网易内部平台比较完善。借助我们的CI自动化、配置管理系统、容器云系统和一致交付系统,可以实现高效部署。4)文档建设:我们在文档建设方面比较完善。从开始接触限流方案到上线整个过程,我们都有非常详细的文档指引。如果遇到问题,可以从文档中查找。可以找专业群的同事了解一下。3、高质量1)技术支持:由专业组同事提供技术支持。2)场景多样:我们的插件机制适应多样化的限流场景和业务需求。3)经验复用:各个项目对接使用中的经验或优化问题可以总结复用,因为我们对不同的项目使用相同的技术体系,便于积累经验。4)自定义扩展:虽然我们自定义了一些插件和一些Kong部署流程,但我们在实际开发中也考虑了对Kong原生态的兼容性,即项目组仍然可以使用Kong提供的丰富功能原生态金刚。5)运维系统:连接了一整套与运维设施相关的功能系统,也就是我们前面提到的监控、告警、日志。6)全面验证:我们的插件功能和性能都经过了全面的测试,上线过程比较完善,可以保证我们的可靠性。通过以上环节,可以保证服务限流方案的低成本、高效率、高质量。Q&AQ1:这个限流方案普遍适用于哪些类型的项目?A1:这个问题其实又回到了我们项目的不同点上。一般来说,对于常见的HTTP短连接项目,或者一些长轮询、长连接的场景,我们都有单独的适配。匹配。因为Kong本身就可以支持这些类型的请求,也就是我们只需要针对这些类型的场景开发相应的插件即可。Q2:Kong和插件对性能影响大吗?A2:我们在实际设计的时候也考虑到了这一点,通过实践发现对性能的影响比较小。一方面,性能会影响我们请求的耗时。经过我们的测试,耗时在5毫秒左右。另一方面是QPS,因为Kong和插件本身是支持横向扩展的,而且Kong本身的性能也是比较高的,所以对QPS的影响比较小。我们在测试的时候最高达到了50000到60000的QPS,所以基本上不用太担心这个问题。Q3:如何实现长轮询连接数的限制?A3:长轮询的本质是请求并发数的一个属性,但是是http请求,可能处于挂起状态,需要挂起一分钟。这个场景的实现过程大致是这样的:Kong本身可以保证我们可以通过插件机制在请求前后插入钩子函数。在请求之前,我们的钩子函数会在Redis的Zset中设置一个请求状态,那么Zset的值就是这个请求的唯一ID,比如是一个雪花ID,然后分钟就是这个请求的过期时间request,也就是当前的时间加上请求配置中的TTL(requesttimeout),两者之和就是请求实际过期的时间。当有请求进来的时候,我们会在Zset中记录请求的数据,表示当前请求的并发数增加了一个。那么当RedisLua脚本执行的时候,会先移除那些已经过期的请求。请求结束后,会从Zset中移除该请求的数据,即并发请求数减一。这是一般原则。
