万字总结线程池

news2024/11/18 13:55:36

本文将从背景、原理、架构、实现、参数状态等方面详细介绍percona-线程池。此外,还将简单介绍腾讯云企业级MySQL(CDB)内核技术--TXSQL,关于线程池的动态启停、负载均衡以及快速断连等优化。

「第一部分 背景」

社区版的MySQL的连接处理方法默认是为每个连接创建一个工作线程的one-thread-per-connection(Per_thread)模式。这种模式下,由于系统的资源是有限的,随着连接数的增加,资源的竞争也增加,连接的响应时间也随之增加,如response time图所示。

 

对于数据库整体吞吐而言,则是在资源未耗尽时随着连接数增加,一旦连接数超过了某个耗尽系统资源的临界点,数据库整体吞吐就会随着各连接的资源争抢而下降,如下图所示。

 

如何避免在连接数暴增时,因资源竞争而导致系统吞吐下降的问题呢?MariaDB&&Percona中给出了简洁的答案:线程池。线程池的原理在博客中(链接参考文献1)有生动的介绍,其大致可类比为早高峰期间大量汽车想通过一座大桥,如果采用one-thread-per-connection的方式则放任汽车自由行驶,由于桥面宽度有限,最终将导致所有汽车寸步难行。线程池的解决方案是限制同时行驶的汽车数,让桥面时刻保持最大吞吐,尽快让所有汽车抵达对岸。回归到数据库本身,线程池的思路即为限制同时运行的线程数,减少线程池间上下文切换和热锁争用,从而对OLTP工作负载(CPU消耗较少的查询)产生积极影响。当连接数上升时,在线程池的帮助下数据库整体吞吐维持在一个较高水准,如图所示。

 

 

「第二部分 Percona线程池实现」

线程池的基本原理为:预先创建一定数量的工作线程(worker线程)。在线程池监听线程(listener线程)从现有连接中监听到新请求时,从工作线程中分配一个线程来提供服务。工作线程在服务结束之后不销毁线程,而是保留在线程池中继续等待下一个请求来临。下面我们将从线程池架构、新连接的创建与分配、listener线程、worker线程、timer线程等几个方面来介绍percona线程池的实现。

2.1 线程池的架构

线程池由多个线程组(thread group)和timer线程组成,如下图所示。线程组的数量是线程池并发的上限,通常而言线程组的数量需要配置成数据库实例的CPU数量,从而充分利用CPU。线程池中还有一个服务于所有线程组的timer线程,负责周期性检查线程组是否处于阻塞状态。当检测到阻塞的线程组时,timer线程会通过唤醒或创建新的工作线程来让线程组恢复工作。

 

线程组内部由多个worker线程、0或1个listener线程、高低优先级事件队列(由网络事件event构成)、mutex、epollfd、统计信息等组成。如下图所示:

 

2.2 新连接的创建与分配

新连接接入时,线程池按照新连接的线程id取模线程组个数来确定新连接归属的线程组(thd→thread_id() % group_count)。这样的分配逻辑非常简洁,但由于没有充分考虑连接的负载情况,繁忙的连接可能会恰巧被分配到相同的线程组,从而导致负载不均衡的现象,这是percona线程池值得被优化的点。

选定新连接归属的线程组后,新连接申请被作为事件放入低优先级队列中,等待线程组中worker线程将高优先级事件队列处理完后,就会处理低优先级队列中的请求。

2.3 listener线程

listener线程是负责监听连接请求的线程,每个线程组都有一个listener线程。percona线程池的listener采用epoll实现。当epoll监听到请求事件时,listener会根据请求事件的类型来决定将其放入哪个优先级事件队列。将事件放入高优先级队列的条件如下,只需要满足其一即可:

  • 当前线程池的工作模式为高优先级模式,在此模式下只启用高优先级队列。(mode == TP_HIGH_PRIO_MODE_STATEMENTS)
  • 当前线程池的工作模式为高优先级事务模式,在此模式下每个连接的event最多被放入高优先级队列threadpool_high_prio_tickets次。超过threadpool_high_prio_tickets次后,该连接的请求事件只能被放入低优先级。(mode == TP_HIGH_PRIO_MODE_TRANSACTIONS)
  • 连接持有表锁
  • 连接持有mdl锁
  • 连接持有全局读锁
  • 连接持有backup锁

被放入高优先级事件队列的事件可以优先被worker线程处理。只有当高优先级队列为空,并且当前线程组不繁忙的时候才处理低优先级队列中的事件。线程组繁忙(too_many_busy_threads)的判断条件是当前组内活跃工作线程数+组内处于等待状态的线程数大于线程组工作线程额定值(thread_pool_oversubscribe+1)。这样的设计可能带来的问题是在高优先级队列不为空或者线程组繁忙时低优先级队列中的事件迟迟得不到响应,这同样也是percona线程池值得被优化的一个点。listener线程将事件放入高低优先级队列后,如果线程组的活跃worker数量为0,则唤醒或创建新的worker线程来处理事件。

percona的线程池中listener线程和worker线程是可以互相切换的,详细的切换逻辑会在worker线程模块介绍。epoll监听到请求事件时,如果高低优先级事件队列都为空,意味着此时线程组非常空闲,大概率不存在活跃的worker线程。listener在此情况下会将除第一个事件外的所有事件按前述规则放入高低优先级事件队列,然后退出监听任务,亲自处理第一个事件。这样设计的好处在于当线程组非常空闲时,可以避免listener线程将事件放入队列,唤醒或创建worker线程来处理事件的开销,提高工作效率。

 

资料领取直通车:大厂面试题锦集+视频教程

Linux服务器学习网站:C/C++Linux服务器开发/后台架构师

2.4 worker线程

worker线程是线程池中真正干活的线程,正常情况下,每个线程组都会有一个活跃的worker线程。worker在理想状态下,可以高效运转并且快速处理完高低优先级队列中的事件。但是在实际场景中,worker经常会遭遇IO、锁等等待情况而难以高效完成任务,此时任凭worker线程等待将使得在队列中的事件迟迟得不到处理、甚至可能出现长时间没有listener线程监听新请求的情况。为此,每当worker遭遇IO、锁等等待情况,如果此时线程组中没有listener线程或者高低优先级事件队列非空,并且没有过多活跃worker,则会尝试唤醒或者创建一个worker。为了避免短时间内创建大量worker,带来系统吞吐波动,线程池创建worker线程时有一个控制单位时间创建worker线程上限的逻辑,线程组内连接数越多则创建下一个线程需要等待的时间越长。

当线程组活跃worker线程数量大于等于too_many_active_threads+1时,认为线程组的活跃worker数量过多。此时需要对worker数量进行适当收敛,首先判断当前线程组是否有listener线程,如果没有则将当前worker线程转化为listener线程。如果当前有listener线程,则在进入休眠前尝试通过epoll_wait获取一个尚未进入队列的事件,成功获取到后立刻处理该事件,否则进入休眠等待被唤醒,等待threadpool_idle_timeout时间后仍未被唤醒则销毁该worker线程。

worker线程与listener线程的切换如下图所示:

 

2.5 timer线程

timer线程每隔threadpool_stall_limit时间进行一次所有线程组的扫描(check_stall)。当线程组高低优先级队列中存在事件,并且自上次检查至今没有新的事件被worker消费则认为线程组处于停滞状态。停滞的主要原因可能是长时间执行的非阻塞请求, 也可能发生于线程正在等待但 wait_begin/wait_end (尝试唤醒或创建新的worker线程)被上层函数忘记调用的场景。timer线程会通过唤醒或创建新的worker线程来让停滞的线程组恢复工作。timer线程为了尽量减少对正常工作的线程组的影响,在check_stall时采用的是try_lock的方式,如果加不上锁则认为线程组运转良好,不再去打扰。

timer线程除上述工作外,还负责终止空闲时间超过 wait_timeout 秒的客户端。

「第三部分 TXSQL动态线程池优化」

线程池采用一定数量的工作线程来处理用户连接请求,通常比较适应于OLTP工作负载的场景。但线程池并不是万能的,线程池的不足在于当用户请求偏向于慢查询时,工作线程阻塞在高时延操作上,难以快速响应新的用户请求,导致系统吞吐量反而相较于Per_thread模式更低。

正如前文所说,Per_thread模式与Thread_pool模式各有优劣,系统需要根据用户的业务类型灵活切换两种模式。在业务高峰时段切换模式,重启服务器,会严重影响用户业务。为了解决此问题,TXSQL提出了线程池动态切换的优化,即在不重启数据库服务的情况下,动态开启或关闭线程池。

详细介绍及使用方法参考下述视频:

腾讯云CDB-动态线程池功能

3.1 动态线程池的实现介绍

在了解了TXSQL动态线程池的使用方法后,我们再来了解一下其具体的实现。

mysql的thread_handling参数代表了连接管理方法。在过去thread_handling是只读参数,不允许在线修改。thread_handling参数对应的底层实现对象是Connection_handler_manager,后者是是mysql提供连接管理服务的单例类,可对外提供Per_thread、No_threads、Thread_pool、Plugin_connection_handler等多种连接管理服务。由于thread_handling在过去是只读参数,在mysql启动时Connection_handler_manager只需要按照thread_handling初始化一种连接管理方法即可。为了支持动态线程池,允许用户连接从Per_thread和Thread_pool模式中来回切换,我们需要允许多种连接管理方法同时存在。因此,在mysql初始化阶段,我们初始化了所有连接管理方法。

在支持thread_handling在Per_thread和Thread_pool模式中来回切换后,我们需要考虑的问题主要有以下几个:

1) 活跃用户连接的thread_handling切换

Per_thread模式下,每个用户连接对应一个handle_connection线程,handle_connection线程既负责用户网络请求的监听,又负责处理请求的处理。Thread_pool模式下,每个thread_group都用epoll来管理其中所有用户连接的网络事件,监听到的事件放入事件队列中,交予worker处理。不论是哪种模式,在处理请求的过程中(do_command)切换都不是一个好选择,而在完成一次command之后,尚未接到下一次请求之前是一个较合适的切换点。

为实现用户连接从Per_thread到Thread_pool的切换,需要在请求处理完(do_command)之后判断thread_handling是否发生了变化。如需切换则立刻按照2.2中介绍的逻辑,通过thread_id%group_size选定目标thread_group,将当前用户连接迁移至Thread_pool的目标thread_group中,后续该用户连接的所有网络事件统一交予thread_group的epoll监听。在完成连接迁移之后,handle_connection线程即可完成退出或者缓存至下一次Per_thread模式处理新连接时复用(此为原生mysql支持的逻辑,目的是避免Per_thread模式下频繁地创建和销毁handle_connection线程)。

为实现用户连接从Thread_pool到Per_thread的切换,需要在请求处理完(threadpool_process_request)后,将用户线程网络句柄重新挂载到epoll(start_io)之前判断thread_handling是否发生了变化。如需切换则先将网络句柄从epoll中移除以及将连接的信息从对应thread_group中清除。由于Per_thread模式下每个连接对应一个handle_connection线程,还需为当前用户连接创建一个handle_connection线程,后续当前用户连接的网络监听和请求处理都交予该handle_connection线程处理。

2) 新连接的处理

由于thread_handling可能随时动态变化,为了使得新连接能被新thread_handling处理,需要在新连接处理接口Connection_handler_manager::process_new_connection中,读取最新的thread_handling,利用其相应的连接管理方法添加新连接。对于Per_thread模式,需要为新连接创建handle_connection线程;对于Thread_pool模式,则需要为新连接选定thread_group和将其网络句柄绑定到thread_group的epoll中。

3) thread_handling切换的快速生效

从上述1)的讨论中可以看到,处于连接状态的用户线程需要等到一个请求处理结束才会等到合适的切换点。如果该用户连接迟迟不发送网络请求,则连接会阻塞在do_command下的get_command的网络等待中,无法及时切换到Thread_pool。如何快速完成此类线程的切换呢?一种比较激进的方法就是迫使此类连接重连,在重连后作为新连接自然地切换到Thread_pool中,其下一个网络请求也将被Thread_pool应答。

「第四部分 TXSQL线程池负载均衡优化」

如前文2.2所述,新连接按照线程id取模线程组个数来确定新连接归属的线程组(thd→thread_id() % group_count)。这样的分配方式未能将各线程组的实际负载考虑在内,因此可能将繁忙的连接分配到相同的线程组,使得线程池出现负载不均衡的现象。为了避免负载不均衡的发生,TXSQL提出了线程池负载均衡优化。

4.1 负载的度量

在提出负载均衡的算法之前,我们首先需要找到一种度量线程组负载状态的方法,通常我们称之为"信息策略“。下面我们分别讨论几种可能的信息策略。

1) queue_length

queue_length代表线程组中低优先级队列和高优先级队列的长度。此信息策略的最大优势在于简单,直接用在工作队列中尚未处理的event的数量描述当前线程组的工作负载情况。此信息策略的不足,无法将每个网络事件event的处理效率纳入考量。由于每个event的处理效率并不相同,简单地以工作队列长度作为度量标准会带来一些误判。

2) average_wait_usecs_in_queue

average_wait_usecs_in_queue表示最近n个event在队列中的平均等待时间。此信息策略的优势在于能够直观地反映线程组处理event的响应速度。某线程组average_wait_usecs_in_queue明显高于其他线程组说明其工作队列中的event无法及时被处理,需要其他线程组对其提供帮助。

3) group_efficiency

group_efficiency表示一定的时间周期内,线程组处理完的event总数占(工作队列存量event数+新增event数)的比例。此信息策略的优势在于能够直观反映出线程组一定时间周期内的工作效率,不足在于对于运转良好的线程组也可能存在误判:当时间周期选择不合适时,运转良好的线程组可能存在时而group_efficiency小于1,时而大于1的情况。
上述三种信息策略只是举例说明,还有更多信息策略可以被采用,就不再一一罗列。

4.2 负载均衡的实现介绍

在明确了度量线程组负载的方法之后,我们接下来讨论如何均衡负载。我们需要考虑的问题主要如下:

1) 负载均衡算法的触发条件

负载均衡操作会将用户连接从一个线程组迁移至另一个线程组,在非必要情况下触发用户连接的迁移将因反而导致用户连接的性能抖动。为尽可能避免负载均衡算法错误触发,我们需要为触发负载均衡算法设定一个负载阈值M,以及负载比例N。只有线程组的负载阈值大于M,并且其与参与均衡负载的线程组的负载比例大于N时,才需要启动负载均衡算法平衡负载。

2) 负载均衡的参数对象

当线程组触发了负载均衡算法后,该由哪些线程组参与平衡高负载线程组的负载呢?

很容易想到的一个方案是我们维护全局的线程组负载动态序列,让负载最轻的线程组负责分担负载。但是遗憾的是为了维护全局线程组负载动态序列,线程组每处理完一次任务都可能需要更新自身的状态,并在全局锁的保护下更新其在全局负载序列中的位置,如此一来对性能的影响势必较大,因此全局线程组负载动态序列的方案并不理想。

为了避免均衡负载对线程池整体性能的影响,需改全局负载比较为局部负载比较。一种可能的方法为当当前线程组的负载高于阈值M时,只比较其与左右相邻的X个(通常1-2个)线程组的负载差异,当当前线程组的负载与相邻线程组的比例也高于N倍时,从当前线程组向低负载线程组迁移用户连接。需要注意的是当当前线程组的负载与相邻线程组的比例不足N倍时,说明要么当前线程组还不够繁忙、要么其相邻线程组也较为忙碌,此时为了避免线程池整体表现恶化,不适合强行均衡负载。

3) 均衡负载的方法

讨论完负载均衡的触发条件及参与对象之后,接下来我们需要讨论高负载线程组向低负载线程组迁移负载的方法。总体而言,包括两种方法:新连接的优化分配、旧连接的合理转移。

在掌握了线程组的量化负载之后,较容易实现的均衡负载方法是在新连接分配线程组时特意避开高负载线程组,这样一来已经处于高负载状态的线程组便不会因新连接的加入进一步恶化。但仅仅如此还不够,如果高负载线程组的响应已经很迟钝,我们还需要主动将其中的旧连接迁移至合适的低负载线程组,具体迁移时机在3.1中已有述及,为在请求处理完(threadpool_process_request)后,将用户线程网络句柄重新挂载到epoll(start_io)之前,此处便不再展开讨论。

「第五部分 TXSQL线程池断连优化」

如前文2.3所述,线程池采用epoll来处理网络事件。当epoll监听到网络事件时,listener会将网络事件放入事件队列或自己处理,此时相应用户连接不会被epoll监听。percona线程池需要等到请求处理结束之后才会使用epoll重新监听用户连接的新网络事件。percona线程池这样的设计通常不会带来问题,因为用户连接在请求未被处理时,也不会有发送新请求的需求。但特殊情况下,如果用户连接在重新被epoll监听前自行退出了,此时用户连接发出的断连信号无法被epoll捕捉,因此在mysql服务器端无法及时退出该用户连接。这样带来的影响主要有两点:

  1. 用户连接客户端虽已退出,但mysql服务器端却仍在运行该连接,继续消耗CPU、内存资源,甚至可能继续持有锁,只有等到连接超时才能退出;
  2. 由于用户连接在mysql服务器端未及时退出,连接数也并未清理,如果用户业务连接数较多,可能导致用户新连接数触达最大连接数上限,用户无法连接数据库,严重影响业务。

为解决上述问题,TXSQL提出了线程池断连优化。

5.1 断连优化的实现介绍

断连优化的重点在于及时监听用户连接的断连事件并及时处理。为此需要作出的优化如下:

  1. 在epoll接到用户连接的正常网络事件后,立刻监听该用户连接的断连事件;
  2. 所有用户连接退出从同步改为异步,所有退出的连接先放入quit_connection_queue,后统一处理;
  3. 一旦epoll接到断连事件后立刻将用户连接thd→killed设置为THD::KILL_CONNECTION状态,并将连接放入quit_connection_queue中异步退出;
  4. listener每隔固定时间(例如100ms)处理一次quit_connection_queue,让其中的用户连接退出。

「第六部分 线程池相关参数、状态介绍」

以下是对线程池相关参数的介绍:

 

 

下面对TXSQL新增的show threadpool status命令展示的相关状态进行说明:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/29834.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“信任机制”才是数字化时代发展中的精髓所在

2008年,美国次贷危机全面爆发并不断蔓延,最终席卷全球,酿成了世界性的金融危机。当年11月1日,一位署名为中本聪的人发表一篇《比特币:一种点对点的电子现金系统》论文提出了一种完全通过点对点技术实现的电子现金系统。…

Flutter 实现背景 Parallax 动画

Flutter 实现背景 Parallax 动画 原文 https://arkapp.medium.com/background-parallax-animation-in-flutter-4aa9e23d6cfb 前言 我们将创建我们的 Flutter 项目惊人的 Parallax 动画。 在本文中,我们将实现一个简单的实用工具 widget ,它将在任何 widg…

Transformer/Bert

诸神缄默不语-个人CSDN博文目录 文章目录1. Transformer1.1 Transformer整体工作流程1.2 Transformer的输入1.2.1 单词 Embedding1.2.2 位置 Embedding1.3 Block1.3.1 Add & Norm层1.3.2 Self-Attention1.3.3 Multi-Head Attention1.3.4 Feed Forward1.3.5 Masked Multi-He…

[附源码]计算机毕业设计JAVA篮球装备商城系统

[附源码]计算机毕业设计JAVA篮球装备商城系统 项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybati…

HTTP/2是什么?和HTTP/1.1有什么不同?和SPDY有什么不同?

HTTP/2简介 HTTP/2 是超文本传输协议第2版,最初命名为 HTTP 2.0 ,其简称为 h2(基于TLS/1.2或以上版本的加密连接)或 h2c(非加密连接),是HTTP协议的的第二个主要版本,使用于万维网。 …

备考2023年软考需要了解什么?

2022年软考已经结束啦!下半年还是有不少地区取消了,没能报上名或是没能正常参考的朋友们,只能把目光转向2023年了。 这篇文章就来给大家讲讲备考2023年软考需要了解什么? 软考小白必看哦! 一、软考是啥?…

带你走进神奇的元宇宙的世界

🏠个人主页:黑洞晓威 🧑个人简介:大家好,我是晓威,一名普普通通的大二在校生,希望在CSDN中与大家一起成长。🎁如果你也在正在学习Java,欢迎各位大佬来到我的博客查漏补缺…

阿里架构师推荐,微服务分布式构架开发实战PDF,快快收藏吧

什么是微服务架构 微服务是一种软件架构风格,目标是将一个复杂的应用拆分成多个服务模块,每个模块专注单一业务功能对外提供服务,并可以独立编译及部署,同时各模块间互相通信彼此协作,组合为整体对外提供完整服务。 微…

嵌入式开发学习之--创建工程

提示:本篇文章依旧是了解为主,实际做项目时直接找开源的类似项目做模板更效率。 文章目录一、前言二、新建工程三、使用模板工程四、总结一、前言 前面的学习,都是文件内的代码联系,这一篇,是学习文件与目录的联系。当…

数字信号处理-8-自相关

1 皮尔森相关系数 假设 x 和 y 均为 N 个样本的数组,皮尔森公式如下: 皮尔森相关系数总是在 -1 到 1 之间(包含这两个字)。ρ 的绝对值意味着相关性的强度。ρ 接近 1 表示强正相关;ρ 接近 -1 表示强负相关&#xf…

上传文件很费时费力?那是你没用对方式

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、前端直传的优点二、实现步骤2.1、后端方面2.1.1 添加依赖2.1.2 增加接口2.1.3 测试接口2.2、前端方面2.2.1 安装 cos-js-sdk-v5 依赖2.2.2 新建组件2.2.3 使…

你的哪些SQL慢?看看MySQL慢查询日志吧

前言 在项目里面,多多少少都隐藏着一些执行比较慢的SQL, 不同的开发测试人员在平时使用的过程中多多少少都能够遇到,但是无法立马有时间去排查解决。那么如果有一个文件能够将这些使用过程中比较慢的SQL记录下来,定期去分析排查,…

meta视口标签

属性解释说明width宽度设置的是viewport宽度,可以设置device-width特殊值initial-scale初始缩放比,大于0的数字maximum-scale最大缩放比,大于0的数字minimum-scale最小缩放比,大于0的数字user-scalable用户是否可以缩放&#xff0…

关于我们编写好的java程序是如何运行部署的

了解如何去查看项目如何运行怎么部署java项目部署到服务器的程序和在本地运行的程序有什么不同java中的Class文件是如何形成的Class文件如何执行的怎么部署java项目 首先宏观的说一下,程序的运行都是要有一个启动入口的,也就是我们经常说的main函数是程…

【Shell 脚本速成】03、Shell 脚本实战案例(一)数据磁盘初始化

目录 一、案例应用场景 二、案例需求 脚本所需相关知识点 三、案例算法 四、代码实现 五、实现验证 一、案例应用场景 生产环境中的服务器一般会分为系统盘和数据盘两种磁盘,以dell R730举例,该服务器是一个2U的机架式服务器,满载可以挂…

HTML+CSS简单的网页制作期末作业——浙江旅游景点介绍网页制作

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有…

Redisson分布锁原理分析及源码解读

本文源码解读基于Redisson 3.18.0 版本 Redisson分布锁实现原理 Redisson锁实现基本原理大致如下图所示: 客户端执行Lua脚本去获取锁,如果获取失败,则订阅解锁消息,并挂起线程。 客户端解锁时执行一段Lua脚本,删除锁的…

[附源码]计算机毕业设计JAVA课程资源系统管理

[附源码]计算机毕业设计JAVA课程资源系统管理 项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybati…

PowerJob 定时从SFTP下载文件拆的坑

一. 业务需求 SFTP上有多个目录, 每小时要下载一次文件, 每个目录的下载任务都是一个独立的工作流任务. 二.问题描述 手动执行每个任务可以正常执行, 但是当所有任务都开启定定时任务执行时(每小时执行一次),任务实例就会报错. 三.问题分析 查看服务端和worker端的日志, …

数据采集-“消防知识网上答题挑战赛”题库

为普及消防法律法规和消防安全知识,提升全员消防安全意识,提高抗御火灾、自防自救和组织疏散能力,集团公司近日部署11月份集中开展消防宣传月活动。 为“全民消防”营造浓厚氛围,集团公司以消防知识为主要内容,整理形…