查看原文
其他

京东10亿级调用量背后的高可用网关系统架构实践!

2017-11-07 王栋 51CTO技术栈

京东开放服务平台是京东对外开发的窗口,每年的 618 大促,京东的网关都要承载十亿级的调用量来保障几十万商家稳定使用的使命。


在保证近千个不同类型服务接口的海量调用的同时,我们还要确保服务接口之间的互不干扰,并且能够快速响应任何复杂情况,因此稳定、快速是我们一直追求的目标。

今年的 618 大促,京东的网关承载了几十亿的流量和调用,在这种情况下,网关系统必须保证整个系统的稳定性和高可用,保证高性能和可靠性,以支撑业务。


我们面临的是一个非常复杂的问题,基于这种复杂问题,怎样做到很好地提高它的性能和稳定性,复杂技术之间怎么整合保证整体网关的高可用,是本文的重点。

网关涵盖技术

网关系统


网关系统主要有两种:

  • 客户端网关,主要用来接收一些客户端的请求,也就是 APP 的服务端。

  • 开放网关,主要是公司(比如京东)对于第三方合作伙伴提供接口。


这两种不同网关所使用的技术非常类似。


流量比较大的网关面临的难点包括:

  • 网关系统需要扛几十亿的流量调用,接口的平稳运行、每一个接口在后端服务之后的性能耗损都非常重要。

比如我们使用了一个 Redis 集群,然后构建了两个机房,每一个机房都搭建了一个 Redis 集群,这样的话就能够很好地保证高可用。


在面对一个瞬间流量的时候,我们采用了一些缓存技术,或者更前置的 Nginx+lua+Redis 技术,让这种大流量应用能够脱离开 JVM 的依赖。


还有我们需要梳理各个接口,通过降级的策略把一些弱依赖的接口进行降级,从而保证核心应用的可用。

  • 网关系统其实就是一个把 HTTP 请求拓展到后端服务的过程。

我们的网关承接了一千以上的后端服务接口,面对这种情况,怎样做到服务与服务之间相互不影响?架构层面怎样能够杜绝蝴蝶效应、防止雪崩?


就是说当一个接口出现问题的时候,不至于影响到其他接口的健康运行。这个说起来简单,但实际却不然。


一千个以上的接口,每个接口性能都不一致,而且每个接口所依赖的外部资源、数据库缓存等都不一样,几乎每天都会出现各种各样的问题,我们怎样通过一些隔离技术、治理技术等,保证当这些接口出现问题的时候,不会影响到全局?

  • 我们对外暴露了一千个服务接口,所有接口的后面意味着几十个甚至上百个团队每天在不停地开发,每天都可能上线新的需求。

面对这么复杂的情况,我们不可能每次后端服务器有任何修改,都需要有网关的修改或上线,这样网关会变得非常脆弱,稳定性极低。


我们采用了一个动态接入的技术,让后端的网关能够通过一种接入的协议进行无缝接入,之后通过一些动态代理的方式,直接让后端的接口,不管做任何修改或上线,都可以通过后端管理平台从网关上对外进行透传发布。


这样就很好地解决了网关所面临的依赖于后端接口服务的上线问题。


网关涵盖技术


网关的四个技术方向:

  • 统一接入,就是前端(包括 APP 或其他来源)的流量,都能在统一网络层进行接入。

    这一层所面临的问题是:高性能透传、高并发接入、高可效性,以及当前端流量来了之后,怎样能够进行一个负载的服务往后端的转发。

  • 流量管控,要指流量治理部分。面对海量流量,我们怎样通过一些防刷技术,保障网关不被大流量冲垮;以及怎样通过一些像限流、降级、熔断等技术,对网关进行全方位保护。

  • 协议适配,就是前文提到的,网关会透传后端上千个服务,而这些服务一定不是每一个都需要网关去开发配置的。

    我们通过一个协议适配的转换,让后端的各种服务通过我们指定的协议、通过 HTTP 的方式从网关开放出去,当然网关不单单是 HTTP 协议,还有一些 TCP 的。

    京东内部的协议相对比较统一,有 HTTP 的 restful 的协议,也有 JSF 的接口,JSF 是京东内部自研的一个框架,一个 RPC 调用框架,和 Double 是类似的,然后基于注册发现的一个 RPC 框架。

  • 安全防护,这一部分对于网络来说非常重要,因为网关是整个公司对外的一个出口,在这一层我们要做一些防刷。

    比如防清洗一些恶意流量、做一些黑名单,当有一些恶意流量的话,通过限制 IP 等限制手段把它拒绝在整个网关之外,防止这些恶意流量把网关冲垮。

自研网关架构



自研网关架构


我们的自研网关架构主要分为三层:


接入层


主要负责一些长短链接的接入、限流、黑白名单、路由、负载均衡、容灾切换等。这一层所采用的技术是 Nginx+lua 的方式。


分发层


这一层是分发层或者叫网关的业务层,它更多的是 NIO+Serviet3 异步的技术。


在这一层中又分为几个部分:

  • 最上层部分是数据校验,在这一层会做一些签名的校验、时间的校验、版本、方法等。

  • 下面一层叫泛化调用层,主要是把网关对外暴露的 restful 请求转换成京东内部的协议,进行一个动态适配调用的过程。


这一块我们更多使用的是一些缓存的技术,线程隔离、熔断等技术也都是在这一层实现的。


因为有大量数据和协议的转换,所以这一层用了多使用缓存的技术,我们网关层所有的数据都不会直接穿透到 DB,而是采用一个叫异构数据的方式直接用缓存去做的。


泛化层中间有两块:

  • 主动通知,就是我们会通过这种 TCP 的下行通道及时通知到客户端,发一些像京东账户优惠券或提醒等。

  • 沙箱测试,主要是在一些接口发布上线之前,进行一个外部的测试。


如上图,最右侧部分是服务降级、日志记录、监控告警,这三个都是我们整个网关的支撑系统。


服务降级是说当有些服务出现问题,第一时间把它降调;日志是给我们排查问题用的。


监控告警在下文会重点介绍,因为一个网关的可用性很大方面是通过监控系统来完善的,没有监控系统、没有告警,就像没有眼睛一样,没办法知道任何事。


后端各种各样的业务 API


这些业务 API(业务接口)通过网关对外进行暴露。整个网关大体上分为如上图的三层,最上面是接入层、中间是网关的分发层,以及业务校验、业务逻辑层,然后通过网关透传请求到后端服务。


除了这三层之外,我们再看两边的系统,都是我们整个网关比较核心和重要的支撑:

  • 网关注册中心,后端各种各样的接口可以通过网关注册中心对外进行发布,这个系统有一个类似的管理界面,只要后端的 API 服务按照固有的协议进行一个编写。

  • 如果格式 OK 的话上传到管理后台,一键就可以发布到线上。当然接口发布之前会有一个测试。

  • OA 鉴权中心,这一块主要是做鉴权用的,像数据校验层的很多签名的校验等安全校验都是在这一层统一做的。


 技术栈


我们的网关系统所涉及到的一些技术栈:

  • 接入层 Nginx+lua 技术。

  • NIO+Serviet3 异步技术。

  • 分离技术。

  • 降级限流。

  • 熔断技术。

  • 缓存,哪些地方该加缓存,哪些地方可以直接读库。

  • 异构数据。

  • 快速失败。

  • 监控统计,这是整个高可用网关系统里非常重要的一部分。


下文会针对这些技术所适用的场景进行深入探讨和分析,包括我们用这些技术解决什么问题。

基本思路及过程改进点

Nginx 层统一接入

先看网关整个线上的部署架构,通过一个软负载 LVS 进入到整个京东的网关,第一层是核心 Nginx,经过核心 Nginx 之后就是后面的业务 Nginx,然后通过业务 Nginx 把我们的请求透传到后端的服务器。


核心 Nginx 主要是前端流量的分配,比如限流、防刷都是在这层去做。下层是业务 Nginx,主要的 Nginx+lua 的逻辑在这一层实现。


这一层还有能减轻核心 Nginx 压力、CPU 压力的作用,而且一些 lua 的应用逻辑,比如限流、防刷、鉴权、降级都是在这一层做的。


为什么要加上 Nginx+lua 这一层?相较于 Tomcat 等,Nginx 是一个能扛特别大并发流量的服务器。


基于这种状况,我们之前出现过问题,当这种并发流量特别大的时候,一旦后面出现单机有问题,哪怕你针对这个接口做了降级,但真正流量还是到了 Tomcat 层的 JVM 里。


当流量很大的时候,很难通过 JVM 能够消化掉,这样导致的结果是:当你的 Tomcat 出现问题了,你很难通过重启去解决这个问题。


因为流量会一直存在,这台 Tomcat 出问题了, 重启完之后是把所有行动都释放了,但是它们就像病毒一样,会来回传染,你重启了一批,这批马上又被传染到。


Nginx 天然就是这种 NIO 异步的方式,能够非常好地支持大并发的业务需求。所以我们把一些核心的,比如降级、流控等,都放在这一层,让它替我们在最前端把流量防住。


引入 NIO、利用 Servlet3 异步化

第二个实践是在 Tomcat 层引入了 NIO,用了一个 JDK7+TOMCAT7+Servlet3 的配置,让同步请求变得异步化,然后利用 NIO 的多路复用处理技术,让我们能够同时处理更高的并发数。

利用 Servlet3 异步化之后可以提升吞吐量,但单个请求的响应时间会略微变长,不过这种损耗是可以忍受的,因为这会带来整个应用吞吐量的增加和灵活性的增强,还是非常值得我们使用的。


具体采用策略:

  • 业务方法开启异步化上下文 AsynContext。

  • 释放 Tomcat 当前处理线程。

  • Tomcat 该线程被释放,然后用于下次请求的处理,提高其吞吐量。

  • 在 AsynContext 环境中完成业务方法的处理,调用其 complete 方法,将响应写回响应流。


这样可以提高 Tomcat 业务逻辑的可能性,让我们在这一层非常少的线程数就能处理更多的请求,而不至于当流量非常大的时候被压垮。


分离之术


在所有分离技术中,我挑两个比较重要的点进行分享。


请求解析和业务处理分离


第一个是通过 NIO 的方式,把请求解析的线程和后面处理的业务线程进行分离。

请求由 Tomcat 单线程处理,在 NIO 模式下可以用非常少量的线程处理大量的链接情况。


业务逻辑处理和生成响应都是由另外的 Tomcat 线程池处理,从而跟请求线程隔离。这里的业务线程池还可以进一步隔离,不同业务设置不同的线程池。


业务线程池分离

第二个是业务线程池分离,就是通过一个线程的隔离技术,把不同的接口或不同类型的接口进行隔离。


比如订单相关的接口,拿 20 个单独线程去处理;商品相关的接口,拿 10 个单独的线程去处理,这样的话就可以让不同的接口之间互不影响,如果订单这块有一个出了问题,最多消耗它自己,不会影响到其他接口的线程的调用。


具体的线程隔离可以根据业务来指定一组线程的数量,这几个线程是为固定接口准备的。


当这个接口出现问题,它就把自己的线程数用掉了,不会去占用其他接口的线程,这样起到了线程隔离的作用,让单个 API 出问题的时候不会影响到其他。


降级


降级主要是说当有某个接口出现问题,我们能够把这个接口直接降调,让它调用直接返回,不会用到其他应用。


还有就是如果某一块弱一点的业务逻辑出现问题,我们直接把这块逻辑降调,不至于影响到其他的黄金逻辑。


降级怎么做?

首先,降级开关要集中化管理,比如通过 Zookeeper 推送到各个应用服务。这样才能在出现问题的第一时间找到对应开关做降级处理。


一个基于开发降级的统一配置本身这个系统要是高可用的、支持多维度的缓存,比如我们如果用 Zookeeper 实现,首先 Zookeeper 会有数据库存储,再上面会有一个本地缓存。


再就是我们会有一个快照,如果 Zookeeper 读不到缓存,会通过快照去加载进来一些托底的数据,以保证开发一旦触发之后能够在第一时间响应。而我们的开关也不至于会成为其他系统的问题,它是非常弱化、非常薄的一层。


精细化流量控制

说完开关、流量控制和降级之后,我们来看通过多维度的流量控制和降级的策略,比如按照单个 API 或 API+ 地域、运营商等维度进行控制。


一旦出问题了,我们会把多种组合方式进行降级,还可以根据秒/分钟级等不同维度进行流量控制,从而达到精细化流量管理。


优雅降级

说到降级,前面说的更多的是技术层面的,在业务层面的话,我们也要讲究优雅降级。我们不能说这个逻辑一旦建立之后就直接返回前端 502,这肯定是不友好的。


我们肯定会跟前端进行沟通,比如降级之后反馈给前端一个对应的错误码,或者给用户反馈一个提示等操作指令,这样能够让用户体验更好一些。


限流


恶意请求、恶意攻击,恶意的请求流量可设置为只访问 Cache,恶意的IP可以使用 Nginx 层的 Deny 进行屛蔽,防止流程超出系统的承载能力,虽然会预估但总有意外,如果没有限流,当超过系统承载峰值的时候,整个系统就会被打垮。


熔断


当我们的后端机构出现问题了,达到某个阀值了,系统就能够自动进行关闭降级,这是熔断的大体思路。


我们会有更灵活的配置:比如当某个接口接连三次访问超时或返回错误的话就自动熔断。


也可以是配置一些超时间,比如连续三次这种方法调用的性能都超过了 50 毫秒,就会自动对这个方法进行熔断,熔断之后就相当于降级了,再次调用的话会返回失败,就是直接拒绝返回了。


熔断之后还可以有一个设置:比如 5 秒或一分钟之后出来一个半打开状态,再次醒来之后,它会去试探一下当天这个服务是否已经 OK 了,如果没有问题了,它就会去把你之前熔断的 API 业务再次打开,能够正常对外提供服务。


现在有一些开源的实践,通过这些实践可以很好的做熔断,当然根据这里边的思路,自己也可以实现,这不是特别复杂的事情。


快速失败-链路中的超时


快速失败是非常重要的一个实践,不光是做网关系统,做其他系统也要记住,特别是调用量大的系统,比如注意到整个链条中的超时设置。


这是我们每年在做双 11 和 618 备战的时候,都需要重点去 review 的一块功能,包括我们平时在做开发的时候、每一次新模块上线之前,我们都要重点去监控这一块。


我们会梳理所有系统对外的依赖,比如网关依赖于我们自己的一些业务的缓存、数据库,更多的是依赖于后端数千个不同的服务。

这种涉及到网络的,我们必须要设置超时间,因为像网关这种调用量比较大的系统,如果不设超时间,有可能它默认时间就是几分钟。


这么长时间,一旦有一个机构出问题了,有可能瞬间整个网关系统会全部雪崩掉,任何一个接口都不能对外使用,因为数据量很大,有可能你都来不及降级就已经被冲垮了。


监控统计-应用层

监控统计是网关系统里非常核心的一部分,只有有了监控,有了报警,才能让我们实时了解所有的运营情况、每一个 API 调用的情况。


监控目标

  • 保证 7*24 小时守护系统。

  • 能够实时监控系统的运营状况,比如哪个 API 是不是调用时间过长了?哪个 API 已经熔断了?等等。

  • 统计数据,分析指标。比如一天过去了,每一个 API 调用情况有没有超时?有没有访问的性能降低等。

  • 实时报警。因为监控是一部分,发现问题之后能够第一时间通知到我们,让我们能够马上处理也是让系统更加健康的一个方面。


监控范围

监控的维度

  • 硬件监控。比如系统的 CPU 内存、网卡等。

  • 自定义监控。比如直接报警。

  • 性能监控。比如每个接口的 TP 指标,TP999、TP99、TP90、TP50 四种性能指标作为 SLA 的参考标准,还有可用率等,这个对于网关来说至关重要。

  • 心跳监控。网关系统线上有很多机器,每个机器现在的情况怎样?有没有存货等。

  • 业务层监控。比如我们会有一些 JVM 监控,监控 Nginx 连接数等。


在京东内部有一个很完善的监控体系,叫 UMP 系统,能够帮助我们做各个层级的监控。


它主要是提供给我们一些类似于配置的文件,我们配置好之后就可以进行系统的监控,我们在做的时候会通过一些 AOP 代理的方式,对所有的方法进行监控。


因为我们是网关,需要大量的后端透传,网关因为是动态地生成这些接口,根本不知道有哪些接口,所以在动态生成接口的时候自动地 AOP 给它注入一个个监控,这样的话就是每一个接口都能够有一个监控。


说到监控不得不提的是,我们做网关系统就是做透传的,后面有各种各样不同的接口、业务逻辑,每个业务逻辑和接口的性能都需要去监控,然后告知对方让对方去整改的。


所以我们除了把这些监控加完之后,有了问题要能够通知到对应的负责人,包括我们自己。


我们每一天每一周都会有邮件以报表形式发出,让所有系统负责人都知道对应的机构的情况,比如性能是否有问题、是否需要整改等。


作者:王栋

编辑:陶家龙、孙淑娟

来源:转载自壹佰案例微信公众号,文章内容为王栋老师线上分享《618大促网关承载十亿调用量背后的架构实践》实录。

王栋

京东商城开放平台高级架构师

拥有 10 多年的架构和团队管理经验,涉及信息安全、互联网、电商等领域。 2011 年底至今一直在京东商城就职,期间负责过商城、POP、京东开放生态、京东移动 APP、京东商户 APP 等业务,熟悉电商核心的流程和移动互联网。在这 4 年当中见证了京东一步步成长成为行业巨头,也见证了京东的技术部从 300 人到 7000 人,从跟不上业务发展到驱动业务发展的过程。 现任京东商城开放平台高级架构师,京东商家移动端负责人,京东创新联盟平台创新评委,新晋架构师评委等。

精彩文章推荐:

阿里背后应对超大流量的高性能负载均衡“帝国”!

喂,小姐姐,你的程序员男朋友已经加入购物车!

腾讯高级工程师自述:十年沉浮,我为什么选择离开管理岗位?

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存