摘要:通过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介绍的更多相关文章

  1. 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 ...

  2. ngx_lua_API 指令详解(五)coroutine.create,coroutine.resume,coroutine.yield 等集合指令介绍

    ngx_lua 模块(原理实现) 1.每个worker(工作进程)创建一个Lua VM,worker内所有协程共享VM: 2.将Nginx I/O原语封装后注入 Lua VM,允许Lua代码直接访问: ...

  3. ngx_lua_API 指令详解(四)ngx.exec指令

    https://github.com/openresty/lua-nginx-module#ngxexec 参照:http://blog.csdn.net/weiyuefei/article/deta ...

  4. ngx_lua_API 指令详解(一)ngx.timer.at 指令

    语法: ok,err = ngx.timer.at(delay,callback,user_arg1,user_arg2 ...) 上下文: init_worker_by_lua *,set_by_l ...

  5. ngx_lua_API 指令详解(三)怎样理解 cosocket指令

    参考:https://moonbingbing.gitbooks.io/openresty-best-practices/content/ngx_lua/whats_cosocket.html 春哥演 ...

  6. [转]JVM指令详解(上)

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 本文主要记录一些JVM指令,便于记忆与查阅. 一.未归类系列A 此系列暂未归类. 指令码    助记符      ...

  7. C#中的预处理器指令详解

    这篇文章主要介绍了C#中的预处理器指令详解,本文讲解了#define 和 #undef.#if.#elif.#else和#endif.#warning和#error.#region和#endregio ...

  8. rsync指令详解

    rsync指令详解(更详细的看官方文档http://rsync.samba.org/ftp/rsync/rsync.html) [root@Centos epel]# rsync --help rsy ...

  9. #pragma 预处理指令详解

    源地址:http://blog.csdn.net/jx_kingwei/article/details/367312 #pragma  预处理指令详解              在所有的预处理指令中, ...

随机推荐

  1. Java设计模式之适配器设计模式(项目升级案例)

    今天是我学习到Java设计模式中的第三个设计模式了,但是天气又开始变得狂热起来,对于我这个凉爽惯了的青藏人来说,又是非常闹心的一件事儿,好了不管怎么样,目标还是目标(争取把23种Java设计模式接触一 ...

  2. Js_闭包跟作用域

    作用域的嵌套将形成作用域链,函数的嵌套将形成闭包.闭包与作用域链是 JavaScript 区别于其它语言的重要特性之一. 作用域 JavaScript 中有两种作用域:函数作用域和全局作用域. 在一个 ...

  3. 一次VB汇编中看-溢出计算

    图文记录 一.观察程序特点和运行逻辑 带弹窗 是VB开发的 需要用户名和注册码 有弹框 具备了很简单的特点…… 错误弹框,如图 二.定位 弹窗内容入手,搜索关键字定位到关键跳,nop掉或者je改jne ...

  4. python基础篇----基本数据类型

    bit  #bit_length 当前数字的二进制,只用用n位来表示a = 123b = a.bit_length()print(b)#==>7

  5. [环境配置]Ubuntu 16.04 源码编译安装OpenCV-3.2.0+OpenCV_contrib-3.2.0及产生的问题

    1.OpenCV-3.2.0+OpenCV_contrib-3.2.0编译安装过程 1)下载官方要求的依赖包 GCC 4.4.x or later CMake 2.6 or higher Git GT ...

  6. Android 使用 OnTouchListener 接口监听双击或多击事件

    这里是使用 OnTouchListener 实现的监听双击 or 多击的监听器.通过 View.setOnTouchListener ,可以实现在任意 View 上监听双击事件. 网上有许多文章简单的 ...

  7. Vue 入门之 Vuex 实战

    Vue 入门之 Vuex 实战 引言 Vue 组件化做的确实非常彻底,它独有的 vue 单文件组件也是做的非常有特色.组件化的同时带来的是:组件之间的数据共享和通信的难题. 尤其 Vue 组件设计的就 ...

  8. 课堂讨论 alpha版最后总结

    议时间:组队开发最后总结会议   星期一   下午4:30-5:30 会议地点:学院楼自习室 到会人员:唐野野 胡潘华 王永伟 魏孟 会议概要: 1.展示最后开发成果: 2.交流开发过程心得体会: 会 ...

  9. JavaMail实现邮箱之间发送邮件功能

    package com.minstone.message.util; import java.util.Date; import java.util.Properties; import javax. ...

  10. Tomcat下bootstrap启动分析

    "C:\Program Files\Java\jdk1.7.0\bin\javaw.exe" -agentlib:jdwp=transport=dt_socket,suspend= ...