分布式服务器框架之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,当这个计时器超时的时候,将堆再次排序,排序完成后判断下一个是否超时,如果超时了循环以上步骤,如果没超时,这次检测结束。

目录
打赏
0
相关文章
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
378 0
分布式爬虫框架Scrapy-Redis实战指南
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
SpringBoot中@Scheduled和Quartz的区别是什么?分布式定时任务框架选型实战
本文对比分析了SpringBoot中的`@Scheduled`与Quartz定时任务框架。`@Scheduled`轻量易用,适合单机简单场景,但存在多实例重复执行、无持久化等缺陷;Quartz功能强大,支持分布式调度、任务持久化、动态调整和失败重试,适用于复杂企业级需求。文章通过特性对比、代码示例及常见问题解答,帮助开发者理解两者差异,合理选择方案。记住口诀:单机简单用注解,多节点上Quartz;若是任务要可靠,持久化配置不能少。
210 4
常见的分布式定时任务调度框架
分布式定时任务调度框架用于在分布式系统中管理和调度定时任务,确保任务按预定时间和频率执行。其核心概念包括Job(任务)、Trigger(触发器)、Executor(执行器)和Scheduler(调度器)。这类框架应具备任务管理、任务监控、良好的可扩展性和高可用性等功能。常用的Java生态中的分布式任务调度框架有Quartz Scheduler、ElasticJob和XXL-JOB。
2334 66
|
8月前
|
如何在Seata框架中配置分布式事务的隔离级别?
总的来说,配置分布式事务的隔离级别是实现分布式事务管理的重要环节之一,需要认真对待和仔细调整,以满足业务的需求和性能要求。你还可以进一步深入研究和实践 Seata 框架的配置和使用,以更好地应对各种分布式事务场景的挑战。
239 63
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
阿里云推出的MaxFrame是链接大数据与AI的分布式Python计算框架,提供类似Pandas的操作接口和分布式处理能力。本文从部署、功能验证到实际场景全面评测MaxFrame,涵盖分布式Pandas操作、大语言模型数据预处理及企业级应用。结果显示,MaxFrame在处理大规模数据时性能显著提升,代码兼容性强,适合从数据清洗到训练数据生成的全链路场景...
252 5
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
433 53
MaxFrame 产品评测:大数据与AI融合的Python分布式计算框架
MaxFrame是阿里云MaxCompute推出的自研Python分布式计算框架,支持大规模数据处理与AI应用。它提供类似Pandas的API,简化开发流程,并兼容多种机器学习库,加速模型训练前的数据准备。MaxFrame融合大数据和AI,提升效率、促进协作、增强创新能力。尽管初次配置稍显复杂,但其强大的功能集、性能优化及开放性使其成为现代企业与研究机构的理想选择。未来有望进一步简化使用门槛并加强社区建设。
314 8
服务器数据恢复—Lustre分布式文件系统服务器数据恢复案例
5台节点服务器,每台节点服务器上有一组RAID5阵列。每组RAID5阵列上有6块硬盘(其中1块硬盘设置为热备盘,其他5块硬盘为数据盘)。上层系统环境为Lustre分布式文件系统。 机房天花板漏水导致这5台节点服务器进水,每台服务器都有至少2块硬盘出现故障。每台服务器中的RAID5阵列短时间内同时掉线2块或以上数量的硬盘,导致RAID崩溃,服务器中数据无法正常读取。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问