ngx_lua_API 指令详解(六)ngx.thread.spawn、ngx.thread.wait、ngx.thread.kill介绍
摘要:通过lua-nginx-module中的ngx.thread同时执行多个任务。
ngx_lua中访问多个第三方服务
ngx_lua中提供了ngx.socket API,可以方便的访问第三方网络服务。如下面的代码,通过get_response函数从两个(或者更多)的源服务器获取数据,再生成响应发给客户端。
location / {
content_by_lua_block {
local get_response(host, port)
local sock = ngx.socket.tcp()
local ok, err = sock:connect(host, port)
if not ok then
return nil, err
end
local data, err = sock:receive()
if not data then
return nil, err
end
return data
end
local first = get_response("lua.org", )
local second = get_response("nginx.org", )
ngx.say(first .. second)
}
}
如果需要10个第三方网络服务,需要调用get_response 10次。总的响应时间与需要连接源的数量成正比。那么如何缩短源的响应时间呢?ngx.thread就是用来解决这种问题的。
二、lua-nginx-module提供了三个API
1、ngx.thread.spawn
2、ngx.thread.wait
3、ngx.thread.kill
三、详解
1、ngx.thread.spawn
重点:ngx.thread.spawn生成新的"light thread",这个"light thread"运行优先级比它的父协程高,会优先运行,父协程被迫暂停。"light thread"运行结束或者yield后,再由ngx_http_lua_run_posted_threads去运行父协程。
参考:ngx_lua中的协程调度(六)之ngx_http_lua_run_posted_thread
通过ngx.thread.spawn可以生成一个"light thread",一个”light thread“和Lua的协程类似,区别在于"light thread"是由ngx_lua模块进行调度的,多个"light thread"同时运行。
"light thread",协程 和 进程。"light thread"比Lua中的协程更像操作系统中的进程。
- fork生成新的进程,生成的多个进程可以同时运行,而ngx.thread.spawn生成新的协程,多个协程同时在跑。
- kill可以杀死不需要的子进程,ngx.thread.kill可以杀死不需要的"light thread"
- wait可以等待子进程结束并取得子进程退出状态,ngx.thread.wait可以等待"light thread"结束并获取其返回值。
ngx.thread的使用,用ngx.thread重写上面的代码
location / {
content_by_lua_block {
local get_response(host, port)
local sock = ngx.socket.tcp()
local ok, err = sock:connect(host, port)
if not ok then
return nil, err
end
local data, err = sock:receive()
if not data then
return nil, err
end
return data
end
local t1 = ngx.thread.spawn(get_response, "lua.org", )
local t2 = ngx.thread.spawn(get_response, "nginx.org", )
local ok, res1, res2 = ngx.thread.wait(t1, t2)
ngx.say(res1 .. res2)
}
}
生成的两个"light thread"可以同时运行,总的耗时只相当于访问一个源服务器的时间,即使需要访问的源服务器增加,耗时没有太大的变化。
"light thread"的调度
Linux中的fork生成新的子进程,父进程与子进程谁先运行呢?都有可能,和系统的调度有关。
把调用ngx.thread.spawn的这个Lua协程称为父协程,生成的"light thread"和父协程谁先运行呢? 在ngx_lua的调度逻辑中,是生成的"light thread"先运行,运行结束或者被挂起后,父协程才会继续运行。实际的代码在ngx_http_lua_run_thread函数中,这个函数比较复杂,涉及的东西太多,稍后再细说。
如下面的代码,没有调用ngx.thread.wait去等待"light thread"的结束。
# [] 没有调用ngx.thread.wait去等待"light thread"的结束
location /thread002 {
content_by_lua_block {
local function f(name)
ngx.log(ngx.ERR, "thread name: ", name, ", now start")
ngx.sleep()
ngx.log(ngx.ERR, "thread name: ", name, ", now end")
end
local t1 = ngx.thread.spawn(f, "first")
local t2 = ngx.thread.spawn(f, "second")
ngx.log(ngx.ERR, "main thread end")
}
}
由Nginx的日志中可以看到当前的请求一直延迟到t1,t2两个"light thread"最后退出才会结束。 Nginx中日志的顺序也可以看出父协程和两个"light thread"的执行那个顺序。
// :: [error] #: * [lua] content_by_lua(thread.conf:):: thread name: first, now start, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"
// :: [error] #: * [lua] content_by_lua(thread.conf:):: thread name: second, now start, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"
// :: [error] #: * [lua] content_by_lua(thread.conf:):: main thread end, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"
// :: [error] #: * [lua] content_by_lua(thread.conf:):: thread name: first, now end, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"
// :: [error] #: * [lua] content_by_lua(thread.conf:):: thread name: second, now end, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread002 HTTP/1.1", host: "127.0.0.1:8689"

而如果代码中主动调用了ngx.exit()结束请求,那么t1,t2两个没有打印出完全的信息就被kill掉了。
# [] 代码中主动调用了ngx.exit()结束请求,那么t1,t2两个没有打印出完全的信息就被kill掉了
location /thread003 {
content_by_lua_block {
local function f(name)
ngx.log(ngx.ERR, "thread name: ", name, ", now start")
ngx.sleep()
ngx.log(ngx.ERR, "thread name: ", name, ", now end")
end
local t1 = ngx.thread.spawn(f, "first")
local t2 = ngx.thread.spawn(f, "second")
ngx.exit()
}
}
相应的Nginx日志
// :: [error] #: * [lua] content_by_lua(thread.conf:):: thread name: first, now start, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread003 HTTP/1.1", host: "127.0.0.1:8689"
// :: [error] #: * [lua] content_by_lua(thread.conf:):: thread name: second, now start, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread003 HTTP/1.1", host: "127.0.0.1:8689"
// :: [error] #: * [lua] content_by_lua(thread.conf:):: main thread end, client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread003 HTTP/1.1", host: "127.0.0.1:8689"

"light thread"的限制
"light thread"毕竟是基于依附于请求的,如在content_by_lua中创建的"light thread",是完全与当前的请求关联的,如果"light thread"没有退出,当前请求也无法结束。同样如果当前请求因为错误退出,或调用ngx.exit强制退出时,处于运行状态的"light thread"也会被kill掉。不像操作系统的进程,父进程退出后,子进程可以被init进程"收养"。
错误代码:
2017/07/21 09:14:10 [error] 114598#0: *1 failed to load inlined Lua code: content_by_lua(thread.conf:128):2: ')' expected near ',', client: 127.0.0.1, server: 127.0.0.1, request: "GET /thread001 HTTP/1.1", host: "127.0.0.1:8689"
ngx_lua_API 指令详解(六)ngx.thread.spawn、ngx.thread.wait、ngx.thread.kill介绍的更多相关文章
- ngx_lua_API 指令详解(二)ngx.re.match/find/gmatch/sub/gsub指令集合
1.先来个官方的ngx.re.match location /ngx_re_match { default_type text/html; content_by_lua_block { local m ...
- ngx_lua_API 指令详解(五)coroutine.create,coroutine.resume,coroutine.yield 等集合指令介绍
ngx_lua 模块(原理实现) 1.每个worker(工作进程)创建一个Lua VM,worker内所有协程共享VM: 2.将Nginx I/O原语封装后注入 Lua VM,允许Lua代码直接访问: ...
- ngx_lua_API 指令详解(四)ngx.exec指令
https://github.com/openresty/lua-nginx-module#ngxexec 参照:http://blog.csdn.net/weiyuefei/article/deta ...
- ngx_lua_API 指令详解(一)ngx.timer.at 指令
语法: ok,err = ngx.timer.at(delay,callback,user_arg1,user_arg2 ...) 上下文: init_worker_by_lua *,set_by_l ...
- ngx_lua_API 指令详解(三)怎样理解 cosocket指令
参考:https://moonbingbing.gitbooks.io/openresty-best-practices/content/ngx_lua/whats_cosocket.html 春哥演 ...
- [转]JVM指令详解(上)
作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 本文主要记录一些JVM指令,便于记忆与查阅. 一.未归类系列A 此系列暂未归类. 指令码 助记符 ...
- C#中的预处理器指令详解
这篇文章主要介绍了C#中的预处理器指令详解,本文讲解了#define 和 #undef.#if.#elif.#else和#endif.#warning和#error.#region和#endregio ...
- rsync指令详解
rsync指令详解(更详细的看官方文档http://rsync.samba.org/ftp/rsync/rsync.html) [root@Centos epel]# rsync --help rsy ...
- #pragma 预处理指令详解
源地址:http://blog.csdn.net/jx_kingwei/article/details/367312 #pragma 预处理指令详解 在所有的预处理指令中, ...
随机推荐
- 阿里云CentOS 7.4 64位,jdk1.6、mysql5.7、tomcat6部署步骤(个人记录)
jdk1.6.mysql5.7.tomcat6部署步骤 一.安装jdk1.6 查看当前系统jdk的版本:java -version 方法一:利用yum源来安装jdk(此方法不需要配置环境变量) 查看y ...
- GitHub 新手教程 五,Git GUI 新手教程(2),Clone Existing Repository 克隆代码库
1,注意: 网上大部分教程都是从“Create New Repository”讲起,这其实给我们新手埋了很大的一个坑.按照类似的教程,仅做到一半,各种错误窗口就会弹出来了,像什么:“非同一代码库”.“ ...
- 一份超全超详细的 ADB 用法大全
http://blog.csdn.net/u010375364/article/details/52344120
- “Linux内核分析”实验一报告
张文俊 + 原创作品转载请注明出处 + <Linux 内核分析> MOOC 课程 实验要求: 1.总结部分要求阐明自己对“计算机是如何工作的”理解: 2.博客中需要使用实验截图: 实验内容 ...
- PAT 甲级 1022 Digital Library
https://pintia.cn/problem-sets/994805342720868352/problems/994805480801550336 A Digital Library cont ...
- Docker(二十一)-Docker Swarm集群部署
介绍 Swarm 在 Docker 1.12 版本之前属于一个独立的项目,在 Docker 1.12 版本发布之后,该项目合并到了 Docker 中,成为 Docker 的一个子命令.目前,Swarm ...
- helm 替换源的方法
网上找了一个 helm 替换源的方法 挺好用的 mark 一下 helm repo remove stable helm repo add stable https://kubernetes.oss- ...
- CentOS修改主机名字
目录 查看hostnmae 修改hostname 远程别名/etc/hosts 查看hostnmae [root@centos ~]$ hostname centos 修改hostname [root ...
- Vue---父子组件之间的通信
在vue组件通信中其中最常见通信方式就是父子组件之中的通信,而父子组件的设定方式在不同情况下又各有不同.最常见的就是父组件为控制组件子组件为视图组件.父组件传递数据给子组件使用,遇到业务逻辑操作时子组 ...
- codeforces4A
Watermelon CodeForces - 4A Qingyu有一个简单的问题想让你解决. 输入一个数,如果它是2,或者它是奇数,输出NO,否则输出YES. 很简单吧,因此你应该很快解决. 输入 ...