APIGateway和VIPServer

API Gateway

构建web应用的时候,往往需要在“真正的web服务”的基础上,增加一些通用功能,比如记录日志、授权管理、请求速率限制等。对于HTTP请求来说,API Gateway扮演着网关的角色,也可以说是进入系统的唯一节点,API Gateway能够集中地控制和管理通用的功能。
下面是一个来自Mashape Kong的对比:
aaaa.jpg
在当前的架构中:

  • 通用的功能在多个服务之间是重复的
  • 业务系统变得庞大,且难于维护
  • 在不影响其他服务的情况下,难于扩展
  • 因为系统限制,生产效率降低

在API Gateway架构中:

  • 通用的功能被放到了一个地方
  • API Gateway可以实现成一个易于扩展的分布式架构
  • 仅仅在一个地方就可以给所有相关的服务扩展功能
  • 开发者可以将精力集中在业务实现上

目前,API Gateway已经成了AWS的一项重要业务,下面两张图分别展示了亚马逊API Gateway调用流程,以及亚马逊API Gateway请求处理流程:
3.jpg 2.jpg

在这里要特别强调的是,上文中提及的Mashape Kong。Kong是一个基于ngx_lua实现的API Gateway,github上的地址是:https://github.com/Mashape/kong


VIPServer

VIPServer是阿里中间件团队开发的一个中间层负载均衡(mid-tier load balancing)产品,VIPServer基于P2P模式,是一个七层负载均衡产品。VIPServer提供动态域名解析和负载均衡服务,支持很多诸如多业务单元同单元优先、同机房优先、同区域优先等一系列流量智能调度和容灾策略,支持多种健康监测协议,支持精细的权重控制,提供多级容灾体系、具有对称调用、健康阈值保护等保护功能的解决方案。(上面的介绍摘自http://www.woaiit.com/news/2016052713841.html

下面开始介绍VIPServer的原理:
假设有一个内部服务A和一个内部服务B,在服务A中需要调用服务B,那么通常情况下时序图可能如下所示:
non-vipserver.jpg
其中:Load Balance层可能是公用的,而每一个服务都有自己的web proxy server和actual web server。
在当前的架构中,有一个缺点:服务间的请求需要经过多层转发,才能到达真正处理请求的后端服务器。当内部服务的数量增大时,服务之间的请求数量也会变得极为可观,如果为了保证高可用,每个服务都使用了公共的内网LVS的话,那么LVS本身的带宽都会成为瓶颈。阿里开发VIPServer的原因之一就是:内网LVS的带宽被跑满。

下面是博主整理的VIPServer工作原理简图:
aaaaaaaaaa

VIPServer服务的大体功能包括:

  • 在收到域名解析请求时,根据配置的策略(比如,同机房优先)和后端节点的健康状态(比如,健康检查时响应时间短者优先)返回IP或IP列表给请求方
  • 在收到域名注册请求后,会根据配置的健康检查参数,使用Diamond对后端服务进行健康检查,并记录其状态和响应时间

上图中没有体现出,但是需要特别说明的包括:

  1. 在服务A从VIPServer服务请求得到服务B的IP或IP列表时,可以缓存一段时间,并且可以自己实现一些负载均衡策略和重试策略
  2. 服务A可以不直接访问VIPServer服务,而是将域名解析请求发送到dns-f,dns-f是一个自己实现的、部署在本机的DNS服务器,对于在VIPServer上注册的域名,它会请求VIPServer进行处理,并缓存;而“真正的”域名,会交给本机设置的DNS服务器处理。

和VIPServer类似的产品是httpdns


ngx-service-discovery

ngx-service-discovery是基于Openresty(ngx_lua)和Redis实现的一个Nginx自动发现后端upstream,以及对后端upstream进行健康检查的服务框架。里面有一些API Gateway和VIPServer的影子。

下面是该框架中的一些概念:

  • 每个数据中心都有一个ID(数据中心ID的名字以DATA_CENTER-开头,比如DATA_CENTER-dc1)。数据中心也可以是虚拟的,比如可以创建一个ID为DATA_CENTER-dc1-and-dc2的数据中心,用来表示数据中心dc1加上数据中心dc2。这样做的主要目的是便于扩展

  • 每个Nginx实例,都有一个节点类型(节点类型以NODE_TYPE-开头,同一个数据中心的不同Nginx实例的节点类型可以相同)。同时,Nginx也需要配置自己所在的数据中心。在ngx-service-discovery中,把NODE_TYPE-xxx@DATA_CENTER-yyy做为Nginx实例的ID

  • 每个Nginx(用ID来区分,ID相同的Nginx共享相同的配置)都有一个存储在Redis中的配置,该配置用来说明Nginx将接收到的请求代理到哪个数据中心;备用的数据中心是什么;当满足AB Test条件时,将请求代理到哪个数据中心。数据中心可以是虚拟的,比如,建立虚拟数据中心DATA_CENTER-dc1-ABTest专门用来处理AB Test请求。我们将该配置称为Nginx ID配置
    Redis中的数据结构类似:
    xxxxxx 其中:hash表名表示Nginx ID,target表示要代理到的数据中心ID,backup表示备用的数据中心ID,abtest表示用来处理AB Test请求的数据中心ID

  • 每个数据中心(包括虚拟的数据中心)在Redis中都有自己的配置,数据结构类似:
    dddddd 其中:hash表名表示数据中心ID;key代表后端节点的地址;value是json格式的,hostname表示该后端节点对应的域名,uripattern表示该后端节点能处理哪些uri,它是一个正则表达式,^/表示所有uri,checkpath是用于健康检查的地址,还可通过checktimeout设置健康检查时的超时时间,weight用于balance算法,表示权重。
    当后端服务启动的时候,可以自己向Redis注册,一个后端服务可以注册到多个数据中心或虚拟数据中心上,比如服务127.0.0.1:80可以同时注册到DATA_CENTER-dc1和DATA_CENTER-dc1-ABTest,注册其实就是在一个或多个表示数据中心的hash表中增加一个key/value对,其中,key是服务的地址,value是相关的配置信息。也可以象VIPServer一样,由服务提供方通过web UI去注册

有了上面的概念之后,就可以很好的理解下面要阐述的服务是如何发现的了:

  • Nginx的worker进程在启动时候,会创建一个“后台任务”,该任务会周期性地轮询Redis,以获取关于Nginx ID和相关数据中心的配置,然后写入到worker内部的cache中。当Nginx向后端代理请求的时候,会从cache中获取后端节点,然后按照加权轮询算法,从其中选择一个后端节点。因此,后端服务只要把自己注册到Redis,就会被Nginx的后台任务轮询到,然后写进cache,进而在balance时被选择到

  • Nginx的worker进程在启动的时候,还会创建一个用于对后端节点进行周期性地健康检查的“后台任务”。假如,有100个后端节点1、...、100,该后台任务会创建一个大小可配置的“线程池”(在异步编程中,称为“协程池”更合适),假设“线程池”的大小是10,那么会先并发地检查后端节点1-10,等他们都检查完毕之后,再检查11-20,等等。对于某一个后端节点而言,如果健康检查失败了,则在cache中对该节点的计数增加1;如果健康检查成功了,则将cache中关于该节点的计数删除。Nginx在向后端代理请求的时候,首先会选择出目标后端节点的列表,然后使用健康检查cache中的计数信息(计数的数量代表连续失败次数)对目标后端节点列表进行过滤,比如后端节点的连续失败次数超过3次(可以配置)就认为是不健康的。在健康检查时,超时时间很重要,如果超时时间过长,那么会影响对其他节点的健康检查的时效性,同时“线程池”的大小也很重要

  • Nginx选择后端节点的粗略流程是: 33ads 接下来用文字描述进行补充:

在Nginx ID配置中,保存的是Nginx需要将请求代理到哪个数据中心,包括target数据中心、backup数据中心、abtest数据中心;在数据中心配置中,保存的是某个数据中心上注册了哪些后端节点。
Balancer先去Nginx ID配置中获取到target数据中心和backup数据中心,再根据数据中心配置生成target后端节点列表和backup后端节点列表,接着用请求的host、uri以及健康检查的计数过滤这两个列表,最后根据负载均衡策略先从target后端节点列表中选择节点,然后将请求代理到该节点,如果没找到合适的,那么再去backup后端节点列表中选择,如何仍然没有合适的后端节点,那么服务返回500 INTERNAL ERROR。
因为数据中心信息、节点信息、节点健康信息都在cache中,如果通过一个接口将他们暴漏出来,那么这个接口就是一个弱化版的VIPServer。


测试ngx-service-discovery

  • 安装最新版本的Openresty,因为balancer_by_lua*指令最早出现在v0.10.0版本的ngx_lua模块中,所以需要选择较新版本的Openresty。目前Openresty的最新版本是1.11.2.1,Openresty在github上的地址是:https://github.com/openresty/openresty
  • 安装和启动Redis
  • 下载ngx-service-discovery
  • cd ngx-service-discovery/
  • mkdir logs
  • plugins/MyConfig.lua是配置文件。测试阶段,首先配置好Redis相关的信息
  • 启动Nginx
    • Openresty的安装路径(默认是/usr/local/openresty)/nginx/sbin/nginx -c nginx.conf -p . #一定要正确的设置-p,最好使用绝对路径
  • 向Redis中增加类似下面的Nginx ID和数据中心配置信息:
    333
    注意:Nginx实例的节点类型是通过plugins/MyConfig.lua的NODE_TYPE配置项来配置的;Nginx实例所在的数据中心是通过plugins/MyConfig.lua的DATA_CENTER配置项来配置的。

  • 发送测试的请求
    3333333

需要特别注意的是:在数据中心配置信息中,每个节点都有一个hostname和一个uripattern。Balancer在过滤后端节点列表的时候,会要求请求的host和节点的hostname相同,并且请求的uri与节点的uripattern(一个正则表达式)相匹配。测试时,要注意请求头中的Host头域配置中节点的hostname。在生产中,将域名指向Nginx所在的服务器即可。

  • 测试仿VIPServer接口 3333333333333

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: