本文是利用上篇文章中介绍的两个openresty的插件来实现发版。
插件如下:
lua-resty-upstream-healthcheck
lua-upstream-nginx-module
OpenResty 1.9.3.2以及之后的版本,都已经内置了这两个插件,所以直接使用就好。
nginx配置
location /upstreams {
default_type text/plain;
content_by_lua_file /usr/local/openresty/nginx/lua/upstream.lua;
}
lua脚本
local cjson = require "cjson"
local hc = require "resty.upstream.healthcheck"
local concat = table.concat
local upstream = require "ngx.upstream"
local get_servers = upstream.get_servers
local get_upstreams = upstream.get_upstreams
local get_primary_peers = upstream.get_primary_peers
local get_backup_peers = upstream.get_backup_peers
local set_peer_down = upstream.set_peer_down
function string:split(sep)
local sep, fields = sep or ":", {}
local pattern = string.format("([^%s]+)", sep)
self:gsub(pattern, function (c) fields[#fields + 1] = c end)
return fields
end
local function get_all_upstream()
local us = get_upstreams()
local upstreams = {}
for _, u in ipairs(us) do
local srvs = get_servers(u)
upstreams[u] = srvs
end
return upstreams
end
local function get_all_peers(upstream_name)
local primary_peers = get_primary_peers(upstream_name)
local backup_peers = get_backup_peers(upstream_name)
local primary_cnt = table.getn(primary_peers)
local backup_cnt = table.getn(backup_peers)
local total = table.getn(primary_peers) + table.getn(backup_peers)
local all_peers = {}
for i = primary_cnt + 1, total do
backup_peers[i - primary_cnt]["backup"] = true
table.insert(primary_peers, i, backup_peers[i - primary_cnt])
end
return primary_peers
end
local function op_server(upstream_name, server_name, op)
local all_peers = get_all_peers(upstream_name)
for i, peer in ipairs(all_peers) do
if peer["name"] == server_name then
target_peer = peer
break
end
end
if target_peer == nil then
ngx.say(cjson.encode({code = "E00001", msg = "error peer name"}))
else
if op == "down" then
down_value = true
else
down_value = false
end
local is_back_up = target_peer["backup"] or false
set_peer_down(upstream_name, is_back_up, target_peer["id"], down_value)
ngx.say(cjson.encode({code = "A00000", msg = "Success"}))
end
end
local http_method = ngx.req.get_method()
local sub_uris = ngx.var.uri:split("/")
if table.getn(sub_uris) == 4 then
op_server(sub_uris[2], sub_uris[3], sub_uris[4])
end
if table.getn(sub_uris) == 3 then
if sub_uris[3] == "primary" then
ngx.say(cjson.encode(get_primary_peers(sub_uris[2])))
else
ngx.say(cjson.encode(get_backup_peers(sub_uris[2])))
end
ngx.exit(ngx.HTTP_OK)
end
if sub_uris[2] then
ngx.say(cjson.encode(get_all_peers(sub_uris[2])))
else
ngx.say(cjson.encode(get_all_upstream()))
end
路径 | 说明 |
---|---|
/upstreams | 获取所有的信息 |
/upstreams/{upstream_name} | 根据名称获取信息 |
/upstreams/{upstream_name}/{server_name} | 根据名称和服务获取 |
/upstreams/{upstream_name}/{server_name}/down | 下线 |
/upstreams/{upstream_name}/{server_name}/up | 上线 |
nginx配置
## 指定共享内存
lua_shared_dict healthcheck 1m;
lua_socket_log_errors off;
## 在worker初始化过程中,启动定时器,进行后端结点的检查
init_worker_by_lua_block {
local hc = require "resty.upstream.healthcheck"
local ok, err = hc.spawn_checker {
-- shm 表示共享内存区的名称,
shm = "healthcheck",
-- type 表示健康检查的类型, HTTP or TCP (目前只支持http)
type = "http",
-- upstream 指定 upstream 配置的名称
upstream = "api",
-- 如果是http类型,指定健康检查发送的请求的URL
http_req = "GET /api/health HTTP/1.0",
-- 请求间隔时间,默认是 1 秒。最小值为 2毫秒
interval = 2000,
-- 请求的超时时间。 默认值为:1000 毫秒
timeout = 5000,
-- 失败多少次后,将节点标记为down。 默认值为 5
fall = 3,
-- 成功多少次后,将节点标记为up。默认值为 2
rise = 2,
-- 返回的http状态码,表示应用正常
valid_statuses = {200, 302},
-- 并发度, 默认值为 1
concurrency = 1,
}
if not ok then
ngx.log(ngx.ERR, "=======> failed to spawn health checker: ", err)
return
end
}
#如果有多个,在这里继续写
# 配置监控检查访问页面(放到server下面)
location /server/status {
access_log off;
default_type text/plain;
content_by_lua_block {
local hc = require "resty.upstream.healthcheck"
ngx.say("Nginx Worker PID: ", ngx.worker.pid())
ngx.print(hc.status_page())
}
}
nginx reload之后访问/server/status就可以看到结果。
APP_START_TIMEOUT=50 # 等待应用启动的时间
APP_PORT=8080 # 应用端口
HEALTH_CHECK_URL=http://127.0.0.1:${APP_PORT}/health # 应用健康检查URL
health_check() {
exptime=0
echo "checking ${HEALTH_CHECK_URL}"
while true
do
status_code=`/usr/bin/curl -L -o /dev/null --connect-timeout 5 -s -w %{http_code} ${HEALTH_CHECK_URL}`
if [ x$status_code != x200 ];then
sleep 1
((exptime++))
echo -n -e "\rWait app to pass health check: $exptime..."
else
break
fi
if [ $exptime -gt ${APP_START_TIMEOUT} ]; then
echo 'app start failed'
exit 1
fi
done
echo "check ${HEALTH_CHECK_URL} success"
}
UPSTREAM_NAME=api
SERVER_NAME=172.31.164.48:8081
ONLINE_OFFLINE_WAIT_TIME=6 # 实例上下线的等待时间
OFFLINE_URL=/upstreams/${UPSTREAM_NAME}/${SERVER_NAME}/down
ONLINE_URL=/upstreams/${UPSTREAM_NAME}/${SERVER_NAME}/up
online() {
status_code=`/usr/bin/curl -L -o /dev/null --connect-timeout 5 -s -w %{http_code} ${ONLINE_URL}`
if [ x$status_code = x200 ];then
sleep ${ONLINE_OFFLINE_WAIT_TIME}
else
echo 'app online failed'
exit 1
fi
}
offline() {
status_code=`/usr/bin/curl -L -o /dev/null --connect-timeout 5 -s -w %{http_code} ${OFF_LINE_URL}`
if [ x$status_code = x200 ];then
sleep ${ONLINE_OFFLINE_WAIT_TIME}
else
echo 'app offline failed'
exit 1
fi
}