分布式服务器框架之Server.Common库中实现 秒级定时器

简介: 定时器模块是服务器中的常用组件,本文带你实现一个具有基本功能的定时器模块要想设计一个定时器模块,一般包含两部分,一个是定时器对象(ServerTimer),另一个管理定时器对象的管理者(TimerManager)也叫定时器容器;定时器使用了C#内System库里面的Timer定时器作为定时器的主驱动

定时器模块设计  


1 定时器模块介绍


定时器模块是服务器中的常用组件,本文带你实现一个具有基本功能的定时器模块要想设计一个定时器模块,一般包含两部分,一个是定时器对象(ServerTimer),另一个管理定时器对象的管理者(TimerManager)也叫定时器容器;定时器使用了C#内System库里面的Timer定时器作为定时器的主驱动


2 定时器对象设计


一个Timer对象就是对一个定时器的包装


过期年月日时分秒、执行间隔、定时事件回调是核心的基本成员。


定时器类型是为了区分每个定时器的类型,在更新的时候好执行对应的逻辑,


定时器一共分成了6中类型


Once:在指定的年月日时分秒调起Timer内的回调


FixedInterlval:固定周期调用Timer内的回调


EveryDay/EveryWeek/EveryMonth/EveryYear:日/周/月/年定时器


ServerTimerRunType.cs实现


public enum ServerTimerRunType
    { 
        //一次性 定时器
        Once,
        //固定时间 循环定时器
        FixedInterval,
        //天 定时器
        EveryDay,
        //周 定时器
        EveryWeek,
        //月 定时器
        EveryMonth,
        //年 定时器
        EveryYear
    }


3 定时器管理者设计


我们现在可以在主程序中创建n个定时器了,但是怎么管理它呢?如何添加一个新的定时器、手动删除一个旧的定时器、检查这些定时器是否已经到期?如果把这部分代码放到主程序中,那么也太繁杂了,所以就对代码分层写了一个TimerMananger类;这个类负责计时器的更新、添加、删除;一个TimeManager对象管理该线程内的所有定时器对象,所以必有一个数据结构来组织这些定时器对象,我们这里使用的链表结构,不过这种结构的缺点是每次都需要重头遍历,如果ServerTimer的实例过多的话对导致很多次不必要的循环。后续会改成小顶堆的计时器。


TimerManager.cs实现


using System;
using System.Collections.Generic;
using System.Text;
using System.Timers;
using Servers.Core;
namespace Servers.Common
{ 
    public class TimerManager
    {
        //服务器开始运行的tick时间(1 tick = 1/一千万 秒)
        private static double m_BeginTickTime;
        //服务器运行时间(秒)
        public static float RunTime
        {
            get
            {
                return (float)(DateTime.UtcNow.Ticks - m_BeginTickTime) / 10000000;
            }
        }
        //秒定时器
        private static Timer m_SecondTimer;
        //定时器链表
        private static LinkedList<ServerTimer> m_ServerTimers;
        //当前年
        private static int m_CurrYear;
        //当前余额
        private static int m_CurrMonth;
        //当前日
        private static int m_CurrDay;
        //当前周几
        private static DayOfWeek m_CurrWeekDay;
        //当前时
        private static int m_CurrHour;
        //当前分
        private static int m_CurrMinute;
        //当前秒
        private static int m_CurrSecond;
        public static void Init()
        {
            m_ServerTimers = new LinkedList<ServerTimer>();
            m_SecondTimer = new Timer();
            m_SecondTimer.Elapsed += SecondTimerElapsed;
            m_SecondTimer.Enabled = true;
            m_SecondTimer.Interval = 1000;
            m_BeginTickTime = DateTime.UtcNow.Ticks;
            Console.WriteLine(" TimerManager.Init Complete");
        }
        private static void SecondTimerElapsed(object sender, ElapsedEventArgs e)
        {
            //Console.WriteLine(" One Second timeout");
            DateTime currTime = DateTime.Now;
            m_CurrYear = currTime.Year;
            m_CurrMonth = currTime.Month;
            m_CurrDay = currTime.Day;
            m_CurrWeekDay = currTime.DayOfWeek;
            m_CurrHour = currTime.Hour;
            m_CurrMinute = currTime.Minute;
            m_CurrSecond = currTime.Second;
            //拉到所有的计时器对象
            ServerTimer serverTimer;
            LinkedListNode<ServerTimer> iter = m_ServerTimers.First;
            while (null != iter)
            {
                serverTimer = iter.Value;
                switch (serverTimer.RunType)
                {
                    //TODO:Once定时器存在的问题,如果链表里Once内容特别多的话,执行了之后仍任不删除,会导致循环次数增加,如果数量巨大会给cpu造成一些压力
                    case ServerTimerRunType.Once:
                        {
                            if (serverTimer.Year == m_CurrYear && serverTimer.Month == m_CurrMonth &&  serverTimer.Day == m_CurrDay &&
                                serverTimer.Hour == m_CurrHour && serverTimer.Minute == m_CurrMinute &&  serverTimer.Second == m_CurrSecond)
                            {
                                serverTimer.DoAction();
                            }
                        }
                        break;
                    case ServerTimerRunType.FixedInterval:
                        {
                            serverTimer.ServerTimerTick();
                        }
                        break;
                    case ServerTimerRunType.EveryDay:
                        {
                            if (serverTimer.Hour == m_CurrHour && serverTimer.Minute == m_CurrMinute && serverTimer.Second == m_CurrSecond)
                            {
                                serverTimer.DoAction();
                            }
                        }
                        break;
                    case ServerTimerRunType.EveryWeek:
                        {
                            if (serverTimer.WeekDay == m_CurrWeekDay &&
                                serverTimer.Hour == m_CurrHour && serverTimer.Minute == m_CurrMinute && serverTimer.Second == m_CurrSecond)
                            {
                                serverTimer.DoAction();
                            }
                        }
                        break;
                    case ServerTimerRunType.EveryMonth:
                        {
                            if (serverTimer.Day == m_CurrDay &&
                                serverTimer.Hour == m_CurrHour && serverTimer.Minute == m_CurrMinute && serverTimer.Second == m_CurrSecond)
                            {
                                serverTimer.DoAction();
                            }
                        }
                        break;
                    case ServerTimerRunType.EveryYear:
                        {
                            if (serverTimer.Month == m_CurrMonth && serverTimer.Day == m_CurrDay &&
                                serverTimer.Hour == m_CurrHour && serverTimer.Minute == m_CurrMinute && serverTimer.Second == m_CurrSecond)
                            {
                                serverTimer.DoAction();
                            }
                        }
                        break;
                }
                iter = iter.Next;
            }
        }
        public static void RegisterServerTime(ServerTimer timer)
        {
            m_ServerTimers.AddLast(timer);
        }
        public static void RemoveServerTimer(ServerTimer timer)
        {
            m_ServerTimers.Remove(timer);
        }
    }
}


4 总结


可以看到定时器模块本身逻辑并不复杂,最重要的是要考虑效率的问题,采用何种数据结构,来使得以上三种基本操作的时间复杂度较小;


常用的数据结构:


1.链表、队列

2.map

3.时间轮

4.时间堆


我们这里使用的链表结构,不过这种结构的缺点是每次都需要重头遍历,然后一个个的比对,如果数量超过10W级性能会急剧下降。如果ServerTimer的实例过多的话对导致很多次不必要的循环。所以在后面我会把这个计时器优化成小顶堆的计时器,性能会有明显的提升。


这里稍微讲一下小顶堆的计时器。小队顶计时器其实就是使用了堆排序的原理。


2e848cfeff834a2b8a0a6cb60648f030.jpg

     

定时器是一个树状结构,每次插入或者是超时的时候都会对堆重新进行排序,更新的时候只取对顶上(第0个)的元素,取到的一定是时间最少的Timer,当这个计时器超时的时候,将堆再次排序,排序完成后判断下一个是否超时,如果超时了循环以上步骤,如果没超时,这次检测结束。

相关文章
|
18天前
|
机器学习/深度学习 自然语言处理 并行计算
DeepSpeed分布式训练框架深度学习指南
【11月更文挑战第6天】随着深度学习模型规模的日益增大,训练这些模型所需的计算资源和时间成本也随之增加。传统的单机训练方式已难以应对大规模模型的训练需求。
64 3
|
23天前
|
机器学习/深度学习 并行计算 Java
谈谈分布式训练框架DeepSpeed与Megatron
【11月更文挑战第3天】随着深度学习技术的不断发展,大规模模型的训练需求日益增长。为了应对这种需求,分布式训练框架应运而生,其中DeepSpeed和Megatron是两个备受瞩目的框架。本文将深入探讨这两个框架的背景、业务场景、优缺点、主要功能及底层实现逻辑,并提供一个基于Java语言的简单demo例子,帮助读者更好地理解这些技术。
45 2
|
1月前
|
Python
Flask学习笔记(二):基于Flask框架上传图片到服务器端并原名保存
关于如何使用Flask框架上传图片到服务器端并以其原名保存的教程。
76 1
|
1月前
|
Python
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
这篇博客文章是关于如何使用Flask框架上传特征值数据到服务器端,并将其保存为txt文件的教程。
31 0
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
|
1月前
|
缓存 NoSQL Ubuntu
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
55 3
|
1月前
|
分布式计算 Hadoop
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
47 1
|
1月前
|
存储 SQL 消息中间件
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
47 0
|
10天前
|
机器学习/深度学习 人工智能 弹性计算
什么是阿里云GPU云服务器?GPU服务器优势、使用和租赁费用整理
阿里云GPU云服务器提供强大的GPU算力,适用于深度学习、科学计算、图形可视化和视频处理等多种场景。作为亚太领先的云服务提供商,阿里云的GPU云服务器具备灵活的资源配置、高安全性和易用性,支持多种计费模式,帮助企业高效应对计算密集型任务。
|
12天前
|
存储 分布式计算 固态存储
阿里云2核16G、4核32G、8核64G配置云服务器租用收费标准与活动价格参考
2核16G、8核64G、4核32G配置的云服务器处理器与内存比为1:8,这种配比的云服务器一般适用于数据分析与挖掘,Hadoop、Spark集群和数据库,缓存等内存密集型场景,因此,多为企业级用户选择。目前2核16G配置按量收费最低收费标准为0.54元/小时,按月租用标准收费标准为260.44元/1个月。4核32G配置的阿里云服务器按量收费标准最低为1.08元/小时,按月租用标准收费标准为520.88元/1个月。8核64G配置的阿里云服务器按量收费标准最低为2.17元/小时,按月租用标准收费标准为1041.77元/1个月。本文介绍这些配置的最新租用收费标准与活动价格情况,以供参考。
|
10天前
|
机器学习/深度学习 人工智能 弹性计算
阿里云GPU服务器全解析_GPU价格收费标准_GPU优势和使用说明
阿里云GPU云服务器提供强大的GPU算力,适用于深度学习、科学计算、图形可视化和视频处理等场景。作为亚太领先的云服务商,阿里云GPU云服务器具备高灵活性、易用性、容灾备份、安全性和成本效益,支持多种实例规格,满足不同业务需求。

热门文章

最新文章

下一篇
无影云桌面