Chinaunix首页 | 论坛 | 博客
  • 博客访问: 830852
  • 博文数量: 167
  • 博客积分: 7173
  • 博客等级: 少将
  • 技术积分: 1671
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-04 23:07
文章分类

全部博文(167)

文章存档

2018年(1)

2017年(11)

2012年(2)

2011年(27)

2010年(88)

2009年(38)

分类: 系统运维

2017-04-29 20:04:24

现在很多地方都使用nginx或者tengine进行前端的负载均衡,但单纯使用nginx(tengine)不能完全满足日常需求,所以,需要进行适当的功能扩展,在以往只能是nginx c模块开发,这样费时费力还难维护,对于中小型公司简直是一万点伤害呢。幸运的是有大神做了nginx的lua模块,使得nginx(tengine)功能扩展变得简单和易维护。

结合我们现在正在使用的例子,分几篇文章和大家分享一下,nginx(tengine)和lua一起搭配使用的例子。

动态后端
面对场景:
自动化扩容和缩容还有智能化运维一直是我们努力实现的目标,但在传统的nginx后端配置中,修改upstream的server配置是需要reload或者restart。对于应用为长链接、流量大系统,这会影响用户体验。

具体做法是:
1.利用lua中 "lua_shared_dict" 申请共享内存空间,这个内存空间能被nginx(tengine)所有子进程读取;
2.构造API,让其通过调用进行修改共享内存空间的值 ;
3.利用 proxy_pass 可使用变量特性及lua指令 "set_by_lua" 动态修改当前proxy的值达到动态效果。

简单代码例子
1.申请共享内存块dyproxy
lua_shared_dict dyproxy 3m;

2.制造修改功能api,host为key,rip为后端ip
location ~ ^/DYPROXYSET {
default_type 'text/plain';
content_by_lua '
local dip = ngx.shared.dyproxy
local uri_args = ngx.req.get_uri_args()
if uri_args["method"] == "add" then
if uri_args["domain"] == nil then
ngx.say("domain is null")
elseif uri_args["rip"] == nil then
ngx.say("rip is null")
else
dip:set(uri_args["domain"],uri_args["rip"]) //设置domain:rip ,eg: xx.pcauto.com.cn:192.168.10.1|192.168.10.2
ngx.say("Set successfully : domain"..uri_args["domain"].."|rip"..uri_args["rip"])
end
elseif uri_args["method"] == "del" then
if uri_args["domain"] == nil then
ngx.say("domain is null")
else
dip:delete(uri_args["domain"])
ngx.say("del successfully")
end
end
';
}

3.使用共享内存的值达到动态变量的效果
location ~ ^/testdyproxy {
default_type 'text/plain';
set_by_lua_file $dybackend conf/dyproxy.lua;
proxy_pass
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 3;
proxy_read_timeout 10;

}

dyproxy.lua:
math.randomseed(tostring(os.time()):reverse():sub(1, 6))
local dip = ngx.shared.dyproxy
backend = dip:get(ngx.var.host)
if backend == nil then
return "192.168.10.208:80"
end
local backendarr = loadstring("return "..ngx.unescape_uri(backend))()
local fm = 10000
nowindex = math.fmod(fm , table.getn(backendarr)) + 1
return backendarr[nowindex] //nowindex简单随机拿出其中一个后端ip


经常使用nginx(tengine)的朋友可能注意到这里有两个问题;就是在后端发生异常情况的时候,后端节点的摘除和共享内存的持久化问题。
这个两个问题点需要存在两个东西,服务健康监控系统和服务节点信息管理系统。
服务健康健康系统:大家业务各有不同,根据情况定造。
服务节点信息管理系统:主要是维护服务注册信息,我们现在lua 内存共享变量是不另外做持久化处理,只是在nginx(tengine)的重启脚本里面加入调用服务节点信息系统步骤,在nginx(tengine)重启完后,进行自动调用服务节点信息管理系统获取信息并写入共享内存中。

总结:经过这样改造,达到不需要reload restart nginx(tengine)情况下,进行服务的扩容和缩容。
-----------------------------------------
-----------------------------------------

以下内容是摘自网上同行统计其它公司或开源模块实现的动态后端介绍:

1. nginx + upsync 模块
这个方案在沙箱试了很久都没有,功能上没有任何问题。但是在线上系统并发超过50k的情况下, reload nginx后几分钟会出现页面无法访问,静态页面的请求都变成下载了,最后导致整个应用crash. 这个应该是upsync的bug导致http response content type类型改变了。没有upsync开发者的帮助这个问题很难解决。

2. 右拍云的slardar
slardar 是基于nginx 1.9.5 然后集成了又拍云自己开发的一些lua module,还有春哥的balancer.lua 新模块 并且结合consul k/v 实现了upstream动态更新
slardar可以通过动态更新consul的k/v存储来添加upstream,添加lua script, 非常灵活。 不过这个需要对nginx的开发流程非常了解,并且熟悉nginx lua代码
github上 目前还没有人提issue和pr,估计只有又拍云自己在用了,目前来说通用性不好(虽然这个技术架构和我们的架构非常契合)

3. tengine + ngx_http_dyups_module
github 上 这个模块已经开源很久el并且merge到了tengine里,说明这个模块已经相当成熟了, 这个模块都是C编写的效率肯定是超过用lua实现的

* ngx_http_dyups_module 提供了可以操作upstream的http restful api, 管理upstream变得非常容易

* 线上系统之前使用的nginx就是tengine, 所以使用这个方案会更容易一些,只需重新编译并加一个dyups模块,原来的配置无需改动

* 现在线上系统的nginx是通过健康监测模块主动check服务的端口号在不在,这个检测时有时间间隔的,线上的配置是

check interval=2000 rise=2 fall=3 timeout=1000;

这个健康监测连续失败3次nginx才会认为后端实例不可用,即6s后,nginx才会把一个有问题的实例屏蔽掉。通过consul service自己的健康监测不需要这么长的时间,几乎是近实时的,dyups可以很快的拿掉有问题的实例,影响小了很多。

4. consul nginx template

*consul template定义nginx配置文件模版,从consul上读取server信息(ip + port),然后更新nginx,并reload nginx. 这些都是consul帮助实现的,不过还是有reload操作。


更多文章,请关注:微信订阅号 轻量运维


阅读(2173) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~