lua-resty-exec
在 OpenResty 中运行外部程序,无需启动 shell 或阻塞
$ opm get jprjr/lua-resty-exec
lua-resty-exec
一个用于执行进程的小型 Lua 模块。它主要用于 OpenResty,但也适用于常规 Lua 应用程序。当与 OpenResty 一起使用时,它完全是非阻塞的(否则它将回退到使用 LuaSocket 并阻塞)。
它类似于(并受到启发)lua-resty-shell,主要区别在于此模块使用 sockexec,它不会启动 shell - 相反,您提供一个参数字符串数组,这意味着您不需要担心 shell 转义/引用/解析规则。
此外,从 2.0.0 版开始,您可以使用 resty.exec.socket
访问更低级的接口,该接口允许与程序进行双向通信。您可以读取和写入正在运行的应用程序!
这需要您的 Web 服务器运行 sockexec 的活动实例。
变更日志
3.0.0
返回的新字段:
unknown
- 如果发生这种情况,请告诉我错误!
2.0.0
新的
resty.exec.socket
模块用于使用双工连接resty.exec
不再使用bufsize
参数resty.exec
现在接受timeout
参数,以毫秒为单位指定,默认为 60 秒这是一个重大修订,请在升级之前彻底测试!
2.0.0
之前没有变更日志
安装
lua-resty-exec
在 luarocks 和 opm 上可用,您可以使用 luarocks install lua-resty-exec
或 opm get jprjr/lua-resty-exec
安装它。
如果您在 OpenResty 之外使用它,您还需要安装 LuaSocket 模块,即 luarocks install luasocket
。
此外,您需要运行 sockexec
,请参阅 其仓库 获取说明。
resty.exec
用法
local exec = require'resty.exec'
local prog = exec.new('/tmp/exec.sock')
创建一个新的 prog
对象,使用 /tmp/exec.sock
与 sockexec 连接。
从那里,您可以通过两种不同的方式使用 prog
ez-模式
local res, err = prog('uname')
-- res = { stdout = "Linux\n", stderr = nil, exitcode = 0, termsig = nil }
-- err = nil
ngx.print(res.stdout)
这将运行 uname
,并且在 stdin 上没有数据。
返回一个输出/错误代码表,其中 err
设置为遇到的任何错误。
事先设置 argv
prog.argv = { 'uname', '-a' }
local res, err = prog()
-- res = { stdout = "Linux localhost 3.10.18 #1 SMP Tue Aug 2 21:08:34 PDT 2016 x86_64 GNU/Linux\n", stderr = nil, exitcode = 0, termsig = nil }
-- err = nil
ngx.print(res.stdout)
事先设置 stdin
prog.stdin = 'this is neat!'
local res, err = prog('cat')
-- res = { stdout = "this is neat!", stderr = nil, exitcode = 0, termsig = nil }
-- err = nil
ngx.print(res.stdout)
使用显式 argv、stdin 数据、stdout/stderr 回调调用
local res, err = prog( {
argv = 'cat',
stdin = 'fun!',
stdout = function(data) print(data) end,
stderr = function(data) print("error:", data) end
} )
-- res = { stdout = nil, stderr = nil, exitcode = 0, termsig = nil }
-- err = nil
-- 'fun!' is printed
注意:这里 argv
是一个字符串,如果您的程序不需要任何参数,这很好。
设置 stdout/stderr 回调
如果您将 prog.stdout
或 prog.stderr
设置为函数,它将为收到的每个 stdout/stderr 数据块调用。
请注意,没有保证 stdout/stderr 是一个完整的字符串,或者任何特别合理的东西!
prog.stdout = function(data)
ngx.print(data)
ngx.flush(true)
end
local res, err = prog('some-program')
将超时视为非错误
默认情况下,sockexec
将超时视为错误。您可以通过将对象的 timeout_fatal
键设置为 false 来禁用此功能。示例
-- set timeout_fatal = false on the prog objects
prog.timeout_fatal = false
-- or, set it at calltime:
local res, err = prog({argv = {'cat'}, timeout_fatal = false})
但我实际上想要一个 shell!
没问题!您可以这样做
local res, err = prog('bash','-c','echo $PATH')
或者,如果您想运行整个脚本
prog.stdin = script_data
local res, err = prog('bash')
-- this is roughly equivalent to running `bash < script` on the CLI
守护进程
我通常建议不要守护进程 - 我认为使用某种消息队列和/或监控系统要好得多,这样您就可以监控进程,在失败时采取行动,等等。
也就是说,如果您想启动一些进程,您可以使用 start-stop-daemon
,即
local res, err = prog('start-stop-daemon','--pidfile','/dev/null','--background','--exec','/usr/bin/sleep', '--start','--','10')
将以分离的后台进程启动 sleep 10
。
如果您不想处理 start-stop-daemon
,我有一个用于启动后台程序的小型实用程序,称为 idgaf,即
local res, err = prog('idgaf','sleep','10')
这基本上会完成 start-stop-daemon
所做的事情,而无需使用数十亿个标志。
resty.exec.socket
用法
local exec_socket = require'resty.exec.socket'
-- you can specify timeout in milliseconds, optional
local client = exec_socket:new({ timeout = 60000 })
-- every new program instance requires a new
-- call to connect
local ok, err = client:connect('/tmp/exec.sock')
-- send program arguments, only accepts a table of
-- arguments
client:send_args({'cat'})
-- send data for stdin
client:send('hello there')
-- receive data
local data, typ, err = client:receive()
-- `typ` can be one of:
-- `stdout` - data from the program's stdout
-- `stderr` - data from the program's stderr
-- `exitcode` - the program's exit code
-- `termsig` - if terminated via signal, what signal was used
-- if `err` is set, data and typ will be nil
-- common `err` values are `closed` and `timeout`
print(string.format('Received %s data: %s',typ,data)
-- will print 'Received stdout data: hello there'
client:send('hey this cat process is still running')
data, typ, err = client:receive()
print(string.format('Received %s data: %s',typ,data)
-- will print 'Received stdout data: hey this cat process is still running'
client:send_close() -- closes stdin
data, typ, err = client:receive()
print(string.format('Received %s data: %s',typ,data)
-- will print 'Received exitcode data: 0'
data, typ, err = client:receive()
print(err) -- will print 'closed'
client
对象方法
ok, err = client:connect(path)
**
通过 unix 套接字连接到给定的路径。如果这在 nginx 中运行,unix:
字符串将自动添加在前面。
bytes, err = client:send_args(args)
**
将参数表发送到 sockexec 并启动程序。
bytes, err = client:send_data(data)
**
将 data
发送到程序的标准输入
bytes, err = client:send(data)
**
只是 client:send_data(data)
的快捷方式
bytes, err = client:send_close()
**
关闭程序的标准输入。您也可以发送一个空字符串,例如 client:send_data('')
data, typ, err = client:receive()
**
从正在运行的进程接收数据。typ
表示数据类型,可以是 stdout
、stderr
、termsig
、exitcode
err
通常是 closed
或 timeout
client:close()
**
强制关闭客户端连接
client:getfd()
**
一个 getfd 方法,如果您想在 select 循环中监控底层套接字连接,这很有用
一些示例 nginx 配置
假设您在 /tmp/exec.sock
处运行 sockexec
$ sockexec /tmp/exec.sock
然后在您的 nginx 配置中
location /uname-1 {
content_by_lua_block {
local prog = require'resty.exec'.new('/tmp/exec.sock')
local data,err = prog('uname')
if(err) then
ngx.say(err)
else
ngx.say(data.stdout)
end
}
}
location /uname-2 {
content_by_lua_block {
local prog = require'resty.exec'.new('/tmp/exec.sock')
prog.argv = { 'uname', '-a' }
local data,err = prog()
if(err) then
ngx.say(err)
else
ngx.say(data.stdout)
end
}
}
location /cat-1 {
content_by_lua_block {
local prog = require'resty.exec'.new('/tmp/exec.sock')
prog.stdin = 'this is neat!'
local data,err = prog('cat')
if(err) then
ngx.say(err)
else
ngx.say(data.stdout)
end
}
}
location /cat-2 {
content_by_lua_block {
local prog = require'resty.exec'.new('/tmp/exec.sock')
local data,err = prog({argv = 'cat', stdin = 'awesome'})
if(err) then
ngx.say(err)
else
ngx.say(data.stdout)
end
}
}
location /slow-print {
content_by_lua_block {
local prog = require'resty.exec'.new('/tmp/exec.sock')
prog.stdout = function(v)
ngx.print(v)
ngx.flush(true)
end
prog('/usr/local/bin/slow-print')
}
# look in `/misc` of this repo for `slow-print`
}
location /shell {
content_by_lua_block {
local prog = require'resty.exec'.new('/tmp/exec.sock')
local data, err = prog('bash','-c','echo $PATH')
if(err) then
ngx.say(err)
else
ngx.say(data.stdout)
end
}
}
许可证
MIT 许可证(见 LICENSE
)
POD 错误
嘿!上面的文档有一些编码错误,解释如下:
- 大约在第 300 行
-
未终止的 B<...> 序列
- 大约在第 314 行
-
未终止的 B<...> 序列
- 大约在第 327 行
-
未终止的 B<...> 序列
- 大约在第 340 行
-
未终止的 B<...> 序列
- 大约在第 353 行
-
未终止的 B<...> 序列
- 大约在第 367 行
-
未终止的 B<...> 序列
- 大约在第 383 行
-
未终止的 B<...> 序列
- 大约在第 396 行
-
未终止的 B<...> 序列
作者
John Regan
许可证
mit
依赖关系
luajit, jprjr/netstring >= 1.0.6
版本
-
jprjr/lua-resty-exec 3.0.3在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-08-22 13:24:45
-
jprjr/lua-resty-exec 3.0.1在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-07-25 21:40:59
-
jprjr/lua-resty-exec 3.0.0在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-07-25 21:38:47
-
jprjr/lua-resty-exec 2.0.1在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-07-05 16:05:43
-
jprjr/lua-resty-exec 2.0.0在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-06-23 02:23:19
-
jprjr/lua-resty-exec 1.2.1在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-06-22 15:52:02
-
jprjr/lua-resty-exec 1.2.0在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-04-24 23:56:42
-
jprjr/lua-resty-exec 1.1.4在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-03-31 20:57:51
-
jprjr/lua-resty-exec 1.1.3在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-03-26 19:56:42
-
jprjr/lua-resty-exec 1.1.2在 OpenResty 中运行外部程序,无需启动 shell 或阻塞 2017-02-02 19:45:18