cosocket即openresty将其协程(coroutine)与网络套接字结合在一起实现的非阻塞网络I/O
其中tcp相关api为:
其中*setkeepalive(timeout, size)*需要关注的api,用于将tcp连接放入连接池,timeout
设置超时时间,size
设置连接池大小
每个worker进程会有一个连接池
连接池是通过ip和port标志的,若ip和port相同则会使用一个连接池
连接池的大小在第一次调用setkeepalive
就会确定下不会再变更
在调用setkeepalive
后即会将tcp连接放入连接池中,无需再调用close
方法
每次调用connect
方法时会优先查询连接池中是否有可用连接,若有则直接返回连接,若无才会新建连接
ngx.ctx此表用于存储请求的上下文环境数据,其生命周期与当前请求的生命请求相同,若当前请求中有子请求存在,子请求会维护自己的ngx.ctx,当前请求并不能与子请求共享ngx.ctx
==注意:==使用ngx.ctx尽量使用传参的方式,ngx.ctx表查询效率略低
local _M = {}
-- 错误
-- ctx变量是在 Lua 模块级别,并且属于单个worker的(worker级变量,同一worker的所有请求共享,多个请求同时写入时不安全)
-- local ctx = ngx.ctx
-- function _M.main()
-- ctx.foo = "bar"
-- end
-- 正确,通过传参方式完成
function _M.main(ctx)
ctx.foo = "bar"
end
return _M
cosocket 是全双工的,如果一个读线程和一个写线程同时操作一个cosocket对象不会有问题,但如果两个线程一起读或者写同一个cosocket则会引发socket busy错误
读线程不会出现socket read busy,因为读取tcp数据的地方有且仅有一个
使用单独的协程来操作socket,其他业务处理协程使用消息队列来通知需要发送的tcp数据
本质上两者实现相同都是基于cosocket,mongodb经常报错而redis不报错的原因是:redis是在每一个调用函数内部建立连接且名字都不相同(保证在同一请求中即使有多个协程ngx.ctx返回的也不是同一个连接)
会导致出错的代码:
-- 会导致出错的代码
-- 若同一请求中有多个协程,它们共享一个上下文环境(ngx.ctx表),并且会对name相同的连接进行读写操作,大概率会出现socket busy的问题
if name and ngx.ctx[name] then
return ngx.ctx[name]
end
local client, errmsg = mongol:new()
if not client then
return nil, 'mongo socket failed: '..(errmsg or 'no errMsg')
end
client:set_timeout(timeout)
-- 若上下文中没有找到相同名字的连接,则从连接池中获取socket,若连接池中也没有则新建tcp连接
local result, errmsg = client:connect(host, port)
if not result then
return nil, errmsg
end
ngx.ctx[name] = client
mongodb数据库认证也需要对数据库进行读取,增加了报错的概率
对于并发的请求,并发的tcp连接不会报错的原因是:它们每一个连接的ngx.ctx表是分开维护的,所以仅当一个请求或者tcp连接中的子协程的socket操作冲突了才会报错
在使用redis中select
命令切换数据库时,要注意在使用完后,将数据库切换为数据库0,否则在执行完set_keepalive
后将连接到其他数据库的tcp长连接放入连接池中,有概率导致其他业务从连接池中取出的连接并不是连接到数据库0的
如果需要在共享模块内操作redis和mongodb,eg: common.lua,需要在调用时传递ngx.ctx所需的名字
local function c(name)
-- 不加name会报错
local cc_config_db = require("models.config_mongo"):new(name)
local config = cc_config_db:get_one({name = "SYSTEM_CONFIG"})
if not config or not next(config) then
ngx.log(ngx.ERR, name .. " failed")
else
ngx.log(ngx.ERR, name .." success")
end
end
local function a()
while true do
c("a")
ngx.sleep(0.1)
end
end
local function b()
while true do
c("b")
ngx.sleep(0.1)
end
end
local function test()
ngx.thread.spawn(a)
ngx.thread.spawn(b)
end