OpenResty (Nginx + Lua) 实现流量定向分发
OpenResty(Nginx + Lua)实现流量定向分发
一、概要
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
由Ngnix实现负载均衡的优势此处不再赘述,本篇主要结合实际的业务场景谈下流量的定向分发负载。基于NB-IoT技术的物联网燃气表设备通讯到基站及运营商IoT平台时,多采用COAP或UDP协议,通讯参数到达运营商IoT平台后由平台自动转为HTTP协议回调发出,由于后台的采集抄表系统使用的是TCP服务,因此在运营商IoT平台和采集抄表系统中间增加了一层HTTP转TCP的前置服务,如下图:
不过当设备数量不断增加时,HTTP转TCP前置服务就需要增加负载,而前置服务在与采集抄表系统进行通讯交互时,要建立一条Socket连接通道,一台设备在通讯周期内仅有一个连接,这就为负载问题增加了复杂度,多机负载时,设备进行指令交互就需要保证运营商IoT平台发出的HTTP请求每次都是发到同一台前置服务上。
运营商平台回调通讯时,请求参数中会附带NB设备的device_id,根据设备请求参数的不同,在Nginx中实现特定的负载均衡算法,从而达到定向的流量分发。
二、环境搭建
OpenResty环境搭建
服务器环境:CentOS 7.5 IP:10.200.6.171
1、安装基础依赖组件
yum install -y libreadline-dev libpcre3-dev pcre-devel openssl-devel libssl-dev perl gcc
2、创建目录并下载安装包,完成后解压即可
mkdir -p /usr/local/software
cd /usr/local/software
wget https://openresty.org/download/ngx_openresty-1.9.7.1.tar.gz
tar -zxvf ngx_openresty-1.9.7.1.tar.gz
3、进入解压目录后编译安装
cd ngx_openresty-1.9.7.1
./configure
配置完成后执行完成安装
gmake
gmake install
默认情况下程序会被安装到 /usr/local/openresty 目录,可以使用
./configure --help
查看更多配置选项。
4、运行实例
安装成功后,进入OpenResty目录中可以看到有很多模块,Nginx和Lua都包含其中,进入nginx目录,启动nginx服务试试。
可以看到nginx服务正常运行,但这并不是我们想要达到的效果,nginx可以作为反向代理服务器拦截特定的请求,做负载均衡转发等,但对于特定接口的类似于http之类的接口的精细化配置,直接使用nginx的话,实际是有点麻烦的,也不推荐这种做法。而OpenResty正是基于这些需求集成了像Redis、MySQL、Cache等大量组件,同时无缝整合了nginx和lua开发环境,使得上述的需求场景实现起来更方便更简单。
三、引入 Lua 脚本
下面通过配置OpenResty来实现访问特定的路径,也就是基于nginx做进一步的拦截,实现nginx和lua的开发。
创建一个工作目录:
mkdir -p /home/www
cd /home/www/
mkdir logs/ conf/
logs目录用于存放日志,conf用于存放配置文件。
然后在conf目录下创建 nginx.conf配置文件,配置内容如下(此处我们将html代码直接写入文件中):
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 9000;
location /lua {
default_type text/html;
content_by_lua '
ngx.say("<p>Hello, OpenResty!</p>")
';
}
}
}
启动openresty
cd /home/www
/usr/local/openresty/nginx/sbin/nginx -p `pwd`/ -c conf/nginx.conf
浏览器访问:http://10.200.6.171:3247/lua 页面展示正常
上述我们实现了一个简单的页面打印"hello,OpenResty"功能,下面我们使用OpenResty来实现nginx集群的负载均衡。
准备三台服务器,
10.200.6.171 cache1 - 部署OpenResty
10.200.6.170 cache2 - 部署nginx
10.20.11.72 cache3 - 部署nginx
将cache1作为流量或接口转发的节点,所有需要通过nginx作为代理的请求节点首先经过OpenResty进行分发,cache1统一将流量按照特定的负载均衡算法转到cache2和cache3服务器。
进入conf目录添加配置文件
cd /home/www/conf
vim hello.conf
server {
listen 8181;
server_name _;
location /lua {
default_type 'text/html';
content_by_lua_file /home/www/conf/hello.lua;
}
}
修改nginx.conf文件,添加lua配置环境
vim nginx.conf
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
lua_package_cpath "/usr/local/openresty/lualib/?.so;;";
include hello.conf;
}
添加hello.lua脚本,实现负载均衡算法(此处演示简单的GET请求的负载方法)
vim hello.lua
-- 获取url地址,并取出GET请求的参数
local uri_args = ngx.req.get_uri_args()
local deviceId = uri_args["deviceId"]
-- 配置负载的服务器地址
local host = {"10.20.11.72:12580", "10.200.6.170:12580"}
-- 对参数进行取模计算
local hash = ngx.crc32_long(deviceId)
hash = (hash % 2) + 1
backend = "http://"..host[hash]
local requestBody = "?deviceId="..deviceId
-- 将请求转发到指定的负载地址中
local http = require("resty.http")
local httpc = http.new()
local urlpath = backend..requestBody
local resp, err = httpc:request_uri(urlpath, {
method = "GET",
keepalive = false
})
if not resp then
ngx.say("request error :", err)
return
end
ngx.say(resp.body)
httpc:close()
由于OpenResty默认包中没有"resty.http"依赖,我们需要手动加入
下载lua-resty-http 依赖包,解压后将http.lua和http_header.lua复制到
/usr/local/openresty/lualib/resty
运行 nging -t
检查下配置文件是否正确
/usr/local/openresty/nginx/sbin/nginx -p `pwd`/ -t
运行nginx
/usr/local/openresty/nginx/sbin/nginx -p `pwd`/ -c conf/nginx.conf
打开浏览器输入地址:
http://10.200.6.171:8181/lua?deviceId=2233
修改地址后的参数:
http://10.200.6.171:8181/lua?deviceId=2234
可以看到根据deviceId参数的不同,经负载均衡算法后请求被转发到指定的节点。
四、一致性哈希算法
当然上述的负载算法仅仅只是计算RCR校验和并进行取模,直接使用取模计算效率较低,实际使用不推荐这种方式,这里建议可以使用nginx实现的一致性哈希算法,原理可参考白话解析:一致性哈希算法
下载依赖包 ngx_http_consistent_hash-master.zip
将压缩包上传至 /usr/local/software 目录后解压
unzip ngx_http_consistent_hash-master.zip
完成后进入之前的openresty的解压目录后,重新配置新模块
cd ngx_openresty-1.9.7.1
./configure --add-module=../ngx_http_consistent_hash-master
完成后使用 gmake
进行编译
复制编译好的nginx包到openresty下的nginx目录中,先将原来的可执行文件备份
cd /usr/local/openresty/nginx/sbin
cp nginx nginx.old
复制编译好的nginx可执行文件
cd /usr/local/software/ngx_openresty-1.9.7.1/build/nginx-1.9.7/objs
cp -r nginx /usr/local/openresty/nginx/sbin/
完成后我们开始修改配置文件(此处演示POST请求的负载方案,负载机部署的为实际使用程序)
cd /home/www/conf
vim hello.conf
server {
listen 8181;
server_name _;
location /HWIoT {
set $hashkey "";
set $backendupstream "hashbackend";
rewrite_by_lua_file '/home/www/conf/hello.lua';
proxy_pass http://$backendupstream;
}
}
upstream hashbackend {
consistent_hash $hashkey;
server 10.200.6.170:30235;
server 10.20.11.72:30238;
}
修改hello.lua脚本
vim hello.lua
-- 入参:$hashkey, $backendupstream
-- 如果是一致性hash,会set $hashkey
-- $backendupstream 表示将会采用的upstream
--
-- 获取POST请求的参数
ngx.req.read_body()
local param = ngx.req.get_body_data()
-- 调用cjson解析库取出参数对象中的deviceId参数
local cjson = require("cjson")
local json = cjson.decode(param)
local deviceId = json["deviceId"]
-- 对nginx中的计算HASH值的参数进行赋值
ngx.var.hashkey = deviceId
ngx.var.backendupstream = "hashbackend"
ngx.log(ngx.INFO, "backendupstream=", ngx.var.backendupstream, ", key=", deviceId)
修改后检测一下
cd /home/www
/usr/local/openresty/nginx/sbin/nginx -p `pwd`/ -t
完成后启动,在postman中使用不同的deviceId参数,查看后台日志也被分发到不同的服务器端中。
cd /home/www
/usr/local/openresty/nginx/sbin/nginx -p `pwd`/ -c conf/nginx.conf
本篇OpenResty的定向负载方案就介绍到这儿,OpenResty的强大之处远不至此,需要大家自己去探索了,附录为OpenResty的一些使用案例。
五、附录
#OpenResty(1)评论