服务器定时器的管理优化

作为一个游戏服务器,必然有很多定时器的使用,算是游戏服务器开发的基础模块,我们经常需要在我们预期的某个时间点执行某项特定的操作。比如每天M点开启某个活动,N小时后之后刷新排行榜等等。这些功能通常需要定时器控制,之前我们的服务器代码中每加一个延迟执行的功能就启动一个定时器,这样到最后往往代码特别臃肿,而且定时器时稀缺资源,过多的定时器必然导致效率问题,于是要想办法优化。
 
想要优化定时器就要看定时器一共有多少种:
1.只执行一次的定时器,比如60秒后执行操作A。
2.循环执行的定时器,比如每隔30分钟执行一次操作B。
3.每日/每周/每月特定时间点执行一次的操作C。(eg.每天下午6点执行操作C)
 
我们要做的就是,把一整套需要定时器的操作整合到一个定时器管理器Manager里面,然后每次有新的定时器要添加,调用Add接口加到Manager里面,Manager只会启动一个定时器,它会自动寻找最近需要执行的定时器然后到时间点后处理相关逻辑。这样我们每次增加定时器操作就是往管理器里面添加一个操作就行了,其他的管理器会替我们解决。
 
from common import Timer
from util import TimeUtil
from base.Defines import *

class TimerObj(object):
	"""封装一个定时器对象"""
	def __init__(self, start_time, interval, callback):
		"""
		:param start_time:定时器开始时间
		:param interval:定时器间隔,如果只执行一次,填0
		:param callback:触发定时器时执行的函数
		:return:
		"""
		super(TimerObj, self).__init__()
		self.start = start_time
		self.interval = interval
		self.callback = callback

	def getEndTime(self):
		if self.interval <= 0:
			return self.start
		return None

	def getNextTime(self):
		now_time = int(TimeUtil.now_time())
		if now_time < self.start:
			return self.start
		else:
			if self.getEndTime() is not None:
				return None
			num = 0
			while self.start + num <= now_time:
				num += self.interval
			return self.start + num



class TimerManager(object):
	"""管理所有的定时器"""
	def __init__(self):
		super(TimerManager, self).__init__()
		self.timer_list = []
		self.timer = None

	def addTimerObj(self, obj):
		self.timer_list.append(obj)
		self.stopTimer()
		self.startTimer()

	def removeTimeObj(self, type):
		pass

	def getMinNextTimer(self):
		min_next = INT_MAX
		result = []
		self.timer_list = filter(lambda x:x.getNextTime() is not None, self.timer_list)
		for obj in self.timer_list:
			next = obj.getNextTime()
			if next < min_next:
				min_next = next
				result = []
				result.append(obj)
			elif next == min_next:
				result.append(obj)

		if min_next == INT_MAX:
			return None,None
		#print "getMinNextTimer",min_next,int(TimeUtil.now_time())
		return min_next, result

	def startTimer(self):
		timestamp, objlist = self.getMinNextTimer()
		if timestamp is None:
			return
		self.stopTimer()
		delay = int(timestamp) - int(TimeUtil.now_time())
		if delay == 0:
			delay = 1
		self.timer = Timer.addTimer(delay, lambda :self.callTimer(objlist))

	def callTimer(self, objlist):
		#print "callTimer",int(TimeUtil.now_time())
		self.startTimer()
		for obj in objlist:
			obj.callback()


	def stopTimer(self):
		if self.timer is not None:
			self.timer.cancel()
			self.timer = None


我用一个列表储存需要管理的定时器对象列表,addTimerObj用来增加定时器对象。startTimer启动管理器的定时器,它会取最小的下次定时器触发时间以及对象列表(因为同一个时间点可能有多个事件),然后启动定时器,到点执行操作。
代码还有需要优化的点:
1.为了简单我用了列表存储,可以考虑其他更好的数据结构,比如使用类似libevent的最小堆。
2.我每次stopTimer再startTimer这点可以考虑优化。
3.我在每次getMinNextTimer的时候才过滤已经过期的定时器。
4.我暴露了TimerObj对象,可以在TimerManager里面增加一个接口,然后根据参数创建TimerObj,就不需要TimerObj给用户。
5. ...
 
使用很简单,创建一个TimerManager对象,然后需要增加的时候,创建一个TimerObj对象,把启动时间,间隔,以及回调传进去,然后调用addTimerObj加到TimerManager对象里面。
onlineCountingTimer = TimerObj(now_time, 5 * 60, lambda : self.onlineCounting())
self.timerManager.addTimerObj(onlineCountingTimer)
 
我之前还有一种做法,基于第三种定时器,在服务器启动的时候启动一个整点定时器。
 
像这种:
def main():
	struct_time = time.localtime()
	min, sec = struct_time.tm_min, struct_time.tm_sec
	remain = 3600 - (min * 60 + sec)
	Timer.addTimer(remain, lambda: self.newhour_callback())

def newhour_callback(self):
	import manager.DaysManager as DaysManager
	DaysManager.NewHour()
	Timer.addRepeatTimer(3600, lambda: NewHour())

def NewHour():
	"""每小时整点会执行到这里""""
	DaysManager.NewHour()
	...
	...


然后在各个Manager里面定义一个NewHour的接口,通过此NewHour调用。
这样,那些需要在整点执行的接口在各个Manger里面处理就好了
def NewHour(self):
	struct_time = time.localtime()
	hour = struct_time.tm_hour

	_logger.info("NewHour: "+str(hour))

	if hour == 0:  # 0点刷新
		self.refresh_new_task()


像这样。
 
不过考虑到代码逻辑的一致性,暂时没用这种方法,只用了TimerManager管理了所有的定时器。

注:有些接口是我们引擎封装的,我就不暴露出来了,权当是伪代码好了。对理解大意无影响。

猜你喜欢

转载自blog.csdn.net/majianfei1023/article/details/52769133