今天运维在执行线上功能时发现服务异常,跟进之后发现Redis出现问题,如下:

[ERROR] [2022-05-26 00:05:01.518] [c.q.d.utils.DataAccessSampleRedisUtil:326][Thread-154] 服务名称:ivdg-data-access-service--> 查询缓存数据出错:Unknown redis exception; nested exception is java.util.concurrent.RejectedExecutionException: event executor terminated
org.springframework.data.redis.RedisSystemException: Unknown redis exception; nested exception is java.util.concurrent.RejectedExecutionException: event executor terminatedat org.springframework.data.redis.FallbackExceptionTranslationStrategy.getFallback(FallbackExceptionTranslationStrategy.java:53)at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:43)at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:273)at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.convertLettuceAccessException(LettuceScriptingCommands.java:236)at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:195)at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1502)at org.springframework.data.redis.connection.DefaultStringRedisConnection.evalSha(DefaultStringRedisConnection.java:1757)at sun.reflect.GeneratedMethodAccessor606.invoke(Unknown Source)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61)at com.sun.proxy.$Proxy258.evalSha(Unknown Source)at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:77)at org.springframework.data.redis.core.script.DefaultScriptExecutor.lambda$execute$0(DefaultScriptExecutor.java:68)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:175)at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:58)at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:52)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:350)at com.qsdi.dataAccess.utils.DataAccessSampleRedisUtil.lambda$scanData$7(DataAccessSampleRedisUtil.java:324)at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:559)at com.qsdi.dataAccess.utils.DataAccessSampleRedisUtil.scanData(DataAccessSampleRedisUtil.java:318)at com.qsdi.dataAccess.service.impl.DataSamplingInspectionHourStatisticsServiceImpl.handleHourData(DataSamplingInspectionHourStatisticsServiceImpl.java:69)at com.qsdi.dataAccess.service.impl.DataSamplingInspectionHourStatisticsServiceImpl.statisticsDataByTime(DataSamplingInspectionHourStatisticsServiceImpl.java:58)at com.qsdi.dataAccess.job.HourStatisticsByVehicleJobHandler.execute(HourStatisticsByVehicleJobHandler.java:29)at com.xxl.job.core.thread.JobThread.run(JobThread.java:163)
Caused by: java.util.concurrent.RejectedExecutionException: event executor terminatedat io.netty.util.concurrent.SingleThreadEventExecutor.reject(SingleThreadEventExecutor.java:926)at io.netty.util.concurrent.SingleThreadEventExecutor.offerTask(SingleThreadEventExecutor.java:353)at io.netty.util.concurrent.SingleThreadEventExecutor.addTask(SingleThreadEventExecutor.java:346)at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:828)at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:818)at io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:261)at io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:177)at io.netty.util.concurrent.AbstractEventExecutorGroup.schedule(AbstractEventExecutorGroup.java:50)at io.netty.util.concurrent.AbstractEventExecutorGroup.schedule(AbstractEventExecutorGroup.java:32)at io.lettuce.core.protocol.CommandExpiryWriter.potentiallyExpire(CommandExpiryWriter.java:168)at io.lettuce.core.protocol.CommandExpiryWriter.write(CommandExpiryWriter.java:115)at io.lettuce.core.RedisChannelHandler.dispatch(RedisChannelHandler.java:195)at io.lettuce.core.StatefulRedisConnectionImpl.dispatch(StatefulRedisConnectionImpl.java:176)at io.lettuce.core.AbstractRedisAsyncCommands.dispatch(AbstractRedisAsyncCommands.java:474)at io.lettuce.core.AbstractRedisAsyncCommands.evalsha(AbstractRedisAsyncCommands.java:512)at sun.reflect.GeneratedMethodAccessor608.invoke(Unknown Source)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:63)at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:79)at com.sun.proxy.$Proxy254.evalsha(Unknown Source)at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:193)... 23 common frames omitted

如上代码段所示, 再调用redis执行luna脚本查询时提示异常,具体异常描述是由SingeThreadEventExecutor这个类调用execute方法时出现的异常,源码如下:

private void execute(Runnable task, boolean immediate) {boolean inEventLoop = inEventLoop();addTask(task);if (!inEventLoop) {startThread();if (isShutdown()) {boolean reject = false;try {if (removeTask(task)) {reject = true;}} catch (UnsupportedOperationException e) {// The task queue does not support removal so the best thing we can do is to just move on and// hope we will be able to pick-up the task before its completely terminated.// In worst case we will log on termination.}if (reject) {reject();}}}if (!addTaskWakesUp && immediate) {wakeup(inEventLoop);}}

分析这个方法,大意是将当前写事件提交到 SingleThreadEventExecutor 任务队列中。报错是在 addTask 这个方法:

/*** Add a task to the task queue, or throws a {@link RejectedExecutionException} if this instance was shutdown* before.*/protected void addTask(Runnable task) {ObjectUtil.checkNotNull(task, "task");if (!offerTask(task)) {reject(task);}}

这个方法判断如果没有插入任务队列成功就调用 reject (task) 拒绝任务,reject (task) 里面抛出的异常就是我们看到的最外面的异常。于是看一看 offerTask 方法:

final boolean offerTask(Runnable task) {if (isShutdown()) {reject();}return taskQueue.offer(task);}@Overridepublic boolean isShutdown() {return state >= ST_SHUTDOWN;}private static final int ST_NOT_STARTED = 1;private static final int ST_STARTED = 2;private static final int ST_SHUTTING_DOWN = 3;private static final int ST_SHUTDOWN = 4;private static final int ST_TERMINATED = 5;

这里可以看出 offerTask 的时候校验了当前的 SingleThreadEventExecutor 的状态是否是结束和终止。那么问题来了,是什么导致的 SingleThreadEventExecutor 的状态为终止的勒,通过前面缘由中日志可以看到 SingleThreadEventExecutor 也打印了堆溢出的错误日志,搜索这个日志是哪里打印的,发现:

private void doStartThread() {assert thread == null;executor.execute(new Runnable() {@Overridepublic void run() {thread = Thread.currentThread();if (interrupted) {thread.interrupt();}boolean success = false;updateLastExecutionTime();try {SingleThreadEventExecutor.this.run();success = true;} catch (Throwable t) {logger.warn("Unexpected exception from an event executor: ", t);} finally {for (;;) {int oldState = state;if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {break;}}// Check if confirmShutdown() was called at the end of the loop.if (success && gracefulShutdownStartTime == 0) {if (logger.isErrorEnabled()) {logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must " +"be called before run() implementation terminates.");}}try {// Run all remaining tasks and shutdown hooks. At this point the event loop// is in ST_SHUTTING_DOWN state still accepting tasks which is needed for// graceful shutdown with quietPeriod.for (;;) {if (confirmShutdown()) {break;}}// Now we want to make sure no more tasks can be added from this point. This is// achieved by switching the state. Any new tasks beyond this point will be rejected.for (;;) {int oldState = state;if (oldState >= ST_SHUTDOWN || STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, oldState, ST_SHUTDOWN)) {break;}}// We have the final set of tasks in the queue now, no more can be added, run all remaining.// No need to loop here, this is the final pass.confirmShutdown();} finally {try {cleanup();} finally {// Lets remove all FastThreadLocals for the Thread as we are about to terminate and notify// the future. The user may block on the future and once it unblocks the JVM may terminate// and start unloading classes.// See https://github.com/netty/netty/issues/6596.FastThreadLocal.removeAll();STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);threadLock.countDown();int numUserTasks = drainTasks();if (numUserTasks > 0 && logger.isWarnEnabled()) {logger.warn("An event executor terminated with " +"non-empty task queue (" + numUserTasks + ')');}terminationFuture.setSuccess(null);}}}}});}

这个方法,这个方法里面同时在 finally 设置了状态为 ST_TERMINATED,这个方法大意是启动 eventloop 真正的线程来轮询读写事件,这个方法将在首次执行 excute 方法的时候被调用,注意这里的 executor 变量是一个线程池,于是查阅资料才认识到,eventloopgroup 下的每个 eventloop 实际上都会引用到一个线程池,这个线程池线程数目与 eventloop 总大小相同,是 fork/join 框架创建的,真正执行轮询逻辑实际上是这个线程池去提交的。eventloop 的意义实际上只是为了保存任务队列。真正的去轮询任务队列的逻辑是由其代理的线程池去执行的。

这样也就清楚了,整个事故实际很简单,就是先由于内存溢出的错误导致 eventloop 的状态为终止,这个终止状态据我看到的代码貌似是不可逆的,然后当在调用 chanel.write 的时候提交任务到 eventloop 校验状态为终止所以报了拒绝错误。

解决这个 bug 的重要途径是找到内存溢出的原因,这里就不赘述了,因为在网上没有搜到这个错的原因,所以跟了一下代码,在这里做个记录,后续分析一下到底是哪里有内存溢出问题再贴上解决方案

生产环境 java.util.concurrent.RejectedExecutionException: event executor terminated 错误分析相关推荐

  1. java.util.concurrent.RejectedExecutionException: event executor terminated 错误分析

    java.util.concurrent.RejectedExecutionException: event executor terminated 错误分析

  2. 关于java.util.concurrent.RejectedExecutionException: event executor terminated

    多线程报了个java.util.concurrent.RejectedExecutionException: event executor terminated 线程池的拒绝策略 ThreadPool ...

  3. 1]解决java.util.concurrent.RejectedExecutionException

    今天学习了java的并发,线程池,同一时间执行一个操作. 报错:java.util.concurrent.RejectedExecutionException,排查发现是等待队列设小了,导致 拒绝策略 ...

  4. java.util.concurrent.RejectedExecutionException

    2019独角兽企业重金招聘Python工程师标准>>> 遇到java.util.concurrent.RejectedExecutionException 目前看来,最主要有2种原因 ...

  5. 【ruoyi】java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoo

    前言 ruoyi 4.6.0 jdk1.8 错误 11:48:16.879 [http-nio-9031-exec-25] INFO c.r.f.s.r.UserRealm - [doGetAuthe ...

  6. java util下的并发包_jdk并发包下:使用java.util.concurrent.Executor线程池

    多线程,线程池Executor的接口类图: 其他都不重要,就ExecutorService是主要的: 基本上分为单纯线程池和定时任务线程池: 说白了除了ForkJoinPool所有都继承于Thread ...

  7. 线程池(java.util.concurrent.ThreadPoolExecutor)的使用

    如果大家觉得这个类还不能完全满足自己的要求的话,其实可以照搬这个源码,然后适当改动一下来适合自己的需求,也不失为一种捷径的.呵呵,本人最近在自己做的一个项目中,就来了这一手.虽然不是多好,主要是为了满 ...

  8. java.util.concurrent 包下面的所有类

    java.util.concurrent 包下面的所有类 原子操作数类: java.util.concurrent.atomic.AtomicBoolean.class java.util.concu ...

  9. 线程池java.util.concurrent.ThreadPoolExecutor总结

    http://uule.iteye.com/blog/1123185 线程池还具有提高系统性能的优点,因为创建线程和清除线程的开销比较大. 有两种不同类型的线程池:一是固定线程数量的线程池:二是可变数 ...

  10. JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor

    http://www.diybl.com/course/3_program/java/javajs/200797/70003.html 在多线程大师Doug Lea的贡献下,在JDK1.5中加入了许多 ...

最新文章

  1. centos中执行apt-get命令提示apt-get command not found
  2. java容器类的继承结构
  3. 说说第二次配置Ubuntu14.04
  4. Python字符串的编码与解码(encode与decode)
  5. Windows驱动程序开发语言
  6. 谁说双非本科就一定无缘阿里?H哥粉丝6棉通过,喜提Offer!
  7. mongodb objetcid_mongodb(1)
  8. 面试题 04.01. 节点间通路
  9. JAVA的第一个小程序:Hello world
  10. cdsn自动添加目录
  11. 这款完全开源可自主DIY的小程序商城太强大了,直接可给客户搭建赚米
  12. 使用easywechat调用微信支付
  13. JAVA适配器特点_适配器模式的优缺点
  14. 关于O、Θ、Ω、o、ω等数学符号
  15. Ynoi(5/35)
  16. (Linux目录操作命令)零基础小白学习_入门到精通03 程序员阿沐
  17. 三国志战略版:登庸令队伍_貂蝉与贾诩的武锋组合
  18. 北京城建:建筑业龙头的数字化修炼之路
  19. 漏斗模型-数据分析师的必备神器
  20. Kali linux 学习笔记(七十五)拒绝服务——teardrop 2020.4.15

热门文章

  1. Microsoft 提供的 USB 驱动程序
  2. 中国农业大学研究生计算机学院宿舍,中国农业大学宿舍条件怎么样
  3. DL | DeepDream过程和原理概要
  4. Elasticsearch7.x学习
  5. win7怎么把计算机图标下的箭头掉,告诉你win7如何去除快捷方式小箭头
  6. linux libssl.so.6,centos6 安装directadmin出现libssl.so.6找不到问题
  7. goodFeaturesToTrack——Shi-Tomasi角点检测
  8. php页面背景url不显示图片,background-image:url(XXXX.gif)为何不显示背景图片_html/css_WEB-ITnose...
  9. 什么是云计算,什么是网格计算,他们之间有什么区别
  10. html当前页面的脚本发生错误,如何解决“当前页面脚本发生错误”的问题