分布式定时任务—xxl-job学习(四)——调度中心web页面端api调用源码分析
分布式定时任务—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)
方法,预处理回调方法,在方法被调用前执行。
- 判断方法是带有映射的方法,不是的话不需要走权限控制;
- 将
handler
强转为HandlerMethod
,并取到方法上的PermissionLimit
注解; - 根据方法上的注解得到是否需要登录、是否需要有管理员权限;
- 如果需要登录则调用
loginService.ifLogin(request, response)
方法判断Cookie中是否有用户信息,没有则跳转到登录页面; - 如果Cookie中取到的用户角色id不是1则认为无管理员权限;
- 调用父类的
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)
后处理方法,在主方法执行后渲染视图前执行。
- 如果视图不为null,将请求中的Cookie信息以
HashMap<String, Cookie> cookieMap
的形式存入视图中; - 如果视图不为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;}
分析:
- 循环处理回调参数集合;
- 根据回调入参里的logId从数据库xxl_job_log表查询XxlJobLog;
- 校验XxlJobLog是否为空?
handleCode
字段是否大于0?(避免重复回调,触发子作业等); - 如果触发调度成功,判断是否有子任务,如果有则触发调度子任务;
- 处理触发调度信息(text最大64kb 避免长度过长,长度截取到15000);
- 更新到日志记录中。
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;}
分析:
- 校验注册参数中的类型、appname、执行器地址;
- 更新xxl_job_registry表,更新结果 < 1表示没有则新增,
- 调用
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;}
分析:
- 校验注册参数中的类型、appname、执行器地址;
- 删除xxl_job_registry表信息,
- 删除成功则调用
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
表。
注意:
- 当选择自动注册时,需要根据appname从
xxl_job_registry
表匹配出最近90秒更新的执行器地址列表。 - 删除的时候会进行校验,如果挂载了任务则不能删除;如果执行器列表只有这一个也不能删除。
2.5 JobInfoController类
该类主要负责任务的新增、修改、删除、查询下次调度时间、启动、停止、调度一次。涉及xxl_job_info
表。
注意:
xxl_job_info
表trigger_status
字段0-停止,1-运行;- 计算下次调度时间需要加5s,避开预读周期;
2.6 JobLogController类
该类负责调度日志的查询、清理、中止任务等。
日志展示和中止会调用ExecutorBizClient
的log、kill方法远程调度执行器。
2.7 UserController类
该类主要负责用户的新增、更新和删除。
三、总结
xxl-job这款轻量级的分布式定时任务还是比较适合我们大多数场景的。后续我们继续学习下其他框架(比如:Elastic-Job等)
分布式定时任务—xxl-job学习(四)——调度中心web页面端api调用源码分析相关推荐
- Framework学习之路(一)—— UI绘制深入源码分析
Framework学习之路(一)-- UI绘制深入源码分析 本篇为笔者对Android SDK 33版本的UI绘制入口进行追踪的过程,主要作笔记作用.由于笔者经验尚浅,水平也有限,所以会存在很多不足的 ...
- SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(下)
源码资料 文档资料 <<Nacos架构与原理>>书籍于2021.12.21发布,并在Nacos官方网站非常Nice的提供其电子书的下载.我们学习Nacos源码更多是要吸取其优秀 ...
- sheng的学习笔记-ConcurrentHashMap(JDK1.7和16)源码分析
1.7版本 概述 注意,以下代码都是1.7版本(不同版本代码不一样),最下面有1.8版本部分内容 ConcurrentHashMap是线程安全的key value存储结构,底层也是数组+链表的结构 下 ...
- 系统学习深度学习(四) --CNN原理,推导及实现源码分析
之前看机器学习中,多层感知器部分,提到可以在设计多层感知器时,对NN的结构设计优化,例如结构化设计和权重共享,当时还没了解深度学习,现在看到CNN,原来CNN就是这方面的一个代表.CNN由纽约大学的Y ...
- SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(上)
Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https ...
- glibc-2.23学习笔记(一)—— malloc部分源码分析
glibc-2.23学习笔记(一)-- malloc部分源码分析 搭建Glibc源码调试环境 1.下载并解压glibc源码 2.配置gdb 3.编译测试程序 第一次调用 源码分析 __libc_mal ...
- 入理解分布式调度框架TBSchedule及源码分析
简介 由于最近工作比较忙,前前后后花了两个月的时间把TBSchedule的源码翻了个底朝天.关于TBSchedule的使用,网上也有很多参考资料,这里不做过多的阐述.本文着重介绍TBSchedule的 ...
- JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler
EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口, 由下面代码可以看出: (ns backtype.storm.scheduler.EvenSch ...
- Java定时任务(一) Timer及TimerTask的案例解析及源码分析
Java定时任务(一) Timer及TimerTask的案例解析及源码分析 一.概述: 定时任务这个概念在Java的学习以及项目的开发中并不陌生,应用场景也是多种多样.比如我们会注意到12306网站 ...
最新文章
- 解决Aireplay-ng信道问题
- mysql 4.0 删除重复_mysql删除重复数据
- 【笔记】Hexo+Github博客网站搭建,初试环境搭建及Matery主题配置感受
- Python基础教程:属性值设置和判断变量是否存在
- 有一次去校内的某个礼堂看电影,在门口有个长得很斯文的陌生人一脸神秘地跟我说:师弟,能不能进去之后,把电影票从厕所的气窗扔出来给我……...
- c __cplusplus详解
- datatable刷新数据_UE4 利用SaveGame和CSV进行Runtime数据更新
- 计算机配件的真假辨伪,专家教你辨真伪 “火眼金睛”辨真假配件
- Machine Learning——Homework4
- #洛谷oj:P1197星球大战
- ngrok跟小米球的使用
- unity下载教育版_新的现场学习系列为Unity教育工作者提供支持
- (附源码)springboot垃圾自动分类管理系统 毕业设计 160846
- Quasi_Newton
- grid布局之容器属性justify-content与align-content
- 网络视频会议 OpenMeetings 介绍 运行 开发
- 计算机基础:源代码如何被计算机执行
- 基于CMake的交叉编译工具链配置问题总结
- MyEclipse优化全攻略
- 无线网络技术导论笔记(第二讲)