Lua+Nginx+Memcached整合

来源:互联网 发布:企业管理论文选题 知乎 编辑:程序博客网 时间:2024/06/13 16:28


1基本介绍

1.1Lua简介

       Lua是轻量级的脚本语言,由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua主要有三种用户,即适用嵌入到某个程序中的Lua用户,适用Lua解释器程序的用户以及同时使用C和Lua的用户。

       同时Lua作为脚本语言,主要有以下几个特性:

       (1)可拓展性:Lua可拓展性非常卓越,不仅仅是一门编程语言,而已是一种用于构建特定领域语言的工具包。

       (2)简易性:Lua是一种简单、小巧的语言。

       (3)高效性:Lua具有非常高效的实现,性能评估显示Lua是脚本(解释型)语言中运行效率最高的语言。

       (4)可移植性:Lua具有跨平台特性,可以运行在任何平台上。

       Lua主要的运用场景:

       (1)游戏以及游戏插件

       (2)嵌入式程序

       (3)大型程序中的部分模块

1.2Nginx简介

       Nginx是一款高性能的Http和反向代理服务器。Nginx能够选择高效的epoll(Linux2.6内核)、kqueue(FreeBSD)、eventport(Solaris10)作为网络I/O模型,在高连接并发情况下,能够支持50000的并发连接数,而占用的内存、CPU等资源却很低,运行非常稳定。

       Nginx主要有以下特性:

       (1)支持高并发多连接,主要是使用epoll和kqueue作为网络I/O模型,减少了处理请求带来的CPU消耗。

图1.1 使用epoll、kqueue、select等网络模型性能测试对比图

       (2)内存消耗少,Nginx单进程的内存消耗在15M左右,通过10个Nginx进程处理30000并发连接的情况下,内存占用量仅为150M,并且运行速度很快。

       (3)成本低廉,Nginx采用2-clauseBSD-like协议,可以免费使用,并且可以在源代码基础上进行二次开发。

       (4)配置文件非常简单。

       (5)支持Rewrite重写规则,能够根据域名、URL不同,将Http请求分到不同的后端服务器群组。

       (6)内置健康检查功能,在使用Nginx Proxy后端的某台服务器宕机的情况下不影响前端访问。

       (7)节省带宽,支持GZIP压缩。

       (8)稳定性高,支持热部署。

                            图1.2 Nginx与Apache、Lighttpd的对比

2Lua整合Nginx

       Lua整合Nginx主要是通过ngx_lua_module实现的,它把 Lua 解析器内嵌到 Nginx,用来解析并执行Lua 语言编写的网页后台脚本。主要特性如下:

(1)支持Windows和Linux平台。

(2)支持高并发高性能。

(3)HTML网页中内嵌LUA脚本代码,类似于PHP。

(4)支持非阻塞的数据库操作,目前只支持MYSQL。

(5)支持异步的文件IO操作。

(6)支持非阻塞的SOCKETIO操作。

2.1环境搭建

(1)下载ngx_openresty,包中集成Nginx、LuaJIT、ngx_lua以及部分第三方模块

wget  http://openresty.org/download/ngx_openresty-1.7.0.1.tar.gztar zxvf ngx_openresty-1.7.0.1.tar.gzcd ngx_openresty-1.7.2.1

(2)编译、安装ngx_openresty以及下载NDK

./configuremakemake install

编译安装完成后生成openrestry目录

包含luajit、lualib以及nginx三个子目录,其中luajit以及lualib包含后续集成nginx的核心模块。

通常程序有两种运行方式:静态编译与动态直译。静态编译的程序在执行前全部被翻译为机器码,而动态直译执行的则是一句一句边运行边翻译。即时编译(Just-In-TimeCompiler)则混合了这二者,一句一句编译源代码,但是会将翻译过的代码缓存起来以降低性能损耗。

NDK(NginxDevelopment Kit)模块是一个拓展Nginx服务器核心功能的模块,
第三方模块开发可以基于它来快速实现NDK提供函数和宏处理一些基本任务,减轻第三方模块开发的代码量。

wget  https://github.com/simpl/ngx_devel_kit/archive/v0.2.19.tar.gztar -xzvf ngx_devel_kit-0.2.19.tar.gz

(3)重新编译Nginx

wget 'http://nginx.org/download/nginx-1.7.4.tar.gz'tar -xzvf nginx-1.7.4.tar.gzcd nginx-1.7.4/ # 定义luajit环境变量,后续nginx编译,整合Lua后编译、解释以及运行需要用到export LUAJIT_LIB=/path/to/luajit/libexport LUAJIT_INC=/path/to/luajit/include/luajit-2.1 # 可以使用配置lua环境变量替代,但是luajit的效率比lua的效率更高,推荐使用luajit#export LUA_LIB=/path/to/lua/lib#export LUA_INC=/path/to/lua/include # 编译安装nginx,增加ndk以及lua_ngx_module两个模块./configure --prefix=/export/service/nginx \            --add-module=/path/to/ngx_devel_kit \            --add-module=/path/to/lua-nginx-modulemake -j2make install

(4)重启Nginx,重新加载配置文件

kill -HUP `cat /export/servers/nginx/logs/nginx.pid` /export/service/nginx/sbin/nginx -s reload 

(5)测试Lua,配置/export/servers/nginx/conf/nginx.conf

location /lua {    default_type 'text/plain';    # ngx.say方法是ngix_lua_module提供的用于在nginx中集成lua脚本的API  content_by_lua 'ngx.say("hello")';  }  

(6)重新加载nginx配置文件

/export/servers/nginx/nginx –s reload

(7)测试访问lua脚本

通过上面的步骤就完成了Lua与Nginx的集成,同时lua-ngx-module提供了很多API(参考http://wiki.nginx.org/HttpLuaModule#Nginx_API_for_Lua)用于在Nginx配置文件中通过Lua脚本进行调用,通过这些API可以直接访问各种HTTP请求变量,实现内部请求重定向,访问后端数据库服务器(目前支持MySQL),访问后端缓存服务器(Redis以及Memcached)等功能。

2.2Lua脚本的使用

2.2.1Lua脚本的调用

目前在Nginx中操作Lua脚本主要有三种方式,一种是直接在Nginx配置文件中通过content_by_lua指令直接在nginx配置文件中编写lua脚本,如下所示:

location /lua {  # 设置响应的页面解析类型为文本类型  default_type 'text/plain';  content_by_lua '        # 调用ngx.location.capture方法实现请求的内部跳转        local res = ngx.location.capture("/some_other_location")        # 响应状态码为200时,打印响应内容        if res.status == 200 then        ngx.print(res.body)        end';}

最后一种方式是通过set_by_lua指令引用外部的lua脚本,如下所示:

location /inline_concat {   default_type 'text/plain';   # 设置变量a的值为hello   set $a "hello";   # 设置变量b的值为world   set $b "world";   # 使用内联脚本的方式,输出传递到lua脚本中得参数   set_by_lua $res "return ngx.arg[1]..ngx.arg[2]" $a $b;   # 输出 hello world   echo $res;}

另外一种方式是通过set_by_lua指令用内联的方式编写lua脚本,如下所示:

# 设置变量a的值为hello   set $a "hello";   # 设置变量b的值为world   set $b "world";   # 使用调用外部lua脚本的方式,输出传递到lua脚本中得参数   set_by_lua $res conf/concat.lua $a $b;   # 输出 hello world   echo $res;


(1)init_by_lua:2.2.2Lua执行顺序

在nginx重新加载配置文件时,运行里面lua脚本,常用于全局变量的申请。例如lua_shared_dict共享内存的申请,只有当nginx重起后,共享内存数据才清空,这常用于统计。

(2)set_by_lua:

设置一个变量,常用与计算一个逻辑,然后返回结果。该阶段不能运行Output API、ControlAPI、SubrequestAPI、CosocketAPI

(3)rewrite_by_lua:

在access阶段前运行,主要用于rewrite

(4)access_by_lua:

主要用于访问控制,能收集到大部分变量,类似status需要在log阶段才有。这条指令运行于nginx access阶段的末尾,因此总是在 allow 和 deny 这样的指令之后运行,虽然它们同属 access 阶段。

(5)content_by_lua:

阶段是所有请求处理阶段中最为重要的一个,运行在这个阶段的配置指令一般都肩负着生成内容(content)并输出HTTP响应。

(6)header_filter_by_lua:

一般只用于设置Cookie和Headers等。该阶段不能运行Output API、ControlAPI、SubrequestAPI、CosocketAPI

(7)body_filter_by_lua:

一般会在一次请求中被调用多次, 因为这是实现基于 HTTP 1.1 chunked 编码的所谓“流式输出”的。该阶段不能运行Output API、ControlAPI、SubrequestAPI、CosocketAPI

(8)log_by_lua:

该阶段总是运行在请求结束的时候,用于请求的后续操作,如在共享内存中进行统计数据,如果要高精确的数据统计,应该使用body_filter_by_lua。该阶段不能运行Output API、ControlAPI、SubrequestAPI、CosocketAPI

(9)timer

2.2.3Nginx执行顺序

Nginx处理每一个用户请求时,都是按照若干个不同阶段(phase)依次处理的,而不是根据配置文件上的顺序。Nginx 处理请求的过程一共划分为 11 个阶段,按照执行顺序依次是post-read、server-rewrite、find-config、rewrite、post-rewrite、preaccess、access、post-access、try-files、content、log。

(1)post-read:

读取请求内容阶段,Nginx读取并解析完请求头之后就立即开始运行。例如模块 ngx_realip 就在post-read 阶段注册了处理程序,它的功能是迫使 Nginx 认为当前请求的来源地址是指定的某一个请求头的值。

(2)server-rewrite

Server请求地址重写阶段,当 ngx_rewrite 模块的set配置指令直接书写在 server 配置块中时,基本上都是运行在server-rewrite 阶段

(3)find-config

配置查找阶段,这个阶段并不支持 Nginx 模块注册处理程序,而是由 Nginx 核心来完成当前请求与 location 配置块之间的配对工作。

(4)rewrite

Location请求地址重写阶段,当 ngx_rewrite 模块的指令用于location 块中时,便是运行在这个 rewrite 阶段。另外,ngx_set_misc(设置md5、encode_base64等)模块的指令,还有 ngx_lua 模块的set_by_lua 指令和rewrite_by_lua 指令也在此阶段。

(5)post-rewrite

请求地址重写提交阶段,由 Nginx 核心完成rewrite 阶段所要求的“内部跳转”操作,如果rewrite 阶段有此要求的话。

(6)preaccess

访问权限检查准备阶段,标准模块ngx_limit_req 和ngx_limit_zone 就运行在此阶段,前者可以控制请求的访问频度,而后者可以限制访问的并发度。

(7)access

访问权限检查阶段,标准模块ngx_access、第三方模块ngx_auth_request 以及第三方模块 ngx_lua 的access_by_lua 指令就运行在这个阶段。配置指令多是执行访问控制性质的任务,比如检查用户的访问权限,检查用户的来源 IP 地址是否合法

(8)post-access

访问权限检查提交阶段,主要用于配合 access 阶段实现标准ngx_http_core 模块提供的配置指令 satisfy 的功能。satisfy all(与关系)satisfy any(或关系)

(9)try-files

配置项try_files处理阶段,专门用于实现标准配置指令 try_files 的功能,如果前N-1 个参数所对应的文件系统对象都不存在,try-files 阶段就会立即发起“内部跳转”到最后一个参数(即第 N 个参数)所指定的 URI.

(10)content

内容产生阶段,Nginx 的content 阶段是所有请求处理阶段中最为重要的一个,因为运行在这个阶段的配置指令一般都肩负着生成“内容”并输出HTTP 响应的使命。

(11)log

日志模块处理阶段,记录日志。

3Lua+Nginx+Memcached整合

3.1使用场景

假设使用Tomcat作为后端服务器,但是Tomcat对于静态文件以及高并发请求的处理能力较弱。使用Lua+Nginx+Memcached的前端部署方式后,通过Lua去作为前置的控制器,处理模块渲染的请求,决定是模块内容由后端Tomcat服务器渲染还是直接从Memcached中获取。通过这种方式,处理高并发请求交由Nginx进行处理,而Tomcat只处理缓存失效或者没有缓存的模块渲染请求,极大的降低了Tomcat的压力,同时由于Nginx具有优秀的处理高并发的能力,并且内存以及CPU暂用小,并且由于直接在Nginx上操作缓存,能提升请求响应速度,节省带宽,提升用户体验。

3.2Lua操作Memcahed

Lua操作Memcached主要是通过调用lua-ngx-module模块提供的API实现的,主要是通过在nginx.conf文件中加载memcached.lua模块,调用该包提供的API实现对Memcached的操作(操作Redis,MySQL的使用方式一致,只需要加载对应的模块即可)。

# 加载memcached.lua包lua_package_path "/usr/local/openresty/lualib/resty/memcached.lua";location /lua {      # 设置响应文本类型为文本类型      default_type 'text/plain';      # 设置通过lua脚本获取响应内容      content_by_lua '            # 调用resty.memcached模块中得memcached函数            local memcached = require "resty.memcached"            # 初始化memcached            local mem,err = memcached:new()            # 初始化失败则返回,并且打印错误日志到/logs/error.log中            if not mem then                 ngx.say("failed to initiall memcached",err)                 return            end            ngx.say("success to initiall memcached")            # 连接memcached实例,ip:127.0.0.0,port:11211            local ok,err = mem:connect("127.0.0.1",11211)                 # 连接失败则返回,并且打印错误日志到/logs/error.log中                 if not ok then                      ngx.say("failed to connect memcached",err)                      return                 end                 ngx.say("success to connect memcached")                 # 获取请求参数                 local args = ngx.req.get_uri_args()                 local key = "Module-Html-Cache-Key-"                 for key,value in pairs(args) do                        # 获取模块实例参数,组装缓存key                        if key == "instanceId"  then                                key = key .. value                                ngx.say("cache key is",key)                         end                 end                 # 根据缓存key从memcached中获取缓存                 local res, flags, err = mem:get(key)                        # 获取缓存失败则返回,并且打印错误日志到/logs/error.log中                        if err then                                ngx.say("failed to get cache: ", err)                                return                        end                        # 获取缓存为nil(lua中nil表示空),则内部转发请求                        if not res then                                ngx.say("cache not found")      # 通过ngx.location.capture函数转发,ngx.location.capture不能跨server转发      local res = ngx.location.capture("/module/renderModule.html?instanceId=10001")                # 判断响应状态是否为200,200则表示正确响应,设置缓存                if res.status == ngx.HTTP_OK then                       local ok, err = mem:set(key, res.body, 30)                       if not ok then                              # 设置缓存失败则返回,并且打印错误日志到/logs/error.log中                              ngx.say("failed to set cache: ", err)                       end                       # 输出响应内容主体                       ngx.print(res.body)                 end       end   ';}


0 1
原创粉丝点击