前言

这两天面试了一个物联网公司高级研发,面试题是下面这样子

公司领导,部门主管,小组组长,组成员4级,假如有个 疫情预警,先通知组人员(对个人,主要有一个处理就算处理了) 如果3分钟没处理,就往组长发短信,如果组长3分钟没处理就往上级推送。一级一级的。 要求单程序并发至少支持1000tps预警事件

说实话上面需求在我做的过往项目里有过类似需求,我也只是依稀记得自己是怎么做的。类似于使用第三方任务调度框架完成上面的3分钟超时处理,然后至于分级发送则更简单了,再不济就是使用if、else这样的最原始验证即可。但是这样的题目丢到面试题上我是一下就不知所措了。自然最终的结果也不尽人意(我最终提交的代码就是采用一个事件总线实现报警的推送,但是并未对其进行超时分级发送) 这个自然是错误的,我其实当时也想过使用Timer,但是内心对Timer就是感觉用性能做代价去换取最终的结果。

解析需求

过了几天还是觉得要手纯撸代码将上面功能撸出来,不然如何成长呢!
拆分下需求可以得到的消息是有一个事件。这个事件就是报警事件,通过报警事件需要将消息推送给不同职位的工作人员,而且必须遵循岗位从下至上,但凡人员收到报警消息则表示报警通知已完成,其次就是有一个三分钟需要处理。

通过上面的需求分析可以知道我们必须要定义一个枚举,记录职称高低。

  1. /// <summary>
  2.     /// 工作级别
  3.     /// </summary>
  4.     public enum JobLevel : int
  5.     {
  6.         公司领导 = 1,
  7.         部门主管 = 2,
  8.         小组组长 = 3,
  9.         组成员 = 4
  10.     }

  

其次我们至少存在两个类,一个是产生报警的对象,这个对象有需要通知的报警信息和报警信息发送结果,当然更加少不了我们订阅了报警消息的订阅者。这里想了下,可以采用观察者设计模式进行解耦。

  1. /// <summary>
  2.     /// 发布者接口
  3.     /// </summary>
  4.     public interface IPublish
  5.     {
  6.         /// <summary>
  7.         /// 新增订阅者(观察者)
  8.         /// </summary>
  9.         /// <param name="subscriber"></param>
  10.         void AddSubscriber(SubscriberPeopleBase subscriber);
  11.  
  12.         /// <summary>
  13.         /// 移除订阅者(观察者)
  14.         /// </summary>
  15.         /// <param name="subscriber"></param>
  16.         void RemoveSubscriber(SubscriberPeopleBase subscriber);
  17.  
  18.         /// <summary>
  19.         /// 发送报警消息
  20.         /// </summary>
  21.         void SendNotify();
  22.    
  23.         /// <summary>
  24.         /// 出现警报
  25.         /// </summary>
  26.         /// <param name="msg">警报消息</param>
  27.         void CreateJB(string msg);

  接下来就是实现上面接口了!

  1. /// <summary>
  2. /// 报警发布者
  3. /// </summary>
  4. public class Baojing : IPublish
  5. {
  6.         /// <summary>
  7.         /// 订阅者集合
  8.         /// </summary>
  9.         public List<SubscriberPeopleBase> SubscriberLst { get; set; }
  10.  
  11.         /// <summary>
  12.         /// 报警消息
  13.         /// </summary>
  14.         public string Msg { get; set; }
  15.  
  16.         /// <summary>
  17.         /// 有无接收成功
  18.         /// </summary>
  19.         public bool IsSucess { get; set; }
  20.  
  21.         /// <summary>
  22.         /// 消息通知计数器
  23.         /// </summary>
  24.         public Timer NotifyTimer { get; set; }
  25.  
  26.         /// <summary>
  27.         /// 记录当前发送消息的岗位
  28.         /// </summary>
  29.         public JobLevel CurrentJobLevel = JobLevel.组成员;
  30.  
  31.         public void AddSubscriber(SubscriberPeopleBase subscriber)
  32.         {
  33.             if (SubscriberLst == null) SubscriberLst = new List<SubscriberPeopleBase>();
  34.             SubscriberLst.Add(subscriber);
  35.         }
  36.  
  37.         public void CreateJB(string msg)
  38.         {
  39.             Msg = msg;
  40.         }
  41.  
  42.         public void RemoveSubscriber(SubscriberPeopleBase subscriber)
  43.         {
  44.             if (SubscriberLst != null) SubscriberLst.Remove(subscriber);
  45.         }
  46.  
  47.         /// <summary>
  48.         /// 发送报警消息
  49.         /// </summary>
  50.         public void SendNotify()
  51.         {
  52.             if (SubscriberLst?.Count > 0)
  53.             {
  54.                 //循环从职位低到高
  55.                 SubscriberLst.OrderByDescending(p => (int)p.Title);    
  56. //立即执行CheckNotifySendResult,以为3秒为间隔
  57.                 NotifyTimer = new Timer(CheckNotifySendResult, null, 0, 3000); 
  58.             }
  59.         }
  60.  
  61.         public void CheckNotifySendResult(object stat)
  62.         {
  63.             //先要停止定时器,防止并发
  64.             NotifyTimer.Change(-1, 3000);
  65.             //第一次发给职位最小 枚举最大的组成员
  66.             SubscriberPeopleBase subscriberPeople = SubscriberLst.Find(p =>
  67. (((int)CurrentJobLevel + 1) - (int)p.Title) == 1);
  68.             if (subscriberPeople == null) return; //已经是最小的
  69.             if (!IsSucess)
  70.             {
  71.                 IsSucess = subscriberPeople.ReceiveData(this);
  72.                 if (!IsSucess)
  73.                 {
  74.                     CurrentJobLevel = CurrentJobLevel!= JobLevel.公司领导?(JobLevel)((int)CurrentJobLevel) - 1: JobLevel.公司领导;
  75.                     NotifyTimer.Change(3000, 3000);
  76.                 }
  77.             }
  78.             else
  79.             {
  80.                 Console.WriteLine($"停止计数器 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
  81.                 NotifyTimer.Dispose();
  82.             }
  83.         }
  84.     }

  还需要有订阅者,订阅者使用抽象类方式降低耦合性(其实还是要用事件总线,将耦合性和扩展性提高一个档次)

  1. /// <summary>
  2.     /// 订阅者
  3.     /// 其实这里还不够完善,理论上后续如果每个职位的订阅者对报警消息的处理不同则在下面的接收消息里会存在一些冗余代码,
  4.     /// 理想情况是不同级别应该有不同的实现,这样可以足够抽象,后续扩展也更加方便
  5.     /// </summary>
  6.     public abstract class SubscriberPeopleBase
  7.     {
  8.         /// <summary>
  9.         /// 工作职位(级别)
  10.         /// </summary>
  11.         public JobLevel Title { get; set; }
  12.  
  13.         public SubscriberPeopleBase(JobLevel title)
  14.         {
  15.             Title = title;
  16.         }
  17.  
  18.         public virtual bool ReceiveData(Baojing baojing)
  19.         {
  20.             if (Title == JobLevel.公司领导)
  21.             {
  22.                 Console.WriteLine($"出现报警信息:{baojing.Msg},当前订阅者:{Title.ToString()} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
  23.                 return true;
  24.             }
  25.             else
  26.             {
  27.                 Console.WriteLine($"出现报警信息:{baojing.Msg},当前订阅者:{Title.ToString()},默认未应答  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
  28.                 return false;
  29.             }
  30.         }
  31.     }

  再加个子类,重写下接收警报的方法

  1. public class SubscriberPeople : SubscriberPeopleBase
  2. {
  3.         public SubscriberPeople(JobLevel title) : base(title) { }
  4.         public override bool ReceiveData(Baojing baojing)
  5.         {          
  6.             if (Title == JobLevel.公司领导)
  7.             {
  8.                 Console.WriteLine($"出现最新报警信息:{baojing.Msg},当前订阅者:{Title.ToString()} 已成功应答 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
  9.                 return true;
  10.             }
  11.             else
  12.             {
  13.                 Console.WriteLine($"出现报警信息:{baojing.Msg},当前订阅者:{Title.ToString()},默认未应答 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
  14.                 return false;
  15.             }
  16.         }
  17. }

  最终我的main方法是这样

  1.   static void Main(string[] args)
  2.   {
  3.             IPublish publish = new Baojing();
  4.             publish.AddSubscriber(new SubscriberPeople(JobLevel.组成员));
  5.             publish.AddSubscriber(new SubscriberPeople(JobLevel.公司领导));
  6.             publish.AddSubscriber(new SubscriberPeople(JobLevel.部门主管));
  7.             publish.AddSubscriber(new SubscriberPeople(JobLevel.小组组长));
  8.             publish.CreateJB("主机温度过高!");
  9.             publish.SendNotify();
  10.             Console.ReadLine();
  11. }

  调试的效果如下:

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

使用C#的计时器加观察者模式完成报警推送需求的更多相关文章

  1. 基于Websocket+SpringMVC4推送部标Jt808终端报警(转)

    原文地址:http://www.jt808.com/?p=1263 在开发部标监控平台的时候,我们要及时的将部标终端报警推送到web界面上,以弹窗的形式提供给用户显示,要将报警显示在界面上,部标808 ...

  2. C++实现微信WeChat网页接口推送股票报警消息

    QStockView微信推送股票报警 1.功能简介 最近很多用户反馈,软件只能在电脑上使用,不能在手机上使用.所以增加了微信推送报警的功能,电脑端的报警提示消息可以通过微信同步发送到手机微信.这样即可 ...

  3. iOS开发笔记10:圆点缩放动画、强制更新、远程推送加语音提醒及UIView截屏

    1.使用CAReplicatorLayer制作等待动画 CALayer+CABasicAnimation可以制作很多简单的动画效果,之前的博客中介绍的“两个动画”,一个是利用一张渐变色图片+CABas ...

  4. 从APP消息推送所理解的观察者模式

    #1.什么是观察者模式? 观察者模式=(出版者+订阅者)模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能 ...

  5. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  6. IM推送保障及网络优化详解(二):如何做长连接加推送组合方案

    对于移动APP来说,IM功能正变得越来越重要,它能够创建起人与人之间的连接.社交类产品中,用户与用户之间的沟通可以产生出更好的用户粘性. 在复杂的 Android 生态环境下,多种因素都会造成消息推送 ...

  7. 避免重复提交?分布式服务的幂等性设计! 架构文摘 今天 点击蓝色“架构文摘”关注我哟 加个“星标”,每天上午 09:25,干货推送! 来源:https://www.cnblogs.com/QG-whz/p/10372458.html 作者:melonstreet

    避免重复提交?分布式服务的幂等性设计! 架构文摘  今天 点击蓝色"架构文摘"关注我哟    加个"星标",每天上午 09:25,干货推送!      来源:h ...

  8. js用计时器加载大量dom

    改进前: <table><tbody></tbody></table> var tbody=document.getElementsByTagName( ...

  9. 从三数之和看如何优化算法,递推-->递推加二分查找-->递推加滑尺

    人类发明了轮子,提高了力的使用效率. 人类发明了自动化机械,将自己从重复的工作中解脱出来. 提高效率的方法好像总是离不开两点:拒绝无效劳动,拒绝重复劳动.人类如此,计算机亦如是. 前面我们说过了四数之 ...

随机推荐

  1. Java单体应用 - 导读

    原文地址:http://www.work100.net/training/monolithic 更多教程:光束云 - 免费课程 Java单体应用 本阶段课程将学习如何进行Java单体Web应用开发,经 ...

  2. lisp学习总结(一)

    lisp太简单 lisp核心太简单了只有几个简单的逻辑定理,简单到你会认为他啥事都做不了. lisp语法太简单了,只有符号,参数,以及括号,组成一种万能的表达式. 由于上述lisp的简单,所以对于初学 ...

  3. Easy C 编程 in Linux

    入坑Ubuntu有4,5天了,当时各种不习惯,但现在渐渐喜欢上了这种简单大方的显示界面和快速高效的命令行模式,各种没有的功能也都有网页版,非常不错呢. 现在最让我感到神奇之处,便是Linux的C编程是 ...

  4. [HNOI2008]Cards(dp,Burnside引理)

    Burnside引理: 参考自 某大佬对Burnside引理和Polya定理的讲解 相关概念 群:在数学中,群表示一个拥有满足封闭性.满足结合律.有单位元.有逆元的二元运算的代数结构. 置换群:由有限 ...

  5. Arduino系列之LCD1602模块使用方法(一)

    下面我将简单介绍LCD1602模块的使用方法: 1602液晶显示器(1602 Liquid Crystal Display,此后简称1602 LCD)是一种常见的字符液晶显示器,因其能显示16*2个字 ...

  6. Arduino通信篇系列之print()和write()输出方式的差异

    我们都知道,在HardwareSerial类中有print()和write()两种输出方式, 两个都可以输出数据,但其输出方式并不相同. 例子: float FLOAT=1.23456; int IN ...

  7. 最新2.7版本丨DataPipeline数据融合产品最新版本发布

    此次发布的2.7版本在进一步优化产品底层数据处理逻辑的同时更加注重提升用户在数据融合任务的日常管理.运行监控及资源分配等管理方面的功能增强与优化,力求帮助大家更为直观.便捷.稳定地管理数据融合任务,提 ...

  8. TortoiseSVN使用教程[多图超详细]

    安装及下载client 端 下载Windows 端程序:http://tortoisesvn.net/downloads.一般而言,如果是32-bit的Windows XP 应该使用TortoiseS ...

  9. tricky c++ new(this)

    题目如下:问下列代码的打印结果为0吗? #include <stdlib.h> #include <iostream> using namespace std; struct  ...

  10. Codeforces_429_B

    http://codeforces.com/problemset/problem/429/B 挺简单的题,先求出四个点到每一点的最大和,然后枚举每一点,取和最大值. 注意两条路相交的点有且只有一个,这 ...