Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

OpenResty学习笔记

24
Feb
2018

OpenResty学习笔记

By Alex
/ in Linux,Lua
/ tags Nginx
0 Comments
简介

OpenResty是一个基于Nginx+Lua的Web运行环境,它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的大多数依赖项。OpenResty可以用来实现高并发的动态Web应用

Open 取自“开放”之意,而Resty便是 REST 风格的意思

OpenResty使用的Lua版本是5.1,不使用更新版本的原因是5.2+版本的Lua API和C API都不兼容于5.1。

自从 OpenResty 1.5.8.1 版本之后,默认捆绑的 Lua 解释器就被替换成了 LuaJIT,而不再是标准 Lua。

安装
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
wget https://openresty.org/download/openresty-1.13.6.1.tar.gz
tar xzf openresty-1.13.6.1.tar.gz
cd openresty-1.13.6.1/
./configure --prefix=/home/alex/Lua/openresty/1.13.6    \
            # 启用LuaJIT,这是一个Lua的JIT编译器,默认没有启用
            --with-luajit \
            # 使用Lua 5.1标准解释器,不推荐,应该尽可能使用LuaJIT
            --with-lua51 \
            # Drizzle、Postgres、 Iconv这几个模块默认没有启用
           --with-http_drizzle_module、--with-http_postgres_module  --with-http_iconv_module
# Nginx 路径如下:
# nginx path prefix: "/home/alex/Lua/openresty/1.13.6/nginx"
# nginx binary file: "/home/alex/Lua/openresty/1.13.6/nginx/sbin/nginx"
# nginx modules path: "/home/alex/Lua/openresty/1.13.6/nginx/modules"
# nginx configuration prefix: "/home/alex/Lua/openresty/1.13.6/nginx/conf"
# nginx configuration file: "/home/alex/Lua/openresty/1.13.6/nginx/conf/nginx.conf"
# nginx pid file: "/home/alex/Lua/openresty/1.13.6/nginx/logs/nginx.pid"
# nginx error log file: "/home/alex/Lua/openresty/1.13.6/nginx/logs/error.log"
# nginx http access log file: "/home/alex/Lua/openresty/1.13.6/nginx/logs/access.log"
# nginx http client request body temporary files: "client_body_temp"
# nginx http proxy temporary files: "proxy_temp"
# nginx http fastcgi temporary files: "fastcgi_temp"
# nginx http uwsgi temporary files: "uwsgi_temp"
# nginx http scgi temporary files: "scgi_temp"
 
make -j8 && make install
起步
创建工程

一个OpenRestry工程,实际上就是对应了Nginx运行环境的目录结构。例如:

Shell
1
2
3
mkdir -p ~/Lua/projects/openrestry
cd ~/Lua/projects/openrestry
mkdir conf && mkdir logs
配置文件

使用lua-nginx-module模块提供的指令,你可以嵌入Lua脚本到Nginx配置文件中,以生成响应内容:

/home/alex/Lua/projects/openrestry/conf/nginx.conf
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
daemon off;
worker_processes  1;
error_log stderr debug;
events {
    worker_connections 1024;
}
http {
    access_log /dev/stdout;
    server {
        listen 8080;
        location / {
            default_type text/html;
            # lua-nginx-module模块,属于OpenResty项目,支持根据Lua脚本输出响应
            content_by_lua_block {
                ngx.say("<p>hello, world</p>")
            }
        }
    }
}
启动服务
Shell
1
2
# 将Nginx运行时的前缀设置为上面的工程目录
~/Lua/openresty/1.13.6/nginx/sbin/nginx -p ~/Lua/projects/openrestry -c conf/nginx.conf
测试服务
Shell
1
2
curl http://localhost:8080/
# <p>hello, world</p>
IDE
Intellij

安装三个插件:

  1. nginx support:支持Nginx配置文件的语法高亮、格式化、自动完成。自动基于Lua语言对lua-nginx-module模块的相关指令进行语法高亮、自动完成
  2. Lua:支持Lua语言的开发和调试
  3. OpenResty Lua Support:为OpenResty提供自动完成
ngx_lua_module
指令执行阶段

ngx-lua-phase

不同类型的指令,职责如下:

指令 说明
set_by_lua* 流程分支处理判断变量初始化
rewrite_by_lua* 转发、重定向、缓存等功能
access_by_lua* IP 准入、身份验证、接口权限、解密
content_by_lua* 内容生成
header_filter_by_lua* 响应头部过滤处理,可以添加响应头
body_filter_by_lua* 响应体过滤处理,例如转换响应体
log_by_lua* 异步完成日志记录,日志可以记录在本地,还可以同步到其他机器

尽管仅使用单个阶段的指令content_by_lua*就可以完成以上职责,但是把逻辑划分在不同阶段,更加容易维护。

HowTos
API框架

一个简单的API框架:

nginx.conf
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
daemon off;
worker_processes  1;
error_log stderr debug;
events {
    worker_connections 1024;
}
http {
    access_log /dev/stdout;
    # lua模块搜索路径
    # 如果使用相对路径,则必须将Nginx所在目录作为工作目录,然后启动服务
    # ${prefix}为Nginx的前缀目录,可以在启动Nginx时使用-p来指定
    lua_package_path '$prefix/scripts/?.lua;;';
 
    # 在开发阶段,可以设置为off,这样避免每次修改代码后都需要reload
    # 生产环境一定要设置为on
    lua_code_cache off;
 
    server {
        listen 80;
 
        location ~ ^/api/([-_a-zA-Z0-9]+) {
            # 在access阶段执行,进行合法性校验
            access_by_lua_file  scripts/auth-and-check.lua;
            # 生成内容,API名称即为Lua脚本名称
            content_by_lua_file scripts/$1.lua;
        }
    }
}

在access阶段,你可以进行身份验证、访问控制、请求参数校验:

auth-and-check.lua
Lua
1
2
3
4
5
6
7
8
9
-- 黑名单
local black_ips = {["127.0.0.1"]=true}
 
-- 当前客户端IP
local ip = ngx.var.remote_addr
if true == black_ips[ip] then
    -- 返回相应的HTTP状态码
    ngx.exit(ngx.HTTP_FORBIDDEN)
end
使用Ng变量

要在OpenResty中引用Nginx变量,可以使用 ngx.var.VARIABLE,要将变量从字符串转换为数字,可以使用 tonumber函数。

经常用到的Ng变量如下表:

变量 说明
arg_name 请求中的name参数
args 请求中的参数
binary_remote_addr 远程地址的二进制表示
body_bytes_sent 已发送的消息体字节数
content_length HTTP请求信息里的"Content-Length"
content_type 请求信息里的"Content-Type"
document_root 针对当前请求的根路径设置值
document_uri 与$uri相同; 比如 /test2/test.php
host 请求信息中的"Host",如果请求中没有Host行,则等于设置的服务器名
hostname 机器名使用 gethostname系统调用的值
http_cookie Cookie信息
http_referer 引用地址
http_user_agent 客户端代理信息
http_via 最后一个访问服务器的Ip地址。
http_x_forwarded_for 相当于网络访问路径
is_args 如果请求行带有参数,返回“?”,否则返回空字符串
limit_rate

对连接速率的限制。此变量支持写入:

Lua
1
2
-- 设置当前请求的响应传递速率限制
ngx.var.limit_rate = 1000 
nginx_version 当前运行的nginx版本号
pid Worker进程的PID
query_string 与$args相同
realpath_root 按root指令或alias指令算出的当前请求的绝对路径。其中的符号链接都会解析成真是文件路径
remote_addr 客户端IP地址
remote_port 客户端端口号
remote_user 客户端用户名,认证用
request 用户请求
request_body 这个变量(0.7.58+)包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义
request_body_file 客户端请求主体信息的临时文件名
request_completion 如果请求成功,设为"OK";如果请求未完成或者不是一系列请求中最后一部分则设为空
request_filename 当前请求的文件路径名,比如/opt/nginx/www/test.php
request_method 请求的方法,比如"GET"、"POST"等
request_uri 请求的URI,带参数
scheme 所用的协议,比如http或者是https
server_addr 服务器地址,如果没有用listen指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费)
server_name 请求到达的服务器名
server_port 请求到达的服务器端口号
server_protocol 请求的协议版本,"HTTP/1.0"或"HTTP/1.1"
uri 请求的URI,可能和最初的值有不同,比如经过重定向之类的
数据共享
跨工作进程

可以使用共享内存方式实现。

跨请求

可以使用Lua模块方式实现。

跨阶段

在单个请求中,跨越多个Ng处理阶段(access、content)共享变量时,可以使用 ngx.ctx表:

Lua
1
2
3
4
5
6
7
8
9
10
11
location /test {
     rewrite_by_lua_block {
         ngx.ctx.foo = 76
     }
     access_by_lua_block {
         ngx.ctx.foo = ngx.ctx.foo + 3
     }
     content_by_lua_block {
         ngx.say(ngx.ctx.foo)
     }
}

ngx.ctx表的生命周期和请求相同,类似于Nginx变量。需要注意,每个子请求都有自己的ngx.ctx表,它们相互独立。

你可以为ngx.ctx表注册元表,任何数据都可以放到该表中。

注意:访问ngx.ctx需要相对昂贵的元方法调用,不要为了避免传参而大量使用,影响性能。

指定Lua包路径

使用如下指令:

Shell
1
2
3
4
5
6
7
#  设置纯 Lua 扩展库的搜寻路径
# ';;' 是默认路径
lua_package_path "/path/to/lua-resty-logger-socket/lib/?.lua;;";
 
# 设置 C 编写的 Lua 扩展模块的搜寻路径
# ';;' 是默认路径
lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;';
请求参数
Shell
1
2
3
4
location /print-params{
    # 使用Ng配置文件外部的Lua脚本
    content_by_lua_file scripts/print-params.lua;
}
获取URI参数
print-params.lua
Lua
1
2
3
4
local args = ngx.req.get_uri_args()
for name, value in pairs(args) do
    ngx.say(name .. ' ' .. value)
end

测试:

Shell
1
2
3
curl 'http://localhost:8080/print-params?user=alex&age=32'
# user alex
# age 32
获取POST参数 
Lua
1
2
3
4
5
6
7
8
9
-- 必须先读取请求体,才能从中解析参数
ngx.req.read_body()
-- 获取POST参数
local args = ngx.req.get_post_args()
 
for name, value in pairs(args) do
    ngx.say(name .. ' ' .. value)
end
-- curl -d 'user=alex&age=32' 'http://localhost:8080/print-params'
传递请求参数

当调用其它location,需要传递请求参数时,可以进行编码:

Lua
1
2
3
4
5
6
7
8
9
10
11
12
local res = ngx.location.capture('/print-param-internal', {
    -- 调用使用的HTTP方法
    method = ngx.HTTP_POST,
    -- URL参数
    args = ngx.encode_args({ a = 1, b = '2&' }), -- 编码为 'a=1&b=2%26'
    -- POST参数
    body = ngx.encode_args({ c = 3, d = '4&' })
 
    -- 注意,capture可以直接接收Lua Table,下面的更简洁
    args = {a = 1, b = '2&'},
})
ngx.say(res.body)
读取请求体 

当用Nginx作为负载均衡或反向代理时,基本上仅仅需要读取请求头就足够了。使用OpenResty 后,你可以把Nginx直接作为API服务器、Web服务器,这是就需要操控请求体、响应体了。

要通过Lua读取请求体,可以添加指令:

Shell
1
2
# 总是让Lua读取请求体
lua_need_request_body on;

如果仅仅希望某个接口读取请求体,可以调用: ngx.req.read_body()

如果请求体已经被存入临时文件,则需要调用 ngx.req.get_body_file()。

如果需要强制把请求体存入临时文件,配置 client_body_in_file_only on;

如果需要强制在内存中保留请求体,配置client_body_buffer_size和client_max_body_size 为相同值。

读取请求体的代码:

Lua
1
2
3
4
5
local data = ngx.req.get_body_data()
ngx.say(data)
 
-- curl -d Greetings 'http://localhost:8080/get-req-body'
-- Greetings
输出响应体 

要输出响应体,可以调用: ngx.say、 ngx.print,输出不会立即写入套接字,你可以调用 ngx.flush()刷出缓冲区。

大响应体

大静态文件的响应,让Nginx自己完成。

如果是应用程序动态生成的大响应体,可以使用HTTP 1.1的CHUNKED编码。对应响应头: Transfer-Encoding: chunked。这样响应就可以逐块的发送到客户端,不至于占用服务器内存。

Lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 可以进行限速,单位字节
ngx.var.limit_rate = 64
--                        获取配置目录
local file, err = io.open(ngx.config.prefix() .. "nginx.conf", "r")
if not file then
    -- 打印Nginx日志
    ngx.log(ngx.ERR, "open file error:", err)
    -- 以指定的HTTP状态码退出处理
    ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end
-- 如果没有ngx.exit,则:
local data
while true do
    data = file:read(64)
    if nil == data then
        break
    end
    ngx.print(data)
    --        true表示等待IO操作完成
    ngx.flush(true)
    ngx.sleep(1)
end
file:close()
 
-- http://localhost:8080/put-res-body-chunked 会一行行的输出
配合其它location
内部调用

使用内部调用(子查询),可以向某个location非阻塞的发起调用。目录location可以是静态文件目录,也可以由gx_proxy、ngx_fastcgi、ngx_memc、ngx_postgres、ngx_drizzle甚至其它ngx_lua模块提供内容生成。

需要注意:

  1. 内部调用仅仅是模拟HTTP的接口形式,不会产生额外的HTTP/TCP流量
  2. 内部调用完全不同于HTTP 301/302重定向指令,和内部重定向(ngx.exec)也完全不同
  3. 、在发起内部调用之前,必须先读取完整的请求体。你可以设置lua_need_request_body指令或者调用ngx.req.read_body。如果请求体太大,可以考虑使用cosockets模块进行流式处理
  4. 子请求默认继承当前请求的所有请求头信息。配置 proxy_pass_request_headers=off;可以忽略父请求的头
  5. ngx.location.capture/capture_multi无法请求包含以下指令的location:add_before_body, add_after_body, auth_request, echo_location, echo_location_async, echo_subrequest, echo_subrequest_async
Lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
location /sum {
    -- 仅允许内部跳转调用
    internal;
    content_by_lua_block {
        -- 解析请求参数
        local args = ngx.req.get_uri_args()
        -- 输出
        ngx.say(tonumber(args.a)+tonumber(args.b))
    }
}
location /sub {
    internal;
    content_by_lua_block{
        -- 休眠
        ngx.sleep(0.1)
        local args = ngx.req.get_uri_args()
        ngx.print(tonumber(args.a) - tonumber(args.b))
    }
 
}
location /test{
    content_by_lua_block{
        -- 发起一个子查询
        -- res.status 子请求的响应状态码
        -- res.header 子请求的响应头,如果某个头是多值的,则存放在table中
        -- res.body 子请求的响应体
        -- res.truncated 标记响应体是否被截断。截断意味着子请求处理过程中出现不可恢复的错误,例如超时、早断
        local res = ngx.location.capture( "/sum", {
            args={a=3, b=8},  -- 为子请求附加URI参数
            method = ngx.HTTP_POST, -- 指定请求方法,默认GET
            body = 'hello, world'   -- 指定请求体
        } )
        -- 并行的发起多个子查询
        local res1, res2 = ngx.location.capture_multi( {
            {"/sum", {args={a=3, b=8}}},
            {"/sub", {args={a=3, b=8}}}
        })
        ngx.say(res1.status," ",res1.body)
        ngx.say(res2.status," ",res2.body)
    }
}
内部跳转
Lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
location ~ ^/static/([-_a-zA-Z0-9/]+).jpg {
    -- 这里将URI中捕获的第一个分组,赋值给变量
    set $image_name $1;
    content_by_lua_block {
        -- ng.var可以读取Nginx变量
        -- ngx.exec执行跳转
        ngx.exec("/download_internal/images/" .. ngx.var.image_name .. ".jpg");
    };
}
 
location /download_internal {
    internal;
    -- 可以在这里进行各种声明,例如限速
    alias ../download;
}

注意,ngx.exec引发的跳转完全在Ng内部完成,不会产生HTTP协议层的信号。

外部跳转

使用ngx.redirect可以进行外部跳转,也就是重定向。

Lua
1
2
3
4
5
location = / {
    rewrite_by_lua_block {
        return ngx.redirect('/blog');
    }
}
日志记录

OpenResty提供的日志API为 ngx.log(log_level, ...) 日志输出到Nginx的errorlog中。 

支持的日志级别如下:

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

模块lua-resty-logger-socket用于替代ngx_http_log_module,将Nginx日志异步的推送到远程服务器上。该模块的特性包括:

  1. 基于 cosocket 非阻塞 IO 实现
  2. 日志累计到一定量,集体提交,增加网络传输利用率
  3. 短时间的网络抖动,自动容错
  4. 日志累计到一定量,如果没有传输完毕,直接丢弃
  5. 日志传输过程完全不落地,没有任何磁盘 IO 消耗

示例代码:

Shell
1
2
lua_package_path "/path/to/lua-resty-logger-socket/lib/?.lua;;";
log_by_lua_file log.lua;

log.lua
Lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
local logger = require "resty.logger.socket"
if not logger.initted() then
    local ok, err = logger.init {
        host = 'ops.gmem.cc',
        port = 8087,
        flush_limit = 1234,
        drop_limit = 5678,
    }
    if not ok then
        ngx.log(ngx.ERR, "failed to initialize the logger: ", err)
        return
    end
end
 
-- 通过变量msg来访问 accesslog
 
local bytes, err = logger.log(msg)
if err then
    ngx.log(ngx.ERR, "failed to log message: ", err)
    return
end
调用数据库
Lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
-- 引入操控MySQL需要的模块
local mysql = require "resty.mysql"
-- 初始化数据库对象
local db, err = mysql:new()
if not db then
    ngx.say("failed to instantiate mysql: ", err)
    return
end
-- 设置连接超时
db:set_timeout(1000)
-- 设置连接最大空闲时间,连接池容量
db:set_keepalive(10000, 100)
 
-- 发起数据库连接
local ok, err, errno, sqlstate = db:connect {
    host = "127.0.0.1",
    port = 3306,
    database = "test",
    user = "root",
    password = "root",
    max_packet_size = 1024 * 1024
}
 
if not ok then
    ngx.say("Failed to connect: ", err, ": ", errno, " ", sqlstate)
    return
end
 
 
local res, err, _, _ = db:query([[
    DROP TABLE IF EXISTS USERS;
]])
if not res then ngx.say(err); return end
 
res, err, errno, sqlstate = db:query([[
    CREATE TABLE USERS
    (
        ID INT ,
        NAME VARCHAR(64)
    );
]])
if not res then ngx.say(err); return end
 
 
res, err, errno, sqlstate = db:query([[
    INSERT INTO USERS (ID,NAME) VALUES ('10000','Alex');
    INSERT INTO USERS (ID,NAME) VALUES ('10001','Meng');
]])
if not res then ngx.say(err); return end
 
local cjson = require "cjson"
ngx.say(cjson.encode(res))

要防止SQL注入,可以预处理一下用户提供的参数:

Lua
1
req_id = ndk.set_var.set_quote_sql_str(req_id)))
调用HTTP

你可以用ngx.location.capture发起对另外一个location的子调用,并将后者配置为上游服务器的代理。如果:

  1. 内部请求数量较多
  2. 需要频繁修改上游服务器的地址

最好使用lua-resty-http模块。该模块提供了基于cosocket的HTTP客户端。具有特性:

  1. 支持HTTP 1.0/1.1
  2. 支持SSL
  3. 支持响应体的流式接口,内存用量可控
  4. 对于简单的应用场景,提供更简单的接口
  5. 支持Keepalive

示例:

Lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ngx.req.read_body()
-- 获取当前请求的参数
local args, err = ngx.req.get_uri_args()
 
local http = require "resty.http"
-- 创建HTTP客户端
local httpc = http.new()
-- request_uri函数在内部自动处理连接池
local res, err = httpc:request_uri("http://media-api.dev.svc.k8s.gmem.cc:8800/media/newpub/2017-01-01", {
    method = "POST",
    body = args.data,  -- 转发请求参数给上游服务器
})
 
if 200 ~= res.status then
    ngx.exit(res.status)
end
 
if args.key == res.body then
    ngx.say("valid request")
else
    ngx.say("invalid request")
end
编解码JSON

cjson模块提供了编解码JSON的支持。

编码
Lua
1
2
local json = require("cjson")
json.encode(data)

对于稀疏数组,例如:

Lua
1
2
local data = {1, 2}
data[1000] = 99     -- 稀疏

会导致编码失败,提示:Cannot serialise table: excessively sparse array。其原因是数组太稀疏了,cjson为了保护资源默认抛出错误。

如果非要编码稀疏数组,考虑使用 encode_sparse_array函数。

由于Lua把字典、数组统一作为表格管理,因此就会牵涉到某个对象是编码为[]还是{}形式的问题:

Lua
1
2
3
4
5
6
7
cjson.encode({})            -- {}
cjson.encode({dogs = {}})   -- {"dogs":{}}
 
-- 可以提示cjson,让它把空表格编码为数组而非字典
cjson.encode_empty_table_as_object(false)
cjson.encode({})            -- []
cjson.encode({dogs = {}})   -- {"dogs":[]}
常见问题
加载模块失败

报错信息:error loading module 'cjson' from file '/home/alex/Lua/sdk/5.3.4': cannot read /home/alex/Lua/sdk/5.3.4: Is a directory

原因:安装了多套Lua环境,错误的设置了LUA_PATH、LUA_CPATH环境变量导致

解决办法:可以清空这些环境变量。另外这两个环境变量中不要包含目录。

← libevent学习笔记
基于Rook的Kubernetes存储方案 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • Nginx知识集锦
  • Ubuntu的时钟同步
  • 操作系统知识集锦
  • Linux目录层次和配置文件
  • 在Ubuntu上安装VNC服务

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
  • Three.js学习笔记 24 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2