openresty 中主要的几种异步执行方式

openresty 中主要的几种异步执行方式

openresty宣传是同步非阻塞(100% non-blocking)的,基于事件通知的 Nginx 给我们带来了足够强悍的高并发支持。可以让我们可以使用同步的编程方式实现异步处理,但在我们难免在应用中会使用非openresty api的调用,比如

  • 高 CPU 的调用(压缩、解压缩、加解密等)
  • 高磁盘的调用(所有文件操作)
  • 非 OpenResty 提供的网络操作(luasocket 等)
  • 系统命令行调用(os.execute 等)

在使用异步方式执行代码的时候我们常会用到第三方工具,比如队列,存储。我在这里介绍几种openresty自带的异步api,讲究一个原汤化原食

脱离请求进程 - ngx.timer.at(delay, callback, user_arg1, user_arg2, ...)

timer是一个计时器,他将在delay s 之后起一个轻线程,然后调用callback,user_arg1 ... 等是传入参数

定时器看起来很完美,但是有个缺点

  • 在timer中无法使用cosocket

无法使用cosocket比较致命,在openresty中cosocket是核心中的核心,不能用就等于openresty废了一大半,所以我建议使用timer只做些简单的工作,比如保存文件到硬盘。

但如果一定要做一些比如网络请求,我们可以绕过cosocket,比如使用 resty.http库。

	local ok, err = ngx.timer.at(0, function(_, _file_id,_file_data)
		local args = {
			func = 'erase_words_collection',
			file_id = _file_id
		}
		curl('http://127.0.0.1/async/upload_file', 'POST', nil, _file_data, nil, args)
	end, file_id, file_data)
	if not ok then
		nlog.error('async_util.upload_file: create timer error :'.. tostring(err))
	end

另外openresty 也提供了指令和api 管理timer的数量

  • lua_max_running_timers 指定最大运行的定时器数量(默认值为256)
  • lua_max_pending_timers 指定最大等待运行的定时器的数量(默认值为1024)
  • ngx.timer.running_count()
  • ngx.timer.pending_count()

拆分请求 ngx.thread.*

协程是 openresty异步高效的最基础组件

如果是需要批量任务,那么使用协程是个不错的选择,但也要注意协程数量以及慎用wait any(可以参考我的上一篇文章)

 local capture = ngx.location.capture
 local spawn = ngx.thread.spawn
 local wait = ngx.thread.wait
 local say = ngx.say

 local function fetch(uri)
     return capture(uri)
 end

 local threads = {
     spawn(fetch, "/foo"),
     spawn(fetch, "/bar"),
     spawn(fetch, "/baz")
 }

 for i = 1, #threads do
     local ok, res = wait(threads[i])
     if not ok then
         say(i, ": failed to run: ", res)
     else
         say(i, ": status: ", res.status)
         say(i, ": body: ", res.body)
     end
 end

提前返回响应 - ngx.eof()

看一个官网的例子

 location = /async {
     keepalive_timeout 0;
     content_by_lua_block {
         ngx.say("got the task!")
         ngx.eof()  -- well written HTTP clients will close the connection at this point
         -- access MySQL, PostgreSQL, Redis, Memcached, and etc here...
     }
 }

作用一目了然,这里先完成http响应,然后在后完成比如mysql等存储的访问。
另外还提供了个指令,打开之后可以使链接关闭之后还保持子请求

proxy_ignore_client_abort on;

官网推荐最好使用 ngx.timer.at,但我觉得不如eof

A better way to do background jobs is to use the ngx.timer.at API.

总结

  • 如果是期望请求结束后执行,有点复杂 用 ngx.eof
  • 如果比较简单的异步任务,使用timer
  • 如果是很复杂的或者好几个异步任务合并不了,用timer 绕过cosocket
  • 如果是批量的任务 使用thread
posted @   文仔菜菜  阅读(869)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示