Openresty最佳案例 | 第7篇: 模块开发、OpenResty连接Redis

Lua模块开发

在实际的开发过程中,不可能把所有的lua代码写在一个lua文件中,通常的做法将特定功能的放在一个lua文件中,即用lua模块开发。在lualib目录下,默认有以下的lua模块。

lualib/
├── cjson.so
├── ngx
│   ├── balancer.lua
│   ├── ocsp.lua
│   ├── re.lua
│   ├── semaphore.lua
│   ├── ssl
│   │   └── session.lua
│   └── ssl.lua
├── rds
│   └── parser.so
├── redis
│   └── parser.so
└── resty
    ├── aes.lua
    ├── core
    │   ├── base64.lua
    │   ├── base.lua
    │   ├── ctx.lua
    │   ├── exit.lua
    │   ├── hash.lua
    │   ├── misc.lua
    │   ├── regex.lua
    │   ├── request.lua
    │   ├── response.lua
    │   ├── shdict.lua
    │   ├── time.lua
    │   ├── uri.lua
    │   ├── var.lua
    │   └── worker.lua
    ├── core.lua
    ├── dns
    │   └── resolver.lua
    ├── limit
    │   ├── conn.lua
    │   ├── req.lua
    │   └── traffic.lua
    ├── lock.lua
    ├── lrucache
    │   └── pureffi.lua
    ├── lrucache.lua
    ├── md5.lua
    ├── memcached.lua
    ├── mysql.lua
    ├── random.lua
    ├── redis.lua
    ├── sha1.lua
    ├── sha224.lua
    ├── sha256.lua
    ├── sha384.lua
    ├── sha512.lua
    ├── sha.lua
    ├── string.lua
    ├── upload.lua
    ├── upstream
    │   └── healthcheck.lua
    └── websocket
        ├── client.lua
        ├── protocol.lua
        └── server.lua

在使用这些模块之前,需要在nginx的配置文件nginx.conf中的http模块加上以下的配置:

 lua_package_path "/usr/example/lualib/?.lua;;";  #lua 模块  
 lua_package_cpath "/usr/example/lualib/?.so;;";  #c模块   

现在来简单的开发一个lua模块:

vim /usr/example/lualib/module1.lua 

在module1.lua文件加上以下的代码:

local count = 0  
local function hello()  
   count = count + 1  
   ngx.say("count : ", count)  
end  

local _M = {  
   hello = hello  
}  

return _M  

开发时将所有数据做成局部变量/局部函数;通过 _M导出要暴露的函数,实现模块化封装。

在/usr/example/lua目录下创建一个test_module_1.lua 文件,在该文件中引用上面的module1.lua文件。

vim /usr/example/lua/test_module_1.lua 

加上以下代码:

local module1 = require("module1")  

module1.hello() 

通过require(“模块名”)来加载模块,如果是多级目录,则需要通过require(“目录1.目录2.模块名”)加载。

在/user/example/example.conf中加上以下的配置:

location /lua_module_1 {  
    default_type 'text/html';  
    lua_code_cache on;  
    content_by_lua_file /usr/example/lua/test_module_1.lua;  
}

多次在浏览器上访问:http://116.196.177.123/lua_module_1,浏览器显示:

count : 1
count : 2
count : 3

...

安装redis

linux下安装:
cd /usr/servers

$ wget http://download.redis.io/releases/redis-3.2.6.tar.gz
$ tar xzf redis-3.2.6.tar.gz
$ cd redis-3.2.6
$ make

启动redis:

nohup /usr/servers/redis-3.2.6/src/redis-server  /usr/servers/redis-3.2.6/redis.conf &  

查看是否启动:

ps -ef |grep redis

终端显示:

root     20985 14268  0 18:49 pts/0    00:00:00 /usr/servers/redis-3.2.6/src/redis-server 127.0.0.1:6379

可见redis已经启动。

lua连接redis

lua_resty_redis模块地址:https://github.com/openresty/lua-resty-redis

lua-resty-redis - Lua redis client driver for the ngx_lua based on the cosocket API

lua_resty_redis 它是一个基于cosocket API的为ngx_lua模块提供Lua redis客户端的驱动。

创建一个test_redis_basic.lua文件

vim /usr/example/lua/test_redis_basic.lua
 local function close_redis(red)  
    if not red then  
        return  
    end  

    local pool_max_idle_time = 10000 --毫秒  
    local pool_size = 100 --连接池大小  
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)  
    if not ok then  
        ngx.say("set keepalive error : ", err)  
    end  
end    

local redis = require("resty.redis")  


local red = redis:new()  

red:set_timeout(1000)  

local ip = "127.0.0.1"  
local port = 6379  
local ok, err = red:connect(ip, port)  
if not ok then  
    ngx.say("connect to redis error : ", err)  
    return close_redis(red)  
end  

ok, err = red:set("msg", "hello world")  
if not ok then  
    ngx.say("set msg error : ", err)  
    return close_redis(red)  
end  


local resp, err = red:get("msg")  
if not resp then  
    ngx.say("get msg error : ", err)  
    return close_redis(red)  
end  

if resp == ngx.null then  
    resp = ''  
end  
ngx.say("msg : ", resp)  

close_redis(red)  

上面的代码很简单,通过连接池连接Redis,连接上redis后,通过set一对键值对(msg,helloword)到redis中,然后get(msg),并通过ngx.say()返回给浏览器。

vim /usr/example/example.conf,添加以下的配置代码:

location /lua_redis_basic {  
    default_type 'text/html';  
    lua_code_cache on;  
    content_by_lua_file /usr/example/lua/test_redis_basic.lua;  
 }  

浏览器访问:http://116.196.177.123/lua_redis_basic

浏览器显示:

msg : hello world

lua_resty_redis支持所有的redis指令,本身Redis就支持lua语言操作。所以lua_resty_redis模块能够提高所有的redis操作的功能。

在很多时候,Redis是设置了口令的,连接时,如果需要验证口令,需要添加 local res, err = red:auth(“foobared”),示例代码如下:

  local redis = require "resty.redis"
    local red = redis:new()

    red:set_timeout(1000) -- 1 sec

    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.say("failed to connect: ", err)
        return
    end

    local res, err = red:auth("foobared")
    if not res then
        ngx.say("failed to authenticate: ", err)
        return
    end

Openresty最佳案例 | 第6篇:OpenResty连接Mysql

centos 安装mysl

Centos系统下安装mysql,先下载mysql-community-release-el7-5.noarch.rpm,然后通过yum安装,安装过程一直确定【Y】即可。

cd /usr/downloads/

wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm

rpm -ivh mysql-community-release-el7-5.noarch.rpm

yum install mysql-community-server

安装成功后,重启mysql,并进入mysql数据库,给root用户设置一个密码,密码为“123”。

service mysqld restart

mysql -u root -p

set password for root@localhost = password('123'); 

openresty连接mysql

lua-resty-mysql模块的官方文档地址: https://github.com/openresty/lua-resty-mysql

lua-resty-mysql - Lua MySQL client driver for ngx_lua based on the cosocket API

lua-resty-mysql模块是基于cosocket API 为ngx_lua提供的一个Lua MySQL客户端。它保证了100%非阻塞。

vim /usr/example/lua/test_mysql.lua,添加以下的代码:

local function close_db(db)  
    if not db then  
        return  
    end  
    db:close()  
end  

local mysql = require("resty.mysql")  

local db, err = mysql:new()  
if not db then  
    ngx.say("new mysql error : ", err)  
    return  
end  

db:set_timeout(1000)  

local props = {  
    host = "127.0.0.1",  
    port = 3306,  
    database = "mysql",  
    user = "root",  
    password = "123"  
}  

local res, err, errno, sqlstate = db:connect(props)  

if not res then  
   ngx.say("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

local drop_table_sql = "drop table if exists test"  
res, err, errno, sqlstate = db:query(drop_table_sql)  
if not res then  
   ngx.say("drop table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  


local create_table_sql = "create table test(id int primary key auto_increment, ch varchar(100))"  
res, err, errno, sqlstate = db:query(create_table_sql)  
if not res then  
   ngx.say("create table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  


local insert_sql = "insert into test (ch) values('hello')"  
res, err, errno, sqlstate = db:query(insert_sql)  
if not res then  
   ngx.say("insert error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

res, err, errno, sqlstate = db:query(insert_sql)  

ngx.say("insert rows : ", res.affected_rows, " , id : ", res.insert_id, "<br/>")  


local update_sql = "update test set ch = 'hello2' where id =" .. res.insert_id  
res, err, errno, sqlstate = db:query(update_sql)  
if not res then  
   ngx.say("update error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

ngx.say("update rows : ", res.affected_rows, "<br/>")  

local select_sql = "select id, ch from test"  
res, err, errno, sqlstate = db:query(select_sql)  
if not res then  
   ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  


for i, row in ipairs(res) do  
   for name, value in pairs(row) do  
     ngx.say("select row ", i, " : ", name, " = ", value, "<br/>")  
   end  
end  

ngx.say("<br/>")  

local ch_param = ngx.req.get_uri_args()["ch"] or ''  

local query_sql = "select id, ch from test where ch = " .. ngx.quote_sql_str(ch_param)  
res, err, errno, sqlstate = db:query(query_sql)  
if not res then  
   ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

for i, row in ipairs(res) do  
   for name, value in pairs(row) do  
     ngx.say("select row ", i, " : ", name, " = ", value, "<br/>")  
   end  
end  


local delete_sql = "delete from test"  
res, err, errno, sqlstate = db:query(delete_sql)  
if not res then  
   ngx.say("delete error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

ngx.say("delete rows : ", res.affected_rows, "<br/>")  


close_db(db)  

在上面的代码中,展示了基本的创表、插入数据、修改数据、查询数据、删除数据的一些功能。

其中用到的lua-resty-mysql的一些API方法:

  • syntax: db, err = mysql:new() 创建一个mysql数据库连接对象
  • syntax: ok, err = db:connect(options) 尝试远程连接mysql
    • host mysql的主机名
    • port 端口
    • database 数据库名
    • user 用户名
    • password 密码
    • charset 编码
  • syntax: db:set_timeout(time) 设置数据库连接超时时间

  • syntax: ok, err = db:set_keepalive(max_idle_timeout, pool_size) 设置连接池
  • syntax: ok, err = db:close() 关闭数据库
  • syntax: bytes, err = db:send_query(query) 发送查询

lua-resty-mysql的一些关键的API方法,见https://github.com/openresty/lua-resty-mysql#table-of-contents

vim /usr/example/example.conf 在配置文件配置:

location /lua_mysql {
   default_type 'text/html';
   lua_code_cache on;
   content_by_lua_file /usr/example/lua/test_mysql.lua;
 }

浏览器访问http://116.196.177.123/lua_mysql,浏览器显示以下的内容:

insert rows : 1 , id : 2
update rows : 1
select row 1 : ch = hello
select row 1 : id = 1
select row 2 : ch = hello2
select row 2 : id = 2

delete rows : 2

Openresty最佳案例 | 第5篇:http和C_json模块

http客户端

Openresty没有提供默认的Http客户端,需要下载第三方的http客户端。

下载lua-resty-http到lualib目录下,使用以下的命令下载:

cd /usr/example/lualib/resty/  
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua  

wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua  

lua-resty-http模块的地址为https://github.com/pintsized/lua-resty-http

安装成功后,通过require(“resty.http”)引入 lua_http模块,它有以下的api方法:

  • syntax: httpc = http.new() 创建一个 http对象
  • syntax: res, err = httpc:request_uri(uri, params)根据参数获取内容,包括:
    • status 状态码
    • headers 响应头
    • body 响应体

vim /usr/example/lua/test_http.lua,写以下代码:

local http = require("resty.http")  

local httpc = http.new()  

local resp, err = httpc:request_uri("http://s.taobao.com", {  
    method = "GET",  
    path = "/search?q=hello",  
    headers = {  
        ["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36"  
    }  
})  

if not resp then  
    ngx.say("request error :", err)  
    return  
end  


ngx.status = resp.status  


for k, v in pairs(resp.headers) do  
    if k ~= "Transfer-Encoding" and k ~= "Connection" then  
        ngx.header[k] = v  
    end  
end  

ngx.say(resp.body)  

httpc:close()  

vim /usr/example/example.conf 加上以下的配置:

 location /lua_http {
   default_type 'text/html';
   lua_code_cache on;
   content_by_lua_file /usr/example/lua/test_http.lua;
 }

在Nginx的配置文件nginx.conf的http部分,加上以下dns解析:

vim /usr/servers/nginx/conf/nginx.conf
resolver 8.8.8.8;  

浏览器访问:http://116.196.177.123/lua_http,浏览器会显示淘宝的搜索页。

lua_cjson模块

Json是一种常见的数据交换格式,常用于http通信协议和其他数据传输领域。在openresty默认内嵌了lua_cjson模块,用来序列化数据。

lua_cjson模块的地址:https://www.kyne.com.au/~mark/software/lua-cjson-manual.html

它常用的API如下:

  • local cjson = require “cjson” 获取一个cjson对象
  • local str = cjson.encode(obj) obj转换成string
  • local obj = cjson.decode(str) 将string转obj

vim /usr/example/lua/test_cjson.lua,添加以下内容:

local cjson = require("cjson")  


local obj = {  
    id = 1,  
    name = "zhangsan",  
    age = nil,  
    is_male = false,  
    hobby = {"film", "music", "read"}  
}  

local str = cjson.encode(obj)  
ngx.say(str, "<br/>")  


str = '{"hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1,"age":null}'  
local obj = cjson.decode(str)  

ngx.say(obj.age, "<br/>")  
ngx.say(obj.age == nil, "<br/>")  
ngx.say(obj.age == cjson.null, "<br/>")  
ngx.say(obj.hobby[1], "<br/>")  

vim /usr/example/example.conf添加以下内容:

 location ~ /lua_cjson {  
   default_type 'text/html';  
   lua_code_cache on;  
   content_by_lua_file /usr/example/lua/test_cjson.lua;  
 }   

在浏览器上访问http://116.196.177.123/lua_cjson,浏览器显示以下内容:

{"hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1}
null
false
true
film

Openresty最佳案例 | 第4篇:OpenResty常见的api

获取请求参数

vim /usr/example/example.conf
 location /lua_var {
        default_type 'text/plain';
        content_by_lua_block {
         ngx.say(ngx.var.arg_a)
        }
   }

重新加载nginx配置文件: nginx -s reload

在浏览器上访问http://116.196.177.123/lua_var?a=323,浏览器显示:

323

在上述代码中,涉及到了2个api, 一是ngx.say(直接返回请求结果);二是ngx.var,它是获取请求的参数,比如本例子上的?a=323,获取之后,直接输出为请求结果。

获取请求类型

vim /usr/example/example.conf
  location /lua_request{
       default_type 'text/html';
       lua_code_cache off;
       content_by_lua_file  /usr/example/lua/lua_request.lua;
   }

vim /usr/example/lua/lua_request.lua ,添加一下代码:

local arg = ngx.req.get_uri_args()
for k,v in pairs(arg) do
   ngx.say("[GET ] key:", k, " v:", v)
end

ngx.req.read_body() -- 解析 body 参数之前一定要先读取 body
local arg = ngx.req.get_post_args()
for k,v in pairs(arg) do
   ngx.say("[POST] key:", k, " v:", v)
end

在上述例子中有以下的api:

  • ngx.req.get_uri_args 获取在uri上的get类型参数,返回的是一个table类型的数据结构。
  • ngx.req.read_body 读取body,这在解析body之前,一定要先读取body。
  • ngx.req.get_post_args 获取form表单类型的参数,返回结果是一个table类型的数据。
    使用curl模拟请求:
curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’

返回的结果:

[GET ] key:b v:ss
[GET ] key:a v:323
[POST] key:d v:2se3
[POST] key:c v:12w

获取请求头

vim /usr/example/lua/lua_request.lua ,在原有的代码基础上,再添加一下代码:

local headers = ngx.req.get_headers()
ngx.say("headers begin", "<br/>")
ngx.say("Host : ", headers["Host"], "<br/>")
ngx.say("user-agent : ", headers["user-agent"], "<br/>")
ngx.say("user-agent : ", headers.user_agent, "<br/>")
for k,v in pairs(headers) do
    if type(v) == "table" then
        ngx.say(k, " : ", table.concat(v, ","), "<br/>")
    else
        ngx.say(k, " : ", v, "<br/>")
    end
end

重新加载nginx -s reload

使用curl模拟请求:

curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’
[GET ] key:b v:ss
[GET ] key:a v:323
[POST] key:d v:2se3
[POST] key:c v:12w
headers begin<br/>
Host : 116.196.77.157<br/>
user-agent : curl/7.53.0<br/>
user-agent : curl/7.53.0<br/>
host : 116.196.77.157<br/>
content-type : application/x-www-form-urlencoded<br/>
accept : */*<br/>
content-length : 12<br/>
user-agent : curl/7.53.0<br/>

获取http的其他方法

vim /usr/example/lua/lua_request.lua ,在原有的代码基础上,再添加一下代码:

ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>")  
--请求方法  
ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>")  
--原始的请求头内容  
ngx.say("ngx.req.raw_header : ",  ngx.req.raw_header(), "<br/>")  
--请求的body内容体  
ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>")  
ngx.say("<br/>") 

重新加载nginx -s reload

使用curl模拟请求:

curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’
//....
ngx.req.http_version : 1.1<br/>
ngx.req.get_method : POST<br/>
ngx.req.raw_header : POST /lua_request?a=323&b=ss HTTP/1.1
Host: 116.196.77.157
User-Agent: curl/7.53.0
Accept: */*
Content-Length: 12

输出响应

vim /usr/example/example.conf,添加一个location,代码如下:

 location /lua_response{
        default_type 'text/html';
        lua_code_cache off;
        content_by_lua_file /usr/example/lua/lua_response.lua ;
  }

vim /usr/example/lua/lua_response.lua 添加一下代码:

ngx.header.a="1"
ngx.header.b={"a","b"}
ngx.say("hello","</br>")
ngx.print("sss")
return ngx.exit(200)

上述代码中有以下api:

  • ngx.header 向响应头输出内容
  • ngx.say 输出响应体
  • ngx.print输出响应体
  • ngx.exit 指定http状态码退出

使用curl模拟请求, curl ‘http://116.196.177.123/lua_response’ ,获取的响应体如下:

hello
sss

日志输出

在配置文件vim /usr/example/example.conf 加上以下代码:

 location /lua_log{
       default_type 'text/html';
       lua_code_cache off;
       content_by_lua_file  /usr/example/lua/lua_log.lua;
  }

vim /usr/example/lua/lua_log.lua ,加上以下代码:

local log="i'm log"
local num =10
ngx.log(ngx.ERR, "log",log)
ngx.log(ngx.INFO,"num:" ,num)

重新加载配置文件nginx -s reload

curl ‘http://116.196.177.123/lua_log’

打开nginx 的logs目录下的error.log 文件:

tail -fn 1000 /usr/servers/nginx/logs/error.log

可以看到在日志文件中已经输出了日志,这种日志主要用于记录和测试。

日志级别:

  • ngx.STDERR – 标准输出
  • ngx.EMERG – 紧急报错
  • ngx.ALERT – 报警
  • ngx.CRIT – 严重,系统故障,触发运维告警系统
  • ngx.ERR – 错误,业务不可恢复性错误
  • ngx.WARN – 告警,业务中可忽略错误
  • ngx.NOTICE – 提醒,业务比较重要信息
  • ngx.INFO – 信息,业务琐碎日志信息,包含不同情况判断等
  • ngx.DEBUG – 调试

内部调用

vim /usr/example/example.conf 添加以下代码:

location /lua_sum{
      # 只允许内部调用
      internal;
      # 这里做了一个求和运算只是一个例子,可以在这里完成一些数据库、
      # 缓存服务器的操作,达到基础模块和业务逻辑分离目的
      content_by_lua_block {
         local args = ngx.req.get_uri_args()
         ngx.say(tonumber(args.a) + tonumber(args.b))
      }
  }

internal 关键字,表示只允许内部调用。使用curl模拟请求,请求命令如下:

$ curl ‘http://116.196.177.123/lua_sum?a=1&b=2’

由于该loction是一个内部调用的,外部不能返回,最终返回的结果为404,如下:

<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>openresty/1.11.2.4</center>
</body>
</html>

vim /usr/example/example.conf 添加以下代码:

location = /lua_sum_test {
   content_by_lua_block {
      local res = ngx.location.capture("/lua_sum", {args={a=3, b=8}})
      ngx.say("status:", res.status, " response:", res.body)
   }  
}

上述的代码通过ngx.location.capture去调用内部的location,并获得返回结果,最终将结果输出,采用curl模拟请求:

$ curl ‘http://116.196.177.123/lua_sum_test’

返回结果如下:

status:200 response:11

重定向

vim /usr

location /lua_redirect{
    default_type 'text/html';
    content_by_lua_file  /usr/example/lua/lua_redirect.lua;
}  
ngx.redirect("http://www.fangzhipeng.com", 302)

http://116.196.177.123/lua_redirect

共享内存

vim /usr/servers/nginx/cong/nginx.conf

在http模块加上以下:

lua_shared_dict shared_data 1m;
 location /lua_shared_dict{
     default_type 'text/html';
     content_by_lua_file /usr/example/lua/lua_shared_dict.lua;
  }
local shared_data = ngx.shared.shared_data
local i = shared_data:get("i")
if not i then
  i = 1
  shared_data:set("i",i)
end
i = shared_data:incr("i",1)
ngx.say("i:",i)

多次访问 http://116.196.177.123/lua_shared_dict,浏览器打印:

i:1
i:2
i:3
i:4
i:5

OpenResty执行阶段的概念

以下内容来自于《openresty 最佳实践》

未分类

如上图所示,openresty的执行阶段分为

这样我们就可以根据我们的需要,在不同的阶段直接完成大部分典型处理了。

  • set_by_lua* : 流程分支处理判断变量初始化
  • rewrite_by_lua* : 转发、重定向、缓存等功能(例如特定请求代理到外网)
  • access_by_lua* : IP 准入、接口权限等情况集中处理(例如配合 iptable 完成简单防火墙)
  • content_by_lua* : 内容生成
  • header_filter_by_lua* : 响应头部过滤处理(例如添加头部信息)
  • body_filter_by_lua* : 响应体过滤处理(例如完成应答内容统一成大写)

执行阶段概念:

  • log_by_lua* : 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其 他机器)
    实际上我们只使用其中一个阶段
  • content_by_lua* ,也可以完成所有的处理。但这样做,会让 我们的代码比较臃肿,越到后期越发难以维护。把我们的逻辑放在不同阶段,分工明确,代 码独立,后期发力可以有很多有意思的玩法。

Openresty最佳案例 | 第3篇:Openresty的安装

我的服务器为一台全新的centos 7的服务器,所以从头安装openresty,并记录了安装过程中出现的问题,以及解决办法。

1. 首先安装openresty

cd /usr
mkdir servers
mkdir downloads 

yum install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl 

cd /usr/servers

wget https://openresty.org/download/openresty-1.11.2.4.tar.gz
tar -zxvf openresty-1.11.2.4.tar.gz
cd /usr/servers/bunble/LuaJIT-2.1-20170405

安装Lua
make clean && make && make install  

安装过程中出现以下的错误:

gcc: Command not found

2. 安装gcc

yum -y install gcc automake autoconf libtool make

3. 重新make

make clean && make && make install

ln -sf luajit-2.1.0-alpha /usr/local/bin/luajit  

4. 下载ngx_cache_purge模块,该模块用于清理nginx缓存

cd /usr/servers/ngx_openresty–1.11.2.4/bundle 
wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz 
tar -xvf 2.3.tar.gz

5. 下载nginx_upstream_check_module模块,该模块用于ustream健康检查

cd /usr/servers/ngx_openresty-1.11.2.4/bundle 
wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/v0.3.0.tar.gz 
tar -xvf v0.3.0.tar.gz

6. 重新安装opresty

cd /usr/servers/ngx_openresty-1.11.2.4

./configure --prefix=/usr/servers --with-http_realip_module  --with-pcre  --with-luajit --add-module=./bundle/ngx_cache_purge-2.3/ --add-module=./bundle/nginx_upstream_check_module-0.3.0/ -j2 

提示错误,安装pcre库

yum install -y pcre pcre-devel

gcc 安装

安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,则需要安装:

yum install gcc-c++

PCRE pcre-devel 安装

PCRE(Perl Compatible Regular Expressions) 是一个Perl库,包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析正则表达式,所以需要在 linux 上安装 pcre 库,pcre-devel 是使用 pcre 开发的一个二次开发库。nginx也需要此库。命令:

yum install -y pcre pcre-devel

zlib 安装

zlib 库提供了很多种压缩和解压缩的方式, nginx 使用 zlib 对 http 包的内容进行 gzip ,所以需要在 Centos 上安装 zlib 库。

yum install -y zlib zlib-devel

OpenSSL 安装

OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。

nginx 不仅支持 http 协议,还支持 https(即在ssl协议上传输http),所以需要在 Centos 安装 OpenSSL 库。

yum install -y openssl openssl-devel

.重新安装OpenResty

cd /usr/servers/ngx_openresty-1.11.2.4

./configure --prefix=/usr/servers --with-http_realip_module  --with-pcre  --with-luajit --add-module=./bundle/ngx_cache_purge-2.3/ --add-module=./bundle/nginx_upstream_check_module-0.3.0/ -j2 

make && make install  

.启动Nginx

/usr/servers/nginx/sbin/nginx

浏览器访问http://116.196.177.123:

Welcome to OpenResty!

If you see this page, the OpenResty web platform is successfully installed and working. Further configuration is required.

For online documentation and support please refer to openresty.org.

Thank you for flying OpenResty.

安装成功了。

6. 配置nginx

vim /usr/servers/nginx/conf/nginx.conf  

错误提示没有安装vim

yum -y install vim*

1、在http部分添加如下配置

lua模块路径,多个之间”;”分隔,其中”;;”表示默认搜索路径,默认到/usr/servers/nginx下找

lua_package_path “/usr/servers/lualib/?.lua;;”; #lua 模块 
lua_package_cpath “/usr/servers/lualib/?.so;;”; #c模块

2、在nginx.conf中的http部分添加include lua.conf包含此文件片段

Java代码 收藏代码

include lua.conf;

在/usr/server/nginx/conf下

vim lua.conf
#lua.conf  
server {  
    listen       80;  
    server_name  _;  

    location /lua {  
    default_type 'text/html';  
        content_by_lua 'ngx.say("hello world")';  
    } 
}  

7. 环境变量

vim /etc/profile
JAVA_HOME=/usr/local/jdk/jdk1.8.0_144
JRE_HOME=$JAVA_HOME/jre
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
CLASSPATH=:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib/dt.jar
export JAVA_HOME JRE_HOME PATH CLASSPATH
export PATH=$PATH:/usr/servers/nginx/sbin

source /etc/profile

测试:

nginx -t

nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok 
nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful

nginx -s reload

浏览器访问http://116.196.177.123/lua
,浏览器显示:

hello world

8. 将Lua项目化

mkdir /usr/example 
cp -r /usr/servers/lualib/ /usr/example/ 
mkdir /usr/example/lua

cd /usr/example 
vim example.conf
server {  
    listen       80;  
    server_name  _;  

    location /lua {  
        default_type 'text/html';  
        lua_code_cache off;  
        content_by_lua_file /usr/example/lua/test.lua;  
    }  
}
vim /usr/example/lua/test.lua
ngx.say("hello world");  
cd /usr/servers/nginx/conf/
````

vim nginx.conf


http模块:

http {
include mime.types;
default_type application/octet-stream;
lua_package_path “/usr/example/lualib/?.lua;;”; #lua 模块
lua_package_cpath “/usr/example/lualib/?.so;;”; #c模块
include /usr/example/example.conf;
….
….

}


nginx -t

nginx: [alert] lua_code_cache is off; this will hurt performance in /usr/example/example.conf:7
nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful


nginx -s reload 浏览器访问http://116.196.177.123/lua ,

hello world


导出history的所有命令:

在你的账户目录下 输入命令
ls -a
找到 .bash_history
这个就是记录命令文件。
输入命令:
cat .bash_history >> history.txt
“`

Openresty最佳案例 | 第2篇:Lua入门

什么是lua

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。
—摘抄 http://www.runoob.com/lua/lua-tutorial.html

环境搭建

注意: 在上一篇文章中,OpenResty已经有了Lua的环境,这里安装的是单独的Lua环境,用于学习和开发Lua。大多数的电脑是Windowds版本的电脑,Windows版本下载地址http://luaforge.net/projects/luaforwindows/。

Linux和Mac电脑下载地址:http://luajit.org/download.html,安装命令如下:

wget http://luajit.org/download/LuaJIT-2.1.0-beta1.tar.gz

tar -xvf LuaJIT-2.1.0-beta1.tar.gz
cd LuaJIT-2.1.0-beta1
make
sudo make install

使用IDEA开发的同学,可以通过安装插件的形式来集成Lua的环境,插件名为EmmyLua,安装插件后,在Idea的右侧栏就会出现Lua的图标,点击图标,就会出现运行Lua代码的窗口。建议使用该插件,可以免去安装Lua环境的麻烦。

第一个Lua程序

安装好环境后,我采用EmmyLua插件的形式,对Lua的入门语法进行一个简单的讲解。
打开EmmyLua的终端,在终端上输入:

print("hi you")

按ctrl+enter,终端显示:

hi you

Lua基本数据类型

lua的基本数据类型有nil、string、boolean、number、function类型。

nil 类型

nil类似于Java中的null ,表示空值。变量第一次赋值为nil。

local num
print(num)
num=100
print(num)

终端输出:

nil

100

number (数字)

Number 类型用于表示实数,和 Java里面的 double 类型很类似。可以使用数学函数
math.floor(向下取整) 和 math.ceil(向上取整) 进行取整操作。

local order = 3.99
local score = 98.01
print(math.floor(order))
print(math.ceil(score))

输出:

3

99

string 字符串

Lua 中有三种方式表示字符串:
1、使用一对匹配的单引号。例:’hello’。
2、使用一对匹配的双引号。例:”abclua
3.字符串还可以用一种长括号(即[[ ]]) 括起来的方式定义

ocal str1 = 'hello world'
local str2 = "hello lua"
local str3 = [["addname",'hello']]
local str4 = [=[string have a [[]].]=]
print(str1) -->output:hello world
print(str2) -->output:hello lua
print(str3) -->output:"addname",'hello'
print(str4) --

table (表)

Table 类型实现了一种抽象的“关联数组”。“关联数组”是一种具有特殊索引方式的数组,索引通常是字符串(string) 或者 number 类型,但也可以是除 nil 以外的任意类型的值。

local corp = {
    web = "www.google.com", --索引为字符串,key = "web",
                              -- value = "www.google.com"
    telephone = "12345678", --索引为字符串
    staff = {"Jack", "Scott", "Gary"}, --索引为字符串,值也是一个表
    100876, --相当于 [1] = 100876,此时索引为数字
            -- key = 1, value = 100876
    100191, --相当于 [2] = 100191,此时索引为数字
    [10] = 360, --直接把数字索引给出
    ["city"] = "Beijing" --索引为字符串
}

print(corp.web) -->output:www.google.com
print(corp["telephone"]) -->output:12345678
print(corp[2]) -->output:100191
print(corp["city"]) -->output:"Beijing"
print(corp.staff[1]) -->output:Jack
print(corp[10]) -->output:36

function(函数)

在 Lua 中,函数 也是一种数据类型,函数可以存储在变量中,可以通过参数传递给其他函
数,还可以作为其他函数的返回值。

local function foo()
   print("in the function")
   --dosomething()
   local x = 10
   local y = 20
   return x + y
end
local a = foo --把函数赋给变量
print(a())

--output:
in the function
30

表达式

~= 不等于

未分类

  • a and b 如果 a 为 nil,则返回 a,否则返回 b;
  • a or b 如果 a 为 nil,则返回 b,否则返回 a。
local c = nil
local d = 0
local e = 100
print(c and d) -->打印 nil
print(c and e) -->打印 nil
print(d and e) -->打印 100
print(c or d) -->打印 0
print(c or e) -->打印 100
print(not c) -->打印 true
print(not d) --> 打印 false

在 Lua 中连接两个字符串,可以使用操作符“..”(两个点).

print("Hello " .. "World") -->打印 Hello World
print(0 .. 1) -->打印 01

控制语句

单个 if 分支 型

x = 10
if x > 0 then
    print("x is a positive number")
end

两个分支 if-else 型

x = 10
if x > 0 then
    print("x is a positive number")
else
    print("x is a non-positive number")
end

多个分支 if-elseif-else 型:

score = 90
if score == 100 then
    print("Very good!Your score is 100")
elseif score >= 60 then
    print("Congratulations, you have passed it,your score greater or equal to 60")
    --此处可以添加多个elseif
else
    print("Sorry, you do not pass the exam! ")
end

for 控制结构

Lua 提供了一组传统的、小巧的控制结构,包括用于条件判断的 if 用于迭代的 while、repeat
和 for,本章节主要介绍 for 的使用.

for 数字型

for 语句有两种形式:数字 for(numeric for) 和范型 for(generic for) 。
数字型 for 的语法如下:

for var = begin, finish, step do
--body
end

实例1:

for i = 1, 5 do
    print(i)
end
-- output:
1 2 3 4 5

实例2:

for i = 1, 10, 2 do
    print(i)
end
-- output:
1 3 5 7 9

for 泛型

泛型 for 循环通过一个迭代器(iterator) 函数来遍历所有值:

-- 打印数组a的所有值

local a = {"a", "b", "c", "d"}
for i, v in ipairs(a) do
    print("index:", i, " value:", v)
end

-- output:
index: 1 value: a
index: 2 value: b
index: 3 value: c
index: 4 value: d

lua的入门就到这里,因为lua语法虽少,但细节有很多,不可能花很多时间去研究这个。入个门,遇到问题再去查资料就行了。另外需要说明的是本文大部分内容为复制粘贴于OPenResty 最佳实践,感谢原作者的开源电子书,让我获益匪浅。更多内容请参考:

lua入门教程:http://www.runoob.com/lua/lua-tutorial.html

OPenResty 最佳实践: https://moonbingbing.gitbooks.io/openresty-best-practices/content/index.html

Openresty最佳案例 | 第1篇:Nginx介绍

Nginx 简介

Nginx是一个高性能的Web 服务器,同时是一个高效的反向代理服务器,它还是一个IMAP/POP3/SMTP
代理服务器。

由于Nginx采用的是事件驱动的架构,能够处理并发百万级别的tcp连接,高度的模块化设计和自由的BSD许可,使得Nginx有着非常丰富的第三方模块。比如Openresty、API网关Kong。

BSD开源协议是一个给予使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。

Nginx的优点

  • 高并发响应性能非常好,官方Nginx处理静态文件并发5w/s
  • 反向代理性能非常强。(可用于负载均衡)
  • 内存和cpu占用率低。(为Apache的1/5-1/10)
  • 对后端服务有健康检查功能。
  • 支持PHP cgi方式和fastcgi方式。
  • 配置代码简洁且容易上手。

Nginx的安装

Centos系统安装,请参考这里http://www.linuxidc.com/Linux/2016-09/134907.htm。先复制粘贴下它的文章。

1. gcc 安装

安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,则需要安装:

yum install gcc-c++

2. PCRE pcre-devel 安装

PCRE(Perl Compatible Regular Expressions) 是一个Perl库,包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析正则表达式,所以需要在 linux 上安装 pcre 库,pcre-devel 是使用 pcre 开发的一个二次开发库。nginx也需要此库。命令:

yum install -y pcre pcre-devel

3. zlib 安装

zlib 库提供了很多种压缩和解压缩的方式, nginx 使用 zlib 对 http 包的内容进行 gzip ,所以需要在 Centos 上安装 zlib 库。

yum install -y zlib zlib-devel

4. OpenSSL 安装

OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。
nginx 不仅支持 http 协议,还支持 https(即在ssl协议上传输http),所以需要在 Centos 安装 OpenSSL 库。

yum install -y openssl openssl-devel

5. 官网下载

(1). 直接下载.tar.gz安装包,地址:https://nginx.org/en/download.html

(2). 使用wget命令下载(推荐)。

wget -c https://nginx.org/download/nginx-1.10.1.tar.gz

6. 解压

依然是直接命令:

tar -zxvf nginx-1.10.1.tar.gz 
cd nginx-1.10.1

7. 配置

其实在 nginx-1.10.1 版本中你就不需要去配置相关东西,默认就可以了。当然,如果你要自己配置目录也是可以的。
使用默认配置

./configure

8. 编译安装

make 
make install

查找安装路径:

whereis nginx

Nginx的模块组成

Nginx的模块从结构上分为核心模块、基础模块和第三方模块:

  • 核心模块:HTTP模块、EVENT模块和MAIL模块
  • 基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块,
  • 第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块。

Nginx的高并发得益于其采用了epoll模型,与传统的服务器程序架构不同,epoll是linux内核2.6以后才出现的。Nginx采用epoll模型,异步非阻塞,而Apache采用的是select模型。

  • Select特点:select 选择句柄的时候,是遍历所有句柄,也就是说句柄有事件响应时,select需要遍历所有句柄才能获取到哪些句柄有事件通知,因此效率是非常低。
  • epoll的特点:epoll对于句柄事件的选择不是遍历的,是事件响应的,就是句柄上事件来就马上选择出来,不需要遍历整个句柄链表,因此效率非常高。

Nginx常用命令

nginx 环境变量配置:

export PATH=$PATH:/usr/servers/nginx/sbin
  • 查看nginx进程
    ps -ef|grep nginx

  • 启动nginx
    nginx
    启动结果显示nginx的主线程和工作线程,工作线程的数量跟nginx.conf中的配置参数worker_processes有关。

  • 平滑启动nginx
    kill -HUP cat /var/run/nginx.pid
    或者
    nginx -s reload

  • 强制停止nginx
    pkill -9 nginx

  • 检查对nginx.conf文件的修改是否正确
    nginx -t

  • 停止nginx的命令
    nginx -s stop或者pkill nginx

  • 查看nginx的版本信息
    nginx -v

  • 查看完整的nginx的配置信息
    nginx -V

Nginx的配置

通常情况下,Nginx的配置在Ngix的安装目录下的/conf/config.default 文件里,基本配置如下:

worker_process # 表示工作进程的数量,一般设置为cpu的核数

worker_connections # 表示每个工作进程的最大连接数

server{} # 块定义了虚拟主机
    listen # 监听端口
    server_name # 监听域名
    location {} # 是用来为匹配的 URI 进行配置,URI 即语法中的“/uri/”
    location /{} # 匹配任何查询,因为所有请求都以 / 开头
        root # 指定对应uri的资源查找路径,这里html为相对路径,完整路径为
        # /opt/nginx-1.7.7/html/
        index # 指定首页index文件的名称,可以配置多个,以空格分开。如有多
        # 个,按配置顺序查找。

location 常用配置如下:

未分类

Nginx的常用配置非常多,以下内容摘自于布尔教育课件,仅供参考:

#定义Nginx运行的用户和用户组
user  www www;
#启动进程,通常设置成和cpu的数量相等
worker_processes  8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
#为每个进程分配cpu,上例中将8个进程分配到8个cpu,当然可以写多个,或者将一个进程分配到多个cpu。
worker_rlimit_nofile 102400;
#这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打
#开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀
#,所以最好与ulimit -n的值保持一致。

#全局错误日志及PID文件
error_log  /usr/local/nginx/logs/error.log; 
#错误日志定义等级,[ debug | info | notice | warn | error | crit ]
pid        /usr/local/nginx/nginx.pid;

#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀.
#所以建议与ulimit -n的值保持一致。
worker_rlimit_nofile 65535;

#工作模式及连接数上限
events {
    use   epoll;                #epoll是多路复用IO(I/O Multiplexing)中的一种方式,但是仅用于linux2.6以上内核,可以大大提高nginx的性能
    worker_connections  102400; #单个后台worker process进程的最大并发链接数 (最大连接数=连接数*进程数)
    multi_accept on; #尽可能多的接受请求
}
#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
    #设定mime类型,类型由mime.type文件定义
    include       mime.types;
    default_type  application/octet-stream;
    #设定日志格式
    access_log    /usr/local/nginx/log/nginx/access.log;
     sendfile      on;
    #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用必须设为 on
    #如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
    #autoindex  on;  #开启目录列表访问,合适下载服务器,默认关闭。
    tcp_nopush on; #防止网络阻塞
    keepalive_timeout 60;
    #keepalive超时时间,客户端到服务器端的连接持续有效时间,当出现对服务器的后,继请求时,keepalive-timeout功能可避免建立或重新建立连接。
    tcp_nodelay   on; #提高数据的实时响应性
   #开启gzip压缩
   gzip on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 2; #压缩级别大小,最大为9,值越小,压缩后比例越小,CPU处理更快。
    #值越大,消耗CPU比较高。
    gzip_types       text/plain application/x-javascript text/css application/xml;
    gzip_vary on;
    client_max_body_size 10m;      #允许客户端请求的最大单文件字节数
    client_body_buffer_size 128k;  #缓冲区代理缓冲用户端请求的最大字节数,
    proxy_connect_timeout 90;      #nginx跟后端服务器连接超时时间(代理连接超时)
    proxy_send_timeout 90;         #后端服务器数据回传时间(代理发送超时)
    proxy_read_timeout 90;         #连接成功后,后端服务器响应时间(代理接收超时)
    proxy_buffer_size 4k;          #设置代理服务器(nginx)保存用户头信息的缓冲区大小
    proxy_buffers 4 32k;           #proxy_buffers缓冲区,网页平均在32k以下的话,这样设置
    proxy_busy_buffers_size 64k;   #高负荷下缓冲大小(proxy_buffers*2)

    #设定请求缓冲
    large_client_header_buffers  4 4k;
    client_header_buffer_size 4k;
    #客户端请求头部的缓冲区大小,这个可以根据你的系统分页大小来设置,一般一个请求的头部大小不会超过1k
    #不过由于一般系统分页都要大于1k,所以这里设置为分页大小。分页大小可以用命令getconf PAGESIZE取得。
    open_file_cache max=102400 inactive=20s;
    #这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。
    open_file_cache_valid 30s;
    #这个是指多长时间检查一次缓存的有效信息。
    open_file_cache_min_uses 1;
    #open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive
    #包含其它配置文件,如自定义的虚拟主机
    include vhosts.conf;
}

配置详解2如下:

    #这里为后端服务器wugk应用集群配置,根据后端实际情况修改即可,tdt_wugk为负载均衡名称,可以任意指定
    #但必须跟vhosts.conf虚拟主机的pass段一致,否则不能转发后端的请求。weight配置权重,在fail_timeout内检查max_fails次数,失败则剔除均衡。
    upstream tdt_wugk {
        server   127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
        server   127.0.0.1:8081 weight=1 max_fails=2 fail_timeout=30s;
    }
   #虚拟主机配置
    server {
        #侦听80端口
        listen       80;
        #定义使用www.wuguangke.cn访问
        server_name  www.wuguangke.cn;
        #设定本虚拟主机的访问日志
        access_log  logs/access.log  main;
            root   /data/webapps/wugk;  #定义服务器的默认网站根目录位置
        index index.php index.html index.htm;   #定义首页索引文件的名称
        #默认请求
        location ~ /{
          root   /data/www/wugk;      #定义服务器的默认网站根目录位置
          index index.php index.html index.htm;   #定义首页索引文件的名称
          #以下是一些反向代理的配置.
          proxy_next_upstream http_502 http_504 error timeout invalid_header;
          #如果后端的服务器返回502、504、执行超时等错误,自动将请求转发到upstream负载均衡池中的另一台服务器,实现故障转移。
          proxy_redirect off;
          #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
          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_pass  http://tdt_wugk;     #请求转向后端定义的均衡模块
       }

        # 定义错误提示页面
            error_page   500 502 503 504 /50x.html;  
            location = /50x.html {
            root   html;
        }
        #配置Nginx动静分离,定义的静态页面直接从Nginx发布目录读取。
        location ~ .*.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
        {
            root /data/www/wugk;
            #expires定义用户浏览器缓存的时间为3天,如果静态页面不常更新,可以设置更长,这样可以节省带宽和缓解服务器的压力。
            expires      3d;
        }
        #PHP脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置.
        location ~ .php$ {
            root /root;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME /data/www/wugk$fastcgi_script_name;
            include fastcgi_params;
        }
        #设定查看Nginx状态的地址
        location /NginxStatus {
            stub_status  on;
        }
     }
}

Nginx 内置绑定变量

未分类

  • documenturi|与uri相同; 比如 /test2/test.php

  • host|请求信息中的”Host”,如果请求中没有Host行,则等于设置的服务器名hostname | 机器名使用 gethostname系统调用的值

  • httpcookie|cookie信息http_referer | 引用地址

  • httpuseragent|客户端代理信息http_via | 最后一个访问服务器的Ip地址。

  • http_x_forwarded_for | 相当于网络访问路径is_args | 如果请求行带有参数,返回“?”,否则返回空字符串

  • limitrate|对连接速率的限制nginx_version |当前运行的nginx版本号

  • pidworker|进程的PIDquery_string | 与args相同realpath_root | 按root指令或alias指令算出的当前请求的绝对路径。其中的符号链接都会解析成真是文件路径,使用 Nginx 内置绑定变量

  • 207remoteaddr|客户端IP地址remote_port | 客户端端口号

  • remoteuser|客户端用户名,认证用request | 用户请求

  • requestbody|这个变量(0.7.58+)包含请求的主要信息。在使用proxypass或fastcgipass指令的location中比较有意义
    request_body_file | 客户端请求主体信息的临时文件名

  • requestcompletion|如果请求成功,设为”OK”;如果请求未完成或者不是一系列请求中最后一部分则设为空request_filename | 当前请求的文件路径名,比如/opt/nginx/www/test.php

  • requestmethod|请求的方法,比如”GET”、”POST”等request_uri | 请求的URI,带参数

  • scheme|所用的协议,比如http或者是httpsserver_addr | 服务器地址,如果没有用listen指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费)

  • servername|请求到达的服务器名server_port | 请求到达的服务器端口号

  • serverprotocol|请求的协议版本,”HTTP/1.0”或”HTTP/1.1”uri| 请求的URI,可能和最初的值有不同,比如经过重定向之类的。

OpenResty + Varnish Cache 实现 WP 的高性能加载教程

前言

Varnish Cache 是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang 使用3台 Varnish 代替了原来的 12 台 Squid,性能比以前更好。(超级老的梗了,但是就这么用吧。)

Varnish Cache 在高端 WordPress 托管上也是非常常见的,由其构成的 Web 服务缓存加速解决方案已经非常成熟了,不过由于其开发者在 HTTPS 问题上并不关切所以很多人又抛弃了它采用了其他方案,不过前面也有提到 Varnish Cache 成熟、稳定,所以尽管不支持 HTTPS,我们也可以再加一层 Nginx 来反代实现 HTTPS。

介绍

Varnish Cache 的开发公司 Varnish Software 从 V5 时代开始了全新的命名方式,我们常提到的 Varnish 由 Varnish-Cache 的命名方式代替。

常见的 Varnish Cache 缓存规则从 V4 开始已经不同于 V3 了,不过 V5 依旧向下兼容 V4 的规则。

本教程主要介绍和前面的 https://www.mf8.biz/the-guide-for-wordpress-ubuntu/ 相呼应。

具体的教程可以参考:https://www.mf8.biz/varnish-wordpress-make-fast-1/

安装 Varnish 5

curl -s https://packagecloud.io/install/repositories/varnishcache/varnish5/script.deb.sh | bash
apt-get install varnish

更多系统的安装请看:

https://www.varnish-cache.org/releases/index.html https://packagecloud.io/varnishcache/varnish5/install

设置 Nginx

编辑虚拟主机的配置文件,将之前的 80 端口内容改成 8080 端口,其实就是将 listen 后改成 8080 ,例如:

  server {
  ##运行端口
  listen 8080; 

  ##这里需要改成你的域名
  server_name www.mf8.biz; 

  access_log /data/wwwlogs/access_nginx.log combined; #日志目录
  root /data/wwwroot/build; #网站文件目录
  index index.html index.htm index.php; #首页文件优先级
  include /usr/local/openresty/nginx/conf/rewrite/wordpress.conf;

  ##PHP
  location ~ [^/].php(/|$) {
      fastcgi_pass unix:/run/php/php7.1-fpm.sock;
      fastcgi_index index.php;
      include fastcgi.conf;
      fastcgi_param PHP_VALUE "open_basedir=$document_root:/tmp/:/proc/";
    }

  ##下面的都是缓存
  location ~ .*.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
    expires 30d;
    access_log off;
    }
  location ~ .*.(js|css)?$ {
    expires 7d;
    access_log off;
    }
  location ~ /.ht {
    deny all;
    }
  }

同理,/usr/local/openresty/nginx/conf/nginx.conf 文件也必须将之前的 80 端口改成 8080 端口,其他所有在 Nginx 上打开的 80 端口都需要修改,不然 Varnish 无法启动。

然后将 HTTPS 部分,改成反代:

    server {

  ##开启 HTTPS 和 HTTP/2
  listen 443 ssl http2;

  ssl_certificate /usr/local/openresty/nginx/conf/ssl/www.mf8.biz.crt; #RSA证书
  ssl_certificate_key /usr/local/openresty/nginx/conf/ssl/www.mf8.biz.key; #RSA密钥

  ##SSL增强安全设置部分
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
  ssl_prefer_server_ciphers on;
  ssl_session_timeout 10m;
  ssl_session_cache builtin:1000 shared:SSL:10m;
  ssl_buffer_size 1400;
  ssl_stapling on;
  ssl_stapling_verify on;

  server_name www.mf8.biz;
  access_log off;

        location / {
            proxy_pass http://127.0.0.1:80;
            proxy_set_header X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            proxy_set_header X-Forwarded-Port 443;
            proxy_set_header Host $host;
        }
}

设置 Varnish

cd /etc/varnish/
mv default.vcl default.vclold
git clone https://gitee.com/mf8/varnish-caching-wordpress.git

cd /lib/systemd/system/
mv varnish.service varnish.service.old
wget https://gitee.com/yunvy/codes/d79nhgw2aitm8xky65p4399/raw?blob_name=varnish.service

cd /etc/default
mv varnish varnish.old
wget https://gitee.com/yunvy/codes/folh28bm9ipveagc04ws740/raw?blob_name=varnish

systemctl daemon-reload
service varnish restart

设置 WordPress

不过呢,由于我们是 Nginx HTTPS 443 端口 —— Varnish 80 端口 —— Nginx 80 端口 饶了三层,所以 WP 就会反应不过来,所有的静态资源的加载依旧走的 HTTP ,这里就需要额外设置一下了。

修改 wp-config.php 文件,加入:

if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')  $_SERVER['HTTPS']='on';

因为我们在 Nginx 443 反代的时候加入了 proxy_set_header X-Forwarded-Proto https; ,所以当 WP 检测 HTTP_X_FORWARDED_PROTO 有 https 反馈的时候就会将静态资源的加载自动走 HTTPS 了!

openresty通过lua增加随机traceid

在没有引入zipkin(或者阿里的鹰眼,百度的华佗)这种trace系统的时候,排查问题的一般思路都是按照请求链路来寻找问题源。因此如果能在请求链路中有一个唯一的标识就最好了,而在nginx/openresty做接入层的架构中,可以通过lua脚本生成一个随机traceid。

随机数的生成原理,都是先初始化一个随机数种子,由于伪随机数的特性,种子的随机性就显得格外重要,而一般种子的生成都是通过时间的倒序来选取

lua 随机数生成方法

首先我们看下通常lua的随机数生成方法

math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6)))
math.random(m,n)

通过时间字符串的逆序初始化随机种子,这里注意到有个sub函数做了截断,是因为

math.randomseed will call the underlying C function srand which takes an unsigned integer valueLua will cast the value of the seed to this format. In case of an overflow the seed will actually become a bad seed, without warning

所以需要避免出现高类型向低类型转换的溢出问题

common 方法的问题

但上面的方法有个问题,就是os.time()函数返回的是秒(10位整数), 所以在做web请求的traceid时很容易就出现重复,影响问题追踪的效率,而lua如果要以毫秒为单位的时间来初始化随机种子,需要引入socket等外部模块,对于openresty来说一般都是封装好的,不方便去做这种定制

利用openresty 与lua生成traceid

幸运的是,nginx-lua中有个函数ngx.now会返回一个浮点数(当然在lua中统一为number类型),3位小数即为毫秒位,所以问题就变得简单了

access_by_lua_block {
    math.randomseed(tonumber(tostring(ngx.now()*1000):reverse():sub(1,9)))
    local randvar = string.format("%.0f",math.random(1000000000000000000,9223372036854775807))
    ngx.req.set_header("traceid", randvar)
}

通过ngx.now()*1000拿到毫秒数据转换为字符串取反,这样毫秒数据的变化才能显出效果; unsigned int(64bit机器下为4byte) 最大值为10位数,我们取前9位避免数据溢出带来的转换问题; lua在显示大于64bit的数据时会自动用科学技术法表示,所以我们需要通过string.format函数来将其转换为19位数字,然后通过ngx.req.set_header添加到请求头中去。

OpenResty下安装luarocks

在做一些openresty的项目的时候,经常会借助一些第三方包来协助开发,为了方便管理,我们可以使用openresy官方的opm,或者lua的包管理工具luarocks,只不过opm的包数量还不是太多,用的较多的还是luarocks,现在只能期待opm社区不断的发展壮大了。

在做一些openresty的项目的时候,经常会借助一些第三方包来协助开发,为了方便管理,我们可以使用openresy官方的opm,或者lua的包管理工具luarocks,只不过opm的包数量还不是太多,用的较多的还是luarocks,现在只能期待opm社区不断的发展壮大了。

安装luarocks

wget https://luarocks.org/releases/luarocks-2.4.1.tar.gz
tar -xzvf luarocks-2.4.1.tar.gz
cd luarocks-2.4.1/

./configure --prefix=/usr/local/openresty/luajit 
    --with-lua=/usr/local/openresty/luajit/ 
    --lua-suffix=jit 
    --with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1
make build
# 安装需要root权限
sudo make install

此处要做说明的是

  • –prefix 设定 luarocks 的安装目录

  • –with-lua 则是系统中安装的 lua 的根目录

  • –lua-suffix 版本后缀,此处因为openresyt的lua解释器使用的是 luajit ,所以此处得写 jit

  • –with-lua-include 设置 lua 引入一些头文件头文件的目录

之后我们就可以看到 luarocks 命令就被安装在了 /usr/local/openresty/luajit/bin 下面

然后我们把它添加到到 PATH 中

vi ~/.bash_profile
export PATH=$PATH:/usr/local/openresty/luajit/bin)

执行 luarocks install package 就可以安装lua的包了

luarocks install package –tree=path 还可以指定你安装的包的存放路径

更多命令大家可以直接使用luarocks help 来查看。