c# 使用计时器和观察者模式实现报警推送需求
作者:长沙大鹏 发布时间:2022-05-24 02:16:46
前言
这两天面试了一个物联网公司高级研发,面试题是下面这样子
公司领导,部门主管,小组组长,组成员4级,假如有个 疫情预警,先通知组人员(对个人,主要有一个处理就算处理了) 如果3分钟没处理,就往组长发短信,如果组长3分钟没处理就往上级推送。一级一级的。 要求单程序并发至少支持1000tps预警事件
说实话上面需求在我做的过往项目里有过类似需求,我也只是依稀记得自己是怎么做的。类似于使用第三方任务调度框架完成上面的3分钟超时处理,然后至于分级发送则更简单了,再不济就是使用if、else这样的最原始验证即可。但是这样的题目丢到面试题上我是一下就不知所措了。自然最终的结果也不尽人意(我最终提交的代码就是采用一个事件总线实现报警的推送,但是并未对其进行超时分级发送) 这个自然是错误的,我其实当时也想过使用Timer,但是内心对Timer就是感觉用性能做代价去换取最终的结果。
解析需求
过了几天还是觉得要手纯撸代码将上面功能撸出来,不然如何成长呢!
拆分下需求可以得到的消息是有一个事件。这个事件就是报警事件,通过报警事件需要将消息推送给不同职位的工作人员,而且必须遵循岗位从下至上,但凡人员收到报警消息则表示报警通知已完成,其次就是有一个三分钟需要处理。
通过上面的需求分析可以知道我们必须要定义一个枚举,记录职称高低。
/// <summary>
/// 工作级别
/// </summary>
public enum JobLevel : int
{
公司领导 = 1,
部门主管 = 2,
小组组长 = 3,
组成员 = 4
}
其次我们至少存在两个类,一个是产生报警的对象,这个对象有需要通知的报警信息和报警信息发送结果,当然更加少不了我们订阅了报警消息的订阅者。这里想了下,可以采用观察者设计模式进行解耦。
/// <summary>
/// 发布者接口
/// </summary>
public interface IPublish
{
/// <summary>
/// 新增订阅者(观察者)
/// </summary>
/// <param name="subscriber"></param>
void AddSubscriber(SubscriberPeopleBase subscriber);
/// <summary>
/// 移除订阅者(观察者)
/// </summary>
/// <param name="subscriber"></param>
void RemoveSubscriber(SubscriberPeopleBase subscriber);
/// <summary>
/// 发送报警消息
/// </summary>
void SendNotify();
/// <summary>
/// 出现警报
/// </summary>
/// <param name="msg">警报消息</param>
void CreateJB(string msg);
接下来就是实现上面接口了!
/// <summary>
/// 报警发布者
/// </summary>
public class Baojing : IPublish
{
/// <summary>
/// 订阅者集合
/// </summary>
public List<SubscriberPeopleBase> SubscriberLst { get; set; }
/// <summary>
/// 报警消息
/// </summary>
public string Msg { get; set; }
/// <summary>
/// 有无接收成功
/// </summary>
public bool IsSucess { get; set; }
/// <summary>
/// 消息通知计数器
/// </summary>
public Timer NotifyTimer { get; set; }
/// <summary>
/// 记录当前发送消息的岗位
/// </summary>
public JobLevel CurrentJobLevel = JobLevel.组成员;
public void AddSubscriber(SubscriberPeopleBase subscriber)
{
if (SubscriberLst == null) SubscriberLst = new List<SubscriberPeopleBase>();
SubscriberLst.Add(subscriber);
}
public void CreateJB(string msg)
{
Msg = msg;
}
public void RemoveSubscriber(SubscriberPeopleBase subscriber)
{
if (SubscriberLst != null) SubscriberLst.Remove(subscriber);
}
/// <summary>
/// 发送报警消息
/// </summary>
public void SendNotify()
{
if (SubscriberLst?.Count > 0)
{
//循环从职位低到高
SubscriberLst.OrderByDescending(p => (int)p.Title);
//立即执行CheckNotifySendResult,以为3秒为间隔
NotifyTimer = new Timer(CheckNotifySendResult, null, 0, 3000);
}
}
public void CheckNotifySendResult(object stat)
{
//先要停止定时器,防止并发
NotifyTimer.Change(-1, 3000);
//第一次发给职位最小 枚举最大的组成员
SubscriberPeopleBase subscriberPeople = SubscriberLst.Find(p =>
(((int)CurrentJobLevel + 1) - (int)p.Title) == 1);
if (subscriberPeople == null) return; //已经是最小的
if (!IsSucess)
{
IsSucess = subscriberPeople.ReceiveData(this);
if (!IsSucess)
{
CurrentJobLevel = CurrentJobLevel!= JobLevel.公司领导?(JobLevel)((int)CurrentJobLevel) - 1: JobLevel.公司领导;
NotifyTimer.Change(3000, 3000);
}
}
else
{
Console.WriteLine($"停止计数器 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
NotifyTimer.Dispose();
}
}
}
还需要有订阅者,订阅者使用抽象类方式降低耦合性(其实还是要用事件总线,将耦合性和扩展性提高一个档次)
/// <summary>
/// 订阅者
/// 其实这里还不够完善,理论上后续如果每个职位的订阅者对报警消息的处理不同则在下面的接收消息里会存在一些冗余代码,
/// 理想情况是不同级别应该有不同的实现,这样可以足够抽象,后续扩展也更加方便
/// </summary>
public abstract class SubscriberPeopleBase
{
/// <summary>
/// 工作职位(级别)
/// </summary>
public JobLevel Title { get; set; }
public SubscriberPeopleBase(JobLevel title)
{
Title = title;
}
public virtual bool ReceiveData(Baojing baojing)
{
if (Title == JobLevel.公司领导)
{
Console.WriteLine($"出现报警信息:{baojing.Msg},当前订阅者:{Title.ToString()} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
return true;
}
else
{
Console.WriteLine($"出现报警信息:{baojing.Msg},当前订阅者:{Title.ToString()},默认未应答 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
return false;
}
}
}
再加个子类,重写下接收警报的方法
public class SubscriberPeople : SubscriberPeopleBase
{
public SubscriberPeople(JobLevel title) : base(title) { }
public override bool ReceiveData(Baojing baojing)
{
if (Title == JobLevel.公司领导)
{
Console.WriteLine($"出现最新报警信息:{baojing.Msg},当前订阅者:{Title.ToString()} 已成功应答 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
return true;
}
else
{
Console.WriteLine($"出现报警信息:{baojing.Msg},当前订阅者:{Title.ToString()},默认未应答 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
return false;
}
}
}
最终我的main方法是这样
static void Main(string[] args)
{
IPublish publish = new Baojing();
publish.AddSubscriber(new SubscriberPeople(JobLevel.组成员));
publish.AddSubscriber(new SubscriberPeople(JobLevel.公司领导));
publish.AddSubscriber(new SubscriberPeople(JobLevel.部门主管));
publish.AddSubscriber(new SubscriberPeople(JobLevel.小组组长));
publish.CreateJB("主机温度过高!");
publish.SendNotify();
Console.ReadLine();
}
调试的效果如下:
明天还是完善下,将使用EventBus,将耦合性再度降低,同时也能让接收方法更加灵活性,能实现不同级别的职员做出不同的响应!
来源:https://www.cnblogs.com/hunanzp/p/12343863.html
猜你喜欢
- 1、为什么使用缓存  我们知道内存的读取速度远大于硬盘的读取速度。当需要重复地获取相同数据时,一次一次地
- 经过上一篇的介绍,相信小伙伴们已经按奈不住内心对springboot的向往,本篇我将继续向小伙伴介绍springboot配置文件的配置,已经
- MybatisAnnotationToolsMybatisAnnotationTools 是基于 Java8 开发的一款可以用于自动化生成
- 1.委托delegate委托delegate也是一种类型,在任何可以声明类的地方都可以声明委托,它将方法当做另一个方法的参数进行传递,这样就
- 一、题目描述题目实现:网络资源的断点续传功能。二、解题思路获取要下载的资源网址显示网络资源的大小上次读取到的字节位置以及未读取的字节数输入下
- 第一步:官网(或跟硬件开发WMI的人沟通你需要的接口和参数定义,如果是和硬件开发的人协定WMI接口,直接看第二步)查找你需要的WMI信息;举
- 最近我在考虑如何远程控制tomcat的启动和关机,最后是有友好的界面,能够实现一键式操作的,这样会肯定是会很方便的,网上找了半天,没找到,有
- 一、什么是命令模式命令模式是一个高内聚的模式,其定义为:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请 求排队或者记录
- 一:前言最近老师布置了给多级菜单的作业,感觉蛮有意思的,可以提升自己的逻辑!下面我写个简易版的多级菜单,本人还是菜鸟,欢迎各位给予宝贵的建议
- 1.问题描述汉诺塔问题是一个经典的问题。汉诺塔(Hanoi Tower),又称河内塔,源于印度一个古老传说。大梵天创造世界的时候做了三根金刚
- 一、背景介绍在微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。我们可以使用JDK原生的URLCo
- 本文讲述了在Java中如何创建和结束线程的最基本方法,只针对于Java初学者。一些高级知识如线程同步、调度、线程池等内容将会在后续章节中逐步
- 一、推迟执行动作可以使用timer+map方法实现.代码如下:Observable.timer(5, TimeUnit.MILLISECON
- Wrapper---条件查询器:使用它可以实现很多复杂的查询1.条件查询1.1查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于1
- 在项目开发上,hibernate提供的经验简化了不少工作量和兼容性,但这些绝对需要有经验后才能明白,对于新手来说使用起来很困难。hibern
- 本文实例为大家分享了Java多线程实现第三方数据同步的具体代码,供大家参考,具体内容如下一、场景最近的一项开发任务是同步第三方数据,而第三方
- 一、题目描述题目实现:在进行网络编程时,由于进行网络连接是比较消耗资源的,因此,可以对连接的等待时间进行设置,如果在规定的时间没有进行连接,
- 在实际项目当中,我们经常会涉及到对时间的处理,例如登陆网站,我们会看到网站首页显示XXX,欢迎您!今天是XXXX年。。。。某些网站会记录下用
- Java自定义注解一般使用场景为:自定义注解+ * 或者AOP,使用自定义注解来自己设计框架,使得代码看起来非常优雅。本文将先从自定义注解的
- 前提最近发现各个频道推荐了很多ULID相关文章,这里对ULID的规范文件进行解读,并且基于Java语言自行实现ULID,通过此实现过程展示U