/Lua-Nginx-Redis

:sunflower: Redis执行Lua脚本、Lua扩展Nginx、Openresty Web、Lua知识、Lua案例、Nginx基础以及Linux基础知识总结等(Redis performs Lua script, Lua extends Nginx, Openresty Web, Lua knowledge, Lua case, Nginx Fundamentals, etc.)

Primary LanguageLua

Markdown

开发过程记录

  • 通过alias 实现别名功能
    location /live {  
        alias /home/tinywan/HLS/;
    }
    
  • curl 请求结果
    tinywan@tinywan:~/HLS$ cat index.html 
    alias /home/tinywan/HLS/index.html
    tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/index.html
    alias /home/tinywan/HLS/index.html
    
  • 结论:
    1. cul 请求 /live/index.html,那么Nginx将会在服务器上查找/home/tinywan/HLS/index.html 文件
    2. 请求的url 中的location后面的部分会被追加到alias 指定的目录后面,而location后面的/live路径将会别自动抛弃
  • 类似案例[2]:
    • config配置信息
       location ~ ^/live/(.*)$ {  
            alias /home/tinywan/HLS/$1;
       }
      
    • curl 请求结果
      tinywan@tinywan:~/HLS$ pwd
      /home/tinywan/HLS
      tinywan@tinywan:~/HLS$ cat txt.txt 
      txt file
      tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt
      txt file
      
    • 如果url请求/live/txt.txt那么Nginx将会在服务器上查找/home/tinywan/HLS/txt.txt 文件
  • 与root 功能的差别
    • config配置信息,注意:一下的alias 换成 root
       location ~ ^/live/(.*)$ {  
            root /home/tinywan/HLS/$1;
       }
      
    • curl 请求结果
      tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt
      <html>
      <head><title>404 Not Found</title></head>
      <body bgcolor="white">
      <center><h1>404 Not Found</h1></center>
      <hr><center>openresty/1.11.2.1</center>
      </body>
      </html>
      
    • 日志文件信息(打开Nginx的rewrite日志:rewrite_log on;):
      /home/tinywan/HLS/txt.txt/live/txt.txt
      
    • 二者的区别
      1. alias 指定的目录是当前目录
      2. root 指定的是根目录
        1. 一般建议的location /中通过root命令配置目录,其他目录匹配的位置使用alias命令
  • 案例[3]:
    • config配置信息
       location ~ ^/live/(\w+)/(.*) {
           alias /home/tinywan/HLS/live/$1/$2;
       }
      
    • curl 请求结果
      tinywan@tinywan:~/HLS/live/stream123$ pwd
      /home/tinywan/HLS/live/stream123
      tinywan@tinywan:~/HLS/live/stream123$ cat index.m3u8 
      12312312312
      tinywan@tinywan:~/HLS/live/stream123$ curl "http://127.0.0.1/live/stream123/index.m3u8?token=1234&api=009132"
      12312312312
      
  • 测试一
  • 基本语法:location [=||*|^~] /uri/ { … }

    1. = :严格匹配。如果这个查询匹配,那么将停止搜索并立即处理此请求。
    2. ~ :为区分大小写匹配(可用正则表达式)
    3. !~ :为区分大小写不匹配
    4. !~*:为不区分大小写不匹配
    5. ^~:如果把这个前缀用于一个常规字符串,那么告诉nginx 如果路径匹配那么不测试正则表达式
  • Perl 正则表达式参考

  • 正则中需要转义的特殊字符小结

    • [1] $ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n' 或 ‘\r'。要匹配 $ 字符本身,请使用 $。
    • [2] ( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 和。
    • [3] * 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 *。
    • [4] + 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。
    • [5] . 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。
    • [6] [ ] 标记一个中括号表达式的开始。要匹配 [,请使用 [。
    • [7] ? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。
    • [8] \ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n' 匹配字符 ‘n'。'\n' 匹配换行符。序列 ‘\' 匹配 “\”,而 ‘(' 则匹配 “(”。
    • [9] ^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 ^。
    • [10] { } 标记限定符表达式的开始。要匹配 {,请使用 {。
    • [11] | 指明两项之间的一个选择。要匹配 |,请使用 |。
  • 正则表达式 (Regular expression) 匹配location

    • [1] location ~* \.(gif|jpg|jpeg)$ { }:匹配所有以 gif,jpg或jpeg 结尾的请求
    • [2] location ~ /documents/Abc { }:匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
    • [3] 目录匹配:
      1. 可以匹配静态文件目录(static/lib)
      2. HLS直播目录(/home/HLS/stream123/index.m3u8)
      3. HLS/MP4/FLV点播视频目录(/home/HLS/stream123.m3u8)
      4. 匹配URL地址:http://127.0.0.1/live/stream123/index.m3u8
      5. nginx.conf 配置信息
        # 匹配任何以/live/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试
        location ^~ /live/ {  
                        root /home/tinywan/HLS/;
        }
        # 以上匹配成功后的组合:/home/tinywan/HLS/live/....
        
  • 后缀匹配

    1. 匹配任何后缀文件名gif|jpg|jpeg|png|css|js|ico|m3u8|ts 结尾的请求
    2. TS 文件匹配http://127.0.0.1/live/stream123/11.ts
    3. M3U8 文件匹配http://127.0.0.1/live/stream123/index.m3u8
    4. 匹配URL地址:http://127.0.0.1/hls/123.m3u8
    5. nginx.conf 配置信息
      location ~* \.(gif|jpg|jpeg|png|css|js|ico|m3u8|ts)$ {
              root /home/tinywan/HLS/;
      }
      
  • HSL直播目录匹配实际案例(请测试上线)

    1. 可以后缀文件名:http://127.0.0.1/live/stream123/index.m3u8
      location ^~ /live/ {
              root /home/tinywan/HLS/;
      }
      
  • nginx配置location总结及rewrite规则写法

  • 测试一
  • Rewrite 常用全局变量

    • 请求案例: curl -G -d "name=Tinywan&age=24" http://127.0.0.1/rewrite_var/1192/index.m3u8
    • 接受结果:
    变量 描述
    $args name=Tinywan&age=24 存放URL 请求的指令
    $content_length 0 请求头中的Content-length字段
    $content_type 0 请求头中的Content-Type字段
    $document_root /opt/openresty/nginx/html 当前请求在root指令中指定的值
    $document_uri /rewrite_var/1192/index.m3u8 与$uri相同
    $host 127.0.0.1 请求主机头字段,否则为服务器名称
    $http_user_agent curl/7.47.0 客户端agent信息
    $http_cookie 0 COOKIE变量的值
    $limit_rate 0 限制连接速率
    $request_body_file null 客户端请求主体信息的临时文件名
    $request_method GET 客户端请求的动作,通常为GET或POST
    $remote_addr 127.0.0.1 客户端的IP地址
    $remote_port 33516 客户端端口
    $remote_user 0 已经经过Auth Basic Module验证的用户名
    $request_filename /opt/openresty/nginx/html/rewrite_var/1192/index.m3u8 当前请求的文件路径
    $request_uri /rewrite_var/1192/index.m3u8?name=Tinywan&age=24 包含请求参数的原始URI,不包含主机名
    $query_string name=Tinywan&age=24 与$args相同
    $scheme http HTTP方法(如http,https
    $server_protocol HTTP/1.1 请求使用的协议,通常是HTTP/1.0或HTTP/1.1
    $server_addr 127.0.0.1 服务器地址
    $server_name localhost 服务器名称
    $server_port 80 请求到达服务器的端口号
    $uri /rewrite_var/1192/index.m3u8 不带请求参数的当前URI
    • uri 介绍 (Nginx中的URI是相对的URI)
      • URL:https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md
      • 绝对URI:https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md
      • 相对URI:/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md Markdown
  • Rewrite 正则匹配uri参数接收

    1. 请求案例:curl http://192.168.18.143/live/tinywan123/index.m3u8
    2. Nginx.conf配置文件
      location ~* ^/live/(\w+)/(\D+)\.(m3u8|ts)$ {
          set $num $2;
          set $arg1 $1;
          echo "args === ${arg1}";
          echo "1==$1 2==$2 3==$3";
          echo "Total_numbser :: $num";
          echo "URI $uri";
      }
      
    3. 输出结果
         args === tinywan123
         $1==tinywan123 $2==index $3==m3u8
         Total_numbser :: index
         URI /live/tinywan123/index.m3u8
         Total_numbser :: 
      
    4. $1为正则匹配多个英文字母或数字的字符串 (\w+)
      $2 为正则匹配多个非数字 (\D+)
      $3 为正则匹配的第一个值 (m3u8|ts)
      . 需要用转义字符转义\.
  • 测试一
  • 测试一
  • 测试一
  • 安装信息
  • 默认配置信息
  • 开发入门
    • Nginx与Lua的整体目录关系
      .
      ├── conf
      │   ├── nginx.conf                  -- Nginx 配置文件
      ├── logs
      │   ├── error.log                   -- Nginx 错误日子
      │   └── nginx.pid
      ├── lua
      │   ├── m3u8_redis_access.lua       -- M3U8地址权限验证文件
      │   ├── business_redis.lua          -- 业务 Redis 处理文件
      │   ├── http-lua-test.lua           -- http lua demo
      │   ├── ...
      │   └── resty                       -- 存放Lua 的所有公共、封装好的库目录
      │       └── redis_iresty.lua        -- Redis 接口的二次封装
      │       └── param.lua               -- 参数过滤库
      └── sbin
          └── nginx
    • 参数总结
      • Lua脚本接受Nginx变量:

        [1] 间接获取:var = ngx.var ,如接受Nginx的变量 $a = 9,则lua_a = ngx.var.a --lua_a = 9
        [2] 直接获取:var = ngx.var ,如接受Nginx的location的第二个变量890,http://127.0.0.1/lua_request/123/890,则lua_2 = ngx.var[2] --lua_2 = 890

      • Lua 脚本接受 Nginx 头部 header:

        [1] 返回一个包含所有当前请求标头的Lua表:local headers = ngx.req.get_headers()
        [2] 获取单个Host:headers["Host"] 或者 ngx.req.get_headers()["Host"]
        [3] 获取单个user-agent:

        [01]headers["user-agent"]
        [02]headers.user_agent
        [03]ngx.req.get_headers()['user-agent']

      • Lua 脚本 Get 获取请求uri参数

        linux curl Get方式提交数据语法:curl -G -d "name=value&name2=value2" https://github.com/Tinywan
        返回一个包含所有当前请求URL查询参数的Lua表:local get_args = ngx.req.get_uri_args()
        请求案例:curl -G -d "name=Tinywan&age=24" http://127.0.0.1/lua_request/123/789
        Lua Get 方式获取提交的name参数的值:get_args['name'] 或者 ngx.req.get_uri_args()['name']

        [01]get_args['name']
        [02]ngx.req.get_uri_args()['name']

      • Lua 脚本 Post 获取请求uri参数

        linux curl Post方式提交数据语法:

        [01] curl -d "name=value&name2=value2" https://github.com/Tinywan
        [02] curl -d a=b&c=d&txt@/tmp/txt https://github.com/Tinywan
        返回一个包含所有当前请求URL查询参数的Lua表:local post_args = ngx.req.get_post_args()
        请求案例:curl -G -d "name=Tinywan&age=24" http://127.0.0.1/lua_request/123/789
        Lua Post 方式获取提交的name参数的值: [01]post_args['name']
        [02]ngx.req.get_post_args()['name']

      • Lua 脚本请求的http协议版本:ngx.req.http_version()
      • Lua 脚本请求方法:ngx.req.get_method()
      • Lua 脚本原始的请求头内容:ngx.req.raw_header()
      • Lua 脚本请求的body内容体:ngx.req.get_body_data()
    • 接收请求:获取如请求参数、请求头、Body体等信息
    • [接收请求:输出响应需要进行响应状态码、响应头和响应内容体的输出](
  • luajit 执行文件默认安装路径:/opt/openresty/luajit/bin/luajit,这样我们直接可以这样运行一个Lua文件:luajit test.lua
    • luajit 运行测试案例:
      tinywan@tinywan:~/Lua$ luajit test.lua    
      The man name is Tinywan            
      The man name is Phalcon
  • 代码引入:lua_package_path "/opt/openresty/nginx/lua/lua-resty-redis/lib/?.lua;;";

  • Lua脚本实现一个CDN的反向代理功能(智能查找CDN节点)(测试成功,可上线)

    • nginx.conf 配置信息
    http {
        lua_package_path "/opt/openresty/nginx/lua/lua-resty-redis/lib/?.lua;;";
        server {
            listen 80;
            server_name  localhost;
            location ~ \/.+\/.+\.(m3u8|ts) {
                if ($uri ~ \/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)(|-).*\.(m3u8|ts)) {
                        set $app_name $1;
                        set $a $2;
                }
                set $stream_id "";
                default_type 'text/html';
                rewrite_by_lua_file  /opt/openresty/nginx/lua/proxy_pass_cdn.lua;
                proxy_connect_timeout       10;
                proxy_send_timeout          30;
                proxy_read_timeout          30;
                proxy_pass                  $stream_id;
            }
    
        }
    }
  • Lua脚本结合 Nginx+Lua+Local Redis+Mysql服务器缓存

    • Nginx+Lua+Local Redis+Mysql集群架构

    • Nginx+Lua+Local Redis+Mysql

    • Lua脚本Nginx+Lua+Redis+Mysql.lua

    • Nginx.conf配置文件Nginx+Lua+Redis+Mysql.conf

    • HELP

    • Lua脚本结合 Redis 统计直播流播放次数、链接次数等等信息

      • nginx.conf
        server {            # 配置虚拟服务器80
             listen 80;
             server_name  127.0.0.1:8088;
             location ~* /live/(\w+)/ {
                    set $total_numbers "";
                    set $stream_name $1;
                    lua_code_cache off;
                    rewrite_by_lua_file /opt/openresty/nginx/conf/Lua/total_numbers.lua;
                    proxy_pass                  http://127.0.0.1:8088;
              }
        }      
      • 代理服务器
         server {            # 配置虚拟服务器8088
             listen 8088;
             server_name  127.0.0.1:8088;
             location /live {
                add_header  Cache-Control no-cache;
                add_header 'Access-Control-Allow-Origin' '*' always;
                add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
                add_header 'Access-Control-Allow-Headers' 'Range';
                types{
                    application/dash+xml mpd;
                    application/vnd.apple.mpegurl m3u8;
                    video/mp2t ts;
                }
             alias /home/tinywan/HLS/live/;
            }
          }
        
      • CURL请求地址:http://192.168.18.143/live/tinywan123/index.m3u8
      • Lua 脚本
  • 代码引入:lua_package_path "/opt/openresty/nginx/lua/lua-resty-websocket/lib/?.lua;;";
  • Lua脚本实现一个websocket连接(测试成功,可上线)
    • nginx.conf 配置信息
    http {
            lua_package_path "/opt/openresty/nginx/lua/lua-resty-websocket/lib/?.lua;;";
            server {
                listen 80 so_keepalive=2s:2s:8;  #为了防止半开TCP连接,最好在Nginx监听配置指令中启用TCP keepaliveserver_name  localhost;
                location /ws {
                    lua_socket_log_errors off;
                    lua_check_client_abort on;
                    lua_code_cache off; # 建议测试的时候最好关闭缓存
                    content_by_lua_file /opt/openresty/nginx/conf/Lua/websocket.lua;
                }
            }
    }
  • 基本用法
    • nginx.conf
      location /cjson {
          content_by_lua_block {
              local cjson = require "cjson"
              local json = cjson.encode({
                      foo = "bar",
                      some_object = {},
                      some_array = cjson.empty_array
              })
              ngx.say(json)
          }
      }
    • curl 请求
      root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/cjson
      {"some_object":{"tel":13669313112,"age":24},"name":"tinywan","some_array":[]}
  • lua对象到字符串、字符串到lua对象
  • OpenResty 引用第三方 resty 库非常简单,只需要将相应的文件拷贝到 resty 目录下即可
  • 我服务器OpenResty 的 resty 路径:/opt/openresty/lualib/resty
  • 下载第三方 resty 库:git clone lua-resty-session 文件路径以及内容:
    tinywan@tinywan:/opt/openresty/nginx/lua/lua-resty-session/lib/resty$ ls
    session  session.lua
  • 特别注意:这里拷贝的时候要要把session文件和session.lua 文件同时吧、拷贝过去,否则会报错误:
    /opt/openresty/lualib/resty/session.lua:34: in function 'prequire'
    /opt/openresty/lualib/resty/session.lua:211: in function 'new'
    /opt/openresty/lualib/resty/session.lua:257: in function 'open'
    /opt/openresty/lualib/resty/session.lua:320: in function 'start'
  • 拷贝完毕后/opt/openresty/lualib/resty OpenResty 引用第三方 resty 的所有库文件
tinywan@tinywan:/opt/openresty/lualib/resty$ ls
aes.lua  core.lua  http_headers.lua  lock.lua  lrucache.lua  memcached.lua  random.lua  session      sha1.lua    sha256.lua  sha512.lua  string.lua  upstream
core     dns       http.lua          lrucache  md5.lua       mysql.lua      redis.lua   session.lua  sha224.lua  sha384.lua  sha.lua     upload.lua  websocket
  • 基本用法
     location /start {
        content_by_lua_block {
            local session = require "resty.session".start()
            session.data.name = "OpenResty Fan Tinywan"
            session:save()
            ngx.say("<html><body>Session started. ",
                    "<a href=/test>Check if it is working</a>!</body></html>")
            ngx.say(session.data.name,"Anonymous")
        }
     }
  • curl 请求
    tinywan@tinywan:/opt/openresty/nginx/conf$ curl http://192.168.18.143/start
    <html><body>Session started. <a href=/test>Check if it is working</a>!</body></html>
    OpenResty Fan Tinywan Anonymous
  • Lua 一个HLS的简单地址访问权限验证
    • Nginx.conf 配置
      location ^~ /live/ {
          add_header Cache-Control no-cache;
          add_header 'Access-Control-Allow-Origin' '*' always;
          add_header 'Access-Control-Allow-Credentials' 'true';
          add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
          add_header 'Access-Control-Allow-Headers' 'Range';
      
          types{
              application/dash+xml mpd;
              application/vnd.apple.mpegurl m3u8;
              video/mp2t ts;
          }
          if ( $uri ~ \.m3u8 ) {
              lua_code_cache off;
              access_by_lua_file /opt/openresty/nginx/lua/access.lua;
          }
          root /home/tinywan/HLS;
       }
    • access.lua 文件内容
      if ngx.req.get_uri_args()["wsSecret"] ~= "e65e6a01cf26523e206d5bb0e2a8a95a" then  
         return ngx.exit(403)  
      end
  • MD5加密的简单基本用法 md5.lua
    local resty_md5 = require "resty.md5"
    local md5 = resty_md5:new()
    if not md5 then
        ngx.say("failed to create md5 object")
        return
    end
    local ok = md5:update("hello")
    if not ok then
        ngx.say("failed to add data")
        return
    end
    local digest = md5:final()
    -- ngx.say("md5",digest)                ---注意:这样直接输出是乱码
    local str = require "resty.string"
    ngx.say("md5: ", str.to_hex(digest))    ---注意:必须通过字符串转码方可打印输出
        -- yield "md5: 5d41402abc4b2a76b9719d911017c592"
  • 在OpenResty中需要避免全局变量的使用,为此春哥写了一个perl工具,可以扫描openresty lua代码的全局变量
  • https://github.com/openresty/openresty-devel-utils/blob/master/lua-releng
  • 用法相当简单
    1. 将代码保存成lua-releng文件
    2. 更改lua-releng的权限,chmod 777 lua-releng
    3. 假设有一个源码文件为test.lua
    4. 执行./lua-releng test.lua,则会扫描test.lua文件的全局变量,并在屏幕打印结果
  • 所有常量列表
    value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release)
    value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release)
    value = ngx.HTTP_OK (200)
    value = ngx.HTTP_CREATED (201)
    value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release)
    value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release)
    value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release)
    value = ngx.HTTP_SPECIAL_RESPONSE (300)
    value = ngx.HTTP_MOVED_PERMANENTLY (301)
    value = ngx.HTTP_MOVED_TEMPORARILY (302)
    value = ngx.HTTP_SEE_OTHER (303)
    value = ngx.HTTP_NOT_MODIFIED (304)
    value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release)
    value = ngx.HTTP_BAD_REQUEST (400)
    value = ngx.HTTP_UNAUTHORIZED (401)
    value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release)
    value = ngx.HTTP_FORBIDDEN (403)
    value = ngx.HTTP_NOT_FOUND (404)
    value = ngx.HTTP_NOT_ALLOWED (405)
    value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release)
    value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release)
    value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release)
    value = ngx.HTTP_GONE (410)
    value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release)
    value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release)
    value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release)
    value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release)
    value = ngx.HTTP_INTERNAL_SERVER_ERROR (500)
    value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501)
    value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release)
    value = ngx.HTTP_SERVICE_UNAVAILABLE (503)
    value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release)
    value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release)
    value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release)
  • 案列使用,get_string_md5.lua:
    local args = ngx.req.get_uri_args()
    local salt = args.salt
    if not salt then
            ngx.say(ngx.HTTP_BAD_REQUEST)
    end
    local string = ngx.md5(ngx.time()..salt)
    ngx.say(string)
    
  • curl 请求(-i 参数,输出时包括protocol头信息):
    tinywan@tinywan:$ curl -i http://127.0.0.1/get_rand_string?salt=tinywan123
    HTTP/1.1 200 OK
    Server: openresty/1.11.2.1
    Date: Fri, 21 Apr 2017 14:27:16 GMT
    Content-Type: application/octet-stream
    Transfer-Encoding: chunked
    Connection: keep-alive
  • 强烈建议使用ngx Lua APi 接口(非阻塞的),而不是Lua自身的API(阻塞的),Lua 自身API会阻塞掉的
  • ngx_lua_api_test.lua
    local json = require "cjson"    -- 引入cjson 扩展
    
    -- 同步读取客户端请求正文,而不会阻止Nginx事件循环
    ngx.req.read_body()
    local args = ngx.req.get_post_args()
    
    if not args or not args.info then
            ngx.say(ngx.HTTP_BAD_REQUEST)   -- ngx.HTTP_BAD_REQUEST (400)
    end
    
    local client_id = ngx.var.remote_addr
    local user_agent = ngx.req.get_headers()['user-agent'] or ""
    local info = ngx.decode_base64(args.info)
    
    local response = {}
    response.info = info
    response.client_id = client_id
    response.user_agent = user_agent
    
    ngx.say(json.encode(response))
    
  • CURL Post 请求
    $ curl -i --data "info=b3ZlcmNvbWUud2FuQGdtYWlsLmNvbQ==" http://127.0.0.1/ngx_lua_api_test
    HTTP/1.1 200 OK
    Server: openresty/1.11.2.1
    Date: Sat, 22 Apr 2017 01:22:07 GMT
    Content-Type: application/octet-stream
    Transfer-Encoding: chunked
    Connection: keep-alive
    
    {"user_agent":"curl\/7.47.0","info":"overcome.wan@gmail.com","client_id":"127.0.0.1"}
    
  • 指令:lua_shared_dict
    • 纯内存的操作,多个worker之间共享的(比如nginx开启10个Worker,则每个worker之间是共享该内存的)
    • 同一份数据在多个worker之间是共享的,只要存储一份数据就可以了
    • 锁的竞争(数据原子性)
  • health.txt 在每个upstream 服务器组的root 目录下创建这个文件,目录结构如下所示
    ├── html
    │   ├── 50x.html
    │   ├── index.html
    │   ├── websocket001.html
    │   └── websocket02.html
    ├── html81
    │   ├── 50x.html
    │   ├── health.txt
    │   └── index.html
    ├── html82
    │   ├── 50x.html
    │   ├── health.txt
    │   └── index.html
    ├── html83
    │   ├── 50x.html
    │   ├── health.txt
    │   └── index.html
    ├── html84
    │   ├── 50x.html
    │   └── index.html
  • nginx.conf
    worker_processes  8;
    
    error_log  logs/error.log;
    pid        logs/nginx.pid;
    
    events {
        use epoll;
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  text/html;
        #lua模块路径,其中”;;”表示默认搜索路径,默认到/usr/servers/nginx下找  
        lua_package_path "/home/tinywan/Openresty_Protect/First_Protect/lualib/?.lua;;";  #lua 模块  
        lua_package_cpath "/home/tinywan/Openresty_Protect/First_Protect/lualib/?.so;;";  #c模块  
        include /home/tinywan/Openresty_Protect/First_Protect/nginx_first.conf;
    }
    
  • nginx_first.conf
    upstream tomcat {
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
        server 127.0.0.1:8083;
        server 127.0.0.1:8084 backup;
    }
    
    lua_shared_dict healthcheck 1m;
    
    lua_socket_log_errors off;
    
    init_worker_by_lua_block {
        local hc = require "resty.upstream.healthcheck"
        local ok, err = hc.spawn_checker{
                shm = "healthcheck",  -- defined by "lua_shared_dict"
                upstream = "tomcat", -- defined by "upstream"
                type = "http",
    
                http_req = "GET /health.txt HTTP/1.0\r\nHost: tomcat\r\n\r\n",
                        -- raw HTTP request for checking
    
                interval = 2000,  -- run the check cycle every 2 sec
                timeout = 1000,   -- 1 sec is the timeout for network operations
                fall = 3,  -- # of successive failures before turning a peer down
                rise = 2,  -- # of successive successes before turning a peer up
                valid_statuses = {200, 302},  -- a list valid HTTP status code
                concurrency = 10,  -- concurrency level for test requests
            }
    }
    
    server {
        listen       80;
        server_name  localhost;
        
        location / {
            proxy_pass          http://tomcat;
         }
    
        location /server/status {
            access_log off;
            allow 127.0.0.1;
    
            default_type text/plain;
            content_by_lua_block {
                local hc = require "resty.upstream.healthcheck"
                ngx.say("Nginx Worker PID: ", ngx.worker.pid())
                ngx.print(hc.status_page())
            }
        }
    }
    
    server {
        listen       8081;
        server_name  localhost;
    
        location / {
            root   html81;
            index  index.html index.htm;
        }
    
        location /health_status {
    
        }
    }
    
    server {
        listen       8082;
        server_name  localhost;
    
        location / {
            root   html82;
            index  index.html index.htm;
        }
    
        location /health_status {
    
        }
    }
    
    server {
        listen       8083;
        server_name  localhost;
    
        location / {
            root   html83;
            index  index.html index.htm;
        }
    
        location /health_status {
    
        }
    }
    
    server {
        listen       8084;
        server_name  localhost;
    
        location / {
            root   html84;
            index  index.html index.htm;
        }
    }
    
  • 状态查看,通过访问:http://127.0.0.1/server/status Markdown

Redis、Lua、Nginx一起工作事迹

Redis执行Lua脚本

Lua 基本语法


  • Hello, Lua!

    我们的第一个Redis Lua 脚本仅仅返回一个字符串,而不会去与redis 以任何有意义的方式交互

    local msg = "Hello, world!"
    return msg

    这是非常简单的,第一行代码定义了一个本地变量msg存储我们的信息, 第二行代码表示 从redis 服务端返回msg的值给客户端。 保存这个文件到Hello.lua,像这样去运行:

    www@iZ239kcyg8rZ:~/lua$ redis-cli EVAL "$(cat Hello.lua)" 0
    "Hello, world!"

    运行这段代码会打印"Hello,world!", EVAL在第一个参数是我们的lua脚本, 这我们用cat命令从文件中读取我们的脚本内容。第二个参数是这个脚本需要访问的Redis 的键的数字号。我们简单的 “Hello Script" 不会访问任何键,所以我们使用0

  • redis.call() 与 redis.pcall()的区别

    • 他们唯一的区别是当redis命令执行结果返回错误时
    • redis.call()将返回给调用者一个错误.
    • redis.pcall()会将捕获的错误以Lua表的形式返回.
    • redis.call() 和 redis.pcall() 两个函数的参数可以是任意的 Redis 命令
  • Lua网络编程

Lua 脚本

  • Lua 实现简单封装
    • man.lua

          local _name = "Tinywan"
          local man = {}
      
          function man.GetName()
              return _name
          end
      
          function man.SetName(name)
              _name = name    
          end
      
          return man 
    • 测试封装,test.lua

          local man = require('man')
          print("The man name is "..man.GetName())
          man.SetName("Phalcon")
          print("The man name is "..man.GetName())
  • 在命令行输入以下命令 git config --global credential.helper store

    这一步会在用户目录下的.gitconfig文件最后添加:

    ```
    [credential]
    helper = store
    ```
    
  • push 代码

    push你的代码 (git push), 这时会让你输入用户名和密码, 这一步输入的用户名密码会被记住, 下次再push代码时就不用输入用户名密码!这一步会在用户目录下生成文件.git-credential记录用户名密码的信息。

  • Markdown 的超级链接技术

    【1】需要链接的地址:

    ```
    [解决向github提交代码不用输入帐号密码](#githubpush)  
    ```
    

    【2】要链接到的地方:

    ``` 
    <a name="githubpush"/> 解决向github提交代码不用输入帐号密码
    ```
    

    通过【1】和【2】可以很完美的实现一个连接哦!

  • 检查网卡是否正确工作
    1. 检查系统路由表信息是否正确
    2. Linux route命令详解和使用示例
    3. 案例介绍:
      www@ubuntu1:/var/log$ route
      Kernel IP routing table
      Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
      default         122.11.11.161 0.0.0.0         UG    0      0        0 em1
      10.10.101.0     *               255.255.255.0   U     0      0        0 em2
      122.11.11.160 *               255.255.255.224 U     0      0        0 em1
      
      • 默认路由为:122.11.11.161,绑定在em1 网卡上
      • 10.10.101.0 段的IP仅供局域网主机之间共享数据,没对外连接访问权限,因而外界是没办法通过10段网络连接到服务器的
      • 如果需要10.10.101.0 段可以让外网放完的,则需要删除122.11.11.161 的默认路由,需要在em2网卡上添加10段的默认路由即可
      • 具体步骤:
        www@ubuntu1:/var/log$   route delete defaul
        www@ubuntu1:/var/log$   route add defaul gw 10.10.101.1
        
      • 此时外界就可以通过ssh www@10.10.101.2连接到服务器了
  • find 命令
    • 查找超出7天前的flv的文件进行删除:
      • 命令:
      find ./ -mindepth 1 -maxdepth 3 -type f -name "*.flv" -mmin +10080 | xargs rm -rf 
      • -type f 按类型查找
      • -mmin +10080 7天之前的文件
      • xargs与-exec功能类似,find ~ -type f | xargs ls -l
      • -r 就是向下递归,不管有多少级目录,一并删除
      • -f 就是直接强行删除,不作任何提示的意思
    • 查找当前目录下.p文件中,最近30分钟内修改过的文件:
      • find . -name '*.p' -type f -mmin -30
    • 查找当前目录下.phtml文件中,最近30分钟内修改过的文件,的详细de情况加上ls:
      • find . -name '*.phtml' -type f -mmin -30 -ls
    • 查找当前目录下,最近1天内修改过的常规文件:find . -type f -mtime -1
    • 查找当前目录下,最近1天前(2天内)修改过的常规文件:find . -type f -mtime +1