openresty健康检查lua-resty-upstream-healthcheck分析和拓展

网关作为流量的入口,承上启下的中枢,对上游节点的健康状态监测是比不可少的;若上游节点异常,网关需要动态摘除此节点,避免流量转到异常节点去,保证服务的稳定。
本文主要分析lua-resty-upstream-healthcheck源码,以及在实际工作的优化应用。
一、背景介绍 目前本人负责的网关是基于orange二次开发的,health_check健康检查功能是基于插件的形式嵌套在网关中。在lua-resty-upstream-healthcheck的基础上根据业务增加了tcp/http检测、异常节点自动摘除、告警等功能。
二、health_check分析 2.1 分析之前 health_check运行在init_worker阶段,也是一个定时任务。在实际项目中,我们用一个nginx work进程来进行健康检查,比如ngx.worker.id() == 0。这种处理方式的好处就是,就算异常情况下当前处理健康检查的nginx进程因为某些原因crash掉了,新fork出来的nginx worker也会集成这个worker id的,只要nginx master进程不挂掉,健康检查都会正常运行。并且最大限度的减少健康检查对网关整体性能的影响,让网关注重做该做的事情。
2.2 代码分析 基本流程图:
openresty健康检查lua-resty-upstream-healthcheck分析和拓展
文章图片

【openresty健康检查lua-resty-upstream-healthcheck分析和拓展】spawn_checker:在health_check模块中,spawn_checker供外部调用,也是代码的入口。这里面主要做了两件事情:初始化健康检查参数和开启定时任务进行check。结合业务需求,增加告警通知配置。同时健康检查摘除的节点是放在内存里面的,当nginx reload时,需要重新检测,重新更新配置,故清空down掉节点信息也放在此函数进行处理。
check :在函数check中,使用pcall进一步处理真正进行健康检查的函数do_check,这样错误代码打印错误后可以继续执行,避免检查检查停止。
do_check: 在do_check中,我们将自己的一些业务逻辑处理放在此处,包括通知告警、自动摘除以及对外提供upstream健康状态api等。这里面自动摘除节点是在内存级别摘除,采用的是dyups模块提供的功能,在此基础上封装了一层api接口,提供健康检查使用。dyups有兴趣的同学可以自行查看,本文不再介绍此内容。对外提供的upstream健康状态,是放到共享内存里面,提供了对外的api接口,直接调用可获取upstream健康状态信息。函数中使用了锁get_lock调用了check_peers方法,是为了防止多个工作进程同时发送测试请求。由于上文中介绍到只使用一个worker进程进行健康检查,所以去除了get_lock。
check_peers:此函数中,针对单个请求和多并发数进行了不同处理。如果请求并发只有一个,直接进行check_peer进行具体的检测;如果是多个请求,则使用ngx.thread.spawn生成新的"light thread"来进行check_peer,多个"light thread"可以同时运行,总的耗时只相当于访问一个源服务器的时间,即使需要访问的源服务器增加,耗时没有太大的变化。
check_peer:此函数才是真正进行健康检查的代码,总结起来包含两步,tcp检测和http检测。

  • tcp检测:tcp检测比较简单,使用ngx.socket.tcp:connect,向上游服务器发送TCP socket,如果失败,说明上游节点挂掉或端口未打开,使用peer_fail对down掉的节点进行处理。
  • http检测:如果tcp检测成功,则进行下一步检测。在spawn_checker初始化参数中,对检测接口重新封装成请求报文,包含请求行和请求头。接下来向上游节点发送请求,读取并解析响应,若响应返回的status不对或者不在配置的正常状态中,表明服务异常,同时去解析header头,判断错误原因。最后用peer_error对异常进行处理。

以上检测没有问题,则进行peer_ok处理。
到此,健康检查处理结束。以下介绍一下peer_fail、peer_error、peer_ok
peer_fail:处理检测节点失败的次数,如果最后失败的次数大于配置的次数,使用set_peer_down_globally在共享内存中记录down掉的upstream以及节点id
peer_error:如果down了打印日志,调用peer_fail进行节点状态处理
peer_ok:记录检测节点成功状态,将之前检测不成功的状态置为0,使用set_peer_down_globally将节点down的状态置为nil

2.3 注意事项
  • 在使用健康检查,如果要用到自动摘除功能,配置上游配置时,proxy_pass后面的域名需要写成nginx变量形式,否则无法生效。注意dyups官方提供的配置说明
比如: proxy_pass http://host.test.com; 需要改写为: set $upstream host.test.com; proxy_pass http://$upstrea;

  • 在对外提供api接口时,提供服务的nginx server配置需要配置白名单,否则可能导致安全漏洞
2.4 相关说明 openresty health_check官方源码地址:https://github.com/openresty/lua-resty-upstream-healthcheck/blob/master/lib/resty/upstream/healthcheck.lua
基于官方代码基础修改后的代码由于商业原因不便透漏,但大概思想如上所述,如有不对之处请指正,互相学习学习。







    推荐阅读