统一对象消息编程详解——定时任务模块

      在之前的文章《统一对象消息编程详解——通过分析定时任务模块来理解消息》中介绍了定时任务模块TLMsgTask。当时那个模块功能比较单一,只是简单周期任务的运行 ,现在对模块功能进行了改进,更加了定时任务的开启、停止等各项功能,并加进去了quartz 的cron表达式定时任务,完全实现了quartz的各种复杂定时。

    为解析cron表达式,直接使用了quartz的类org.quartz.CronExpression,用来解析表达式及获取执行时间,这样保证了cron解析的准确及符合cron表达式的规范。除了使用quartz的cron解析以外,没有使用quartz的其他类或功能。

  对于一个执行中的消息,如何能自由控制它的起、停或热加载,我这里采取了一个链条消息,在被执行的消息前增加了一个控制消息,每次任务执行时,先执行控制消息,然后在执行任务消息,当执行控制消息时,就可以自由的决定下一个任务消息的执行。设置控制消息非常简单:

 private void startTask(TLMsg msg) {
        String taskid = getTaskid(msg);
        if (taskDatas.get(taskid) != null)
            return;
        TLMsg controlMsg = createMsg().setAction("taskControl");
        TLMsg bmsg = createMsg();
        bmsg.copyFrom(msg);
        HashMap<String, Object> taskData = new HashMap<>();
        taskData.put("times", 0);
        taskDatas.put(taskid, taskData);
        controlMsg.setParam("taskid", taskid);
        controlMsg.setNextMsg(bmsg);
        if (msg.getParam("cronExp") != null) {
            CronExpression cron = null;
            try {
                cron = new CronExpression((String) msg.getParam("cronExp"));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            Date now = new Date();
            controlMsg.setParam("cron", cron).setParam("startTime", now).setParam("lastDisplayTime", 0);
        }
        executeTask(controlMsg);
        putLog("task start ,taskid: " + taskid, LogLevel.INFO);
    }

代码中,我们构建一个控制消息 controlMsg ,然后设置任务消息msg为它的下一个执行消息, controlMsg.setNextMsg(bmsg);

这样每次任务执行时先执行controlMsg。如果任务消息msg参数中含有cron表达式cronExp,则构建cron解析类加入控制消息中供控制方法taskControl使用。

在处理cron定时时,我们的消息模块是对每一个任务启动一个线程而周期运行,每次运行由控制消息来决定是否执行任务消息,如果达到下个定时则执行任务消息,否则继续循环。

配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
    <params>
        <status value="run"/>
        <poolSize value="5"/>
    </params>
    <taskMsgTable>
        <msg destination="pcboy" action="eat" taskid="pcboy_eat" status="stop" times="5" timeUnit="ms" begin="111" delay="30"></msg>
        <msg destination="pcboy" action="play" taskid="pcboy_play"  status="stop" cronExp="*/10 * * * * ?"></msg>
        <msg destination="pcboy" action="sleep" taskid="pcboy_sleep" status="run" cronExp="0 25-35 15 * * ?"></msg>
    </taskMsgTable>
</moduleConfig>

 现在任务控制将非常灵活,参数:

params定义全局控制:

 status :

       run 定时模块可以启动;stop 停止所有任务不关闭线程;shutdown ,所有任务停止并关闭线程;

poolSize :线程池数量

<taskMsgTable>用来配置任务消息:

taskid:消息id,用来标识任务,唯一性

status:

       stop  任务不可运行、停止任务。当管理模块运行后停止任务,线程不关闭

       run   任务可运行,对于stop状态的任务再次执行 。管理模块启动时,只有状态为 run 的任务运行。

      shutdown   停止任务并关闭线程。

      restart   在管理模块启动后,启动或重启一个任务。

cronExp:cron表达式,对于设置cron表达式的消息任务,其他定时参数不在起作用。

以下参数用于非cron任务:

  times:运行次数 ,例如运行5次后停止

  begin:开始时间 

  delay:任务间隔时间

 timeUnit: 时间单位  ms 毫秒  s秒   m分钟  h 小时  d 天

如  times="5" timeUnit="ms" begin="111" delay="30"  则只运行5次,时间单位是毫秒 ,管理模块启动后111毫秒开始任务运行 ,任务间隔为30毫秒。

通过配置文件监听模块可以动态更改配置文件,因此也就可以动态启停、增加任务。

程序入口函数:

package cn.tianlong.java.tlobjdemo.task;
import cn.tianlong.tlobjcet.base.TLMsg;
import cn.tianlong.tlobjcet.base.TLObjectFactory;
public class Main  {

    public static void main(String[] args)
    {
        String configdir = "/person/";
        TLObjectFactory myfactory = TLObjectFactory.getInstance(configdir,"moduleFactory_config.xml");
        myfactory.boot();
        TLMsg taksMsg =new TLMsg().setAction("getModule").setParam("moduleName","myTaskManger");
      myfactory.putMsg(myfactory,taksMsg);
    }
}

 myTaskManger即我们定时管理模块TLMsgTask 。

任务管理模块:

package cn.tianlong.tlobjcet.base;
import org.quartz.CronExpression;
import org.xmlpull.v1.XmlPullParser;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 创建日期:2018/4/23 on 21:00
 * 描述:
 * 作者:tianlong
 */

public class TLMsgTask extends TLBaseModule {
    protected ScheduledExecutorService executor;
    protected ArrayList<TLMsg> taskMsgTable;
    protected Map<String, HashMap<String, Object>> taskDatas = new ConcurrentHashMap<>();
    protected int poolSize = 5;

    public TLMsgTask() {
        super();
    }

    public TLMsgTask(String name) {
        super(name);
    }

    public TLMsgTask(String name, TLObjectFactory modulefactory) {
        super(name, modulefactory);
    }

    @Override
    protected Object setConfig() {
        myConfig config = new myConfig();
        mconfig = config;
        super.setConfig();
        taskMsgTable = config.getTaskMsgTable();
        return config;
    }

    @Override
    protected void initProperty() {
        super.initProperty();
        if (params != null) {
            if (params.get("poolSize") != null) {
                poolSize = Integer.parseInt(params.get("poolSize"));
            }            ;
        }
    }

    @Override
    protected void init() {
        if (params.get("status") != null && !params.get("status").equals("run")) {
            putLog("if you wang to run ,set status to run ", LogLevel.WARN);
            return;
        }        ;
        if (taskMsgTable == null || taskMsgTable.isEmpty())
            return;
        if (executor == null)
            executor = Executors.newScheduledThreadPool(poolSize);
        for (TLMsg msg : taskMsgTable)
        {
            if (msg.getParam("status").equals("run")) {
                if (msg.getParam("cronExp") != null) {
                    msg.setParam("delay", "100").setParam("timeUnit","ms");
                }
                startTask(msg);
            }
        }
    }

    @Override
    protected TLMsg checkMsgAction(Object fromWho, TLMsg msg) {
        TLMsg returnMsg = null;
        switch (msg.getAction()) {
            case "registTask":
                registTask(fromWho, msg);
                break;
            case "setTaskStatus":
                setTaskStatus(fromWho, msg);
                break;
            case "unRegistTask":
                unRegistTask(fromWho, msg);
                break;
            case "startTask":
                init();
                break;
            case "taskControl":
                returnMsg = taskControl(fromWho, msg);
                break;
            case "shutdown":
                executor.shutdown();
                break;
            default:
        }
        return returnMsg;
    }

    private void setTaskStatus(Object fromWho, TLMsg msg) {
        String nowTaskid = (String) msg.getParam("taskid");
        TLMsg nowTaskMsg = null;
        for (TLMsg tmsg : taskMsgTable) {
            String taskid = getTaskid(tmsg);
            if (nowTaskid.equals(taskid)) {
                nowTaskMsg = tmsg;
                break;
            }
        }
        msg.removeParam("taskid");
        nowTaskMsg.addArgs(msg.getArgs());
    }
    @Override
       protected void reConfig()  {
        if (params != null) {
            if (params.get("status") != null && params.get("status").equals("shutdown")) {
                executor.shutdown();
                putLog("all task shutdown", LogLevel.WARN);
                return;
            }
            if (params.get("status") != null && params.get("status").equals("stop")) {
                for (TLMsg tmsg : taskMsgTable) {
                    tmsg.setParam("status", "stop");
                }
            }            ;
        }
        for (TLMsg tmsg : taskMsgTable)
        {
            String status = (String) tmsg.getParam("status");
            if(status ==null)
                continue;
            HashMap<String, Object> nowTaskdata = taskDatas.get(tmsg.getParam("taskid"));
            if( status.equals("restart"))
            {
                if(nowTaskdata !=null )
                    shutdownTask((String) tmsg.getParam("taskid"));
                tmsg.setParam("status", "run");
                if (tmsg.getParam("cronExp") != null) {
                    tmsg.setParam("delay", "100").setParam("timeUnit","ms");;
                }
                startTask(tmsg);
            }
        }
    }

    private TLMsg taskControl(Object fromWho, TLMsg msg) {
        String nowTaskid = (String) msg.getParam("taskid");
        TLMsg nowTaskMsg = null;
        for (TLMsg tmsg : taskMsgTable) {
            String taskid = getTaskid(tmsg);
            if (nowTaskid.equals(taskid)) {
                nowTaskMsg = tmsg;
                break;
            }
        }
        if (nowTaskMsg == null)
            return null;
        String status = (String) nowTaskMsg.getParam("status");
        if (status != null && status.equals("shutdown")) {
            shutdownTask(nowTaskid);
            return new TLMsg().setParam(DONEXTMSG, "false");
        }
        if (status != null && status.equals("stop")) {
            nowTaskMsg.setParam("status","stoped");
            putLog("taskid:" + nowTaskid + " stop", LogLevel.WARN);
            return new TLMsg().setParam(DONEXTMSG, "false");
        }
        if (status != null && status.equals("stoped")) {
            return new TLMsg().setParam(DONEXTMSG, "false");
        }
        HashMap<String, Object> nowTaskdata = taskDatas.get(nowTaskid);
        int nowTimes = (int) nowTaskdata.get("times");
        String timesLimit = (String) nowTaskMsg.getParam("times");
        if (timesLimit != null) {
            int times = Integer.parseInt(timesLimit);
            if (times > 0) {
                if (times == nowTimes) {
                    nowTaskMsg.setParam("status","stoped");
                    putLog("taskid:" + nowTaskid + " stop,runing times:" + nowTimes, LogLevel.WARN);
                    return new TLMsg().setParam(DONEXTMSG, "false");
                }
            }
        }
        if (msg.getParam("cron") != null) {
            Date statTime = (Date) msg.getParam("startTime");
            CronExpression cron = (CronExpression) msg.getParam("cron");
            Long now = System.currentTimeMillis();
            Date execDate = cron.getTimeAfter(statTime);
            if (execDate == null) {
                ScheduledFuture<?> sf = (ScheduledFuture<?>) nowTaskdata.get("future");
                putLog("taskid:" + nowTaskid + " is over,session shutdown", LogLevel.WARN);
                taskDatas.remove(nowTaskid);
                sf.cancel(true);
                return new TLMsg().setParam(DONEXTMSG, "false");
            }
            long execTime = execDate.getTime();
            long timeUntilExec = execTime - now;
            if (timeUntilExec > 2) {
                displayTimeUntil(nowTaskid, timeUntilExec / 1000, msg);
                return new TLMsg().setParam(DONEXTMSG, "false");
            } else
                msg.setParam("startTime", new Date());
        }
        nowTaskdata.put("times", nowTimes + 1);
        putLog("taskid:" + nowTaskid + "  has runing times:" + (nowTimes + 1), LogLevel.INFO);
        return null;
    }
    private void shutdownTask(String taskid)
    {
        HashMap<String, Object> nowTaskdata = taskDatas.get(taskid);
        if(nowTaskdata ==null )
        {
            putLog("shutdown error, no taskid:" + taskid , LogLevel.ERROR);
            return;
        }
        ScheduledFuture<?> sf = (ScheduledFuture<?>) nowTaskdata.get("future");
        putLog("taskid:" + taskid + " shutdown", LogLevel.WARN);
        taskDatas.remove(taskid);
        sf.cancel(true);
    }
    private void displayTimeUntil(String taskid, Long time, TLMsg msg) {
        int lastDisplayTime = (int) msg.getParam("lastDisplayTime");
        String logContent =null;
        if (time < 60 && lastDisplayTime > 100)
          logContent= time + "s";
         else if (60 <= time && lastDisplayTime > 600 )
                logContent= time / 60 + "m";
        if(logContent!=null)
        {
            putLog("taskid:" + taskid + "  waiting:" + logContent, LogLevel.INFO);
            msg.setParam("lastDisplayTime", 0);
        }
        else
           msg.setParam("lastDisplayTime", lastDisplayTime + 1);
    }

    private void registTask(Object fromWho, TLMsg msg) {
        TLMsg tmsg = (TLMsg) msg.getParam("msg");
        if (taskMsgTable == null)
            taskMsgTable = new ArrayList<>();
        taskMsgTable.add(tmsg);
        if (tmsg.getParam("status") != null && tmsg.getParam("status").equals("run"))
            executeTask(tmsg);
    }

    private void startTask(TLMsg msg) {
        String taskid = getTaskid(msg);
        if (taskDatas.get(taskid) != null)
            return;
        TLMsg controlMsg = createMsg().setAction("taskControl");
        TLMsg bmsg = createMsg();
        bmsg.copyFrom(msg);
        controlMsg.setParam("taskid", taskid);
        controlMsg.setNextMsg(bmsg);
        if (msg.getParam("cronExp") != null) {
            CronExpression cron = null;
            try {
                cron = new CronExpression((String) msg.getParam("cronExp"));
            } catch (ParseException e) {
                putLog("cronExp error " , LogLevel.ERROR);
              return;
            }
            Date now = new Date();
            controlMsg.setParam("cron", cron).setParam("startTime", now).setParam("lastDisplayTime", 0);
        }
        HashMap<String, Object> taskData = new HashMap<>();
        taskData.put("times", 0);
        taskDatas.put(taskid, taskData);
        executeTask(controlMsg);
        putLog("taskid: " + taskid+"  start ", LogLevel.INFO);
    }

    private void unRegistTask(Object fromWho, TLMsg msg) {
        for (TLMsg tmsg : taskMsgTable) {
            if (tmsg.getMsgId().equals((String) msg.getParam("msgId")))
                taskMsgTable.remove(tmsg);
        }
    }

    private String getTaskid(TLMsg msg) {
        String taskid = (String) msg.getParam("taskid");
        if (taskid == null || taskid.isEmpty())
            taskid = msg.getDestination() + msg.getMsgId() + msg.getAction();
        return taskid;
    }

    private void executeTask(TLMsg msg) {
        Long begin = Long.valueOf(0);
        TLMsg taskMsg = msg.getNextMsg();
        String taskid = (String) msg.getParam("taskid");
        String sbegin = (String) taskMsg.getParam("begin");
        if (sbegin != null)
            begin = Long.parseLong(sbegin);
        String sdelay = (String) taskMsg.getParam("delay");
        if (sdelay == null) {
            putLog("no set delay,taskid" + taskid, LogLevel.ERROR);
            return;
        }
        String timeUnitStr = (String) taskMsg.getParam("timeUnit");
        if(timeUnitStr==null)
            timeUnitStr="s";
        TimeUnit timeUnit =getTimeUnit(timeUnitStr);
        Long delay = Long.parseLong(sdelay);
        taskMsg.removeParam("delay");
        taskMsg.removeParam("begin");
        taskMsg.removeParam("taskid");
        taskMsg.removeParam("times");
        taskMsg.removeParam("status");
        taskMsg.removeParam("timeUnit");
        taskMsg.removeParam("cronExp");
        Runnable task = getMsgTask(this, msg);
        HashMap<String, Object> nowTaskdata = taskDatas.get(taskid);
        ScheduledFuture<?> sf = executor.scheduleAtFixedRate(task, begin, delay, timeUnit);
        nowTaskdata.put("future", sf);
    }

    private TimeUnit getTimeUnit(String timeUnitStr) {
        TimeUnit timeUnit;
        switch (timeUnitStr) {
            case "ms":
                timeUnit =TimeUnit.MILLISECONDS;
                break;
            case "s":
                 timeUnit =TimeUnit.SECONDS;
                break;
            case "m":
                timeUnit =TimeUnit.MINUTES;
                break;
            case "h":
                timeUnit =TimeUnit.HOURS;
                break;
            default:
                timeUnit =TimeUnit.SECONDS;
        }
        return timeUnit;
    }

    private Runnable getMsgTask(final TLMsgTask object, final TLMsg msg) {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                object.getMsg(object, msg);
            }
        };
        return task;
    }

    protected class myConfig extends TLModuleConfig {
        protected ArrayList<TLMsg> taskMsgTable;

        public myConfig() {

        }

        public ArrayList<TLMsg> getTaskMsgTable() {
            return taskMsgTable;
        }

        protected void myConfig(XmlPullParser xpp) {
            super.myConfig(xpp);
            try {
                if (xpp.getName().equals("taskMsgTable")) {
                    taskMsgTable = getMsgList(xpp, "taskMsgTable");
                }

            } catch (Throwable t) {

            }
        }

    }
}

  在模块工厂配置文件中设置启动配置文件监听模块:

        <factoryBoot>
            <msg action="getModule"  moduleName="log" usePreReturnMsg="false" />
            <msg action="getModule"  moduleName="monitorConfig" usePreReturnMsg="false" />
        </factoryBoot>

监听模块"monitorConfig自动监听配置文件改变。监听模块配置中增加监听任务定时管理模块:

<moduleConfig>
    <params>
        <delay value="5" />
        <modules value="myTaskManger"  />
    </params>
</moduleConfig>

案例任务模块PcBoy 见上篇文章。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值