Healthcheck

由于服务无法保证永远不会下线,而且下线时不一定能有人员能及时发现,

所以api gateway 一般会引入一个监工 Healthcheck, 像大家每年体检一样定时确认服务是否存活。

这样就可以在上游节点发生故障或者迁移时,将请求代理到健康的节点上,最大程度避免服务不可用的问题。

一般其分为主动检查和被动检查。

主动检查

其一般为使用单独的线程、进程、甚至独立的程序的探针,不断轮休式主动检查服务存活性。

一般支持 HTTP、HTTPS、TCP 三种探针类型, 也就是实际存不存活就是访问大家服务,看能不能得到正常结果。

其判定存活逻辑一般为:当发向健康节点 A 的 N 个连续探针都失败时(取决于如何配置),则该节点将被标记为不健康,不健康的节点将会被 api gateway忽略,无法收到请求;若某个不健康的节点,连续 M 个探针都成功,则该节点将被重新标记为健康,进而可以被代理。

(PS: 一般很多api gateway 为了方便大家使用,程序自带主动检查,所以api gateway 实例很多时,这样主动检查的请求就会过于大量,有些就会独立搭建独立的检查服务,减少请求量级)

被动检查

其一般为根据上游服务返回的情况,来判断对应的上游节点是否健康。相对于主动健康检查,被动健康检查的方式无需发起额外的探针,但是也无法提前感知节点状态,可能会有一定量的失败请求。

同理一般也是发向健康节点 A 的 N 个连续请求都被判定为失败(取决于如何配置),则该节点才被标记为不健康。

实践

由于篇幅关系,这里就只介绍被动检查的实现例子。

更具体实现可以参考 简化的healthcheck 或者 完整的lua-resty-healthcheck (ps: lua-resty-healthcheck 被动检查被标记为不健康之后无法恢复健康状态)

worker_processes  1;        #nginx worker 数量
error_log logs/error.log; #指定错误日志文件路径
events {
worker_connections 1024;
} http {
log_format main '$remote_addr [$time_local] $status $request_time $upstream_status $upstream_addr $upstream_response_time';
access_log logs/access.log main buffer=16384 flush=3; #access_log 文件配置 lua_package_path "$prefix/deps/share/lua/5.1/?.lua;$prefix/deps/share/lua/5.1/?/init.lua;$prefix/?.lua;$prefix/?/init.lua;;./?.lua;/usr/local/openresty/luajit/share/luajit-2.1.0-beta3/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/openresty/luajit/share/lua/5.1/?.lua;/usr/local/openresty/luajit/share/lua/5.1/?/init.lua;";
lua_package_cpath "$prefix/deps/lib64/lua/5.1/?.so;$prefix/deps/lib/lua/5.1/?.so;;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/openresty/luajit/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so;";
# 开启 lua code 缓存
lua_code_cache on;
lua_shared_dict http_healthcheck 20m; upstream nature_upstream {
server 127.0.0.1:6699; #upstream 配置为 hello world 服务 # 一样的balancer
balancer_by_lua_block {
local balancer = require "ngx.balancer"
local upstream = ngx.ctx.api_ctx.upstream
local ok, err = balancer.set_current_peer(upstream.host, upstream.port)
if not ok then
ngx.log(ngx.ERR, "failed to set the current peer: ", err)
return ngx.exit(ngx.ERROR)
end
}
} init_by_lua_block { -- 初始化 lb
local roundrobin = require("resty.roundrobin")
local nodes = {k1 = {host = '127.0.0.1', port = 6698}, k2 = {host = '127.0.0.1', port = 6699}}
local ns = {}
for k, v in pairs(nodes) do
-- 初始化 weight
ns[k] = 1
end
local picker = roundrobin:new(ns) -- 被动检查
local shm_healthcheck = ngx.shared["http_healthcheck"]
local check_healthcheck = function()
for _ in pairs(nodes) do
local k, err = picker:find()
if not k then
return nil, err
end
local node = nodes[k]
-- 检查是否不健康
local status = shm_healthcheck:get(node.host..':'..tostring(node.port))
if not status then
return node
end
end
return nil, 'no health node'
end -- 初始化路由
local radix = require("resty.radixtree")
local r = radix.new({
{paths = {'/aa/d'}, metadata = {
find = check_healthcheck
}},
}) -- 匹配路由
router_match = function()
local p, err = r:match(ngx.var.uri, {})
if err then
log.error(err)
end -- 执行 roundrobin lb 选择
local k, err = p:find()
if not k then
return nil, err
end
return k
end
} server {
#监听端口,若你的8699端口已经被占用,则需要修改
listen 8699 reuseport; location / { # 在access阶段匹配路由
access_by_lua_block {
local upstream = router_match()
if upstream then
ngx.ctx.api_ctx = { upstream = upstream }
else
ngx.exit(404)
end
} proxy_http_version 1.1;
proxy_pass http://nature_upstream; #转发到 upstream # 上游节点 502 记录到不健康列表,这里为了理解简单,失败一次就写入
log_by_lua_block {
local s = ngx.var.upstream_status
if s and s == '502' then
ngx.shared["http_healthcheck"]:incr(ngx.var.upstream_addr, 1, 5)
end
}
}
} #为了大家方便理解和测试,我们引入一个hello world 服务
server {
#监听端口,若你的6699端口已经被占用,则需要修改
listen 6699;
location / {
default_type text/html; content_by_lua_block {
ngx.say("HelloWorld")
}
}
}
}

启动服务并测试

$ openresty -p ~/openresty-test -c openresty.conf #启动
$ curl --request GET 'http://127.0.0.1:8699/aa/d' #第一次
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
</body>
</html>
$ curl --request GET 'http://127.0.0.1:8699/aa/d' #第二次
HelloWorld
$ curl --request GET 'http://127.0.0.1:8699/aa/d' #第三次
HelloWorld
$ curl --request GET 'http://127.0.0.1:8699/aa/d' #第四次
HelloWorld

可以看到不可访问的服务节点只被访问了一次,后续都到了健康的节点上

目录

构建api gateway之 健康检查的更多相关文章

  1. [转载] 构建微服务:使用API Gateway

    原文: http://mp.weixin.qq.com/s?__biz=MzA5OTAyNzQ2OA==&mid=206889381&idx=1&sn=478ccb35294c ...

  2. docker 构建带健康检查的redis镜像

    =============================================== 2018/11/5_第1次修改                       ccb_warlock == ...

  3. 架构模式: 健康检查API

    架构模式: 健康检查API 上下文 您已应用微服务架构模式.有时,服务实例可能无法处理仍在运行的请求.例如,它可能已用完数据库连接.发生这种情况时,监控系统应生成警报.此外,负载平衡器或服务注册表不应 ...

  4. 0601-Zuul构建API Gateway-API gateway简介、基础使用、路由配置、负载配置

    一.API Gateway简介 参看:http://www.cnblogs.com/bjlhx/p/8794437.html 二.zuul简介[路由器和过滤器:Zuul] 在微服务架构的组成部分进行路 ...

  5. Amazon API Gateway Importer整合过程小结

    (1)需要将swagger json转换成amazon api gateway 所需要的格式(根据Method Request中 Request PathsURL Query String Param ...

  6. 聊聊 API Gateway 和 Netflix Zuul

    最近参与了公司 API Gateway 的搭建工作,技术选型是 Netflix Zuul,主要聊一聊其中的一些心得和体会. 本文主要是介绍使用 Zuul 且在不强制使用其他 Neflix OSS 组件 ...

  7. 几种部署Goku API Gateway的方式,最快一分钟可使用上网关

    本文将介绍几种部署Goku API Gateway的方式,最快一分钟可使用上为网关,详情请看全文. 什么是Goku API Gateway? Goku API Gateway (中文名:悟空 API ...

  8. 使用AWS Lambda,API Gateway和S3 Storage快速调整图片大小

    https://www.obytes.com/blog/2019/image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-stor ...

  9. Openshift中Pod的SpringBoot2健康检查

    Openshift中Pod的SpringBoot2应用程序健康检查 1. 准备测试的SpringBoot工程, 需要Java 8 JDK or greater and Maven 3.3.x or g ...

  10. 关于kong | API Gateway

    目录 为什么需要 API 网关(more) kong的概念 为什么使用Kong Kong 的管理方式 高可扩展性的背后-插件机制 [前言]: Kong是一个云原生,高效,可扩展的分布式 API 网关. ...

随机推荐

  1. C. 连锁商店(状压dp)

    C. 连锁商店 time limit per test 1 second memory limit per test 512 megabytes input standard input output ...

  2. PHY驱动调试之 --- MDIO/MDC接口22号和45号条款(一)

    最近在调试一款Phy的驱动,从没有任何头绪到略有了解经历了太多的痛苦,于是决定写这个系列篇记录一下.特别感谢无数优秀的博主无私奉献很多优秀的博文给予了我很大的帮助.在这个系列篇中,我也会转载部分优秀的 ...

  3. i春秋Login

    打开是个很普通的登录网页 查看源码看看有没有东西 找到绿色的提示,可能是账号密码,试试 成功进来了,再右键源码,没东西...抓包试试,传repeater里go一下 发现一个奇怪的变量,在request ...

  4. linux sublime-text ctrl+shift+b 快捷键失效问题解决

    解决办法 由于fcitx拦截了这个ctrl+shift+b 这个快捷键,所以取消即可 点击全局配置里面高级选项,然后找到ctrl+shift+b这个快捷键,点击后,按esc就可以将快捷键设置为空,不过 ...

  5. 解决"raise EnvironmentError("%s not found" % (_mysql_config_path,)) OSError: mysql_config not found"报错

    redhat系(如centos) yum install mysql-devel debain系(如ubuntu) sudo apt-get install libmysqlclient-dev

  6. <七>理解多态

    理解多态 多种多样的形态(静态多态,动态多态) 静态多态(编译时期) 1:函数重载 bool comparet(int ,int); bool compare(double,double); comp ...

  7. Day31面向对象之魔法方法

    Day31面向对象之魔法方法 类的常用魔法方法如下 序号 双下方法 触发条件 1 init 对象添加独有数据的时候自动触发 2 str 对象被执行打印操作的时候自动触发 3 call 对象加括号调用的 ...

  8. On Java 8读书笔记

    第一章 什么是对象 1.1 抽象的历程 "对象":问题空间中的元素及其解决方案空间中的具体呈现. 理念即是通过添加各种新的对象,可以将程序改编为一种描述问题的语言. 对象是具有状态 ...

  9. this关键字在JAVA和JS中的异同

    this在JS中的用法 由于js中this 是在运行期进行绑定的,所以js中的 this 可以是全局对象.当前对象或者任意对象,这完全取决于函数的调用方式.JavaScript 中函数的调用有以下几种 ...

  10. JavaScript入门⑦-DOM操作大全

    JavaScript入门系列目录 JavaScript入门①-基础知识筑基 JavaScript入门②-函数(1)基础{浅出} JavaScript入门③-函数(2)原理{深入}执行上下文 JavaS ...