SymmetricDS的同步机制是定时周期性同步,我们项目根据业务需要,只需要客户在UI激活一次replication时候才开始同步,所以这里我们的设计是每次激活时,用symmetric自己的deploy/undeploy机制让其在启动时候自动跑replication任务。
最近遇到一个问题, 在使用中,发现当数据同步时间间隔设在1小时以内时,运行一天以上会发现大量的SymmetricSchedule线程积压在JVM中没有释放,怀疑是Symmetric 2.2.5中在undeploy时释放线程有问题。
为了解决这个问题,先看下SymmetricDS中其需要的定时任务都定义在哪里。
在Symmetric engine中,整个symmetricDS的入口类在AbstractSymmetricEngine,在这个类的启动方法如下:
public synchronized boolean start() {
setup();
if (isConfigured()) {
...
getTriggerRouterService().syncTriggers();
heartbeat(false);
jobManager.startJobs();
...
started = true;
} finally {
starting = false;
}
return true;
} else {
return false;
}
} else {
...
}
}
可以看到其内置的job是由jobManager启动的,再看其实现类JobManager代码片段:
private List<IJob> jobs;
public synchronized void startJobs() {
for (IJob job : jobs) {
if (job.isAutoStartConfigured()) {
job.start();
} else {
log.info("JobNoAutoStart", job.getName());
}
}
}
可以看到是直接调用集合jobs中的job实例,该实例在SymmetricDS 2 中是由spring注入的,其spring xml配置如下:
<bean id="jobManager" class="org.jumpmind.symmetric.job.JobManager"> <property name="jobs"> <list> <ref bean="job.routing" /> <ref bean="job.push" /> <ref bean="job.pull" /> <ref bean="job.purge.outgoing" /> <ref bean="job.purge.incoming" /> <ref bean="job.purge.datagaps" /> <ref bean="job.stat.flush" /> <ref bean="job.synctriggers" /> <ref bean="job.heartbeat" /> <ref bean="job.watchdog" /> </list> </property> <property name="taskScheduler" ref="symmetricScheduler" /> </bean> <bean id="job.routing" parent="job.abstract" class="org.jumpmind.symmetric.job.RouterJob"> <property name="routingService" ref="routingService" /> <property name="autoStartParameterName" value="start.route.job" /> </bean>以上就是其内置的job,支持周期性运行和定时运行,以便进行数据库同步。这些job就是在undeploy时候没有杀掉的线程,由于我们关注的是undeploy时这些job的卸载情况,回到AbstractSymmetricEngine:
private ThreadPoolTaskScheduler taskScheduler;
public synchronized void stop() {
...
jobManager.stopJobs();
getRouterService().stop();
started = false;
starting = false;
}
public synchronized void destroy () {
stopJobs();
}
回到JobManager:
public synchronized void stopJobs() {
for (IJob job : jobs) {
job.stop();
}
}
jobManager直接遍历每个job并调用其stop方法。
所有job都有个超类AbstractJob,这个类的stop方法如下:
public boolean stop() {
boolean success = false;
if (this.scheduledJob != null) {
success = this.scheduledJob.cancel(true);
this.scheduledJob = null;
if (success) {
log.info("JobCancelled", jobName);
started = false;
} else {
log.warn("JobFailedToCancel", jobName);
}
}
return success;
}
这里只是调用了线程的cancel方法,在我们实际使用中,只是cancel并没有终结掉这些线程,这是我们JVM中的thread dump:
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x1d6873b8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)
at java.util.concurrent.DelayQueue.take(DelayQueue.java:164)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:609)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:602)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:662)
找到这里,我们知道这是其释放线程的一个bug,所以我直接去找了一下symmetricDS的bug track里有没有解决,发现这是2.2.*中的一个bug,在2.4.0以后的版本中已经fix,其JIRA地址在:
这个fix主要是在JobManager中加入了调用spring的线程池的shutdow方法:
public synchronized void destroy () {
stopJobs();
if (taskScheduler != null) {
taskScheduler.shutdown();
}
}
至此,在项目中使用symmetricDS新的代码,undeploy后线程都被杀掉,重新deploy后没有多余的job线程运行。
我们的启动方式:
没有在配置文件中配SymmetricDS需要的.cron项,直接让其按照线程的scheduleWithFixedDelay方式运行。该任务模式的配置在symmetric.properties文件中,在symmetric源码(AbstractJob)中可以清晰的看到如何获取该配置文件的值:
this.cronExpression = parameterService.getString(jobName + ".cron", null);
之后在该类的启动线程方法中会有判断:
public void start() {
if (this.scheduledJob == null) {
log.info("JobStarting", jobName);
if (!StringUtils.isBlank(cronExpression)) {
this.scheduledJob = taskScheduler.schedule(this, new CronTrigger(cronExpression));
started = true;
} else {
int startDelay = randomTimeSlot.getRandomValueSeededByExternalId();
if (this.timeBetweenRunsInMs > 0) {
this.scheduledJob = taskScheduler.scheduleWithFixedDelay(this,
new Date(System.currentTimeMillis() + startDelay),
this.timeBetweenRunsInMs);
started = true;
} else {
log.error("JobFailedToSchedule", jobName);
}
}
}
}
下面是正常后的JVM 线程截图:
相关推荐
AForge.dll 2.2.5版本 AForge.dll 2.2.5版本 AForge.dll 2.2.5版本
android_2.2.5.rar
AForge.NET Framework-2.2.5 文档 源码 例子
hornetq-2.2.5.Final.ziphornetq-2.2.5.Final.ziphornetq-2.2.5.Final.ziphornetq-2.2.5.Final.ziphornetq-2.2.5.Final.ziphornetq-2.2.5.Final.ziphornetq-2.2.5.Final.zip
该资源为Keras-2.2.5.tar.gz,欢迎下载使用哦!
jemalloc-2.2.5
layui2.2.5离线文档下载
官网下载太慢 此处自取 Vagrant2.2.5_64
vagrant是虚拟机软件中的系统的软件。这个是vagrant 2.2.5 windows64位的版本。官网下载的是非常的慢。
linux code 2.2.5
webwork 2.2.5框架资源,大家自由学习.学习框架
snmp4j 2.2.5 版本的 snmp4j 2.2.5 版本的 snmp4j 2.2.5 版本的
Badboy是专门录制测试脚本的工具,Badboy录制脚本的方法比较方便易懂,Badboy-2.2.5是最新版可以配合Jmeter使用,快速录制脚本且可以保存为JMeter支持的脚本。badboy测试工具是一个强大WEB测试工具,badboy测试工具...
railsinstaller-2.2.5.exe 官网有,但是总是被墙,方便大家下载。
cocos2d-x-2.2.5.z05
2.2.3 暂停及恢复线程 2.2.4 破坏线程 2.2.5 连接线程 2.3 为什么线程不是万能的 2.4 使用线程提供的机会 2.4.1 后台进程 2.4.2 访问外部资源 2.5 线程的陷阱 2.5.1 再次访问的执行顺序 2.5.2 一个循环中的线程 2.6 ...
ste 2.2.5 For mac pojie 不解释ste 2.2.5 For mac pojie 不解释
Zmodeler 中文版 2.2.5 B989(CHS)
BadboyInstaller-2.2.5.exe
最新android studio/idea 调试 smali插件.android studio安装setting->plugins->install plugin from disk...->选择smalidea.zip所在目录(不需要解压)->安装/...包含了baksmali-2.2.5.jar和smali-2.2.5.jar,功能不多说.