0%

kong源码分析记要

kong的代码运行于nginx的worker进程中。kong对数据的修改会在一个worker中进行,修改后需通知给本机其他worker进程和其他机器上的worker进程。
kong 会将数据库记录缓存起来,这里只是缓存kong自身数据比如插件, service, route 数据, 自定义插件的数据表数据并未缓存, 例如: rate limit 插件的数据也要自己通过kong.cache来缓存, 通过/etc/kong/kong.conf 配置db_update_frequency确定更新route, service, plugins等数据的缓存时间。

kong使用的进程间通信主要方式有:

  1. 本机间通信-共享内存
  2. 跨机器通信-数据库

数据共享

kong 数据存在数据库,同时在缓存中保留一份。

当数据库数据被修改,需发出事件通知其他worker。其他worker收到事件后,删除缓存中对应的数据。

事件

1.本地事件: 通知本机上的worker, 由 lua-resty-worker-events 来处理

2.集群事件: 通知在多台机器上的worker, 由 cluster_events.lua 来处理

本地事件(共享内存)
  本地事件: 通过共享内存实现。

​ kong实现了一套基于nginx共享内存的事件发布-订阅机制,源码见仓库https://github.com/Kong/lua-resty-worker-events

​ 该包提供post_local方法在worker进程内进行事件发布
​ 提供post方法在本机worker进程间进行事件发布
​ 这2个方法需要指定source, event来确定事件源。
  kong的数据访问层dao.lua封装了insert、update和delete三个对数据操作的方法。

​ 这三个方法分别会使用post_local发出
source为: dao:crud
event为insert、delete、update 事件。

事件的数据格式如下:

1
2
3
4
5
{
schema = self.schema, --表名
operation = "create", --操作类型
entity = res, --数据
}

​ worker进程启动的时候会在init_worker阶段注册这些事件的订阅方法,见/usr/local/share/lua/5.1/kong/runloop/handler.lua:worker_events.register。

​ 订阅方法中把所有的dao:crud事件按表名称使用post_local再进行分发。

集群事件(数据库)

​ 集群事件通过数据库实现。数据库表cluster_events存放用于集群间分发的事件.

  Column   |           Type           | Collation | Nullable | Default 
```

-----------+--------------------------+-----------+----------+---------
 id        | uuid                     |           | not null | 
 node_id   | uuid                     |           | not null | 
 at        | timestamp with time zone |           | not null | 
 nbf       | timestamp with time zone |           |          | 
 expire_at | timestamp with time zone |           | not null | 
 channel   | text                     |           |          | 
 data      | text                     |           |          | 

channel的类型有:

  • invalidations, 表示路由规则、插件配置的变更
  • balancer:targets, 表示负载均衡的targets列表发生变更
  • balancer:upstreams, 表示upstream对象发生变更
  • balancer:post_health, 表示target的健康状态发生变更。由于被动健康检查拉出实例后,kong不会在对该实例进行自动拉入,需要通过该事件来拉入实例。

./runloop/handler.lua

​ 调用 cluster_events.lua 代码中的 _M:broadcast(channel, data, nbf)方法会往cluster_events表中新增一条记录。
​ 在init_worker阶段通过调用cluster_events:subscribe开启一个定时器,定时查询出cluster_events表中新增的记录。注意: 本机只会有一个worker进程会对数据库进行查询(通过加锁实现,代码见cluster_events:get_lock),查询出来后再通过共享内存的方式通知给本机其他worker.
​ /etc/kong/kong.conf#db_update_frequency 配置参数db_update_frequency 用于确定查询数据库的间隔. 数据范围根据at字段是否落在(起始时间, 结束时间]确定。起始时间第一次设置在init_worker阶段,调用ngx.now()获取当前时间(精确到毫秒)并放入key为cluster_events:at的共享内存中。之后抢到锁的worker会从共享内存中取出该时间,该时间需要减去db_update_propagation + 0.001来确定起始时间,以防止事件丢失。配置参数db_update_propagation默认为0。结束时间取ngx.now()的值。查询成功后会把结束时间覆盖之前的起始时间,并把该事件分发到本机的其他worker。对于设置了nbf的事件,kong如果发现还没到生效时间,就会通过ngx.timer设置一个定时器延后分发该事件.

详见: /usr/local/share/lua/5.1/kong/init.lua 的 Kong.init_worker() 中:

      local cluster_events, err = kong_cluster_events.new {
              db                      = kong.db,
            poll_interval           = kong.configuration.db_update_frequency,
            poll_offset             = kong.configuration.db_update_propagation, 
      }

Kong 通过这两个核心组件来完成集群中各个节点的状态更新。多个 Kong 节点连接到相同数据库时,便构建起了一个可以动态水平扩展的集群。

KONG 需关注参数:

kong_db_cache 设置kong缓存大小, 用于缓存数据库数据
lua_max_running_timers 4096;
lua_max_pending_timers 16384;

lua_code_cache

http://cyukang.com/2017/07/22/kong-intro-4.html

https://ms2008.github.io/2018/06/11/kong-events-cache/

http://cyukang.com/2017/07/22/kong-intro-4.html

OpenResty-Best-Practices 最佳实践

如何设计可扩展的速率限制算法

https://konghq.com/blog/how-to-design-a-scalable-rate-limiting-algorithm/