设计模式——命令模式实战之WCS详细设计与实现(三)

在这里插入图片描述

上一篇设计模式——命令模式实战之WCS概要设计(三)基于WCS的逻辑进行了简单抽象,并基于命令模式进行了概要设计。接下来,基于实际仓内情况,进行比较详细的设计。

基础信息

在这里插入图片描述

从上图仓库内的情况很复杂,在进行概要设计的时候,需要进行高度的抽象,在详细设计的时候,需要对很多细节进行明确的设计和实现。

这里只基于仓库中的两种设备进行抽象,对场景进行一定的抽象。具体场景如下:

  • 五条巷道,一条巷道里面有一台堆垛机(Stacker)
  • 每条巷道口有接驳口,要放入当前巷道的货物由输送线(ConveyorLine)送至接驳口

这里涉及到两种设备StackerConveyorLine,两种设备的关系是任务执行过程中的两个阶段。文章重点对Stacker进行设计。

Stacker 控制详细设计

更直观的看一下堆垛机。
在这里插入图片描述
图中就是堆垛机,底部是在轨道上的,带有可上下移动并且可以内外伸缩的叉子。

功能点

  1. 堆垛机的健康状态监控
    监控堆垛机的健康状态,确保堆垛机一直在线,对上游的分货位进行通知等逻辑
  2. 堆垛机的工作线程的健康状态
    堆垛机获取到执行命令以后,会有一个执行命令的执行中的一个时间段,需要对工作线程保持监控,如果线程发生异常,需要即时重建工作线程,保证流水线的正常运转。
  3. 即时获取命令去执行
    工作线程要及时获取,待执行的任务或命令,去执行。保证任务被读取到以后,可以准确及时执行。
  4. 即时反馈命令执行结果
    堆垛机执行完以后,需要及时的反馈执行结果,给上游业务逻辑,更新进数据库;以便后续对异常任务人工修复。

分析上述功能点以后,第一思路产生的是实现所有功能,需要多线程定时任务阻塞队列 等Java技术实现。

基于队列模型实现

代码流程 如图:
在这里插入图片描述

从流程中可以看到核心控制命令顺序和并发性是通过阻塞队列完成的。阻塞队列可以实现任务的有序性工作线程的阻塞监听
接下来详细介绍关键类的实现:
堆垛机的实例

// 存放堆垛机实例,
    private Map<String/*stacker id*/, Stacker> stackerMap=new HashMap<>();

使用map保存堆垛机的实例,key为堆垛机的id,值为堆垛机实例。

任务队列的声明

	// 任务队列
    private Map<String/*stacker id*/, LinkedBlockingQueue<StackerTask>> taskQueueMap =new HashMap<>();

使用map声明任务队列,key为堆垛机的id,值为此堆垛机的任务队列,这里使用阻塞队列,工作线程可以利用阻塞队列的阻塞等待特性,实现监听。

堆垛机工作线程监听阻塞队列的核心实现如下

Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                LinkedBlockingQueue<StackerTask> q = taskQueueMap.get(stacker.getId());
                // 阻塞等待任务
                while (q.peek() != null) {
                    StackerTask task = q.poll();
                    // 执行任务
                    stacker.action(task, taskCallbackMap.get(task.getTaskId()));
                }
            }
        }, "stacker-"+stacker.getId());

堆垛机的监控实现

	if(healthCheckLock.isLocked()){
	   return;
   }
   healthCheckLock.lock();

   // 检查堆垛机状态
   heartbeatPool.execute(new Runnable() {
       @Override
       public void run() {
           try {
               for (Stacker stacker: stackerMap.values()) {
                   stacker.checkHealth();
               }
           }finally {
               healthCheckLock.unlock();
           }
       }
   });

工作线程的监控

// 检查工作线程状态
  for (Map.Entry<String,Thread> t :  workerMap.entrySet()) {
        if(!t.getValue().isAlive()){
            rebuildWorker(stackerMap.get(t.getKey()));
        }
    }

核心代码的完整示例如下:

public class StackerManager {
	// 存放堆垛机实例,
    private Map<String/*stacker id*/, Stacker> stackerMap=new HashMap<>();
  	// 任务队列
    private Map<String/*stacker id*/, LinkedBlockingQueue<StackerTask>> taskQueueMap =new HashMap<>();
  	// 存放任务对应的回调函数
    private Map<Long/*task id*/, Function<StackerTaskResult,Boolean>> taskCallbackMap=new HashMap<>();
	// 调度线程池,负责周期性调度需要执行的任务
    private ScheduledExecutorService scheduledExecutorService= Executors.newSingleThreadScheduledExecutor();
	// 健康检查锁,避免多个线程同时检查一台设备或者避免一个线程还没检查完,进行第二次检查
    private ReentrantLock healthCheckLock= new ReentrantLock();


    /**
     * 健康检查线程池
     */
    private ExecutorService heartbeatPool=Executors.newSingleThreadExecutor();
    // 工作线程池
    private ExecutorService taskWorkerPool;
    // 独立持有工作线程
    private Map<String/*stacker id*/,Thread> workerMap=new HashMap<>();
    private StackerNotify stackerNotify;

    public StackerManager(int stackerCount,StackerNotify stackerNotify){
        // 匹配堆垛机的数量
        taskWorkerPool =Executors.newFixedThreadPool(stackerCount);
        this.stackerNotify=stackerNotify;

    }
    public void start(){
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                if(healthCheckLock.isLocked()){
                    return;
                }
                healthCheckLock.lock();

                // 检查堆垛机状态
                heartbeatPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            for (Stacker stacker: stackerMap.values()) {
                                stacker.checkHealth();
                            }
                        }finally {
                            healthCheckLock.unlock();
                        }
                    }
                });
                // 检查工作线程状态
                for (Map.Entry<String,Thread> t :  workerMap.entrySet()) {
                    if(!t.getValue().isAlive()){
                        rebuildWorker(stackerMap.get(t.getKey()));
                    }
                }

            }
        }, 0, 500, TimeUnit.MILLISECONDS);
        // 启动堆垛机,执行任务
        for (Stacker stacker: stackerMap.values()) {
            buildWorker(stacker);
        }
    }
    private void buildWorker(Stacker stacker){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                LinkedBlockingQueue<StackerTask> q = taskQueueMap.get(stacker.getId());
                // 阻塞等待任务
                while (q.peek() != null) {
                    StackerTask task = q.poll();
                    // 执行任务
                    stacker.action(task, taskCallbackMap.get(task.getTaskId()));
                }
            }
        }, "stacker-"+stacker.getId());
        // 注册工作线程
        workerMap.put(stacker.getId(),thread);
        // 执行工作线程
        taskWorkerPool.execute(thread);
    }

    /**
     * 重建工作线程
     * @param stacker
     */
    private void rebuildWorker(Stacker stacker){
        // 中止线程
        workerMap.get(stacker).interrupt();
        if(stacker.getStackerStatus().isHealth()){
            buildWorker(stacker);
        }else {
            log.warn("堆垛机无法连接,销毁工作线程:{}",stacker.getId());
            stackerNotify.notify(stacker.getId(), "无法连接堆垛机");
        }

    }
    public void registerStacker(String id,String ip,int port){
        stackerMap.put(id,new Stacker(id,ip,port));
    }

    /**
     * 任务注册状态
     * @param task
     * @param callback
     * @return
     */
    public boolean asyncExecuteTask(StackerTask task, Function<StackerTaskResult,Boolean> callback){
        Stacker stacker = stackerMap.get(task.getTunnel());
        if(!stacker.getStackerStatus().isHealth()){
            log.error("堆垛机{}异常,无法注册任务:{}",stacker.getId(),task.getTaskId());
            return false;
        }
       

       taskQueueMap.get(task.getTunnel()).add(task);
       taskCallbackMap.put(task.getTaskId(),callback);
        // 异步放入成功
        return true;
    }
}

上面代码是和整个堆垛机控制的核心类。

归纳复盘

通过上面的核心实现,发现并没有按照预期的命令模式实现,相对于频繁读取数据库,顺序调用代码来说,加入了队列,任务,命令,健康之间的互动等。并且硬件健康状态也没有反馈给上游任务等,实现不完整,代码不够优雅。

下一篇真正的引入命令模式,进行代码重构实现类图如下:
在这里插入图片描述

本次代码示例下载

  • 32
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五之

真实案例以及商业项目技术

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值