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

相关文章
|
2月前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
2月前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
8天前
|
存储 监控 数据可视化
常见的分布式定时任务调度框架
分布式定时任务调度框架用于在分布式系统中管理和调度定时任务,确保任务按预定时间和频率执行。其核心概念包括Job(任务)、Trigger(触发器)、Executor(执行器)和Scheduler(调度器)。这类框架应具备任务管理、任务监控、良好的可扩展性和高可用性等功能。常用的Java生态中的分布式任务调度框架有Quartz Scheduler、ElasticJob和XXL-JOB。
170 66
|
1天前
|
数据采集 人工智能 分布式计算
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
阿里云推出的MaxFrame是链接大数据与AI的分布式Python计算框架,提供类似Pandas的操作接口和分布式处理能力。本文从部署、功能验证到实际场景全面评测MaxFrame,涵盖分布式Pandas操作、大语言模型数据预处理及企业级应用。结果显示,MaxFrame在处理大规模数据时性能显著提升,代码兼容性强,适合从数据清洗到训练数据生成的全链路场景...
12 5
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
|
9天前
|
运维 数据挖掘 索引
服务器数据恢复—Lustre分布式文件系统服务器数据恢复案例
5台节点服务器,每台节点服务器上有一组RAID5阵列。每组RAID5阵列上有6块硬盘(其中1块硬盘设置为热备盘,其他5块硬盘为数据盘)。上层系统环境为Lustre分布式文件系统。 机房天花板漏水导致这5台节点服务器进水,每台服务器都有至少2块硬盘出现故障。每台服务器中的RAID5阵列短时间内同时掉线2块或以上数量的硬盘,导致RAID崩溃,服务器中数据无法正常读取。
|
15天前
|
分布式计算 大数据 数据处理
技术评测:MaxCompute MaxFrame——阿里云自研分布式计算框架的Python编程接口
随着大数据和人工智能技术的发展,数据处理的需求日益增长。阿里云推出的MaxCompute MaxFrame(简称“MaxFrame”)是一个专为Python开发者设计的分布式计算框架,它不仅支持Python编程接口,还能直接利用MaxCompute的云原生大数据计算资源和服务。本文将通过一系列最佳实践测评,探讨MaxFrame在分布式Pandas处理以及大语言模型数据处理场景中的表现,并分析其在实际工作中的应用潜力。
51 2
|
2月前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
85 6
|
2月前
|
数据库
如何在Seata框架中配置分布式事务的隔离级别?
总的来说,配置分布式事务的隔离级别是实现分布式事务管理的重要环节之一,需要认真对待和仔细调整,以满足业务的需求和性能要求。你还可以进一步深入研究和实践 Seata 框架的配置和使用,以更好地应对各种分布式事务场景的挑战。
43 6
|
2月前
|
消息中间件 运维 数据库
Seata框架和其他分布式事务框架有什么区别
Seata框架和其他分布式事务框架有什么区别
35 1
|
2月前
|
机器学习/深度学习 自然语言处理 并行计算
DeepSpeed分布式训练框架深度学习指南
【11月更文挑战第6天】随着深度学习模型规模的日益增大,训练这些模型所需的计算资源和时间成本也随之增加。传统的单机训练方式已难以应对大规模模型的训练需求。
191 3

热门文章

最新文章