xxl-job优雅停止执行器即客户端tomcat

xxl-job优雅停止停止执行器即客户端tomcat的临时解决办法

    • 前言
    • 评测
    • 问题
      • ■ 描述
      • ■ 可能引发的另一个bug
      • ■ 任务
    • 解决
      • ■ 修改xxl-job-admin源码
      • ■ 手动调用job服务端下线接口(2选1)
      • ■ job客户端改造优雅停机(2选1)
    • 最后

如果着急,可以直接跳到解决方案,不忙可以唠嗑一下。

前言

我所了解的目前主流分布式job就是elastic-job、xxl-job和PowerJob。ejob太重,pjob开源太晚,xxljob评价又不错,中庸地选型了xxl-job。
本文提供xxl-job优雅停止停止执行器即客户端tomcat时没有及时剔除服务,不停报错500的临时解决办法。(最近较忙积累了一堆内容,没空写博客,由于github上有道友翻出了这个问题,我就先写一下这个内容,后续还有一篇关于xxl-job企微告警的内容)

评测

如果把它看做是个人行为的开源框架那确实是没话说——起立致敬,要是站在分布式job唯一解决方案的角度去看待它,我觉得是不够理想的。如果是单体项目qz就足够了,然而xxl-job在分布式的支持上,我认为还不够好,目前仍有非常多的瑕疵,远远没达到可以停止维护的程度。由于是个人项目,观察github上的情况,可以发现作者基本已经停止维护了,项目状态就是open-issue很多,解决问题的人很少,换成中间件的话,这种程度基本意味着项目要凉。

(题外话——这就是国内的情况,大多有能力的程序员把时间花在较为功利性的行为上,要么走面霸路专注于底层的吹牛逼,要么会用就行类尝遍所有的潮流框架级技术,鲜少有人会去为这类开源框架贡献一份力,做些脚踏实地的事,社区活跃只有提问,没有解决,也就造成作者的力不从心,开源项目也就处于停摆状态——没有什么大bug,凑合着也能用)

网上评测的都是营销号复制来复制去,我甚至怀疑他们连github都没登过,这也就造成一搜xxl-job,搜索结果——好东西,什么bug都没有,而真正商用的人,就跟藏宝贝一样,改造的内容不会开源出来,也不会提出问题。

问题

■ 描述

执行器重启也就是tomcat重启时,如果你直接杀死tomcat,xxl-job不会认为执行器下线的,会在一段时间内不停调用这台tomcat的执行器,并且调度失败,500报错,如果你配置了告警,那完蛋了,邮箱轰炸,钉钉轰炸,企微轰炸,被运维立马拖出去暴打。

后面发现xxl-job是提供执行器下线接口的

==> com.xxl.job.admin.controller.JobApiController
==> @RequestMapping("/{uri}")
==> } else if (“registryRemove”.equals(uri))

,并且在客户端,只要是优雅停机,客户端就会调用xxl-job-admin端的停机。但是,随即又发现服务端的停机仍然存在问题。

这是21年5月17日我在github上提出的issue,后面发现2020年8月20日有个类似的issue,就明白凉了凉了,要么重新选型,要么自己解决吧。

issue2422:https://github.com/xuxueli/xxl-job/issues/2422

类似issue

issue1917:https://github.com/xuxueli/xxl-job/issues/1917

v-2.3.0
目前是 客户端优雅停机时会调用destroy方法 方法里调用api api/registryRemove 通知调度中心
xxl-job-admin里的registryRemove方法只删除了xxl_job_registry表的内容 剔除服务仍需等待线程registryMonitorThread(BEAT_TIMEOUT = 30) 默认为30s执行才会真正剔除执行器,会导致大概0-30s的时间分流某执行器全是500报错
JobRegistryHelper里预留了freshGroupRegistryInfo方法,我将registryMonitorThread内容放入大大降低了报错,只会损失执行registryMonitorThread内容期间的调用,是否计划有更优雅的方法。
从某种程度来说既然调用api/registryRemove肯定意味着客观停机,应即时剔除服务,执行器上线延迟可以接受,执行器下线延迟不太接受。

■ 可能引发的另一个bug

如果你没配置告警无所谓这个报错,后续可能也会出现慢sql,类似这个issue

issue2415:https://github.com/xuxueli/xxl-job/issues/2415

我帮助了这个题主找到了慢sql的原因,并提供了解决方案。题主项目调度log非200即失败日志太多,就会产生慢sql(应该是告警方法的sql查询)。

难过的是——费力帮忙解决,连声道谢都没有,拜托,我们也是时薪近百的人,帮你解决没给钱就算了,说声谢谢是应该的吧,最后更是没有把引起问题的原因发出来供大家开源参考。鄙视一下认为理所应当的伸手党行为,这也是很多开源项目作者寒心的原因,人家没收你一分钱,你只会责骂项目有问题,却没有帮助作者让开源项目做得更好,所以即使xxl-job存在很多问题,我是不敢说一声不好,只能提出问题,我自己有解决的发一份供大家参考。

■ 任务

保证job客户端优雅停机时,执行器可以及时剔除服务,防止无效调度。

解决

我处理这个问题的心态是能解决就好,坐等官方方案(就目前来看,短期内不太可能解决),花太多时间的话宁可选型其它框架,哈哈,也是属于白嫖党,不愿花太多精力。

■ 修改xxl-job-admin源码

找到
com.xxl.job.admin.core.thread.JobRegistryHelper
拉到最下面,有这么一个方法

private void freshGroupRegistryInfo(RegistryParam registryParam){
		// Under consideration, prevent affecting core tables
}

翻译:正在考虑,防止影响核心表
说明作者也是有在考虑这个问题,但是还没实现预留了一个方法。

我给大家一个临时解决方案。

很简单,将61行处// for monitor ==》while (!toStop) { 里的部分内容复制过来即可。
即——

private void freshGroupRegistryInfo(RegistryParam registryParam){
		// Under consideration, prevent affecting core tables
		try {
			// auto registry group
			List<XxlJobGroup> groupList = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().findByAddressType(0);
			if (groupList!=null && !groupList.isEmpty()) {

				// remove dead address (admin/executor)
				List<Integer> ids = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findDead(RegistryConfig.DEAD_TIMEOUT, new Date());
				if (ids!=null && ids.size()>0) {
					XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(ids);
				}

				// fresh online address (admin/executor)
				HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
				List<XxlJobRegistry> list = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findAll(RegistryConfig.DEAD_TIMEOUT, new Date());
				if (list != null) {
					for (XxlJobRegistry item: list) {
						if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {
							String appname = item.getRegistryKey();
							List<String> registryList = appAddressMap.get(appname);
							if (registryList == null) {
								registryList = new ArrayList<String>();
							}

							if (!registryList.contains(item.getRegistryValue())) {
								registryList.add(item.getRegistryValue());
							}
							appAddressMap.put(appname, registryList);
						}
					}
				}

				// fresh group address
				for (XxlJobGroup group: groupList) {
					List<String> registryList = appAddressMap.get(group.getAppname());
					String addressListStr = null;
					if (registryList!=null && !registryList.isEmpty()) {
						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);
					}
					group.setAddressList(addressListStr);
					group.setUpdateTime(new Date());

					XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
				}
			}
		} catch (Exception e) {
			logger.error(">>>>>>>>>>> xxl-job, job registry remove thread error:{}", e);
		}
	}

■ 手动调用job服务端下线接口(2选1)

如果你是暴力关闭服务tomcat类似kill -9 类型,可以选择手动调用下线接口。
在关闭tomcat前调用该接口,过个几秒后再杀死服务的tomcat。

curl.exe -H "Content-Type:application/json" 
-H "XXL-JOB-ACCESS-TOKEN:你的鉴权token" 
-X POST --data 
"{\"registryGroup\": \"EXECUTOR\",\"registryKey\": \"xxl-job-executor-zzdserver\",\"registryValue\": \"http://执行器ip:port/\"}" 
http://服务端ip:port/api/registryRemove

■ job客户端改造优雅停机(2选1)

上面的做法当然是不推荐的,服务最好是优雅停机,我是选型actuator来优雅停机的(actuator内容这里不介绍,后续会写一篇jenkins的文章集成actuator),在优雅停机的时候,spring自然会调用执行器的销毁方法,销毁方法内包含上面那个接口的调用,内容也会更多,类似停止新调度,执行完旧调度最后关闭,不过作者写的现有内容好像有问题,好像是先杀再停止新调度,我在引入xxl-job的销毁后,shutdown接口无法很快关闭,基本上都是等到超时。

bat内容如下,sh其实类似,可供参考

rem ****** 重启前准备 ********
@echo off
	set "FILE_HOME=C:\Program Files\JenkinsFileA"
		rem 进入d盘
		d:
		D:\...\curl.exe -X POST http://服务ip:8085/actuator/shutdown
		rem 延迟20s
		ping -n 20 1271	
		for /F "tokens=5" %%i in ( 'netstat -ano ^| findstr :8080' ) do taskkill /f /pid  %%i  /t
		xcopy /e /y "%FILE_HOME%\config\*" "D:\...\A\config\"	
		rem 复制jar到运行目录
		copy "%FILE_HOME%\***.jar" "D:\...\A\***.jar"
		schtasks /Run /TN "deploy-restart-a"
	echo good bye
@echo on

最后

这样重启我们自己的服务的时候,xxl-job就基本不会500报错,只是偶尔的报kill job,这也是漏洞,由于出现概率较低可以接受,所以我没有去解决。如果你有更好的方案,欢迎提供。

你可能感兴趣的:(Java,中间件,java,分布式,xxl-job)