构建api gateway之 健康检查
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之 健康检查的更多相关文章
- [转载] 构建微服务:使用API Gateway
原文: http://mp.weixin.qq.com/s?__biz=MzA5OTAyNzQ2OA==&mid=206889381&idx=1&sn=478ccb35294c ...
- docker 构建带健康检查的redis镜像
=============================================== 2018/11/5_第1次修改 ccb_warlock == ...
- 架构模式: 健康检查API
架构模式: 健康检查API 上下文 您已应用微服务架构模式.有时,服务实例可能无法处理仍在运行的请求.例如,它可能已用完数据库连接.发生这种情况时,监控系统应生成警报.此外,负载平衡器或服务注册表不应 ...
- 0601-Zuul构建API Gateway-API gateway简介、基础使用、路由配置、负载配置
一.API Gateway简介 参看:http://www.cnblogs.com/bjlhx/p/8794437.html 二.zuul简介[路由器和过滤器:Zuul] 在微服务架构的组成部分进行路 ...
- Amazon API Gateway Importer整合过程小结
(1)需要将swagger json转换成amazon api gateway 所需要的格式(根据Method Request中 Request PathsURL Query String Param ...
- 聊聊 API Gateway 和 Netflix Zuul
最近参与了公司 API Gateway 的搭建工作,技术选型是 Netflix Zuul,主要聊一聊其中的一些心得和体会. 本文主要是介绍使用 Zuul 且在不强制使用其他 Neflix OSS 组件 ...
- 几种部署Goku API Gateway的方式,最快一分钟可使用上网关
本文将介绍几种部署Goku API Gateway的方式,最快一分钟可使用上为网关,详情请看全文. 什么是Goku API Gateway? Goku API Gateway (中文名:悟空 API ...
- 使用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 ...
- Openshift中Pod的SpringBoot2健康检查
Openshift中Pod的SpringBoot2应用程序健康检查 1. 准备测试的SpringBoot工程, 需要Java 8 JDK or greater and Maven 3.3.x or g ...
- 关于kong | API Gateway
目录 为什么需要 API 网关(more) kong的概念 为什么使用Kong Kong 的管理方式 高可扩展性的背后-插件机制 [前言]: Kong是一个云原生,高效,可扩展的分布式 API 网关. ...
随机推荐
- 使用idea创建第一个Mybatis程序及可能遇到的问题
第一个Mybatis程序 思路:搭建环境->导入Mybatis->编写代码->执行 搭建环境 创建数据库 CREATE DATABASE `mybatis` USE `mybatis ...
- 嵌入式-C语言基础:结构体
数组只能存放一种类型的数据,而结构体内可以存放不同类型的数据. #include<stdio.h> #include <string.h> struct Student { c ...
- Go语言核心36讲53
你好,我是郝林. 在2019年的春节来临之际,我恰好也更新完了专栏所有的配图和思考题答案.希望这些可以帮助到你,在新的一年中,祝你新年快乐,Go语言学习之路更加顺利. 基础概念篇 1. Go语言在多个 ...
- Selenium4+Python3系列(七) - Iframe、Select控件、交互式弹出框、执行JS、Cookie操作
前言 突然,想把所有之前未更新的常用Api操作.演示写出来,算是对API的一种完结吧. 下面按照Api模块来做逐一介绍. 一.iframe操作 iframe识别: 语法: driver.switch_ ...
- Windows自带管理工具
exe类notepad 记事本 control 控制面板 mstsc 远程桌面连接explorer 资源管理器 taskmgr 任务管理器resmon 资源监视器 perfmon 性能监视器reged ...
- dfs 序
dfs序可以\(O(1)\)判断书上两个点的从属关系 Tree Queries 题面翻译 给你一个以\(1\)为根的有根树. 每回询问\(k\)个节点\({v_1, v_2 \cdots v_k}\) ...
- Vue 双向绑定数据已经更新,但是视图更新:
使用ElementUI做动态增减表单项的时候,发现数据刷新后视图未更新 Vue包装了数个数组操作函数,使用这些方法操作的数组去,其数据变动时会被vue监测: push() pop() shift() ...
- 使用Python实现多线程、多进程、异步IO的socket通信
多线程实现socket通信服务器端代码 import socket import threading class MyServer(object): def __init__(self): # 初始化 ...
- 【大数据课程】高途课程实践-Day02:利用Hive SQL编写离线数仓实现可视化展示
〇.概述 1.实现内容 使用Hive SQL编程,构造分层离线数仓 并可以通过Quick Bi进行展示 2.过程 (1)数据接⼊到ODS层 (2)进⾏ODS到DWD层数据开发 (3)进⾏ODS到DIM ...
- 自定义RBAC(5)
您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来- 把实体类及Service类都准备好了之后,就可以开始继续写业务代码了.Spring Security的强大之一就在于它的拦截器.那么这里也可以参 ...