网站域名空间地址,郑州百度seo关键词,网络营销软文案例,优化大师app下载xxl-job是一款非常优秀的任务调度中间件#xff0c;轻量级、使用简单、支持分布式等优点#xff0c;让它广泛应用在我们的项目中#xff0c;解决了不少定时任务的调度问题。
我们都知道#xff0c;在使用过程中需要先到xxl-job的任务调度中心页面上#xff0c;配置执行器…xxl-job是一款非常优秀的任务调度中间件轻量级、使用简单、支持分布式等优点让它广泛应用在我们的项目中解决了不少定时任务的调度问题。我们都知道在使用过程中需要先到xxl-job的任务调度中心页面上配置执行器executor和具体的任务job这一过程如果项目中的定时任务数量不多还好说如果任务多了的话还是挺费工夫的。假设项目中有上百个这样的定时任务那么每个任务都需要走一遍绑定jobHander后端接口填写cron表达式这个流程…我就想问问填多了谁能不迷糊于是出于功能优化偷懒这一动机前几天我萌生了一个想法有没有什么方法能够告别xxl-job的管理页面能够让我不再需要到页面上去手动注册执行器和任务实现让它们自动注册到调度中心呢。分析分析一下其实我们要做的很简单只要在项目启动时主动注册executor和各个jobHandler到调度中心就可以了流程如下有的小伙伴们可能要问了我在页面上创建执行器的时候不是有一个选项叫做自动注册吗为什么我们这里还要自己添加新执行器其实这里有个误区这里的自动注册指的是会根据项目中配置的xxl.job.executor.appname将配置的机器地址自动注册到这个执行器的地址列表中。但是如果你之前没有手动创建过执行器那么是不会给你自动添加一个新执行器到调度中心的。既然有了想法咱们就直接开干先到github上拉一份xxl-job的源码下来https://github.com/xuxueli/xxl-job/https://github.com/xuxueli/xxl-job/整个项目导入idea后先看一下结构结合着文档和代码先梳理一下各个模块都是干什么的xxl-job-admin任务调度中心启动后就可以访问管理页面进行执行器和任务的注册、以及任务调用等功能了xxl-job-core公共依赖项目中使用到xxl-job时要引入的依赖包xxl-job-executor-samples执行示例分别包含了springboot版本和不使用框架的版本为了弄清楚注册和查询executor和jobHandler调用的是哪些接口我们先从页面上去抓一个请求看看好了这样就能定位到xxl-job-admin模块中/jobgroup/save这个接口接下来可以很容易地找到源码位置按照这个思路可以找到下面这几个关键接口/jobgroup/pageList执行器列表的条件查询/jobgroup/save添加执行器/jobinfo/pageList任务列表的条件查询/jobinfo/add添加任务但是如果直接调用这些接口那么就会发现它会跳转到xxl-job-admin的的登录页面其实想想也明白出于安全性考虑调度中心的接口也不可能允许裸调的。那么再回头看一下刚才页面上的请求就会发现它在Headers中添加了一条名为XXL_JOB_LOGIN_IDENTITY的cookie至于这条cookie则是在通过用户名和密码调用调度中心的/login接口时返回的在返回的response可以直接拿到。只要保存下来并在之后每次请求时携带就能够正常访问其他接口了。到这里我们需要的5个接口就基本准备齐了接下来准备开始正式的改造工作。改造我们改造的目的是实现一个starter以后只要引入这个starter就能实现executor和jobHandler的自动注册要引入的关键依赖有下面两个dependency groupIdcom.xuxueli/groupId artifactIdxxl-job-core/artifactId version2.3.0/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-autoconfigure/artifactId /dependency1、接口调用在调用调度中心的接口前先把xxl-job-admin模块中的XxlJobInfo和XxlJobGroup这两个类拿到我们的starter项目中用于接收接口调用的结果。登录接口创建一个JobLoginService在调用业务接口前需要通过登录接口获取cookie并在获取到cookie后缓存到本地的Map中。private final MapString,String loginCookienew HashMap(); public void login() { String urladminAddresses/login; HttpResponse response HttpRequest.post(url) .form(userName,username) .form(password,password) .execute(); ListHttpCookie cookies response.getCookies(); OptionalHttpCookie cookieOpt cookies.stream() .filter(cookie - cookie.getName().equals(XXL_JOB_LOGIN_IDENTITY)).findFirst(); if (!cookieOpt.isPresent()) thrownew RuntimeException(get xxl-job cookie error!); String value cookieOpt.get().getValue(); loginCookie.put(XXL_JOB_LOGIN_IDENTITY,value); }其他接口在调用时直接从缓存中获取cookie如果缓存中不存在则调用/login接口为了避免这一过程失败允许最多重试3次。public String getCookie() { for (int i 0; i 3; i) { String cookieStr loginCookie.get(XXL_JOB_LOGIN_IDENTITY); if (cookieStr !null) { return XXL_JOB_LOGIN_IDENTITYcookieStr; } login(); } throw new RuntimeException(get xxl-job cookie error!); }执行器接口创建一个JobGroupService根据appName和执行器名称title查询执行器列表public ListXxlJobGroup getJobGroup() { String urladminAddresses/jobgroup/pageList; HttpResponse response HttpRequest.post(url) .form(appname, appName) .form(title, title) .cookie(jobLoginService.getCookie()) .execute(); String body response.body(); JSONArray array JSONUtil.parse(body).getByPath(data, JSONArray.class); ListXxlJobGroup list array.stream() .map(o - JSONUtil.toBean((JSONObject) o, XxlJobGroup.class)) .collect(Collectors.toList()); return list; }我们在后面要根据配置文件中的appName和title判断当前执行器是否已经被注册到调度中心过如果已经注册过那么则跳过而/jobgroup/pageList接口是一个模糊查询接口所以在查询列表的结果列表中还需要再进行一次精确匹配。public boolean preciselyCheck() { ListXxlJobGroup jobGroup getJobGroup(); OptionalXxlJobGroup has jobGroup.stream() .filter(xxlJobGroup - xxlJobGroup.getAppname().equals(appName) xxlJobGroup.getTitle().equals(title)) .findAny(); return has.isPresent(); }注册新executor到调度中心public boolean autoRegisterGroup() { String urladminAddresses/jobgroup/save; HttpResponse response HttpRequest.post(url) .form(appname, appName) .form(title, title) .cookie(jobLoginService.getCookie()) .execute(); Object code JSONUtil.parse(response.body()).getByPath(code); return code.equals(200); }任务接口创建一个JobInfoService根据执行器idjobHandler名称查询任务列表和上面一样也是模糊查询public ListXxlJobInfo getJobInfo(Integer jobGroupId,String executorHandler) { String urladminAddresses/jobinfo/pageList; HttpResponse response HttpRequest.post(url) .form(jobGroup, jobGroupId) .form(executorHandler, executorHandler) .form(triggerStatus, -1) .cookie(jobLoginService.getCookie()) .execute(); String body response.body(); JSONArray array JSONUtil.parse(body).getByPath(data, JSONArray.class); ListXxlJobInfo list array.stream() .map(o - JSONUtil.toBean((JSONObject) o, XxlJobInfo.class)) .collect(Collectors.toList()); return list; }注册一个新任务最终返回创建的新任务的idpublic Integer addJobInfo(XxlJobInfo xxlJobInfo) { String urladminAddresses/jobinfo/add; MapString, Object paramMap BeanUtil.beanToMap(xxlJobInfo); HttpResponse response HttpRequest.post(url) .form(paramMap) .cookie(jobLoginService.getCookie()) .execute(); JSON json JSONUtil.parse(response.body()); Object code json.getByPath(code); if (code.equals(200)){ return Convert.toInt(json.getByPath(content)); } thrownew RuntimeException(add jobInfo error!); }2、创建新注解在创建任务时必填字段除了执行器和jobHandler之外还有任务描述、负责人、Cron表达式、调度类型、运行模式。在这里我们默认调度类型为CRON、运行模式为BEAN另外的3个字段的信息需要用户指定。因此我们需要创建一个新注解XxlRegister来配合原生的XxlJob注解进行使用填写这几个字段的信息Target({ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) public interface XxlRegister { String cron(); String jobDesc() default default jobDesc; String author() default default Author; int triggerStatus() default 0; }最后额外添加了一个triggerStatus属性表示任务的默认调度状态0为停止状态1为运行状态。3、自动注册核心基本准备工作做完后下面实现自动注册执行器和jobHandler的核心代码。核心类实现ApplicationListener接口在接收到ApplicationReadyEvent事件后开始执行自动注册逻辑。Component publicclass XxlJobAutoRegister implements ApplicationListenerApplicationReadyEvent, ApplicationContextAware { privatestaticfinal Log log LogFactory.get(); private ApplicationContext applicationContext; Autowired private JobGroupService jobGroupService; Autowired private JobInfoService jobInfoService; Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContextapplicationContext; } Override public void onApplicationEvent(ApplicationReadyEvent event) { addJobGroup();//注册执行器 addJobInfo();//注册任务 } }自动注册执行器的代码非常简单根据配置文件中的appName和title精确匹配查看调度中心是否已有执行器被注册过了如果存在则跳过不存在则新注册一个private void addJobGroup() { if (jobGroupService.preciselyCheck()) return; if(jobGroupService.autoRegisterGroup()) log.info(auto register xxl-job group success!); }自动注册任务的逻辑则相对复杂一些需要完成通过applicationContext拿到spring容器中的所有bean再拿到这些bean中所有添加了XxlJob注解的方法对上面获取到的方法进行检查是否添加了我们自定义的XxlRegister注解如果没有则跳过不进行自动注册对同时添加了XxlJob和XxlRegister的方法通过执行器id和jobHandler的值判断是否已经在调度中心注册过了如果已存在则跳过对于满足注解条件且没有注册过的jobHandler调用接口注册到调度中心具体代码如下private void addJobInfo() { ListXxlJobGroup jobGroups jobGroupService.getJobGroup(); XxlJobGroup xxlJobGroup jobGroups.get(0); String[] beanDefinitionNames applicationContext.getBeanNamesForType(Object.class, false, true); for (String beanDefinitionName : beanDefinitionNames) { Object bean applicationContext.getBean(beanDefinitionName); MapMethod, XxlJob annotatedMethods MethodIntrospector.selectMethods(bean.getClass(), new MethodIntrospector.MetadataLookupXxlJob() { Override public XxlJob inspect(Method method) { return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class); } }); for (Map.EntryMethod, XxlJob methodXxlJobEntry : annotatedMethods.entrySet()) { Method executeMethod methodXxlJobEntry.getKey(); XxlJob xxlJob methodXxlJobEntry.getValue(); //自动注册 if (executeMethod.isAnnotationPresent(XxlRegister.class)) { XxlRegister xxlRegister executeMethod.getAnnotation(XxlRegister.class); ListXxlJobInfo jobInfo jobInfoService.getJobInfo(xxlJobGroup.getId(), xxlJob.value()); if (!jobInfo.isEmpty()){ //因为是模糊查询需要再判断一次 OptionalXxlJobInfo first jobInfo.stream() .filter(xxlJobInfo - xxlJobInfo.getExecutorHandler().equals(xxlJob.value())) .findFirst(); if (first.isPresent()) continue; } XxlJobInfo xxlJobInfo createXxlJobInfo(xxlJobGroup, xxlJob, xxlRegister); Integer jobInfoId jobInfoService.addJobInfo(xxlJobInfo); } } } }4、自动装配创建一个配置类用于扫描beanConfiguration ComponentScan(basePackages com.xxl.job.plus.executor) public class XxlJobPlusConfig { }将它添加到META-INF/spring.factories文件org.springframework.boot.autoconfigure.EnableAutoConfiguration\ com.xxl.job.plus.executor.config.XxlJobPlusConfig到这里starter的编写就完成了可以通过maven发布jar包到本地或者私服mvn clean install/deploy测试新建一个springboot项目引入我们在上面打好的包dependency groupIdcom.cn.hydra/groupId artifactIdxxljob-autoregister-spring-boot-starter/artifactId version0.0.1/version /dependency在application.properties中配置xxl-job的信息首先是原生的配置内容xxl.job.admin.addresseshttp://127.0.0.1:8080/xxl-job-admin xxl.job.accessTokendefault_token xxl.job.executor.appnamexxl-job-executor-test xxl.job.executor.address xxl.job.executor.ip127.0.0.1 xxl.job.executor.port9999 xxl.job.executor.logpath/data/applogs/xxl-job/jobhandler xxl.job.executor.logretentiondays30此外还要额外添加我们自己的starter要求的新配置内容# admin用户名 xxl.job.admin.usernameadmin # admin 密码 xxl.job.admin.password123456 # 执行器名称 xxl.job.executor.titletest-title完成后在代码中配置一下XxlJobSpringExecutor然后在测试接口上添加原生XxlJob注解和我们自定义的XxlRegister注解XxlJob(value testJob) XxlRegister(cron 0 0 0 * * ? *, author hydra, jobDesc 测试job) public void testJob(){ System.out.println(#公众号码农参上); } XxlJob(value testJob222) XxlRegister(cron 59 1-2 0 * * ?, triggerStatus 1) public void testJob2(){ System.out.println(#作者Hydra); } XxlJob(value testJob444) XxlRegister(cron 59 59 23 * * ?) public void testJob4(){ System.out.println(hello xxl job); }启动项目可以看到执行器自动注册成功再打开调度中心的任务管理页面可以看到同时添加了两个注解的任务也已经自动完成了注册从页面上手动执行任务进行测试可以执行成功到这里starter的编写和测试过程就算基本完成了项目中引入后以后也能省出更多的时间来摸鱼学习了~看完等于学会点个赞吧