有这样的场景,淘宝开放平台上有销售订单API,销售订单金额API,商品上下架API,退款API等各种开放的 API,稍微有点规模的商家都会自己开发基于淘宝平台的信息管理系统,里面会涉及到订单管理,采购管理,库存管理,售后管理等,这些管理系统里面的数据都是通过API获取,或者通过API推送到淘宝平台上,这些系统的好处就是避免工作人员直接在淘宝后台进行各项操作。
基于上面的情况,那么自然会有很多后台程序来跑这些 API,刚开始我是这样设计的。
在一个后台程序里面开几个线程,每个线程对应几个API,这样也能够满足日常需求,但是随着业务量增大,会有各种数据或者任务需要处理,这个时候上面的程序也能满足需求,但是每次都要修改程序,然后在发布,实在是有些头疼。后来我就想,能不能开发一个管理平台,来自动管理这些任务,什么时候想停止任务,直接在配置管理网站上点一下按钮,想开启也是一样操作,如果推出这样的一个管理平台,那么像管理这种业务数据的活可以直接丢给店铺管理人员。
经过一番思考,想到了这样一种方案,先看图。
上面的图可以很清晰看出工作管理模型。其实到这个时候,最最关心还是工作管理器的内核到底是怎样设计的?那我就说说我的思路是怎样的。
首先是工作任务运行时间的设计,因为有的API是需要白天运行的有的是晚上闲时运行的,有的是7*24小时不间断运行的,那么我们可以设计这样的一个枚举,命名WorkType,具体内容是:
public enum WorkType { ALLWork = 1, WorkTime = 2, Morning = 3, Night = 4 }
ALLWork 的运行时间是全天,WorkTime的运行时间是早上9点到晚上22点,Morning的运行时间是凌晨3点到早上9点,Night的运行时间是晚上22点到凌晨3点,相信这几个时间区域很多开发同事都会涉及到的。
然后是工作线程类WorkThread,将它定义为一个抽象类,里面含有一个抽象方法WorkRun,每一个详细的任务类都会继承WorkThread类,实现抽象方法,也就是说,每个任务的业务处理逻辑都是在WorkRun方法里面实现的。因为上面提到了任务的运行时间区间,那么我们在初始化一个继承WorkThread类的时候,会设置它的运行时间区间。然后在这个抽象类中还还有一个方法,它的作用就是将实现了WorkThread的类丢到ThreadPool里面,看看具体的实现代码。
public abstract class WorkThread { private int index; public WorkThread(int Step,WorkType worktype) { this.WorkType = worktype; this.Step = Step; this.index = Step; } public int Step { get; set; } public WorkType WorkType { get; set; } public bool HasWork { get { bool result = false; switch (WorkType) { case WorkType.Morning: result = index == Step && 3<=DateTime.Now.Hour && DateTime.Now.Hour<=9 ? true : false; break; case WorkType.WorkTime: result = index == Step && 9 <= DateTime.Now.Hour && DateTime.Now.Hour <= 22 ? true : false; break; case WorkType.Night: result = index == Step && 22 <= DateTime.Now.Hour && DateTime.Now.Hour <= 3 ? true : false; break; case WorkType.ALLWork: default: result = index == Step ? true : false; break; } return result; } } public abstract void WorkRun(object ob); public void Run() { ThreadPool.QueueUserWorkItem(new WaitCallback(WorkRun)); index = 0; } public void AddStep() { index += 1; } }
那么具体的任务类如下。
public class TestA : WorkThread { public TestA(int step, WorkType type) : base(step, type) { } public override void WorkRun(object ob) { Console.WriteLine(string.Format("我是A小姐,我开始工作了。时间:{0}", DateTime.Now)); } }
其实到这里了,有的人会想到,那我们怎么来管理这些WorkThread类了,这里我们专门有一个类来管理WorkThread,这里命名为WorkManager。这个类里面的结构就相当简单了。看到上面的模型图,可以猜出一些端倪。里面专门定义了一个字典项,用来存储每一个WorkThread,它是ConcurrentDictionary,表示可由多个线程同时访问的键值对的线程安全集合。管理类里面还定义了4个方法,增加删除WorkThread,开始结束WorkThread。具体看下面的代码。
public class WorkManager { static readonly ConcurrentDictionary<string, WorkThread> WorkCache = new ConcurrentDictionary<string, WorkThread>(); public int WorkCount { get { return WorkCache.Count; } } public void AddWork(WorkThread workThread) { WorkCache.TryAdd(workThread.GetType().FullName, workThread); } public void RemoveWork(string workThreadId) { var obj = WorkCache[workThreadId]; if(obj != null) WorkCache.TryRemove(workThreadId, out obj); } public void StartWork() { Action action = () => { while (true) { foreach (KeyValuePair<string, WorkThread> work in WorkCache) { if (work.Value.HasWork) { work.Value.Run(); } else { work.Value.AddStep(); } } Thread.Sleep(5000); } }; action.BeginInvoke(cb => action.EndInvoke(cb), null); } public void EndWork() { Action action = () => { WorkCache.Clear(); }; action.BeginInvoke(cb => action.EndInvoke(cb), null); } }
代码是不是很简单,到这里位置,工作管理的内核代码全部上完了,那我们该怎么调用了,看看下面调用代码。
Console.Title = "工作管理器"; WorkManager manager = new WorkManager(); manager.AddWork(new TestA(1, WorkType.ALLWork)); manager.AddWork(new TestB(1, WorkType.ALLWork)); manager.StartWork(); Console.WriteLine("1.全部开始工作"); Thread.Sleep(15 * 1000); manager.EndWork(); Console.WriteLine("2.全部卸载"); Thread.Sleep(10 * 1000); manager.AddWork(new TestB(1, WorkType.ALLWork)); Console.WriteLine("3.加入B"); Console.Read();
看一下运行效果图:
大家看完之后是不是觉得很简单,不过有什么疑问可以在这里留言,谢谢。