180719-Quick-Task 動態腳本支持框架之使用介紹篇

logo

文章連接:https://liuyueyi.github.io/hexblog/2018/07/19/180719-Quick-Task-動態腳本支持框架之使用介紹篇/java

Quick-Task 動態腳本支持框架之使用介紹篇

相關博文:git

QuickTask這個項目主要就是爲了解決數據訂正和接口驗證不方便的場景,設計的一個及其簡單的動態腳本調度框架,前面一篇總體介紹篇博文,主要介紹了這是個什麼東西,總體的運行原理,以及一些簡單的使用demogithub

本篇博文將主要放在應用場景的探討上,在實際的項目環境中,能夠怎麼用spring

<!-- more -->緩存

I. 框架使用姿式

支目前來講,有兩種簡單的使用方式,一是以獨立的jar包來運行,二是集成在已有的項目中運行;下面分別給出介紹app

1. 獨立jar包運行

獨立jar包下載,首先下載原始工程,而後打出一個可執行的jar包便可框架

git clone https://github.com/liuyueyi/quick-task
cd quick-task/task-core
mvn clean package -Dmaven.test.skip
cd target
java -jar task-core-0.0.1.jar --task /tmp/script

注意上面的jar包執行中,傳入的--task參數,這個就是制定監聽動態腳本的目錄,如上面的腳本,表示框架會自動加載 /tmp/script 目錄下的Groovy腳本,並執行maven

當腳本發生變更時,一樣會從新加載更新後的groovy並執行,且會停掉原來的腳本ide

2. 項目依賴使用

做爲一個依賴來使用也是能夠的,首先是添加pom依賴工具

<repositories>
    <repository>
        <id>yihui-maven-repo</id>
        <url>https://raw.githubusercontent.com/liuyueyi/maven-repository/master/repository</url>
    </repository>
</repositories>

<dependency>
    <groupId>com.git.hui</groupId>
    <artifactId>task-core</artifactId>
    <version>0.0.1</version>
</dependency>

而後在本身的代碼中,顯示的調用下面一行代碼便可,其中run方法的參數爲動態腳本的目錄

new ScriptExecuteEngine().run("/tmp/script");

對於SpringBoot項目而言,能夠在入口Application類的run方法中調用,一個demo以下

@SpringBootApplication
public class Application implements CommandLineRunner {
    public static void main(String[] args) throws Exception {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args);
    }

    @Override
    public void run(String... strings) throws Exception {
        new ScriptExecuteEngine().run("/tmp/script");
    }
}

對於傳統的Spring項目而言,能夠新建一個Listener, 監聽全部的bean初始化完成以後,開始註冊任務引擎,一個可參考的使用case以下

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class RegisterTaskEngineListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == ContextRefreshedEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        return true;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        new ScriptExecuteEngine().run("/tmp/script");
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

3. 對比小結

兩種使用方式,從我的角度出發,並無什麼優劣之別,主要仍是看具體的業務場景,當但願部署一個獨立的任務腳本支持時,可能獨立的部署更加的方便,能夠在內部進行資源隔離,減小對線上生產環境的影響;

如果單純的把這個做爲一個檢測項目運行的輔助工具時,如回調線上的服務接口,判斷輸出,獲取運行項目中的內部參數等,集成在已有的項目中也是比較簡單的

II. 實際場景演示

使用了這個框架,到底有什麼用處呢?或者說是否有一些適用的經典case呢?

1. 數據查看

這種場景比較常見,但通常配套設施齊全的公司,也不會出現這個問題,咱們最多見的查看數據有如下幾類

  • DB數據查看
  • 緩存數據查看
  • 內存數據查看

對於DB查看,通常沒啥問題,要麼能夠直連查詢要麼就是有查詢工具;而緩存數據的查詢,主要是咱們經過序列化後存入的數據,直接從緩存中獲取可能並不太友好;對於運行時內存中的數據,就不太好獲取了,特別是咱們使用Guava緩存的數據,如何在項目運行中判斷緩存中的數據是否有問題呢?

一個查看內存的僞代碼

class DemoScript implements ITask {
    @Override
    void run() {
        // 獲取目標對象
        xxxBean = ApplicationContextHolder.getBean(xxx.class);
        xxxBean.getXXX();
    }
}

上面的腳本中,關鍵就是在於獲取目標對象,拿到目標對象以後,再獲取內部的局部變量或者內存數據就比較簡單了(不能直接訪問的局部變量能夠經過反射獲取)

因此關鍵就是獲取目標對象,有下面幾種思路可供參考:

  • 目標對象時單例或者靜態類,則能夠直接訪問
  • 若是項目運行在Spring容器中,且目標對象爲Bean,則能夠經過 ApplicationContext#getBean 方式獲取

2. 接口調用

在問題復現的場景下,比較經常使用了,傳入相同的參數,判斷接口的返回結果是否ok,用於定位數據異常

class DemoScript implements ITask {
    @Override
    void run() {
        // 獲取目標對象
        xxxService = ApplicationContextHolder.getBean(xxx.class);
        
        req = buildRequest();
        result = xxxService.execute(req);
        log.info("result: {}", result);
    }
}

其實實際使用起來和前面沒什麼區別,無非是線獲取到對應的Service,而後執行接口,固然在Spring的生態體系中,一個可展望的點就是支持自動注入依賴的bean

3. 定時任務

首先明確一點,在咱們的框架中,全部的任務都是隔離的,獨立的線程中調度的,當咱們但願一個新的任務每隔多久執行一次,能夠怎麼作?

一個簡單的僞代碼以下

class DemoScript implements ITask {
    private volatile boolean run = false;
    @Override
    void run() {
        run = true;
        while(true) {
          doXXX();
          
          try {
            Thread.sleep(1000);
          } catch(Exception e) {
          }
          
          if(!run) break;
        }
    }
    
    @Override
    void interrupt() {
        run = false;
    }
}

注意下上面的實現,在run方法中,有一個死循環,一直在重複的調用 doxxx() 方法,在內部經過 Thread.sleep() 來控制頻率

在腳本改變或刪除以後,框架會回調 interrupt 方法,所以會將上面的run變量設置爲false,從而結束死循環

注意:

  • 對於定時任務而言,後續會擴展一個對應ScheduleTask抽象類出來,將循環和中斷的邏輯封裝一下,對於使用方而言,只須要寫業務邏輯便可,不須要關心這些重複的邏輯

4. mq消息消費

這種更多的是把這個框架做爲一個調度來用,咱們接收mq的消息,而後在動態腳本中進行處理,再傳給第三方(若是集成在本身的項目中時,一個demo就是能夠直接調用項目中的Dao保存數據)

一個RabbitMq的消費任務,對應的僞代碼以下

class DemoScript implements ITask {
    @Override
    void run() {
      ConnectionFactory fac = new CachingConnectionFactory();
      fac.setHost("127.0.0.1");
      fac.setPort(5672);
      fac.setUsername("admin")
      fac.setPassword("admin")
      fac.setVirtualHost("/")
      
      //建立鏈接
      Connection connection = factory.newConnection();

      //建立消息信道
      final Channel channel = connection.createChannel();

      //消息隊列
      channel.queueDeclare(queue, true, false, false, null);
      //綁定隊列到交換機
      channel.queueBind(queue, exchange, routingKey);
     
      Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                    byte[] body) throws IOException {
                String message = new String(body, "UTF-8");

                try {
                    System.out.println(" [" + queue + "] Received '" + message);
                } finally {
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        // 取消自動ack
        channel.basicConsume(queue, false, consumer);   
    }
}

注意:

  • 對於RabbitMQ的任務,後續計劃封裝一個抽象的任務腳本,使業務方只須要關注本身的消息處理便可,上面只是一個業務場景的使用演示

III. 其餘

0. 相關

博文:

項目:

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛

2. 聲明

盡信書則不如,已上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

3. 掃描關注

blogInfoV2.png

相關文章
相關標籤/搜索