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、每个外部请求都由一个Lua协程处理,协程之间数据隔离; 4、Lua代码调用I/O操作...

ngx_lua 模块(原理实现)  

1、每个worker(工作进程)创建一个Lua VM,worker内所有协程共享VM;

2、将Nginx I/O原语封装后注入 Lua VM,允许Lua代码直接访问;

3、每个外部请求都由一个Lua协程处理,协程之间数据隔离;

4、Lua代码调用I/O操作等异步接口时,会挂起当前协程(并保护上下文数据),而不阻塞worker;

5、I/O等异步操作完成时还原相关协程上下文数据,并继续运行;

lua-nginx-module中,在Log,Header_filter,Body_filter几个阶段直接调用lua_pcall执行Lua脚本,

而在Access,Rewrite,Content等阶段Lua脚本的执行是在ngx_http_lua_run_thread函数中调用lua_resume实现的。再根据lua_resume的返回值进行处理。

一、命令列表

  1、coroutine.create - 创建协程

  2、coroutine.resume - 执行协程

  3、coroutine.yield - 中断协程运行

  4、以 coroutine.wrap 的方式创建协程

  5、coroutine.running - 返回正在运行中的协程

  6、coroutine.status - 查看协程状态

  7、resume-yield 交互

二、命令详解

1、coroutine.create - 创建协程

语法: co = coroutine.create(f)

上下文: rewrite_by_lua *,access_by_lua *,content_by_lua *,init_by_lua *,ngx.timer。*,header_filter_by_lua *,body_filter_by_lua *,ssl_certificate_by_lua *,ssl_session_fetch_by_lua *,ssl_session_store_by_lua *

描述:函数 coroutine.create 用于创建一个新的协程,它只有一个以函数形式传入的参数,该函数是协程的主函数,它的代码是协程所需执行的内容

co = coroutine.create(function() 
    io.write("coroutine create!\n") 
end)
print(co)

当创建完一个协程后,会返回一个类型为 thread 的对象,但并不会马上启动运行协程主函数,协程的初始状态是处于挂起状态,如图所示:

 

lua-nginx-module中的coroutine API和原生Lua中类似,和ngx.thread不同,coroutine.create创建的协程需要手动去运行,所以resume和yield都需要在ngx_http_lua_run_thread中进行协程的切换。

2、coroutine.resume - 执行协程

语法: ok,... = coroutine.resume(co,...)

上下文: rewrite_by_lua *,access_by_lua *,content_by_lua *,init_by_lua *,ngx.timer。*,header_filter_by_lua *,body_filter_by_lua *,ssl_certificate_by_lua *,ssl_session_fetch_by_lua *,ssl_session_store_by_lua *

描述:协程有 4 种状态,分别是:挂起(suspended)、运行(running)、死亡(dead)和正常(normal),可以通过 coroutine.status 来输出查看协程当前的状态。

以上可以看出,新创建的初始状态是处于挂起(suspended)状态

 3、coroutine.yield - 中断协程运行

语法: ... = coroutine.yield(...)

上下文: rewrite_by_lua *,access_by_lua *,content_by_lua *,init_by_lua *,ngx.timer。*,header_filter_by_lua *,body_filter_by_lua *,ssl_certificate_by_lua *,ssl_session_fetch_by_lua *,ssl_session_store_by_lua *

描述:coroutine.yield 函数可以让一个运行中的协程中断挂起,运行以下代码:

co = coroutine.create(function()
    for i = 1, 3 do
        print("before coroutine yield", i)
        -- 协程中断挂起
        coroutine.yield()
        print("after coroutine yield", i)
    end
end)
-- run an coroutine
coroutine.resume(co)

RUN 结果

coroutine.resume(co) 上面第一个 resume 唤醒执行协程主函数代码,直到第一个 yield。第二个 resume 激活被挂起的协程,并从上一次协程被中断 yield 的位置继续执行协程主函数代码,直到再次遇到 yield 或程序结束。

resume 执行完协程主函数或者中途被挂起(yield)时,会有返回值返回,第一个值是 true,表示执行没有错误。如果是被 yield 挂起暂停,yield 函数有参数传入的话,这些参数会接着第一个值后面一并返回:

co = coroutine.create(function(a, b, c)
    coroutine.yield(a, b, c)
end)
print(coroutine.resume(co, 1, 2, 3))

RUN结果

》》ngx_lua 导致协程yield的API主要分以下几种

(1)、ngx.exit,

(2)、ngx.exec

(3)、ngx.redirect

这三种情况都不需要协程继续运行了,退出执行相应的处理,更多了解:ngx_lua的协程调度(五)之ngx_http_lua_run_thread 

 

4、以 coroutine.wrap 的方式创建协程

语法: co = coroutine.wrap(f)

上下文: rewrite_by_lua *,access_by_lua *,content_by_lua *,init_by_lua *,ngx.timer。*,header_filter_by_lua *,body_filter_by_lua *,ssl_certificate_by_lua *,ssl_session_fetch_by_lua *,ssl_session_store_by_lua *

描述:跟 coroutine.create 一样,函数 coroutine.wrap 也是创建一个协程,但是它并不返回一个类型为 thread 的对象,而是返回一个函数。每当调用这个返回函数,都会执行协程主函数运行。所有传入这个函数的参数等同于传入 coroutine.resume 的参数。 coroutine.wrap 会返回所有应该由除第一个(错误代码的那个布尔量) 之外的由 coroutine.resume 返回的值。 和 coroutine.resume 不同之处在于, coroutine.wrap 不会返回错误代码,无法检测出运行时的错误,也无法检查 wrap 所创建的协程的状态。

function wrap(param)
    print("Before yield", param)
    obtain = coroutine.yield()
    print("After yield", obtain)
    return 3
end
resumer = coroutine.wrap(wrap) 
 
print(resumer(1))
 
print(resumer(2))

RUN结果

5、coroutine.running - 返回正在运行中的协程。

语法: co = coroutine.running()

上下文: rewrite_by_lua *,access_by_lua *,content_by_lua *,init_by_lua *,ngx.timer。*,header_filter_by_lua *,body_filter_by_lua *,ssl_certificate_by_lua *,ssl_session_fetch_by_lua *,ssl_session_store_by_lua *

描述:函数 coroutine.running 用于返回正在运行中的协程,如果没有协程运行,则返回 nil

print(coroutine.running()) -- nil
 
co = coroutine.create(function() 
    print(coroutine.running())         -- thread
    print(coroutine.running() == co)   -- true
end)
coroutine.resume(co)
 
print(coroutine.running())   -- nil

RUN结果

 6、coroutine.status - 查看协程状。

语法: status = coroutine.status(co)

上下文: rewrite_by_lua *,access_by_lua *,content_by_lua *,init_by_lua *,ngx.timer。*,header_filter_by_lua *,body_filter_by_lua *,ssl_certificate_by_lua *,ssl_session_fetch_by_lua *,ssl_session_store_by_lua *

描述:可以通过 coroutine.status 来输出查看协程当前的状态。

function greet()
    print "hello world"
end

co = coroutine.create(greet) -- 创建 coroutine

print(coroutine.status(co))  -- 输出 suspended
print(coroutine.resume(co))  -- 输出 hello world
                             -- 输出 true (resume 的返回值)
print(coroutine.status(co))  -- 输出 dead
print(coroutine.resume(co))  -- 输出 false    cannot resume dead coroutine (resume 的返回值)
print(type(co))              -- 输出 thread

RUN结果

协程在创建时,需要把协程体函数传递给创建函数 create。新创建的协程处于 suspended 状态,可以使用 resume 让其运行,全部执行完成后协程处于 dead 状态。如果尝试 resume 一个 dead 状态的,则可以从 resume 返回值上看出执行失败。另外你还可以注意到 Lua 中协程(coroutine)的变量类型其实叫做「thread」Orz...

7、resume-yield 交互

描述:下面代码放在一个 lua 文件里运行,随便输入一些字符后按回车,则会返回输出刚才输入的内容:

function receive(prod)
    local status, value = coroutine.resume(prod)
    return value
end
 
function send(x)
    coroutine.yield(x)
end
 
function producer()
    return coroutine.create(function()
        while true do
            local x = io.read()
            send(x)
        end
    end)
end
 
function filter(prod)
    return coroutine.create(function()
--      for line = 1, math.huge do
        for line = 1, 5 do
            local x = receive(prod)
            x = string.format("%5d Enter is %s", line, x)
            send(x)
        end
    end)
end
 
function consumer(prod)
--  repeat
--      local x = receive(prod)
--      print(type(x))
--      if x then
--          io.write(x, "\n")
--      end
--  until x == nil 
    while true do
        local obtain = receive(prod)
        if obtain then
            io.write(obtain, "\n\n")
        else
            break
        end
    end
end
 
p = producer()
f = filter(p)
consumer(f)

RUN结果:

 

 参照文献:https://github.com/ffmpeg-live/luaprimer/edit/master/05.md

目录
相关文章
|
10天前
|
安全 Java 大数据
|
5月前
|
存储 算法 Java
【Java 集合框架API接口】Collection,List,Set,Map,Queue,Deque
【Java 集合框架API接口】Collection,List,Set,Map,Queue,Deque
|
5月前
|
存储 JavaScript 前端开发
java9新特性之-String存储结构变更--集合工厂方法-- InputStream 加强--增强的 Stream API讲解
java9新特性之-String存储结构变更--集合工厂方法-- InputStream 加强--增强的 Stream API讲解
42 0
|
5月前
|
前端开发 JavaScript API
AngularJS 的 API:模块 API、指令 API、服务 API、过滤器 API、路由 API
AngularJS 的 API:模块 API、指令 API、服务 API、过滤器 API、路由 API
47 0
|
12月前
|
API
java202303java学习笔记第三十二天Map集合常见api
java202303java学习笔记第三十二天Map集合常见api
44 0
|
12月前
java202303java学习笔记第三十二天Map集合常见api2
java202303java学习笔记第三十二天Map集合常见api2
34 0
|
存储 JSON 自然语言处理
国王小组:数字货币交易所开发API客户端的集合
DAPP交易所开发稳定版丨DAPP交易所系统开发(开发案例)丨DAPP交易所系统源码部署 数字货币去中心化交易所开发详情版丨数字货币去中心化交易所系统开发(原生开发)丨数字货币去中心化交易所现成源码模板 去中心化交易所开发丨去中心化交易所系统开发(功能及逻辑)丨去中心化交易所系统源码部署 海外版数字货币交易所系统开发(逻辑及功能)丨多语言数字货币交易所系统开发(案例及源码) 数字货币交易所系统开发(功能版)丨数字货币交易所开发源码案例部署 交易所系统开发(原生开发)丨 交易所系统开发(多语言)丨交易所源码详情 交易所源码案例丨交易所APP开发丨交易所系统开发(海外版) 秒合约交易所系统开发实
|
存储 网络协议 API
案例分享:某品牌音响系列协议调试工具(搜寻主机,查询通道,基本控制API,云音乐API,语言节目API等,可增删改指令)
案例分享:某品牌音响系列协议调试工具(搜寻主机,查询通道,基本控制API,云音乐API,语言节目API等,可增删改指令)
案例分享:某品牌音响系列协议调试工具(搜寻主机,查询通道,基本控制API,云音乐API,语言节目API等,可增删改指令)
J3
|
存储 达摩院 NoSQL
Redis之Set集合数据类型API使用及图文并茂应用场景,不看血亏!
Redis之Set集合数据类型API使用及图文并茂应用场景,不看血亏!
J3
394 0
Redis之Set集合数据类型API使用及图文并茂应用场景,不看血亏!
|
JavaScript 前端开发 定位技术
Vue 集成腾讯地图基础api Demo集合(基础地图引入与展示模块,地址逆解析,3D/2D切换 ,位置服务,mark标记)
Vue 集成腾讯地图基础api Demo集合(基础地图引入与展示模块,地址逆解析,3D/2D切换 ,位置服务,mark标记)
857 0
Vue 集成腾讯地图基础api  Demo集合(基础地图引入与展示模块,地址逆解析,3D/2D切换 ,位置服务,mark标记)