openresty协程

admin 2023年10月7日15:43:54评论26 views字数 2603阅读8分40秒阅读模式
cosocket 是把协程和网络套接字的英文拼在一起形成的,即 cosocket = coroutine + socket遇到网络 I/O 时,它会交出控制权(yield),把网络事件注册到 Nginx 监听列表中,并把权限交给 Nginx;当有 Nginx 事件达到触发条件时,便唤醒对应的协程继续处理(resume),最终实现了非阻塞网络 I/O
API
  • 创建对象:ngx.socket.tcp

  • 设置超时:tcpsock:settimeout tcpsock:settimeouts

  • 建立连接:tcpsock:connect

  • 发送数据:tcpsock:send

  • 接受数据:tcpsock:receivetcpsock:receiveany tcpsock:receiveuntil

  • 连接池tcpsock:setkeepalive

  • 关闭连接:tcpsock:close

  • 上下文:

rewrite_by_lua*,

access_by_lua*,

content_by_lua*,

ngx.timer.*,

ssl_certificate_by_lua*,

ssl_session_fetch_by_lua*_

cosocket API set_by_lua log_by_lua header_filter_by_lua* body_filter_by_lua* 中是无法使用的。init_by_lua* init_worker_by_lua* 中暂时也不能用。与这些API相应的Nginx指令:

    lua_socket_connect_timeout:连接超时,默认 60 秒。lua_socket_send_timeout:发送超时,默认 60 秒。lua_socket_send_lowat:发送阈值(low water),默认为 0。lua_socket_read_timeout:读取超时,默认 60 秒。lua_socket_buffer_size:读取数据的缓存区大小,默认 4k/8k。lua_socket_pool_size连接池大小,默认 30。lua_socket_keepalive_timeout:连接池 cosocket 对象的空闲时间,默认 60 秒。lua_socket_log_errors:cosocket 发生错误时,是否记录日志,默认为 on
例子
协程使用例子是这样的:

$ resty -e 'local sock = ngx.socket.tcp()        sock:settimeout(1000)  -- one second timeout        local ok, err = sock:connect("www.baidu.com", 80)        if not ok then            ngx.say("failed to connect: ", err)            return        end        local req_data = "GET / HTTP/1.1rnHost: www.baidu.comrnrn"        localbytes, err = sock:send(req_data)        if err then            ngx.say("failed to send: ", err)            return        end        localdata, err, partial = sock:receive()        if err then            ngx.say("failed to receive: ", err)            return        end        sock:close()        ngx.say("response is: ", data)
  • 1、首先,通过 ngx.socket.tcp() ,创建 TCP cosocket 对象,名字是 sock
  • 2、然后,使用 settimeout() ,把超时时间设置为 1 秒。注意这里的超时没有区分 connectreceive,是统一的设置。
  • 3、接着,使用 connect() 去连接指定网站的 80 端口,如果失败就直接退出。
  • 4、连接成功的话,就使用 send() 来发送构造好的数据,如果发送失败就退出。
  • 5、发送数据成功的话,就使用 receive() 来接收网站返回的数据。这里 receive() 的默认参数值是 l,也就是只返回第一行的数据;如果参数设置为了a,就是持续接收数据,直到连接关闭;
  • 6、最后,调用 close() ,主动关闭 socket 连接。

超时时间

在上面settimeout() 的作用是把连接、发送和读取超时时间统一设置为一个值。如果要想分开设置,就需要使用 settimeouts() 函数:

sock:settimeouts(1000, 2000, 3000)
接收数据

receive 接收指定大小:

localdata, err, partial = sock:receiveany(10240)

这段代码就表示,最多只接收 10K 的数据。

关于 receive,还有另一个很常见的用户需求,那就是一直获取数据,直到遇到指定字符串才停止。

local reader = sock:receiveuntil("rn") while true do     local data, err, partial = reader(4)     if not data then         if err then             ngx.say("failed to read the data stream: ", err)             break         end         ngx.say("read done")         break     end     ngx.say("read chunk: [", data, "]") end

这段代码中的 receiveuntil 会返回 rn 之前的数据,并通过迭代器每次读取其中的 4 个字节。


连接池

没有连接池的话,每次请求进来都要新建一个连接,就会导致 cosocket 对象被频繁地创建和销毁,造成不必要的性能损耗。

在你使用完一个 cosocket 后,可以调用 setkeepalive() 放到连接池中:

local ok, err = sock:setkeepalive(2 * 1000, 100)if not ok then    ngx.say("failed to set reusable: ", err)end

这段代码设置了连接的空闲时间为 2 秒,连接池的大小为 100。在调用 connect() 函数时,就会优先从连接池中获取 cosocket 对象。

需注意

1、不能把发生错误的连接放入连接池;

2、要搞清楚连接的数量。连接池是 worker 级别的,每个 worker 都有自己的连接池。所以,如果你有 10 个 worker,连接池大小设置为 30,那么对于后端的服务来讲,就等于有 300 个连接。

原文始发于微信公众号(安全防护):openresty协程

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年10月7日15:43:54
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   openresty协程http://cn-sec.com/archives/2089325.html

发表评论

匿名网友 填写信息