skynet框架:并发热点处理方案

  1. 对于关键流程,所有请求都要求返回有效结果,如创建socket连接:
function luasocket:connect()
		return socketcore.open(self.__host, self.__port)
end

显然外部调用需要获取到正确的socket句柄用于数据交互,当并发调用此接口时,所有调用都需要获取到有效的句柄以保证业务正常;

方案:加锁,保证临界区只由唯一的coroutine正确执行,并发的其他coroutine在临界区处休眠等待,临界区执行完成后,负责唤醒所有休眠的coroutine,直接获取有效结果返回;

function luasocket:safe_connect()
  	local conn
  	local nwaitingco = #self.__connecting -- wait queue
  	if nwaitingco > 0 then
  			-- connecting in other coroutine
  			local co = coroutine.running()
  			table.insert(self.__connecting, co)
  			skynet.wait(co)
  			return assert(conn) -- return conn when being awakened
  	end

  	-- the effective coroutine which build connect
  	self.__connecting[1] = true -- lock
  	conn = socketcore.open(self.__host, self.__port)
  	-- wake all wait coroutines
  	for i=2, #self.__connecting do
  			local co = self.__connecting[i]
  			self.__connecting[i] = nil
  			skynet.wakeup(co)
  	end
  	self.__connecting[1] = nil -- unlock
  	return assert(conn)
end
  1. 对于非关键或接受单次请求无效,支持重入的流程,如定时存盘:
function user:save(query, document)
  	return mongodb.update(query, document)
end

数据以全量覆盖的形式存盘,那么单次存盘无效认为是可以接受的,下一次请求到来的时候数据能够正确写入;

方案:临界区加锁,无效并发的存盘请求;

function user:safe_save(query, document)
		if user.__saving then
				-- saving in other coroutine, return succ
				return true
  	end
  	user.__saving = true -- lock
  	-- do save
  	mongodb.update(query, document)
  	user.__saving = nil -- unlock
  	return true
end

做得更好的方案,对并发的coroutine做消息降级,兼顾请求有效同时确保请求能被正确唤醒;

function user:safe_save2(query, document)
		if user.__saving then
				-- saving in other coroutine, sleep
				skynet.sleep(delay)
				if user.__saving then -- double check
						return true
				end
  	end
  	user.__saving = true -- lock
  	-- do save
  	mongodb.update(query, document)
  	user.__saving = nil -- unlock
  	return true
end
posted @ 2024-05-13 20:09  linxx-  阅读(29)  评论(0编辑  收藏  举报