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();
 }

调试的效果如下:

c# 使用计时器和观察者模式实现报警推送需求

明天还是完善下,将使用EventBus,将耦合性再度降低,同时也能让接收方法更加灵活性,能实现不同级别的职员做出不同的响应!

来源:https://www.cnblogs.com/hunanzp/p/12343863.html

标签:c#,计时器,观察者模式,报警推送需求
0
投稿

猜你喜欢

  • Java 使用getClass().getResourceAsStream()方法获取资源

    2023-07-23 08:59:56
  • java二维数组遍历的2种代码

    2022-05-03 08:52:50
  • SpringBoot2使用JTA组件实现基于JdbcTemplate多数据源事务管理(亲测好用)

    2021-10-12 06:36:09
  • spring-boot读取props和yml配置文件的方法

    2022-06-01 14:54:26
  • 在Struts2中如何将父类属性序列化为JSON格式的解决方法

    2022-08-01 09:30:34
  • C++string中的insert()插入函数详解

    2023-11-02 14:05:51
  • mybatis 传入null值的解决方案

    2023-11-23 06:54:44
  • java8中的lambda表达式简介

    2022-09-12 04:14:10
  • SpringBoot定时任务设计之时间轮案例原理详解

    2023-04-24 00:48:36
  • Flutter 容器盒子模型的使用示例

    2023-06-18 18:47:43
  • Java 提取照片的EXIF信息批量重命名

    2023-10-05 14:11:28
  • Java多线程实现Callable接口

    2022-09-01 17:53:54
  • java多线程编程实例

    2022-12-08 18:51:29
  • 关于Java中finalize析构方法的作用详解

    2023-12-09 23:46:27
  • 完美解决idea创建文件时,文件不分级展示的情况

    2022-01-01 22:10:19
  • List调用toString()方法后,去除两头的中括号实例

    2023-09-28 11:18:56
  • SpringBoot2.0集成WebSocket实现后台向前端推送信息

    2023-08-22 18:50:39
  • 实现java简单的线程池

    2023-08-09 06:05:15
  • OpenCV图像旋转Rotate的详细介绍

    2023-07-01 08:22:27
  • java中利用List的subList方法实现对List分页(简单易学)

    2022-06-18 23:33:09
  • asp之家 软件编程 m.aspxhome.com