XXL-JOB GLUE任務(wù)第三方依賴包的管理實(shí)踐
背景
xxl-job 是一個(gè)分布式任務(wù)調(diào)度平臺(tái),它的應(yīng)用場(chǎng)景非常廣泛,例如定時(shí)任務(wù)、消息推送、批處理等。xxl-job 中的任務(wù)類型主要有兩種:
BEAN模式(方法形式)
支持基于方法的開發(fā)方式,每個(gè)任務(wù)對(duì)應(yīng)一個(gè)方法。
- 優(yōu)點(diǎn):每個(gè)任務(wù)只需要開發(fā)一個(gè)方法,并添加”@XxlJob”注解即可,更加方便、快速。支持自動(dòng)掃描任務(wù)并注入到執(zhí)行器容器。
- 缺點(diǎn):要求Spring容器環(huán)境;
基于方法開發(fā)的任務(wù),底層會(huì)生成JobHandler代理,和基于類的方式一樣,任務(wù)也會(huì)以JobHandler的形式存在于執(zhí)行器任務(wù)容器中。
GLUE模式(源碼方式)
任務(wù)以源碼方式維護(hù)在調(diào)度中心,支持通過Web IDE在線更新,實(shí)時(shí)編譯和生效,因此不需要指定JobHandler。例如我的代碼是python類型的代碼,點(diǎn)擊該任務(wù)右側(cè)“GLUE”按鈕,將會(huì)前往GLUE任務(wù)的Web IDE界面,在該界面支持對(duì)任務(wù)代碼進(jìn)行開發(fā)。
GLUE Python任務(wù)代碼編輯
- 優(yōu)點(diǎn):這種方式相比 BEAN 模式更加靈活,因?yàn)槿蝿?wù)邏輯不一定要用 Java 實(shí)現(xiàn)。
- 缺點(diǎn):存在一定的安全風(fēng)險(xiǎn),因?yàn)閳?zhí)行器可以執(zhí)行任意的腳本文件
但是大家注意到?jīng)]有,這個(gè)地方?jīng)]有涉及應(yīng)用的環(huán)境問題,比如我的是java代碼,那我的程序中的某個(gè)包是否在本地能被引用到,即通過maven或是gradle已經(jīng)下載本地了,或是我的python代碼的依賴包是否在當(dāng)前環(huán)境pip install了,我們從官方的github issues中,看到有很多同學(xué)提到了這些問題.但是這個(gè)官方?jīng)]有給出具體的解決方案。這篇文檔,就給大家談?wù)勥@個(gè)問題。
https://github.com/xuxueli/xxl-job/issues/129
https://github.com/xuxueli/xxl-job/issues/254
https://github.com/xuxueli/xxl-job/issues/1401
GLUE模式(Java)任務(wù)依賴環(huán)境構(gòu)建
在 xxl-job 的 GLUE 模式下,如果任務(wù)類型為 Java 類型,那么需要保證任務(wù)依賴的相關(guān) JAR 包已經(jīng)下載到本地環(huán)境中,否則任務(wù)執(zhí)行會(huì)出現(xiàn) ClassNotFoundException 等類加載異常。
一種比較簡(jiǎn)單的方式是將任務(wù)依賴的 JAR 包打成一個(gè) Fat Jar,然后將 Fat Jar 放在執(zhí)行器的 classpath 中即可。Fat Jar 是將多個(gè) JAR 包合并成一個(gè) JAR 包的方式,執(zhí)行時(shí)只需要引入這個(gè) JAR 包即可。例如我們修改執(zhí)行器的pom.xml,加入fastjson(相當(dāng)于把jar放到了執(zhí)行器的classpath中),然后我們的DemoGlueJobHandler便可以引用fastjson里面的類了。
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
demo glue直接引用相關(guān)類
demo glue執(zhí)行結(jié)果
除了把依賴包提前放到執(zhí)行器的classpath之外(可能會(huì)有包的沖突),還可以在任務(wù)執(zhí)行前,通過代碼動(dòng)態(tài)加載依賴的 JAR 包,避免手動(dòng)打包依賴的麻煩??梢允褂?URLClassLoader 類實(shí)現(xiàn)動(dòng)態(tài)加載。首先我們把依賴的包放在一個(gè)共享盤上,保證執(zhí)行器可以直接訪問到,然后通過反射機(jī)制實(shí)現(xiàn)代碼的調(diào)用與執(zhí)行。具體實(shí)現(xiàn)方式可以參考以下代碼示例:
package com.xxl.job.service.handler;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.IJobHandler;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class DemoGlueJobHandler extends IJobHandler {
// 定義一個(gè)用于加載外部 JAR 包的 ClassLoader
public class MyClassLoader extends URLClassLoader {
public MyClassLoader(URL[] urls) {
super(urls);
}
}
// 加載外部 JAR 包的方法
public void loadJars(String[] jarPaths) {
URL[] urls = new URL[jarPaths.length];
for (int i = 0; i < jarPaths.length; i++) {
try {
urls[i] = new URL("file:" + jarPaths[i]);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
MyClassLoader myClassLoader = new MyClassLoader(urls);
Thread.currentThread().setContextClassLoader(myClassLoader);
}
@Override
public void execute() throws Exception {
// 加載外部 JAR 包
loadJars(new String[] {"/Users/dongluyang1/.m2/repository/com/alibaba/fastjson/1.2.28/fastjson-1.2.28.jar"});
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Class<?> jsonObjectClass = contextClassLoader.loadClass("com.alibaba.fastjson.JSONObject");
Method put = jsonObjectClass.getMethod("put", String.class,Object.class);
Object object = jsonObjectClass.newInstance();
put.invoke(object, "keyTest","valueTest");
Method toJSONString = jsonObjectClass.getMethod("toJSONString");
// 執(zhí)行任務(wù)邏輯
XxlJobHelper.log("XXL-JOB, Hello World."+toJSONString.invoke(object));
}
}
demo glue反射運(yùn)行結(jié)果
GLUE模式(Python)任務(wù)依賴環(huán)境構(gòu)建
在 xxl-job 的 GLUE 模式下,如果任務(wù)類型為 Python 類型,那么需要保證任務(wù)依賴的相關(guān)庫(kù)已經(jīng)下載到本地環(huán)境中,否則任務(wù)執(zhí)行會(huì)出現(xiàn) ImportError 等異常。
一種常用的方式是使用 Python 虛擬環(huán)境(Virtual Environment)來(lái)管理依賴庫(kù)。虛擬環(huán)境是 Python 的一個(gè)功能,可以在一個(gè)獨(dú)立的環(huán)境中安裝 Python 和相關(guān)庫(kù),不會(huì)影響到全局環(huán)境。
- 首先生成python代碼文件,存儲(chǔ)到公共目錄,比如NFS某個(gè)目錄下面
- 創(chuàng)建一個(gè)腳本文件,同時(shí)在任務(wù)的腳本中創(chuàng)建虛擬環(huán)境,安裝所需的依賴庫(kù)??梢允褂靡韵旅睿?/span>
# 創(chuàng)建虛擬環(huán)境
python3 -m venv /path/to/virtualenv
# 激活虛擬環(huán)境
source /path/to/virtualenv/bin/activate
# 安裝依賴庫(kù)
pip3 install package1 package2 ...
# 執(zhí)行任務(wù)邏輯
...
# 退出虛擬環(huán)境
deactivate