文章連接:https://liuyueyi.github.io/hexblog/2018/07/23/180723-Quick-Task-動態腳本支持框架之結構設計篇/java
相關博文:git
前面兩篇博文,主要是總體介紹和如何使用;接下來開始進入正題,逐步剖析,這個項目是怎麼一步一步搭建起來的;本篇博文則主要介紹基本骨架的設計,圍繞項目的核心點,實現一個基礎的原型系統github
總體設計圖以下:redis
對於上面的圖,得有一個基本的認知,最好是能在腦海中構想出整個框架運行的方式,在正式開始以前,先簡單的過一下這張結構圖api
抓要點緩存
即圖中的每一個task就表示一個基本的任務,有下面幾個要求框架
在圖中表現很明顯了,在內存中會保存一個當前全部執行的任務隊列(或者其餘的容器)ide
這個的目的是什麼?工具
雖然圖中並無明確的說有這麼個東西,但也好理解,咱們的系統設計目標就是支持多任務的執行和熱加載,那麼確定有個任務管理的角色,來處理這些事情學習
其要作的事情就一個任務熱加載
這個與核心功能關係不大,能夠先不care,簡單說一下就是爲task提供更好的使用的公共類
這裏不詳細展開,後面再說
有了上面的簡單認知以後,開始進入正題,編碼環節,省略掉建立工程等步驟,第一步就是設計Task的API
抽象公共的任務接口,從任務的標識區分,和業務調度執行,很容易寫出下面的實現
public interface ITask { /** * 默認將task的類名做爲惟一標識 * * @return */ default String name() { return this.getClass().getName(); } /** * 開始執行任務 */ void run(); /** * 任務中斷 */ default void interrupt() {} }
前面兩個好理解,中斷這個接口的目的何在?主要是出於任務結束時的收尾操做,特別是在使用到流等操做時,有這麼個回調就比較好了
任務裝飾類,爲何有這麼個東西?出於什麼考慮的?
從上面能夠知道,全部的任務最終都是在獨立的線程中調度執行,那麼咱們本身實現的Task確定都是會封裝到線程中的,在Java中能夠怎麼起一個線程執行呢?
一個順其天然的想法就是包裝一下ITask接口,讓它集成自Thread,而後就能夠簡單的直接將任務丟到線程池中便可
@Slf4j public class ScriptTaskDecorate extends Thread { private ITask task; public ScriptTaskDecorate(ITask task) { this.task = task; setName(task.name()); } @Override public void run() { try { task.run(); } catch (Exception e) { log.error("script task run error! task: {}", task.name()); } } @Override public void interrupt() { task.interrupt(); } }
說明:
上面這個並非必須的,你也徹底能夠本身在線程池調度Task任務時,進行硬編碼風格的封裝調用,徹底沒有問題(只是代碼將不太好看而已)
上面兩個是具體的任務相關定義接口,接下來就是維護這些任務的容器了,最簡單的就是用一個Map來保存,uuid到task的映射關係,而後再須要卸載/更新任務時,停掉舊的,添加新的任務,對應的實現也比較簡單
public class TaskContainer { /** * key: com.git.hui.task.api.ITask#name() */ private static Map<String, ScriptTaskDecorate> taskCache = new ConcurrentHashMap<>(); /** * key: absolute script path * * for task to delete */ private static Map<String, ScriptTaskDecorate> pathCache = new ConcurrentHashMap<>(); public static void registerTask(String path, ScriptTaskDecorate task) { ScriptTaskDecorate origin = taskCache.get(task.getName()); if (origin != null) { origin.interrupt(); } taskCache.put(task.getName(), task); pathCache.put(path, task); AsynTaskManager.addTask(task); } public static void removeTask(String path) { ScriptTaskDecorate task = pathCache.get(path); if (task != null) { task.interrupt(); taskCache.remove(task.getName()); pathCache.remove(path); } } }
說明
爲何有兩個map,一個惟一標識name爲key,一個是task的全路徑爲key?
pathCache
前面介紹了任務的定義和裝載任務的容器,接下來能夠想到的就是如何發現任務並註冊了,這一塊這裏不要詳細展開,後面另起一篇詳解;主要說一下思路
在設計之初,就決定任務採用Groovy腳原本實現熱加載,因此有兩個很容易想到的功能點
TaskChangeWatcher
GroovyCompile
有了上面四個是否能夠搭建一個原型框架呢?
答案是能夠的,整個框架的運行過程
固然其餘一些輔助的工具類無關緊要了,固然從使用的角度出發,有不少東西仍是頗有必要的,如
博文:
項目:
一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
盡信書則不如,已上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激