饿了么异地多活技术实现(二)API-Router的设计与实现

2,162 阅读4分钟
原文链接: zhuanlan.zhihu.com

背景:

饿了么随着业务量的增长,单个数据中心的容量无法支撑全部流量,同时传统机房冷备份方式的缺陷,促使饿了么异地多活应运而生;而作为多活重要组件之一的API-Router,扮演着客户端入口流量到后端机房路由的重要角色,当一个机房出现故障甚至宕机,可以实现机房快速切换。


设计目标:

1、入口流量可以根据sharding key规则路由到对应的机房(准确的说是ezone);

2、配合机房切换规则,能实现流量的转移;

3、尽可能的实现高性能、高可用、高并发,保护后端服务、削峰填谷;

前两点,主要是API-Router所需要实现的主要功能;第三点,是面对饿了么业务体量成倍的上涨带来的挑战所需要解决的问题;因此,API-Router定位为一个HTTP反向代理和负载均衡器,在此基础上实现路由的分发。


一、流量路由实现篇:

API-Router根据业务分集群部署在云上,参考饿了么异地多活的整体架构,API-Router的物理架构如下,

1、API-Router从Router控制台获取维护的每个域名的ezone到数据中心地址的映射关系;

2、API-Router将订阅GZS的关于shard到机房ezone的路由表;

3、API-Router在内存中,将请求头中的sharding key(地理位置、商户ID、订单ID),根据一套sharding规则,计算出shard;最常见的一种规则算法是,根据用户的经纬度,通过地里围栏算法,计算出相应的多边形,而这个多边形提前被分配了一个shard;

3、根据前3步,API-Router就拥有了sharding key-->shard-->ezone-->数据中心的映射表,最后将请求转发到对应的数据中心上;


流量转移的实现:

当后端机房故障时,API-Router会根据GZS主动推送的shard到ezone映射关系变更的消息,实现流量到ezone的切换,从而保证了服务的高可用;


二、技术实现篇:

API-Router采用了基于Netty的全后端异步非阻塞模型,以此来尽可能减少请求在这一层上的网络损耗,Epoll的特性使其支持更高的连接数,同时采取一些保护后端服务的措施来保证整个服务链路的稳定运行。


1、削峰填谷:

从架构图上可知,API-Router处于网站的入口,因此在这一层对流量进行削峰填谷,可以对后端服务免受瞬时流量的冲击起很大的保护作用。

每个http请求过来,首先会根据路由规则计算出后端的upstream,每个upstream对应一个scheduler,然后在scheduler里面依次通过信号量来限并发、通过入队列排队来堆积请求,从而实现流量的整形,示意图如下:

2、连接复用

随着饿了么用户量增长,客户端到router机器连接数越来越高,鉴于TCP连接对性能的影响,router到后端采用了连接池复用技术,因此减少了到后端的连接数(以生产某机器为例,前端到Router连接数在10w左右,经过连接复用到后端减少到140+),示意图如下:

3、热加载filter,处理request & response

API-Router作为处于网站链路最前端的一个Proxy,会有很多需要处理请求/响应的需求。为此,我们实现了一个类似拦截器链的功能,每个拦截器称之为Filter,可以拦截request和response,然后对其做一些类似限流、熔断、反爬虫、白名单、跨域等处理。而每个filter都是动态从控制台下发的,根据自定义的ClassLoader实现修改然后被动态加载到JVM,从而减少了发布的次数。Filter的示意图如下:


4、多协议支持

API-Router目前支持了包括

  • http/https
  • websocket
  • grpc
  • http2
  • TCP

5、更多

为了进一步提升单节点每秒所承载的流量,降低资源损耗,我们还从以下等方面实现无阻塞,提高性能。

  • 异步日志
  • 异步心跳
  • 异步打点
  • 异步Filter
  • 异步链接创建

展望未来:

1、针对饿了么外卖订单高峰低谷波动特点,实现服务器资源分钟级动态扩容和缩容,提高资源利用率;

2、进一步提供高系统的可用性,防爬、防刷、防网络攻击;


作者介绍

罗辉, 2015年加入饿了么, 现任饿了么框架工具部资深工程师, 负责饿了么API-Router项目。