openresty+redis+lua 简单实现灰度发布

利用openresty+redis+lua实现灰度发布

为什么需要?

Nginx去开发一个官方自带了非常多的核心模块再加上第三方的模块能够满足我们大部分的业务需要,但是业务的需求、业务的场景变化需要添加些额外的功能,如果自己nginx模块相对来说比较笨重,我们可以使用lua脚本直接内嵌到nginx当中实现一些业务逻辑,完成一些特殊的功能需求。

什么是lua?
Lua并发,比起回调机制的并发来说代码更容易编写和理解,排查问题也会容易是一种轻量级、可嵌入式的脚本语言,这样可以非常容易的嵌入到其他语言中使用。另外Lua提供了协程并发,即以同步调用的方式进行异步执行,从而实现

什么是ngx_lua?
ngx_lua个Web容器;这样开发人员就可以使用是Nginx的一个模块,将Lua嵌入到LuaNginx语言开发高性能中,从而可以使用Web应用了。Lua来编写脚本,这样就可以使用\ Lua编写应用脚本,部署到Nginx中运行,即Nginx变成了一

场景
理论上可以使用到几千行。目前见到的一些应用场景:ngx_lua开发各种复杂的web应用,不过Lua是一种脚本/动态语言,不适合业务逻辑比较重的场景,适合小巧的应用场景,代码行数保持在几十行

web京东的列表页应用:会进行一些业务逻辑处理,甚至进行耗/商品详情页; CPU的模板渲染,一般流程:mysql/redis/http获取数据、业务处理、产生JSON/XML/模板渲染内容,比如
接入网关:实现如数据校验前置、缓存前置、数据过滤、正在开发的无线网关、单品页统一服务、实时价格、动态服务;API请求聚合、AB测试、灰度发布、降级、监控等功能,比如京东的交易大Nginx节点、无线部门
Web防火墙:可以进行IP/URL/UserAgent/Referer黑名单、限流等功能;
缓存服务器:可以对响应内容进行缓存,减少到后端的请求,从而提升性能;

一、安装Lua模块

1、安装lua

wget http://luajit.org/download/LuaJIT-2.0.5.tar.gz tar -zxvf LuaJIT-2.0.5.tar.gz
cd LuaJIT-2.0.5 make && make install PREFIX=/usr/local/LuaJIT

2、/etc/profile 文件中加入环境变量

装export LUAJIT_LIB=/usr/local/LuaJIT/libnginx的lua模块时会报错很早不到luajit 库 source /etc/profile^ export LUAJIT_INC=export LUAJIT_LIB=/usr/local/LuaJIT/lib立刻生效,如果ngixn安装的时候不生效需要重启服务器^ #路径是上面luajit实际安装路径,路径错误安

3、下载ngx_devel_kit模块
wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
NDK减轻第三方模块开发的代码量。(nginx development kit)模块是一个拓展nginx服务器核心功能的模块,第三方模块开发可以基于它来快速实现。 NDK提供函数和宏处理一些基本任务,

4、下载lua-nginx-module模块

wget https://github.com/openresty/lua-nginx-module/archive/v0.10.9rc7.tar.gz
lua-nginx-module 模块使nginx中能直接运行lua

5、再次编译nginx
增加这两个模块

--add-module=/root/download/lua-nginx-module-0.10.9rc7--add-module=/root/download/ngx_devel_kit-0.3.
并且在编译的时候指定动态链接库位置/usr/local/LuaJIT/lib 为你安装lua脚本的位置
./configure --with-ld-opt="-Wl,-rpath,/usr/local/LuaJIT/lib" --with-http_gzip_static_module --with-file-aio --add-module=/root/course/nginx-upsync-module-2.1.0 --with-threads --add-module=/root/course/ngx_cache_purge-2.3 --add-
module=/root/course/lua-nginx-module-0.10.9rc7 --add-module=/root/course/ngx_devel_kit-0.3.

二、ngx_lua入门

所有阶段都会运行的;另外指令可以在ngx_lua^ 属于 nginx的一部分,它的执行指令都包含在http、server、server ifnginx的^11 、个步骤之中了,相应的处理阶段可以做插入式处理,即可插拔式架构,不过location、location if几个范围进行配置: ngx_lua并不是

指令指令 所处处理阶段所处处理阶段 使用范围使用范围 解释解释
init_by_luainit_by_lua_file loading-config http nginx Master 块 进程加载配置时执行;通常用于初始化全局配置/预加载Lua模
init_worker_by_luainit_worker_by_lua_file starting-worker http 每个在init_by_luaNginx Worker之后调用;进程启动时调用的计时器,如果 通常用于定时拉取配置/数据,或者后端服务的健Master进程不允许则只会
康检查
set_by_luaset_by_lua_file rewrite server,serverif,location,location if 设置到非常快;nginx变量,可以实现复杂的赋值逻辑;此处是阻塞的,Lua代码要做
rewrite_by_luarewrite_by_lua_file rewritetail http,server,location,locationif rewrite阶段处理,可以实现复杂的转发/重定向逻辑;
access_by_luaaccess_by_lua_file accesstail http,server,location,locationif 请求访问阶段处理,用于访问控制
content_by_luacontent_by_lua_file content location,location if 内容处理器,接收请求处理并输出响应
header_filter_by_luaheader_filter_by_lua_file output-header-
filter
httplocation if,server,location, 设置header和cookie
body_filter_by_luabody_filter_by_lua_file output-body-
filter
httplocation if,server,location, 对响应数据进行过滤,比如截断、替换。
log log阶段处理,比如记录访问量/统计平均响应时间
log_by_lualog_by_lua_file httplocation if,server,location,

nginx-lua 部分api说明
参数参数 说明说明
ngx.arg 指令参数,如跟在content_by_lua_file后面的参数
ngx.var 变量,ngx.var.VARIABLE引用某个变量
ngx.ctx 请求的lua上下文
ngx.header 响应头,ngx.header.HEADER引用某个头
ngx.status 响应码
API 说明
ngx.log 输出到error.log
print 等价于 ngx.log(ngx.NOTICE, ...)
ngx.send_headers 发送响应头
ngx.headers_sent 响应头是否已发送
ngx.resp.get_headers 获取响应头
ngx.timer.at 注册定时器事件
ngx.is_subrequest 当前请求是否是子请求
ngx.location.capture 发布一个子请求
ngx.location.capture_multi 发布多个子请求
ngx.print 输出响应
ngx.say 输出响应,自动添加'\n'
ngx.flush 刷新响应
ngx.exit 结束请求
ngx.sleep 无阻塞的休眠(使用定时器实现)
ngx.on_abort 注册client断开请求时的回调函数
ngx.req.start_time 请求的开始时间
ngx.req.http_version 请求的HTTP版本号
ngx.req.raw_header 请求头(包括请求行)

ngx.req.get_method 请求方法
ngx.req.set_method 请求方法重载
ngx.req.set_uri 请求URL重写
ngx.req.get_uri_args 获取请求参数
ngx.req.get_post_args 获取请求表单
ngx.req.get_headers 获取请求头
ngx.escape_uri 字符串的url编码
ngx.unescape_uri 字符串url解码
ngx.encode_args 将table编码为一个参数字符串
ngx.decode_args 将参数字符串编码为一个table
ngx.encode_base64 字符串的base64编码
ngx.decode_base64 字符串的base64解码
ngx.crc32_short 字符串的crs32_short哈希
ngx.crc32_long 字符串的crs32_long哈希
ngx.hmac_sha1 字符串的hmac_sha1哈希
ngx.md5 返回 16 进制MD
ngx.md5_bin 返回 2 进制MD
ngx.sha1_bin 返回 2 进制sha1哈希值
ngx.quote_sql_str SQL语句转义
ngx.today 返回当前日期
ngx.time 返回UNIX时间戳
ngx.now 返回当前时间
ngx.update_time 刷新时间后再返回
ngx.cookie_time 返回的时间可用于cookie值
ngx.http_time 返回的时间可用于HTTP头
ngx.parse_http_time 解析HTTP头的时间

相应的api在这里可以找到

https://github.com/openresty/lua-nginx-module#nginx-api-for-lua

openresty+redis+lua实现灰度发布

grays.lua

-- 引入redis的客户端
local redis=require "resty.redis"

-- 获取客户端的ip地址
--ngx.req.get_headers()
local_ip = ngx.var.remote_addr

--记录日志
if local_ip == nil then
    ngx.log(ngx.ERR,'IP-----',local_ip)
end

ngx.log(ngx.ERR,'IP-----',local_ip)
--连接redis,查询当前ip,是否再redis当中
local cache=redis.new()
local ok,err = cache.connect(cache,'127.0.0.1',6379)
if not ok then
  --ngx.log(ngx.ERR,'连接失败',local_ip)
  return
end

local allow_ip=cache:get(local_ip)

if allow_ip == local_ip then
  ngx.exec("@client2")
  return
end

ngx.exec("@client1")
cache:close()

--ngx.header.content_type="text/plain"
--ngx.say(local_ip);


--[[


]]
nginx.conf

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    lua_code_cache off; #关闭代码缓存
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
    #access_log  logs/access.log  main;
    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;
    #gzip  on;
    upstream client1 {
        server 127.0.0.1:9501; #模拟生产服务器
    }
    upstream client2 {
           server 127.0.0.1:9502; #模拟预发布服务器
    }
    #加载lua包
    lua_package_path "/usr/local/openresty/lualib/project/common/?.lua;;";
    server {
        listen       80;
        #server_name  localhost;
        #charset koi8-r;
        set $test "hello world";
        #access_log  logs/host.access.log  main;

        location /test {
            #root   /www;
            #content_by_lua '
            #    ngx.header.content_type="text/plain";
            #    ngx.say (ngx.var.test);
            #';
          content_by_lua_file /usr/local/openresty/lualib/project/grays/gray.lua;
          #index  index.html index.htm;
        }

        location @client1 {
             proxy_pass http://client1;
        }

        location @client2 {
           proxy_pass http://client2;
        }

        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容