diff --git a/doc/XXL-JOB官方文档.md b/doc/XXL-JOB官方文档.md index 796671a7..56cdddb1 100644 --- a/doc/XXL-JOB官方文档.md +++ b/doc/XXL-JOB官方文档.md @@ -2904,7 +2904,8 @@ alter table xxl_job_log ### 7.47 版本 v3.5.0 Release Notes[ING] - 1、【新增】GLUE模式开关:新增GLUE模式开关(xxl.job.executor.glueenabled),支持执行器维度设置是否启用GLUE模式; - 2、【TODO】执行器AccessToken:执行器维度AccessToken,线上化动态管理;执行器间AccessToken隔离,提升安全性; - - 执行器 AccessToken: + - 执行器:ExecutorBiz 服务处理、AdminBiz请求发起,统一附加 appname + accessToken; + - 调度中心:AppName 限制不可重复;JobGoup本地缓存; - 3、【TODO】调度中心OpenAPI增强:提供任务管理能力;封装Agent Skill并推送ClawHub; - 4、【TODO】配置线上化:发送邮箱配置线上管理、线程池配置调整; - 5、【TODO】任务告警:拆分“告警类型、告警配置”属性,支持Webhook、邮箱多种方式; diff --git a/doc/db/tables_xxl_job.sql b/doc/db/tables_xxl_job.sql index 31463f75..29a88adb 100644 --- a/doc/db/tables_xxl_job.sql +++ b/doc/db/tables_xxl_job.sql @@ -17,7 +17,8 @@ CREATE TABLE `xxl_job_group` `address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入', `address_list` text COMMENT '执行器地址列表,多地址逗号分隔', `update_time` datetime DEFAULT NULL, - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + UNIQUE KEY `i_app_name` (`app_name`) USING BTREE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/business/controller/JobGroupController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/business/controller/JobGroupController.java index ffc59efd..6b07970b 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/business/controller/JobGroupController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/business/controller/JobGroupController.java @@ -111,6 +111,11 @@ public class JobGroupController { } } + // valid appname duplicate + if (xxlJobGroupMapper.loadByAppname(xxlJobGroup.getAppname()) != null) { + return Response.ofFail("AppName "+I18nUtil.getString("system_repeat")); + } + // process xxlJobGroup.setUpdateTime(new Date()); @@ -123,15 +128,10 @@ public class JobGroupController { @XxlSso(role = Consts.ADMIN_ROLE) public Response update(XxlJobGroup xxlJobGroup){ - // valid appname - if (StringTool.isBlank(xxlJobGroup.getAppname())) { - return Response.ofFail((I18nUtil.getString("system_please_input")+"AppName") ); - } - if (xxlJobGroup.getAppname().length()<4 || xxlJobGroup.getAppname().length()>64) { - return Response.ofFail( I18nUtil.getString("jobgroup_field_appname_length") ); - } - if (XssUtil.hasXss(xxlJobGroup.getAppname())) { - return Response.ofFail( "AppName"+I18nUtil.getString("system_invalid") ); + // load existing, appname not allowed to change + XxlJobGroup exists = xxlJobGroupMapper.load(xxlJobGroup.getId()); + if (exists == null) { + return Response.ofFail(I18nUtil.getString("system_opt_error")); } // valid title diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/business/mapper/XxlJobGroupMapper.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/business/mapper/XxlJobGroupMapper.java index 9dcb4ff2..e7c875ad 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/business/mapper/XxlJobGroupMapper.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/business/mapper/XxlJobGroupMapper.java @@ -24,6 +24,8 @@ public interface XxlJobGroupMapper { public XxlJobGroup load(@Param("id") int id); + public XxlJobGroup loadByAppname(@Param("appname") String appname); + public List pageList(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("appname") String appname, diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/business/model/XxlJobGroup.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/business/model/XxlJobGroup.java index ac165a18..ae8c9fbc 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/business/model/XxlJobGroup.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/business/model/XxlJobGroup.java @@ -76,4 +76,17 @@ public class XxlJobGroup { this.addressList = addressList; } + @Override + public String toString() { + return "XxlJobGroup{" + + "id=" + id + + ", appname='" + appname + '\'' + + ", title='" + title + '\'' + + ", addressType=" + addressType + + ", addressList='" + addressList + '\'' + + ", updateTime=" + updateTime + + ", registryList=" + registryList + + '}'; + } + } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/business/scheduler/thread/JobRegistryHelper.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/business/scheduler/thread/JobRegistryHelper.java index b394d392..f7960d9e 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/business/scheduler/thread/JobRegistryHelper.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/business/scheduler/thread/JobRegistryHelper.java @@ -7,7 +7,9 @@ import com.xxl.job.core.constant.Const; import com.xxl.job.core.constant.RegistTypeEnum; import com.xxl.job.core.openapi.admin.dto.RegistryRequest; import com.xxl.tool.concurrent.CyclicThread; +import com.xxl.tool.core.CollectionTool; import com.xxl.tool.core.StringTool; +import com.xxl.tool.json.GsonTool; import com.xxl.tool.response.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +36,12 @@ public class JobRegistryHelper { */ private CyclicThread registryMonitorThread; + /** + * job group cache + */ + private volatile Map appname2GroupCache = new ConcurrentHashMap<>(); + private volatile Map id2GroupCache = new ConcurrentHashMap<>(); + /** * start */ @@ -64,24 +72,24 @@ public class JobRegistryHelper { registryMonitorThread = new CyclicThread("JobRegistryHelper#registryMonitorThread", true, new Runnable() { @Override public void run() { - // auto registry group + // 2.1、refresh auto-registry group List groupList = XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().findByAddressType(0); if (groupList!=null && !groupList.isEmpty()) { - // remove dead address (admin/executor) + // a、remove dead address (admin/executor) List ids = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().findDead(Const.DEAD_TIMEOUT, new Date()); if (ids!=null && !ids.isEmpty()) { XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().removeDead(ids); } - // fresh online address (admin/executor) - HashMap> appAddressMap = new HashMap>(); + // b、fresh online address (appname : List
) + HashMap> appnameAddressMap = new HashMap<>(); List list = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().findAll(Const.DEAD_TIMEOUT, new Date()); if (list != null) { for (XxlJobRegistry item: list) { if (RegistTypeEnum.EXECUTOR.name().equals(item.getRegistryGroup())) { String appname = item.getRegistryKey(); - List registryList = appAddressMap.get(appname); + List registryList = appnameAddressMap.get(appname); if (registryList == null) { registryList = new ArrayList(); } @@ -89,30 +97,46 @@ public class JobRegistryHelper { if (!registryList.contains(item.getRegistryValue())) { registryList.add(item.getRegistryValue()); } - appAddressMap.put(appname, registryList); + appnameAddressMap.put(appname, registryList); } } } - // fresh group address + // c、write group address for (XxlJobGroup group: groupList) { - List registryList = appAddressMap.get(group.getAppname()); + List registryList = appnameAddressMap.get(group.getAppname()); + // generate address list String addressListStr = null; - if (registryList!=null && !registryList.isEmpty()) { + if (CollectionTool.isNotEmpty(registryList)) { Collections.sort(registryList); - StringBuilder addressListSB = new StringBuilder(); - for (String item:registryList) { - addressListSB.append(item).append(","); - } - addressListStr = addressListSB.toString(); - addressListStr = addressListStr.substring(0, addressListStr.length()-1); + addressListStr = StringTool.join(registryList, ","); } + // fill address group.setAddressList(addressListStr); group.setUpdateTime(new Date()); XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().update(group); } } + + // 2.2、refresh localcache + List jobGroupList = XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().findAll(); + Map appname2GroupCacheNew = new ConcurrentHashMap<>(); + Map id2GroupCacheNew = new ConcurrentHashMap<>(); + if (CollectionTool.isNotEmpty(jobGroupList)) { + for (XxlJobGroup group: jobGroupList) { + group.setUpdateTime(null); + appname2GroupCacheNew.put(group.getAppname(), group); + id2GroupCacheNew.put(group.getId(), group); + } + } + if (!GsonTool.toJson(appname2GroupCacheNew).equals(GsonTool.toJson(appname2GroupCache))) { + appname2GroupCache = appname2GroupCacheNew; + id2GroupCache = id2GroupCacheNew; + logger.info(">>>>>>>>>>> xxl-job, JobRegistryHelper, detect changes and refresh JobGroupCache success, appname2GroupCache:{}, id2GroupCache:{}", appname2GroupCache, id2GroupCache); + } + logger.debug(">>>>>>>>>>> xxl-job, JobRegistryHelper, refresh JobGroupCache success, appname2GroupCache:{}, id2GroupCache:{}", appname2GroupCache, id2GroupCache); + } }, Const.BEAT_TIMEOUT * 1000L, true); registryMonitorThread.start(); @@ -200,5 +224,21 @@ public class JobRegistryHelper { // Under consideration, prevent affecting core tables } + // ---------------------- cache ---------------------- + + /** + * load JobGroup by id + */ + public XxlJobGroup load(int jobGroup){ + return id2GroupCache.get(jobGroup); + } + + /** + * load JobGroup by appname + */ + public XxlJobGroup loadByAppName(String appname){ + return appname2GroupCache.get(appname); + } + } diff --git a/xxl-job-admin/src/main/resources/i18n/message_en.properties b/xxl-job-admin/src/main/resources/i18n/message_en.properties index 6afcacea..9b5f2e2a 100644 --- a/xxl-job-admin/src/main/resources/i18n/message_en.properties +++ b/xxl-job-admin/src/main/resources/i18n/message_en.properties @@ -38,6 +38,7 @@ system_num_range=Numerical range limit system_one=One system_data=Data system_selected_nothing=No data selected +system_repeat=Repeat ## tab tab_opt=Tab Operation diff --git a/xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties b/xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties index 24b0a1a7..95e7cc3f 100644 --- a/xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties +++ b/xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties @@ -38,6 +38,7 @@ system_num_range=数值范围限制 system_one=一条 system_data=数据 system_selected_nothing=未选择 +system_repeat=重复 ## tab tab_opt=页签操作 diff --git a/xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties b/xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties index 354d0871..c467e711 100755 --- a/xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties +++ b/xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties @@ -38,6 +38,7 @@ system_num_range=數值範圍限制 system_one=一條 system_data=數據 system_selected_nothing=请選擇 +system_repeat=重複 ## tab tab_opt=頁籤操作 diff --git a/xxl-job-admin/src/main/resources/mapper/business/XxlJobGroupMapper.xml b/xxl-job-admin/src/main/resources/mapper/business/XxlJobGroupMapper.xml index 0ac45dab..55dba957 100644 --- a/xxl-job-admin/src/main/resources/mapper/business/XxlJobGroupMapper.xml +++ b/xxl-job-admin/src/main/resources/mapper/business/XxlJobGroupMapper.xml @@ -41,8 +41,7 @@ UPDATE xxl_job_group - SET `app_name` = #{appname}, - `title` = #{title}, + SET `title` = #{title}, `address_type` = #{addressType}, `address_list` = #{addressList}, `update_time` = #{updateTime} @@ -60,6 +59,12 @@ WHERE t.id = #{id} + + +