前言
最近项目中有个定时任务的需求,定时检查mysql数据与etcd数据的一致性,具体实现细节就不说了,今天要说的就是实现过程中遇到了druid抛出的异常,以及解决的过程
路漫漫其修远兮,吾将上下而求索!
github:https://github.com/youzhibing
码云(gitee):https://gitee.com/youzhibing
异常
详细信息
五月 05, 2017 4:16:00 下午 com.alibaba.druid.proxy.DruidDriver warn
警告: register druid-driver mbean error
javax.management.InstanceAlreadyExistsException: com.alibaba.druid:type=DruidDriver
at com.sun.jmx.mbeanserver.Repository.addMBean(Repository.java:437)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerWithRepository(DefaultMBeanServerInterceptor.java:1898)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerDynamicMBean(DefaultMBeanServerInterceptor.java:966)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerObject(DefaultMBeanServerInterceptor.java:900)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerMBean(DefaultMBeanServerInterceptor.java:324)
at com.sun.jmx.mbeanserver.JmxMBeanServer.registerMBean(JmxMBeanServer.java:522)
at com.alibaba.druid.proxy.DruidDriver.registerDriver(DruidDriver.java:99)
at com.alibaba.druid.proxy.DruidDriver$1.run(DruidDriver.java:84)
at java.security.AccessController.doPrivileged(Native Method)
at com.alibaba.druid.proxy.DruidDriver.<clinit>(DruidDriver.java:81)
at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:579)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:986)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:982)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:102)
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:211)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:447)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)
at com.huawei.wgp.task.TaskDbEtcdConsistencyCheck$$EnhancerBySpringCGLIB$$e1d6d636.consistencyCheck(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745) 五月 05, 2017 4:16:00 下午 com.alibaba.druid.stat.DruidDataSourceStatManager error
严重: register mbean error
javax.management.InstanceAlreadyExistsException: com.alibaba.druid:type=DruidDataSourceStat
at com.sun.jmx.mbeanserver.Repository.addMBean(Repository.java:437)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerWithRepository(DefaultMBeanServerInterceptor.java:1898)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerDynamicMBean(DefaultMBeanServerInterceptor.java:966)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerObject(DefaultMBeanServerInterceptor.java:900)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerMBean(DefaultMBeanServerInterceptor.java:324)
at com.sun.jmx.mbeanserver.JmxMBeanServer.registerMBean(JmxMBeanServer.java:522)
at com.alibaba.druid.stat.DruidDataSourceStatManager.addDataSource(DruidDataSourceStatManager.java:139)
at com.alibaba.druid.pool.DruidDataSource$1.run(DruidDataSource.java:1446)
at java.security.AccessController.doPrivileged(Native Method)
at com.alibaba.druid.pool.DruidDataSource.registerMbean(DruidDataSource.java:1442)
at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:706)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:986)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:982)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:102)
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:211)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:447)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)
at com.huawei.wgp.task.TaskDbEtcdConsistencyCheck$$EnhancerBySpringCGLIB$$e1d6d636.consistencyCheck(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
出现场景
当前开发只是定时任务,那么工程启动后一直在等定时任务的启动,没有进行其他任何的操作,而一旦定时任务启动就出现如上异常信息,但是定时任务正常执行完成,并且不影响应用的其他任何服务,就和没抛异常一样!
druid分析
程序员多多少少都有强迫症,看不得出现异常。虽说上述的异常不影响应用,但抛出来了就给我一种不爽的感觉,强迫自己解决它。
所有的异常信息都将矛头指向了druid,那么我们就从druid下手。很幸运,网上这方面的资料有不少,最后在druid的github上找到了javax.management.InstanceAlreadyExistsException异常的问题和CentOS release 6.5 总是抛InstanceAlreadyExistsException这两篇issue,一篇说把spring的自动探测关闭掉,一篇说应用容器中同一实例被启动了两遍,引起的并发执行了两个定时任务;同一实例被启动了两遍,这个应该不可能,就不去探究了(真不用探究吗);那么我们将spring的自动探测关闭掉试试,结果定时任务还是执行了两遍。按照网上提供的其他方式也没能解决这个问题,本以为这个问题要求助领导了,可上天还是眷顾我的。
一次偶然的巧合,我在定时任务启动前,从浏览器请求了我的应用,再当定时任务启动的时候,异常没有产生,更巧合的是,数据库中的数据在浏览器上显示重复了,也就说定时任务产生了两份相同的数据插入到了数据库中,定时任务还是执行了两次! 这么说来,异常的产生和druid没关系,很有可能是定时任务的原因。
那么问题就转移了,从druid转移到定时任务了。
定时任务分析
不加定时任务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd"> <!-- <bean id="taskDbEtcdConsistencyCheck" class="com.yzb.wgp.task.TaskDbEtcdConsistencyCheck" />
<task:scheduled-tasks scheduler="schedulerDbEtcdConsistencyCheck">
<task:scheduled ref="taskDbEtcdConsistencyCheck" method="consistencyCheck" cron="0 58 23 * * ?" />
</task:scheduled-tasks>
<task:scheduler id="schedulerDbEtcdConsistencyCheck" pool-size="2" /> -->
</beans>
结果是:启动之后,不请求应用则dataSource-1不初始化,请求应用,dataSource-1只初始化一次
五月 08, 2017 8:56:12 上午 com.alibaba.druid.pool.DruidDataSource info 信息: {dataSource-1} inited
这个很好理解,连接池采用了懒加载,真正用到的时候才初始化
加定时任务
a)、定时任务启动前,向应用SLBAdmin发送请求
结果是:请求的时候初始化dataSource-1一次,接着执行一次定时任务,然后又初始化一次dataSource-1,最后执行一次定时任务
五月 08, 2017 9:21:02 上午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
定时任务...
五月 08, 2017 9:22:00 上午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
定时任务...
这就有了疑问:
1、定时任务为什么启动了2次
2、dataSource-1为什么会初始化2次,照理说应该只初始化一次,这个两次是怎么出现的了,感觉初始化3次都比初始化2次好理解(一次初始化对应所有前端请求,另外2次初始化分别对应两次定时任务)
3、没有抛异常:javax.management.InstanceAlreadyExistsException,这又是为什么了
b)、定时任务启动前,不向应用SLBAdmin发送任何请求
结果是:先出现异常 ,再初始化dataSource-1两次,最后定时任务执行两次
异常信息:略,在上面有
五月 08, 2017 9:38:00 上午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
五月 08, 2017 9:38:00 上午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
定时任务...
定时任务...
这也有了疑问:
1、这里为什么出现异常,而且这个异常没有终止应用程序,应用程序还是能够正常提供服务
2、为什么是初始化dataSource-1两次后调用两次定时任务,而不是像a)那样初始化一次dataSource-1,调用一次定时任务,再初始化一次dataSource-1,再调用一次定时任务
分析
<bean id="taskDbEtcdConsistencyCheck" class="com.yzb.wgp.task.TaskDbEtcdConsistencyCheck" />
<task:scheduled-tasks>
<task:scheduled ref="taskDbEtcdConsistencyCheck" method="consistencyCheck" cron="0 07 10 * * ?" />
</task:scheduled-tasks>
<task:scheduler id="schedulerConsistencyCheck" pool-size="1"/>
System.out.println("Thread-id=" + Thread.currentThread().getId() + " ThreadName=" + Thread.currentThread().getName() + " : 定时任务...");
将执行定时任务的线程id及线程名打印出来,配置及打印代码如上,结果和 2、加定时任务 一样, 只是有我们需要的信息:
Thread-id=17 ThreadName=pool-1-thread-1 : 定时任务...
Thread-id=18 ThreadName=pool-2-thread-1 : 定时任务...
线程id不同,线程池也不同,那么就说明这是两个不同的线程(线程名相同只是spring采用了默认的命名规则,如果我们在配置文件中指定线程的名字,那么线程名也是相同的),这就证明不是同一个线程调用了两次目标方法,那么问题又来了:怎么会产生两个定时任务的线程了
我们来列举下可能两个线程的产生的场景:
i、配置了两个定时任务,目标类和目标方法是同一个,同时同一时间点执行,像如下配置
<bean id="taskDbEtcdConsistencyCheck" class="com.huawei.wgp.task.TaskDbEtcdConsistencyCheck" /> <task:scheduled-tasks>
<task:scheduled ref="taskDbEtcdConsistencyCheck" method="consistencyCheck" cron="0 07 10 * * ?" />
<task:scheduled ref="taskDbEtcdConsistencyCheck" method="consistencyCheck" cron="0 07 10 * * ?" />
</task:scheduled-tasks>
<task:scheduler id="schedulerConsistencyCheck" pool-size="2"/>
ii、应用中只有一个定时任务,但是应用被tomcat启动了两遍
i场景很容易排除,定时任务的配置文件内容很少,很容易排查;但是ii场景是怎么产生的了,那么我们去看下tomcat的应用列表
tomcat应用列表
不加定时任务
定时任务启动前,向应用SLBAdmin发送请求
定时任务启动前,不向应用SLBAdmin发送请求
注意上面三张图片中标红的地方,理论上来讲,每张图片的第二处标红的地方是不应该出现的,也就是Path为wgp-Web的那一行应该是不存在的,那么它是怎么出现的了? 我们来看下我们的工程
这种不一致是怎么产生的了,工程名和发布路径一开始都是叫wgp-Web,后面发布路径改成了SLBAdmin,而工程名没有改,就导致了这种不一致。那么我们将工程名也改成SLBAdmin,再看看tomcat应用列表
应用列表正常,而SLBAdmin应用也达到了预料中的正常,既没有抛出异常,定时任务也只在定点执行了一次。
疑问解答
定时任务启动前,向应用SLBAdmin发送请求
1、定时任务为什么启动了2次
解答:应用容器中同一实例被启动了两遍,导致并发执行了两个定时任务。
2、dataSource-1为什么会初始化2次,照理说应该只初始化一次,这个两次是怎么出现的了,感觉初始化3次都比初始化2次好理解(一次初始化对应所有前端请求,另外2次初始化分别对应两次定时任务)
解答:因为在定时任务启动前请求了应用SLBAdmin,spring初始化了SLBAdmin的dataSource-1,当SLBAdmin定时任务启动的时候,就不会再去初始化SLBAdmin的dataSource-1了;但是容器中还存在wgp-Web的定时任务,当到达定点时,SLBAdmin的定时任务和wgp-Web的定时任务会同时并发启动,由于wgp-Web的dataSource-1还没有初始化,所以定时任务执行时初始化wgp-Web的dataSource-1。
3、没有抛异常:javax.management.InstanceAlreadyExistsException,这又是为什么了
解答:dataSource-1的初始化不是同时并发进行的,SLBAdmin在请求的时候初始化dataSource-1,在定时任务执行前,而wgp-Web是在定时任务启动的时候初始化的, spring会根据这个时间差来使得两次连接池的获取指向同一个连接池。这个答案是我猜想的,真正知道原因的朋友可以留个言!
定时任务启动前,不向应用SLBAdmin发送请求
1、这里为什么出现异常,而且这个异常没有终止应用程序,应用程序还是能够正常提供服务
解答:dataSource-1的初始化是同时并发进行的,这个更应该说是spring的bug,spring将实现了符合MBean规范的beans注册到mbeanserver中,但没处理好异常,所以悲剧了; 也就是说druid是没有问题的,那么服务是能正常提供的。
2、为什么是初始化dataSource-1两次后调用两次定时任务,而不是像a)那样初始化一次dataSource-1,调用一次定时任务,再初始化一次dataSource-1,再调用一次定时任务
解答:两个定时任务是在同一个定点执行,所以dataSource-1的初始化是同时并发进行的; 而a)中SLBAdmin是在定时任务执行前响应了请求,并初始化了dataSource-1,wgp-Web是在定时任务执行时初始化的dataSource-1。
为什么之前的版本没有出现这个问题了:之前的版本没有定时任务,另外,转测试版本和上线版本的Path与Display Name是一致的,都是SLBAdmin
总结
问题产生的根本原因还真是:同一实例被启动了两遍,Path为/SLBAdmin启动一次,Path为/wgp-Web启动一次,
开发过程中最好保证工程名与发布路径保证一直,避免不必要的麻烦
参考
https://github.com/alibaba/druid/issues/1200
https://github.com/alibaba/druid/issues/538
http://jingyan.baidu.com/article/48206aeaf9422e216ad6b39b.html
https://my.oschina.net/superkangning/blog/467487
druid抛出的异常------javax.management.InstanceAlreadyExistsException引发的一系列探索的更多相关文章
-
解决访问HTTPS,抛出的异常javax.net.ssl.SSLHandshakeException
本地测试没问题,http换成https抛出异常javax.net.ssl.SSLHandshakeException,网上有说是服务器证书,有说要启动SSL3协议的,反正没有找到有用的. 在GET和P ...
-
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑. 1.1 异常处理思路 系统中异常包括两类:预期异常和运行时异常RuntimeEx ...
-
记录一次dubbo不能正常抛出特定异常
BUG场景 今天同事的代码中出现一个问题,让我帮忙排查一下.原代码大致如下 dubbo服务消费者: @Resource private IPayWayService payWayService; @R ...
-
nested exception is javax.management.InstanceAlreadyExistsException: webservice:name=statFilter,type=StatFilter
Caused by: org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [ ...
-
将Controller抛出的异常转到特定View
<!-- 将Controller抛出的异常转到特定View --> <bean class="org.springframework.web.servlet.handler ...
-
使用visual studio 2015调用阿里云oss .net sdk 2.2的putobject接口抛出outofmemory异常
问题描述: 使用阿里云oss .net sdk 2.2版本,使用putobject接口上传文件时,抛出outofmemory异常. 原因分析: 上传时,用于准备上传的数据缓冲区内存分配失败.与应用软件 ...
-
捕获Java线程池执行任务抛出的异常
捕获Java线程池执行任务抛出的异常Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常, public interface Runnable { publi ...
-
为什么只有在用Visual Studio启动程序时会抛出InvalidOperationException异常
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:为什么只有在用Visual Studio启动程序时会抛出InvalidOperationExceptio ...
-
java 检查抛出的异常是否是要捕获的检查性异常或运行时异常或错误
/** * Return whether the given throwable is a checked exception: * that is, neither a RuntimeExcepti ...
随机推荐
-
UIControl的使用
在开发当中,可能很多时候都需要做个点赞的需求,如果用按钮实现,按钮作为一个系统复合控件,外部是一个 View-->UIControl的容器, 内部包含了UILabel和UIImage,以及一些排 ...
-
Java基础知识强化之IO流笔记41:字符流缓冲流之复制文本文件案例02(使用 [ newLine() / readLine() ] )(重要)
1. 使用字符流缓冲流的特殊功能 [ newLine() / readLine() ] 需求:把当前项目目录下的a.txt内容复制到当前项目目录下的b.txt中 数据源: a.txt -- 读取数据 ...
-
High performance web site
http://www.cnblogs.com/Blog-Yang/archive/2013/08/16/3261284.html http://www.kuqin.com/webpagedesign/ ...
-
atitit..主流 浏览器 js 发动机 内核 市场份额 attialx总结vOa9
atitit..主流 浏览器 js 发动机 内核 市场份额 attialx总结vOa9 1. 浏览器内核 1 2. 浏览器的主要组件包含: 2 2.1. 主要组件体系结构 2 2.2. WebCor ...
-
安卓Acitivity的启动模式
活动的四大启动模式 Ps:除了standar模式外,其他启动模式都要在AndroidManifest.xml中设置 android:lauchMode的值 安卓活动的启动模式(LaunchMode)有 ...
-
微信小程序:将中文语音直接转化成英文语音
作者:瘟小驹 文章来源<微信小程序个人开发全过程> 准备工作: 准备工具:Eclipse.FileZilla.微信开发者工具.一个配置好SSL证书(https)的有域名的服务器 所需 ...
-
Java并发知识分享
volatile的内存语义 从JSR-133(即从JDK1.5开始),volatile变量的写-读可以实现线程之间的通信 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷 ...
-
MySQL中format()函数
format()函数 format()函数:将数据内容格式化的,可以将数据格式化为整数或者带几位小数的浮点数(四舍五入). SELECT FORMAT();//取整 SELECT FORMAT();/ ...
-
Oracle生成关闭外键的SQL语句
select 'alter table ' || t.table_name || ' disable constraint ' || t.constraint_name || ';' from DBA ...
-
Linux磁盘分区与文件系统
一 Linux磁盘分区与文件系统 在Linux中常见的操作系统有:ext2 ext3 ext4 xfs btrfs reiserfs等文件系统的作用主要是明确磁盘或分区上的文件存储方法以及数据结构,L ...