Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >通过 Consul+OpenResty 实现无reload动态负载均衡

通过 Consul+OpenResty 实现无reload动态负载均衡

原创
作者头像
后端老鸟
修改于 2020-05-15 06:31:46
修改于 2020-05-15 06:31:46
2.6K0
举报
文章被收录于专栏:服务端技术服务端技术

【转载请注明出处】:https://cloud.tencent.com/developer/article/1628590

动态Nginx负载均衡的配置,可以通过Consul+Consul-Template方式,但是这种方案有个缺点:每次发现配置变更都需要reload Nginx,而reload是有一定损耗的。而且,如果你需要长连接支持的话,那么当reload时Nginx长连接所在worker进程会进行优雅退出,并当该worker进程上的所有连接都释放时,进程才真正退出(表现为worker进程处于worker process is shutting down)。因此,如果能做到不reload就能动态更改upstream,那么就完美了。

目前的开源解决方法有3种:

1、Tengine的Dyups模块

2、微博的Upsync模块+Consul

3、使用OpenResty的balancer_by_lua。

这里使用的是Consul+OpenResty 来实现动态负载均衡。Consul的安装这里将不再介绍。

OpenResty简介

OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

Lua简介

Lua是一个简洁、轻量、可扩展的程序设计语言,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。

如何做到动态呢?

正常使用Nginx作为API网关, 需要如下配置, 当加后端实例时, reload一下Nginx, 使其生效

代码语言:txt
AI代码解释
复制
upstream backend {
    server 192.168.0.1;
    server 192.168.0.2;
}

那么只要让upstream变成动态可编程就OK了, 当新增后端实例时, 无需reload, 自动生效。

在OpenResty通过长轮训和版本号及时获取Consul的kv store变化。Consul提供了time_wait和修改版本号概念,如果Consul发现该kv没有变化就会hang住这个请求5分钟,在这5分钟内如果有任何变化都会及时返回结果。通过比较版本号我们就知道是超时了还是kv的确被修改了。

Consul的node和service也支持阻塞查询,相对来说用service更好一点,毕竟支持服务的健康检查。阻塞api和kv一样,加一个index就好了。

OpenResty安装

笔者使用的是MAC,下面重点介绍MAC系统上的操作。

Mac
代码语言:txt
AI代码解释
复制
brew tap openresty/brew
brew install openresty
Linux
代码语言:txt
AI代码解释
复制
sudo yum install yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo

sudo yum install openresty

#命令行工具 resty
sudo yum install openresty-resty

Mac OS系统上安装完之后Nginx的配置文件在目录/usr/local/etc/openresty,启动命令在目录/usr/local/opt/openresty/nginx/sbin

启动
代码语言:txt
AI代码解释
复制
openresty

在浏览器输入localhost

image.png
image.png
代码语言:txt
AI代码解释
复制
openresty -h
image.png
image.png

到这里OpenResty就已经安装好了。

重启

代码语言:txt
AI代码解释
复制
openresty -s reload

安装Lua的包管理器LuaRocks

LuaRocks是Lua模块的包管理器,安装LuaRocks之后可以通过LuaRocks来快速安装Lua的模块,官网地址是https://luarocks.org/

进入OpenResty的安装目录/usr/local/opt/openresty可以看到已经安装了luajit,也是OpenResty默认使用的lua环境。

image.png
image.png

下载LuaRocks安装包http://luarocks.github.io/luarocks/releases/luarocks-3.0.4.tar.gz,解压之后进入LuaRocks源码目录编译安装到OpenResty的安装目录。

代码语言:txt
AI代码解释
复制
./configure --prefix=/usr/local/opt/openresty/luajit --with-lua=/usr/local/opt/openresty/luajit --lua-suffix=luajit --with-lua-include=/usr/local/opt/openresty/luajit/include/luajit-2.1
make build
make install

安装完LuaRocks之后,可以看到LuaRocks的执行命令在目录/usr/local/opt/openresty/luajit

image.png
image.png

安装完LuaRocks之后就可以安装搭建环境需要的依赖包了。

代码语言:txt
AI代码解释
复制
./luarocks install luasocket

修改配置文件

在Consul注册好服务之后通过Consul的http://127.0.0.1:8500/v1/catalog/service/api_tomcat2接口就可以获取到服务的列表。这里不再讲述Consul的服务注册。

在默认配置文件目录/usr/local/etc/openresty创建一个servers文件夹来放新的配置文件,创建lualib文件夹来放lua脚本,修改配置文件nginx.conf,添加include servers/*.conf;

lualib文件夹下创建脚本upstreams.lua

代码语言:txt
AI代码解释
复制
local http = require "socket.http"
local ltn12 = require "ltn12"
local cjson = require "cjson"

local _M = {}

_M._VERSION="0.1"

function _M:update_upstreams()
	local resp = {}

	http.request{
	    url = "http://127.0.0.1:8500/v1/catalog/service/api_tomcat2", sink = ltn12.sink.table(resp)
	}
       
        local resp = table.concat(resp);
	local resp = cjson.decode(resp);

	local upstreams = {}
	for i, v in ipairs(resp) do
     	upstreams[i] = {ip=v.Address, port=v.ServicePort}
   	end
        
  	ngx.shared.upstream_list:set("api_tomcat2", cjson.encode(upstreams))
end

function _M:get_upstreams()
   local upstreams_str = ngx.shared.upstream_list:get("api_tomcat2");
   local tmp_upstreams = cjson.decode(upstreams_str);
   return tmp_upstreams;
end

return _M

过luasockets查询Consul来发现服务,update_upstreams用于更新upstream列表,get_upstreams用于返回upstream列表,此处可以考虑worker进程级别的缓存,减少因为json的反序列化造成的性能开销。

还要注意使用的luasocket是阻塞API,这可能会阻塞我们的服务,使用时要慎重。

创建文件test_openresty.conf

代码语言:txt
AI代码解释
复制
lua_package_path "/usr/local/etc/openresty/lualib/?.lua;;";
lua_package_cpath "/usr/local/etc/openresty/lualib/?.so;;";

lua_shared_dict upstream_list 10m;

# 第一次初始化
init_by_lua_block {
    local upstreams = require "upstreams";
    upstreams.update_upstreams();
}

# 定时拉取配置
init_worker_by_lua_block {
    local upstreams = require "upstreams";
    local handle = nil;

    handle = function ()
        --TODO:控制每次只有一个worker执行
        upstreams.update_upstreams();
        ngx.timer.at(5, handle);
    end
    ngx.timer.at(5, handle);
}

upstream api_server {
    server 0.0.0.1 down; #占位server

    balancer_by_lua_block {
        local balancer = require "ngx.balancer";
        local upstreams = require "upstreams";    
        local tmp_upstreams = upstreams.get_upstreams();
        local ip_port = tmp_upstreams[math.random(1, table.getn(tmp_upstreams))];
        balancer.set_current_peer(ip_port.ip, ip_port.port);
    }
}

server {
    listen       8000;
    server_name  localhost;
    charset utf-8;
    location / {
         proxy_pass http://api_server;
         access_log  /usr/local/etc/openresty/logs/api.log  main;
    }
}

init_worker_by_lua是每个Nginx Worker进程都会执行的代码,所以实际实现时可考虑使用锁机制,保证一次只有一个人处理配置拉取。另外ngx.timer.at是定时轮询,不是走的长轮询,有一定的时延。有个解决方案,是在Nginx上暴露HTTP API,通过主动推送的方式解决。

image.png
image.png

Agent可以长轮询拉取,然后调用HTTPAPI推送到Nginx上,Agent可以部署在Nginx本机或者远程。

对于拉取的配置,除了放在内存里,请考虑在本地文件系统中存储一份,在网络出问题时作为托底。

获取upstream列表,实现自己的负载均衡算法,通过ngx.balancer API进行动态设置本次upstream server。通过balancer_by_lua除可以实现动态负载均衡外,还可以实现个性化负载均衡算法。

可以使用lua-resty-upstream-healthcheck模块进行健康检查,后面会单独介绍。

到这里,其实还有一个问题没有解决掉,虽然upstream中的占位server是下线的,但是nginx在检测upstream列表中server的健康状态的时候是会去检测这个占位server的,最好的方式还是在启动之后把它彻底从upstream列表中给移除掉。

【转载请注明出处】:https://cloud.tencent.com/developer/article/1628590

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
OpenResty 101
初学者在刚开始学习 OpenResty 的时候,肯定要搭建一个环境,通常来说,我们推荐直接使用官方提供的二进制包,比如 CentOS 的话,直接用 yum 安装即可,不过二进制包有一个限制是它的各种编译选项都是固定的,没办法修改,比如现在新版的二进制包缺省开启了 GC64,用来支持大内存,但是目前的火焰图工具并不支持 GC64,报错:
LA0WAN9
2021/12/14
1.1K0
Openresty最佳案例 | 第7篇: 模块开发、OpenResty连接Redis
方志朋
2017/12/29
3K0
OpenResty + Lua + Kafka 实现日志收集系统以及部署过程中遇到的坑
********************* 部署过程 **************************
小勇DW3
2019/08/05
3.4K0
OpenResty + Lua + Kafka 实现日志收集系统以及部署过程中遇到的坑
APISIX架构分析:如何动态管理Nginx集群?
开源版Nginx最为人诟病的就是不具备动态配置、远程API及集群管理的能力,而APISIX作为CNCF毕业的开源七层网关,基于etcd、Lua实现了对Nginx集群的动态管理。
陶辉
2023/10/18
1.1K0
APISIX架构分析:如何动态管理Nginx集群?
并发编程之缓存:OpenResty+lua实现一级缓存
因为缓存属于业务模块,因此需要在cache1000和cache1001两台机器上配置Nginx的缓存,如下图:
一行Java
2022/04/06
1.1K0
并发编程之缓存:OpenResty+lua实现一级缓存
Openresty最佳案例 | 第3篇:Openresty的安装
方志朋
2017/12/29
2.1K0
4 OpenResty & Lua 处理各类Http请求
测试有不同的结果,form-data数据是混乱的,x-www-form-urlencoded才是正常的!
收心
2022/08/24
1.9K0
4 OpenResty & Lua 处理各类Http请求
APISIX架构分析:如何动态管理Nginx集群?
开源版 Nginx 最为人诟病的就是不具备动态配置、远程 API 及集群管理的能力,而 APISIX 作为 CNCF 毕业的开源七层网关,基于 etcd、Lua 实现了对 Nginx 集群的动态管理。
米开朗基杨
2021/12/20
3.3K0
APISIX架构分析:如何动态管理Nginx集群?
Openresty 配合 redis 实现无感知灰度发布系统(基础篇)
Openresty 是基于 Nginx 的一个成熟的网络平台,集成 Nginx 和加强的 LuaJit,许多实用的 Lua 库和一些高品质高性能的第三方 Nginx 模块。
代码宇宙
2023/02/23
1.3K0
每个后端都应该了解的OpenResty入门以及网关安全实战
在官网上对 OpenResty 是这样介绍的(http://openresty.org):
wayn
2023/10/25
1.4K0
每个后端都应该了解的OpenResty入门以及网关安全实战
Nginx学习之Nginx的扩展openresty(三)
worker_processes 1 建议设置成cpu总核心数 epoll . select …
周杰伦本人
2022/10/25
6590
Nginx学习之Nginx的扩展openresty(三)
远程管理openresty的reload
默认情况下,nginx/openresty会启动一个root权限运行的master进程,之后再用指定的普通用户权限启动对应的worker,如果需要对整个openresty进行reload或者重启操作,则必须使用root权限才行,为了解决普通worker的提权问题,openresty提供了一个privileged agent的patch来对这些提权操作进行处理,本文就是利用privileged agent来实现对nginx进程状态的控制,最终来实现远程reload。
用户1260683
2018/10/25
2.7K0
APISIX初探
Apisix 是一个用使用 lua 语言编写的网关控制器,相比官网介绍的 apisix 是一个网关,apisix 的实际用途更像是一个控制器。因为其本身代码不承载流量。apisix 运行于 openresty 之上,openresty 运行于 nginx 之上。
云原生小白
2021/08/13
6K0
APISIX初探
openresty下lua的function定义及调用
/usr/local/openresty/lualib/resty/string.lua
code4it
2018/09/17
2.2K0
Openresty最佳案例 | 第5篇:http和C_json模块
该文章介绍了如何使用OpenResty和lua-resty-http库实现一个HTTP客户端,以发送HTTP请求并处理响应。文章包括以下步骤:下载和安装OpenResty,下载和安装lua-resty-http库,创建一个HTTP客户端,发送GET请求,解析响应,获取响应状态码,获取响应头,获取响应体,处理压缩响应,获取JSON格式的响应,解析JSON响应,以及使用cjson库解析JSON。此外,文章还介绍了如何使用OpenResty的Lua库来处理JSON响应,包括解析JSON对象和数组。通过使用这些技术和库,可以方便地实现一个HTTP客户端,并处理各种类型的响应,包括压缩和未压缩的响应,以及JSON格式的响应。
方志朋
2017/12/29
1.8K0
Lua+OpenResty快速入门
Lua是一种轻量、小巧的脚本语言,用标准C语言编写并以源代码形式开发。设计的目的是为了嵌入到其他应用程序中,从而为应用程序提供灵活的扩展和定制功能。
大忽悠爱学习
2022/05/11
2.3K0
Lua+OpenResty快速入门
Nginx Lua扩展模块
淘宝开发的 ngx_lua 模块通过将 Lua 解释器集成进 Nginx,可以采用 Lua 脚本实现业务逻辑,由于 Lua 的紧凑、快速以及内建协程,所以在保证高并发服务能力的同时极大地降低了业务逻辑实现成本。
用户9615083
2022/12/25
3.7K0
Nginx Lua扩展模块
WAF代码剖析之初识openresty
自从这几年信息安全的大力发展,信息安全的建设是逐步发展起来,作为甲方安全工程师,一个人的安全部,使用开源的WAF部署防御攻击,仿佛是件很平常的事情,但是开源的坏处就是没有人能够及时提供技术支撑,出现问题就只能自己维护,我相信维护WAF的代码和规则是一件很耗费精力的事情,还不如用商用的香。如果没安全预算的朋友,不妨跟我一起走向WAF开发的世界。
FB客服
2020/10/27
8960
WAF代码剖析之初识openresty
OpenResty学习指南(一)
我的博客: https://www.luozhiyun.com/archives/217
luozhiyun
2020/02/18
1.9K0
OpenResty学习指南(一)
在Linux上安装配置OpenResty
OpenResty是一个基于 Nginx 与 Lua 的开源高性能 Web 平台,OpenResty团队为Nginx开发了Lua模块,使得开发者/运维可以使用Lua为OpenResty开发扩展,或者为Nginx定制功能,另外OpenResty团队也内置了很多Lua扩展(JWT、MySQL、Redis等),可以通过OpenResty高效率的开发高性能Web服务
KenTalk
2024/04/28
1.3K0
在Linux上安装配置OpenResty
相关推荐
OpenResty 101
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档