分布式定时任务—xxl-job学习(四)——调度中心web页面端api调用源码分析

  • 前言
  • 一、controller目录下非controller类
    • 1.1 PermissionLimit自定义注解
    • 1.2 拦截器
      • 1.2.1 PermissionInterceptor权限拦截器
      • 1.2 CookieInterceptor Cookie缓存拦截器
  • 二、controller目录下controller类
    • 2.1 IndexController类
    • 2.2 JobApiController类
      • 2.2.1 AdminBizImpl.callback(callbackParamList)
      • 2.2.2 AdminBizImpl.registry(registryParam)
      • 2.2.3 AdminBizImpl.registryRemove(registryParam)
    • 2.3 JobCodeController类
    • 2.4 JobGroupController类
    • 2.5 JobInfoController类
    • 2.6 JobLogController类
    • 2.7 UserController类
  • 三、总结

前言

接上篇:分布式定时任务—xxl-job学习(三)——调度中心(xxl-job-admin)的启动和任务调度过程源码分析

本篇我们从调度中心的wen页面端分析下api调用的源码。

也就是上图中的一些api调用类。

一、controller目录下非controller类

1.1 PermissionLimit自定义注解

/*** 权限限制* @author xuxueli 2015-12-12 18:29:02*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionLimit {/*** 登录拦截 (默认拦截)*/boolean limit() default true;/*** 要求管理员权限** @return*/boolean adminuser() default false;}

分析:
自定义了一个方法上的权限限制注解,主要控制登录拦截和是否要求有管理员权限。

1.2 拦截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Resourceprivate PermissionInterceptor permissionInterceptor;@Resourceprivate CookieInterceptor cookieInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");registry.addInterceptor(cookieInterceptor).addPathPatterns("/**");}}

分析:
WebMvcConfig 配置类实现了WebMvcConfigurer接口,进行框架个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter等。

在这里重写了void addInterceptors(InterceptorRegistry registry)方法增加拦截器配置

  • addInterceptor:需要一个实现HandlerInterceptor接口的拦截器实例
  • addPathPatterns:用于设置拦截器的过滤路径规则;addPathPatterns("/**")对所有请求都拦截
  • excludePathPatterns:用于设置不需要拦截的过滤规则。

1.2.1 PermissionInterceptor权限拦截器

@Component
public class PermissionInterceptor extends HandlerInterceptorAdapter {@Resourceprivate LoginService loginService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return super.preHandle(request, response, handler);}// if need loginboolean needLogin = true;boolean needAdminuser = false;HandlerMethod method = (HandlerMethod)handler;PermissionLimit permission = method.getMethodAnnotation(PermissionLimit.class);if (permission!=null) {needLogin = permission.limit();needAdminuser = permission.adminuser();}if (needLogin) {XxlJobUser loginUser = loginService.ifLogin(request, response);if (loginUser == null) {response.sendRedirect(request.getContextPath() + "/toLogin");//request.getRequestDispatcher("/toLogin").forward(request, response);return false;}if (needAdminuser && loginUser.getRole()!=1) {throw new RuntimeException(I18nUtil.getString("system_permission_limit"));}request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);}return super.preHandle(request, response, handler);}}

分析: 该类继承HandlerInterceptorAdapter适配器类,此处只重写了boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)方法,预处理回调方法,在方法被调用前执行。

  1. 判断方法是带有映射的方法,不是的话不需要走权限控制;
  2. handler强转为HandlerMethod,并取到方法上的PermissionLimit注解;
  3. 根据方法上的注解得到是否需要登录、是否需要有管理员权限;
  4. 如果需要登录则调用loginService.ifLogin(request, response)方法判断Cookie中是否有用户信息,没有则跳转到登录页面;
  5. 如果Cookie中取到的用户角色id不是1则认为无管理员权限;
  6. 调用父类的super.preHandle(request, response, handler)

1.2 CookieInterceptor Cookie缓存拦截器

@Component
public class CookieInterceptor extends HandlerInterceptorAdapter {@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {// cookieif (modelAndView!=null && request.getCookies()!=null && request.getCookies().length>0) {HashMap<String, Cookie> cookieMap = new HashMap<String, Cookie>();for (Cookie ck : request.getCookies()) {cookieMap.put(ck.getName(), ck);}modelAndView.addObject("cookieMap", cookieMap);}// static methodif (modelAndView != null) {modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName()));}super.postHandle(request, response, handler, modelAndView);}}

分析: 该类继承HandlerInterceptorAdapter适配器类,此处只重写了postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)后处理方法,在主方法执行后渲染视图前执行。

  1. 如果视图不为null,将请求中的Cookie信息以HashMap<String, Cookie> cookieMap的形式存入视图中;
  2. 如果视图不为null,将国际化工具类存入模板信息中再放入视图。

二、controller目录下controller类

2.1 IndexController类

包括登录、图表展示等方法

 @InitBinderpublic void initBinder(WebDataBinder binder) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");dateFormat.setLenient(false);binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));}

注意:
该类有一个数据绑定方法:Spring在绑定请求参数到HandlerMethod的时候会借助WebDataBinder进行数据转换,"yyyy-MM-dd HH:mm:ss"这种类型的字符串直接使用Date类型接收。

2.2 JobApiController类

该类提供了三个api方法供执行器组件端调用,具体包括"callback"、“registry"和"registryRemove”。

2.2.1 AdminBizImpl.callback(callbackParamList)

    @Overridepublic ReturnT<String> callback(List<HandleCallbackParam> callbackParamList) {for (HandleCallbackParam handleCallbackParam: callbackParamList) {ReturnT<String> callbackResult = callback(handleCallbackParam);logger.debug(">>>>>>>>> JobApiController.callback {}, handleCallbackParam={}, callbackResult={}",(callbackResult.getCode()==IJobHandler.SUCCESS.getCode()?"success":"fail"), handleCallbackParam, callbackResult);}return ReturnT.SUCCESS;}private ReturnT<String> callback(HandleCallbackParam handleCallbackParam) {// valid log itemXxlJobLog log = xxlJobLogDao.load(handleCallbackParam.getLogId());if (log == null) {return new ReturnT<String>(ReturnT.FAIL_CODE, "log item not found.");}if (log.getHandleCode() > 0) {return new ReturnT<String>(ReturnT.FAIL_CODE, "log repeate callback.");     // avoid repeat callback, trigger child job etc}// trigger success, to trigger child jobString callbackMsg = null;if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());if (xxlJobInfo!=null && xxlJobInfo.getChildJobId()!=null && xxlJobInfo.getChildJobId().trim().length()>0) {callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<< </span><br>";String[] childJobIds = xxlJobInfo.getChildJobId().split(",");for (int i = 0; i < childJobIds.length; i++) {int childJobId = (childJobIds[i]!=null && childJobIds[i].trim().length()>0 && isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1;if (childJobId > 0) {JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null);ReturnT<String> triggerChildResult = ReturnT.SUCCESS;// add msgcallbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"),(i+1),childJobIds.length,childJobIds[i],(triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")),triggerChildResult.getMsg());} else {callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"),(i+1),childJobIds.length,childJobIds[i]);}}}}// handle msgStringBuffer handleMsg = new StringBuffer();if (log.getHandleMsg()!=null) {handleMsg.append(log.getHandleMsg()).append("<br>");}if (handleCallbackParam.getExecuteResult().getMsg() != null) {handleMsg.append(handleCallbackParam.getExecuteResult().getMsg());}if (callbackMsg != null) {handleMsg.append(callbackMsg);}if (handleMsg.length() > 15000) {handleMsg = new StringBuffer(handleMsg.substring(0, 15000));  // text最大64kb 避免长度过长}// success, save loglog.setHandleTime(new Date());log.setHandleCode(handleCallbackParam.getExecuteResult().getCode());log.setHandleMsg(handleMsg.toString());xxlJobLogDao.updateHandleInfo(log);return ReturnT.SUCCESS;}

分析:

  1. 循环处理回调参数集合;
  2. 根据回调入参里的logId从数据库xxl_job_log表查询XxlJobLog;
  3. 校验XxlJobLog是否为空?handleCode字段是否大于0?(避免重复回调,触发子作业等);
  4. 如果触发调度成功,判断是否有子任务,如果有则触发调度子任务;
  5. 处理触发调度信息(text最大64kb 避免长度过长,长度截取到15000);
  6. 更新到日志记录中。

2.2.2 AdminBizImpl.registry(registryParam)

    @Overridepublic ReturnT<String> registry(RegistryParam registryParam) {// validif (!StringUtils.hasText(registryParam.getRegistryGroup())|| !StringUtils.hasText(registryParam.getRegistryKey())|| !StringUtils.hasText(registryParam.getRegistryValue())) {return new ReturnT<String>(ReturnT.FAIL_CODE, "Illegal Argument.");}int ret = xxlJobRegistryDao.registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());if (ret < 1) {xxlJobRegistryDao.registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());// freshfreshGroupRegistryInfo(registryParam);}return ReturnT.SUCCESS;}

分析:

  1. 校验注册参数中的类型、appname、执行器地址;
  2. 更新xxl_job_registry表,更新结果 < 1表示没有则新增,
  3. 调用freshGroupRegistryInfo方法------------作者还在考虑中,这个方法无具体内容
        private void freshGroupRegistryInfo(RegistryParam registryParam){// Under consideration, prevent affecting core tables}
    

2.2.3 AdminBizImpl.registryRemove(registryParam)

    @Overridepublic ReturnT<String> registryRemove(RegistryParam registryParam) {// validif (!StringUtils.hasText(registryParam.getRegistryGroup())|| !StringUtils.hasText(registryParam.getRegistryKey())|| !StringUtils.hasText(registryParam.getRegistryValue())) {return new ReturnT<String>(ReturnT.FAIL_CODE, "Illegal Argument.");}int ret = xxlJobRegistryDao.registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());if (ret > 0) {// freshfreshGroupRegistryInfo(registryParam);}return ReturnT.SUCCESS;}

分析:

  1. 校验注册参数中的类型、appname、执行器地址;
  2. 删除xxl_job_registry表信息,
  3. 删除成功则调用freshGroupRegistryInfo方法------------作者还在考虑中,这个方法无具体内容
        private void freshGroupRegistryInfo(RegistryParam registryParam){// Under consideration, prevent affecting core tables}
    

2.3 JobCodeController类

该类主要是GLUE(xxx)模式的可在线编辑的任务的查询、修改和保存;
涉及:xxl_job_info任务表和xxl_job_logglue任务日志表(glue类型);
注意:
每次保存会清楚30次之前的备份记录。

2.4 JobGroupController类

该类主要是执行器管理控制层:包括执行器的查询、保存、更新、删除。涉及xxl_job_group表。
注意:

  1. 当选择自动注册时,需要根据appname从xxl_job_registry表匹配出最近90秒更新的执行器地址列表。
  2. 删除的时候会进行校验,如果挂载了任务则不能删除;如果执行器列表只有这一个也不能删除。

2.5 JobInfoController类

该类主要负责任务的新增、修改、删除、查询下次调度时间、启动、停止、调度一次。涉及xxl_job_info表。
注意:

  1. xxl_job_infotrigger_status字段0-停止,1-运行;
  2. 计算下次调度时间需要加5s,避开预读周期;

2.6 JobLogController类

该类负责调度日志的查询、清理、中止任务等。
日志展示和中止会调用ExecutorBizClient的log、kill方法远程调度执行器。

2.7 UserController类

该类主要负责用户的新增、更新和删除。

三、总结

xxl-job这款轻量级的分布式定时任务还是比较适合我们大多数场景的。后续我们继续学习下其他框架(比如:Elastic-Job等)

分布式定时任务—xxl-job学习(四)——调度中心web页面端api调用源码分析相关推荐

  1. Framework学习之路(一)—— UI绘制深入源码分析

    Framework学习之路(一)-- UI绘制深入源码分析 本篇为笔者对Android SDK 33版本的UI绘制入口进行追踪的过程,主要作笔记作用.由于笔者经验尚浅,水平也有限,所以会存在很多不足的 ...

  2. SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(下)

    源码资料 文档资料 <<Nacos架构与原理>>书籍于2021.12.21发布,并在Nacos官方网站非常Nice的提供其电子书的下载.我们学习Nacos源码更多是要吸取其优秀 ...

  3. sheng的学习笔记-ConcurrentHashMap(JDK1.7和16)源码分析

    1.7版本 概述 注意,以下代码都是1.7版本(不同版本代码不一样),最下面有1.8版本部分内容 ConcurrentHashMap是线程安全的key value存储结构,底层也是数组+链表的结构 下 ...

  4. 系统学习深度学习(四) --CNN原理,推导及实现源码分析

    之前看机器学习中,多层感知器部分,提到可以在设计多层感知器时,对NN的结构设计优化,例如结构化设计和权重共享,当时还没了解深度学习,现在看到CNN,原来CNN就是这方面的一个代表.CNN由纽约大学的Y ...

  5. SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(上)

    Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https ...

  6. glibc-2.23学习笔记(一)—— malloc部分源码分析

    glibc-2.23学习笔记(一)-- malloc部分源码分析 搭建Glibc源码调试环境 1.下载并解压glibc源码 2.配置gdb 3.编译测试程序 第一次调用 源码分析 __libc_mal ...

  7. 入理解分布式调度框架TBSchedule及源码分析

    简介 由于最近工作比较忙,前前后后花了两个月的时间把TBSchedule的源码翻了个底朝天.关于TBSchedule的使用,网上也有很多参考资料,这里不做过多的阐述.本文着重介绍TBSchedule的 ...

  8. JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler

    EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口,  由下面代码可以看出: (ns backtype.storm.scheduler.EvenSch ...

  9. Java定时任务(一) Timer及TimerTask的案例解析及源码分析

    Java定时任务(一)  Timer及TimerTask的案例解析及源码分析 一.概述: 定时任务这个概念在Java的学习以及项目的开发中并不陌生,应用场景也是多种多样.比如我们会注意到12306网站 ...

最新文章

  1. 解决Aireplay-ng信道问题
  2. mysql 4.0 删除重复_mysql删除重复数据
  3. 【笔记】Hexo+Github博客网站搭建,初试环境搭建及Matery主题配置感受
  4. Python基础教程:属性值设置和判断变量是否存在
  5. 有一次去校内的某个礼堂看电影,在门口有个长得很斯文的陌生人一脸神秘地跟我说:师弟,能不能进去之后,把电影票从厕所的气窗扔出来给我……...
  6. c __cplusplus详解
  7. datatable刷新数据_UE4 利用SaveGame和CSV进行Runtime数据更新
  8. 计算机配件的真假辨伪,专家教你辨真伪 “火眼金睛”辨真假配件
  9. Machine Learning——Homework4
  10. #洛谷oj:P1197星球大战
  11. ngrok跟小米球的使用
  12. unity下载教育版_新的现场学习系列为Unity教育工作者提供支持
  13. (附源码)springboot垃圾自动分类管理系统 毕业设计 160846
  14. Quasi_Newton
  15. grid布局之容器属性justify-content与align-content
  16. 网络视频会议 OpenMeetings 介绍 运行 开发
  17. 计算机基础:源代码如何被计算机执行
  18. 基于CMake的交叉编译工具链配置问题总结
  19. MyEclipse优化全攻略
  20. 无线网络技术导论笔记(第二讲)

热门文章

  1. 深度学习真的working吗
  2. 光大证券5名高管被罚260万元
  3. 2013年中国搜索引擎市场分析
  4. egg extend ts_王者荣耀KPL秋季赛赛果预测: 重庆QGhappy对阵WB.TS 小胖野区迎战暖阳...
  5. 深度学习——卷积神经网络的应用——目标检测
  6. 网站被恶意篡改了,这样去解决就好
  7. Mac:option键的一些极客用法
  8. 中职高考计算机专业真题,中职高考计算机试题及答案
  9. Rabbitmq 和erlang 安装成功但是网面访问不了
  10. 【大学物理实验】弦振动的研究