基于 ngx_lua 的动态服务路由方案

  第一是如果你 频繁的Reload会有性能损耗;

  第二个是 长时间处于Shutting down的状态, 如果连接里头有长连接,旧的进程会一直处于一个中间进程,这个时间是不定的,就是说你不知道到底什么时候Reload真正完成;

  第三是 进程内缓存失效, 我们会把数据库的一些信息,一些代码全部缓存进本地,这样缓存就全部失效了。

  当然前面三点也不是非常的严重,毕竟reload的操作不是特别的频繁。

  最后一点是 与设计初衷不符, 这也是我们最关心的一点,它设计的初衷是做什么呢?就是方便运维不去影响当前的请求,就相当于我们拿Docker做虚拟机用一样走歪了,走歪了之后最后很可能会碰到很多奇怪的坑,所以当时没有用这个方案。

  2. 内部DNS方案

物联网

  DNS的方案也是比较常用的,比如我把之前是一个IP地址的Server,现在改成一个域名,只要把它解析掉一批IP就好了,这个听起来已经很完美了,而且Consul本身支持DNS,我们也不用维护另外的DNS了,只要把这个ID换成域名就好了。

  这样做的话,我们感觉还不如做Reload,因为首先 多了一层DNS解析时间, 再怎么快都是需要解析时间的,第二个是 有DNS缓存, 这是最主要的原因,因为缓存的存在,没办法立即把一台有问题的机器切掉,如果你要缓解这个问题,就要把缓存设得短一点,但这样解析次数就多了。还有一个就是 端口号会改变, 物理机一般我们会配置同一个端口,在Docker里面也可以这么做,但对于一些对网络不是很敏感的应用,比如说一些强CPU的应用,我们会直接把容器的网络,用桥接的方式连接起来,而这时候端口是随机分配的,可能每个容器分配的都不一样,所以就不行。

  那我们到底想要怎么样呢? 我们想要的非常简单,就是要通过HTTP接口,动态修改Nginx的上游服务列表。 这样的方案我们找了之后发现有一个现成的,叫ngx_http_dyups_module。

  3. Ngx_http_dyups_module

物联网

  它能干什么事情呢?可以通过GET接口查询当前的一些信息;POST可以更新上游;也能通过Ddelete删除上游。

物联网

  上图是一个例子,这个例子有三个请求,也就是发了三个指令:

  第一个,给8080这个服务端口发了请求之后,发现后面根本就没有任何的上游服务,所以它就502了;

  第二个,通过一个Curl的请求把两个服务地址给加进来;

  第三个,重新访问了一下,第三条指令跟第一条指令是一模一样,因为第二条已经把服务加进来了,所以这是一个正常的输出。

  在这个过程里头没有任何的Reload的操作,也没有改配置,它就完成了一个功能。

  这个模块写得非常好,我们用了一段时间,但一段时间后把它下掉了,主要原因不是因为它不好,主要是我们结合了一些自身的情况,发现了一些问题:

  第一, 导致依赖Nginx本身的负载均衡算法。 如果我们内部用Ngx_lua写得比较多,用了这个模块之后,会导致我们非常依赖C模块,也就是自身的一些负载均衡算法,我们有自己特有的需求,比如说本机优先,就是优先访问本机的服务,这样听起来比较奇怪的负载均衡,如果要做这些事情的话,我们就要改C代码。

  第二, 二次开发效率低, C的开发效率远不及Lua。

  第三, 纯lua的方案无法使用, 我们做这样一个方案并不是说我云处理能用就行了,有一个项目能用就行了,做这个方案最好是其他一些项目都可以用。

  造自己的轮子

  基于以上这些原因,我们开始造自己的轮子。

物联网

  这个轮子是这样的,有四个部分:

  第一个部分,是最基础的nginx,我们希望用一些原生的指令和重试的策略;

  第二就是lua的模块;

  第三个是lua_resty_checkups,这是我们lua版的管理模块,实现了动态的upstream管理,这个模块实现了大概30%的功能,而且还有一些主动的健康检查功能,它的代码量大概也就是1500左右,那C模块估计至少有1万行。