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 代码分析

     基本流程图:

     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

基于官方代码基础修改后的代码由于商业原因不便透漏,但大概思想如上所述,如有不对之处请指正,互相学习学习。

 

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Lua-Resty-Checkups是一个基于luaupstream管理和健康检查模块,由又拍云开源。特点:支持周期性upstream服务管理操作支持管理和健康检查支持upstream动态更新有利于加权轮询或哈希平衡支持 Nginx C upstream同步操作可使用级别和键值实现集群使用简介:-- config.lua _M = {} _M.global = {     checkup_timer_interval = 15,     checkup_shd_sync_enable = true,     shd_config_timer_interval = 1, } _M.ups1 = {     cluster = {         {             servers = {                 {host="127.0.0.1", port=4444, weight=10, max_fails=3, fail_timeout=10},             }         },     }, }lua_package_path "/path/to/lua-resty-checkups/lib/checkups/?.lua;/path/to/config.lua;;"; lua_shared_dict state 10m; lua_shared_dict mutex 1m; lua_shared_dict locks 1m; lua_shared_dict config 10m; server {     listen 12350;     return 200 12350; } server {     listen 12351;     return 200 12351; } init_worker_by_lua_block {     local config = require "config"     local checkups = require "resty.checkups.api"     checkups.prepare_checker(config)     checkups.create_checker() } server {     location = /12350 {         proxy_pass http://127.0.0.1:12350/;     }     location = /12351 {         proxy_pass http://127.0.0.1:12351/;     }     location = /t {         content_by_lua_block {             local checkups = require "resty.checkups.api"             local callback = function(host, port)                 local res = ngx.location.capture("/" .. port)                 ngx.say(res.body)                 return 1             end             local ok, err             -- connect to a dead server, no upstream available             ok, err = checkups.ready_ok("ups1", callback)             if err then ngx.say(err) end             -- add server to ups1             ok, err = checkups.update_upstream("ups1", {                     {                         servers = {                             {host="127.0.0.1", port=12350, weight=10, max_fails=3, fail_timeout=10},                         }                     },                 })             if err then ngx.say(err) end             ngx.sleep(1)             ok, err = checkups.ready_ok("ups1", callback)             if err then ngx.say(err) end             ok, err = checkups.ready_ok("ups1", callback)             if err then ngx.say(err) end             -- add server to new upstream             ok, err = checkups.update_upstream("ups2", {                     {                         servers = {                             {host="127.0.0.1", port=12351},                         }                     },                 })             if err then ngx.say(err) end             ngx.sleep(1)             ok, err = checkups.ready_ok("ups2", callback)             if err then ngx.say(err) end             -- add server to ups2, reset rr state             ok, err = checkups.update_upstream("ups2", {                     {                         servers = {                             {host="127.0.0.1", port=12350, weight=10, max_fails=3, fail_timeout=10},                             {host="127.0.0.1", port=12351, weight=10, max_fails=3, fail_timeout=10},                         }                     },                 })             if err then ngx.say(err) end             ngx.sleep(1)             ok, err = checkups.ready_ok("ups2", callback)             if err then ngx.say(err) end             ok, err = checkups.ready_ok("ups2", callback)             if err then ngx.say(err) end     } }Lua 配置示例:_M = {} -- Here is the global part _M.global = {     checkup_timer_interval = 15,     checkup_timer_overtime = 60,     default_heartbeat_enable = true,     checkup_shd_sync_enable = true,     shd_config_timer_interval = 1, } -- The rests parts are cluster configurations _M.redis = {     enable = true,     typ = "redis",     timeout = 2,     read_timeout = 15,     send_timeout = 15,     protected = true,     cluster = {         {   -- level 1             try = 2,             servers = {                 { host = "192.168.0.1", port = 6379, weight=10, max_fails=3, fail_timeout=10 },                 { host = "192.168.0.2", port = 6379, weight=10, max_fails=3, fail_timeout=10 },             }         },         {   -- level 2             servers = {                 { host = "192.168.0.3", port = 6379, weight=10, max_fails=3, fail_timeout=10 },             }         },     }, } _M.api = {     enable = false,     typ = "http",     http_opts = {         query = "GET /status HTTP/1.1\r\nHost: localhost\r\n\r\n",         statuses = {             [500] = false,             [502] = false,             [503] = false,             [504] = false,         },     },     mode = "hash",     cluster = {         dc1 = {             servers = {                 { host = "192.168.1.1", port = 1234, weight=10, max_fails=3, fail_timeout=10 },             }         },         dc2 = {             servers = {                 { host = "192.168.1.2", port = 1234, weight=10, max_fails=3, fail_timeout=10 },             }         }     } } _M.ups_from_nginx = {     timeout = 2,     cluster = {         {   -- level 1             upstream = "api.com",         },         {   -- level 2             upstream = "api.com",             upstream_only_backup = true,         },     }, } return _M

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值