开源版 Nginx 最为人诟病的就是不具备动态配置、远程 API 及集群管理的能力,而 APISIX 作为 CNCF 毕业的开源七层网关,基于 etcd、Lua 实现了对 Nginx 集群的动态管理 。
文章插图
让 Nginx 具备动态、集群管理能力并不容易,因为这将面临以下问题:
- 微服务架构使得上游服务种类多、数量大,这导致路由规则、上游 Server 的变更极为频率 。而 Nginx 的路由匹配是基于静态的 Trie 前缀树、哈希表、正则数组实现的,一旦 server_name、location 变动,不执行 reload 就无法实现配置的动态变更;
- Nginx 将自己定位于 ADC 边缘负载均衡,因此它对上游并不支持 HTTP2 协议 。这增大了 OpenResty 生态实现 etcd gRPC 接口的难度,因此通过 watch 机制接收配置变更必然效率低下;
- 多进程架构增大了 Worker 进程间的数据同步难度,必须选择 1 个低成本的实现机制,保证每个 Nginx 节点、Worker 进程都持有最新的配置;
基于 etcd watch 机制的配置同步方案管理集群必须依赖中心化的配置,etcd 就是这样一个数据库 。APISIX 没有选择关系型数据库作为配置中心,是因为 etcd 具有以下 2 个优点:
- etcd 采用类 Paxos 的 Raft 协议保障了数据一致性,它是去中心化的分布式数据库,可靠性高于关系数据库;
- etcd 的 watch 机制允许客户端监控某个 key 的变动,即,若类似/nginx/http/upstream 这种 key 的 value 值发生变动,watch 的客户端会立刻收到通知,如下图所示:
文章插图
因此,不同于Orange采用 MySQL、Kong采用 PostgreSQL 作为配置中心(这二者同样是基于 OpenResty 实现的 API Gateway),APISIX 采用了 etcd 作为中心化的配置组件 。
因此,你可以在生产环境的 APISIX 中通过 etcdctl 看到如下的类似配置:
$ etcdctl get "/apisix/upstreams/1"
/apisix/upstreams/1
{"hash_on":"vars","nodes":{"httpbin.org:80":1},"create_time":1627982128,"update_time":1627982128,"scheme":"http","type":"roundrobin","pass_host":"pass","id":"1"}
其中,/apisix 这个前缀可以在 conf/config.yaml 中修改,比如:
etcd:
host:
- "http://127.0.0.1:2379"
prefix: /apisix ## apisix configurations prefix
而 upstreams/1 就等价于 nginx.conf 中的 http { upstream 1 {} }配置 。类似关键字还有/apisix/services/、/apisix/routes/等,不一而足 。
那么,Nginx 是怎样通过 watch 机制获取到 etcd 配置数据变化的呢?有没有新启动一个 agent 进程?它通过 HTTP/1.1 还是 gRPC 与 etcd 通讯的?
ngx.timer.at 定时器APISIX 并没有启动 Nginx 以外的进程与 etcd 通讯 。它实际上是通过 ngx.timer.at 这个定时器实现了 watch 机制 。为了方便对 OpenResty 不太了解的同学,我们先来看看 Nginx 中的定时器是如何实现的,它是 watch 机制实现的基础 。
Nginx 的红黑树定时器Nginx 采用了 epoll + nonblock socket 这种多路复用机制实现事件处理模型,其中每个 worker 进程会循环处理网络 IO 及定时器事件:
//参见Nginx的src/os/unix/ngx_process_cycle.c文件
【前端开发之动态管理Nginx集群的方法】static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
for ( ;; ) {
ngx_process_events_and_timers(cycle);
}
}
// 参见ngx_proc.c文件
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
timer = ngx_event_find_timer();
(void) ngx_process_events(cycle, timer, flags);
ngx_event_process_posted(cycle, &ngx_posted_accept_events);
ngx_event_expire_timers();
ngx_event_process_posted(cycle, &ngx_posted_events);
}
ngx_event_expire_timers 函数会调用所有超时事件的 handler 方法 。事实上,定时器是由红黑树(一种平衡有序二叉树)实现的,其中 key 是每个事件的绝对过期时间 。这样,只要将最小节点与当前时间做比较,就能快速找到过期事件 。
OpenResty 的 Lua 定时器当然,以上 C 函数开发效率很低 。因此,OpenResty 封装了 Lua 接口,通过ngx.timer.at将 ngx_timer_add 这个 C 函数暴露给了 Lua 语言:
//参见OpenResty /ngx_lua-0.10.19/src/ngx_http_lua_timer.c文件
推荐阅读
- win7开机后无法进入系统的处理
- 如何更改电脑的开机密码
- 孩子多大分房睡比较好
- 怎么查询被开通手机号?
- 电脑系统win10忘记开机密码强制重置教程
- 电脑没声音怎么办?电脑没声音了怎么恢复?
- 5 个网站将您的前端技能提升 100 倍
- 红茶中的生物碱,历史上最早的红茶
- 西安全运会开始和结束时间是多久?
- 客服|南宁一公司全是美女,都开奔驰奥迪,警方一查真相不堪入目