读 RocketMQ 源码学会如何优雅的创建线程

    1. 创建单线程
    2. 单线程抽象类
    3. 线程池原理
    4. 线程池封装
    5. 线程名很重要
    6. 总结
  • RocketMQ 是一款由阿里巴巴集团开源的分布式消息系统。它基于高可用分布式集群技术,提供低延时、高可靠的消息发布与订阅服务。RocketMQ 的主要特点之一是其高可靠性和强大的容错能力,在分布式架构中表现出色。它还具有高度的可扩展性,可以与各种数据存储技术集成,并且可以适应各种复杂的应用程序场景。

    在本文中,笔者将以 RocketMQ 源码中创建线程的几点技巧为例,深入探讨 RocketMQ 的架构和技术原理。我们将学习如何使用 RocketMQ 创建线程,以及如何在系统的不同部分之间实现高效的线程通信。此外,我们还将介绍一些常见的线程问题,并提供一些解决方案,希望读者能够从中受益。

    1.创建单线程

    首先我们先温习下常用的创建单线程的两种方式:

  • 实现 Runnable 接口

  • 继承 Thread 类

  • 一、实现 Runnable 接口  

图中,MyRunnable 类实现了 Runnable 接口的 run 方法,run 方法中定义具体的任务代码或处理逻辑,而Runnable 对象是作为线程构造函数的参数。

2.线程池原理

创建单线程的两种方式都很简单,但每次创建线程代码显得有点冗余,于是 RocketMQ 里实现了一个抽象类 ServiceThread 。

抽象类 ServiceThread

我们可以看到抽象类中包含了如下核心方法:

  1. 定义线程名;

  2. 启动线程;

  3. 关闭线程。

下图展示了 RocketMQ 众多的单线程实现类。

 实现类的编程模版类似 :

我们仅仅需要继承抽象类,并实现 getServiceName 和 run 方法即可。启动的时候,调用 start 方法 , 关闭的时候调用 shutdown 方法。

3.线程池原理

线程池是一种基于池化思想管理线程的工具,线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。关注公z号:码猿技术专栏,回复关键词:1111 获取阿里内部性能调优手册

JDK中提供的 ThreadPoolExecutor 类,是我们最常使用的线程池类。

ThreadPoolExecutor构造函数 

任务的调度通过执行 execute方法完成,方法的核心流程如下:

  1. 如果 workerCount < corePoolSize,创建并启动一个线程来执行新提交的任务。

  2. 如果 workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。

  3. 如果 workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。

  4. 如果 workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

    4. 线程池封装

    在 RocketMQ 里 ,网络请求都会携带命令编码,每种命令映射对应的处理器,而处理器又会注册对应的线程池。

 

当服务端 Broker 接收到发送消息命令时,都会有单独的线程池 sendMessageExecutor 来处理这种命令请求。这个线程池负责将消息从客户端发送到目标主题,确保消息的可靠传输。此外,还可以通过配置线程池的大小、队列容量和拒绝策略来优化 Broker 的性能。例如,将线程池的大小调整为适当的大小可以减少线程上下文切换的开销,提高消息的处理效率;增加队列容量可以缓解突发流量的压力,降低消息丢失的风险;使用适当的拒绝策略可以避免系统超载而导致的雪崩效应。因此,在设计和部署分布式消息系统时,合理配置线程池是非常重要的。

基于 ThreadPoolExecutor 做了一个简单的封装 ,BrokerFixedThreadPoolExecutor 构造函数包含六个核心参数:

  1. 核心线程数和最大线程数相同 ,数量是:cpu核数和4比较后的最小值;

  2. 空闲线程的回收的时间限制,默认1分钟;

  3. 发送消息队列,有界队列,默认10000;

  4. 线程工厂 ThreadFactoryImpl ,定义了线程名前缀:SendMessageThread_ 。

RocketMQ 实现了一个简单的线程工厂:ThreadFactoryImpl ,线程工厂可以定义线程名称,以及是否是守护线程 。

线程工厂

开源项目 Cobar ,Xmemcached,Metamorphosis 中都有类似线程工厂的实现 。 

5. 线程名很重要

线程名很重要,线程名很重要,线程名很重要 ,重要的事情说三遍。

我们看到 RocketMQ 中,无论是单线程抽象类还是多线程的封装都会配置线程名 ,因为通过线程名,非常容易定位问题,从而大大提升解决问题的效率。

定位的媒介常见有两种:日志文件 和堆栈记录 。

▍一、日志文件

经常处理业务问题的同学,一定都经常与日志打交道。

  • 查看 ERROR 日志,追溯到执行线程, 要是线程池隔离做的好,基本可以判断出哪种业务场景出了问题;

  • 通过查看线程打印的日志,推断线程调度是否正常,比如有的定时任务线程打印了开始,没有打印结束,推论当前线程可能已经挂掉或者阻塞。

    二、堆栈记录

    jstack 是 java 虚拟机自带的一种堆栈跟踪工具 ,主要用来查看 Java 线程的调用堆栈,线程快照包含当前 java 虚拟机内每一条线程正在执行的方法堆栈的集合,可以用来分析线程问题。

 

笔者通常会查看线程堆栈,以确保系统的正常运行。以下是我通常会关注的几点:

检查当前 jvm 进程中的线程数量和线程分类是否在预期的范围内,以便及时识别并解决潜在问题;

在系统接口超时或者定时任务停止的异常场景下,我会分析堆栈中是否有锁未释放,或者线程一直等待网络通讯响应等异常情况,以便及时排查和修复问题;

我会分析 jvm 进程中哪个线程占用的 CPU 最高,并采取相应措施来优化线程性能,以提高系统的整体运行效率。

6. 总结

本文介绍了 RocketMQ 中创建线程的技巧,但是还有其他方面值得探究。以下是一些可以进一步研究的内容:

多线程编程技巧

除了本文提到的线程创建技巧之外,RocketMQ 中还有很多其他的多线程编程技巧,比如线程通讯、并发控制和线程模型等等。这些技巧可以帮助开发人员更好地掌握 RocketMQ 的多线程编程。

RocketMQ 的应用场景

RocketMQ 作为一款优秀的消息队列系统,在很多应用场景中得到了广泛的应用。开发人员可以深入了解 RocketMQ 的应用场景,从而更好地应用 RocketMQ。

RocketMQ 的性能优化

RocketMQ 的性能优化一直是开发人员关注的重点。开发人员可以深入了解 RocketMQ 的性能优化技巧,从而优化 RocketMQ 的性能。

RocketMQ 的未来发展

RocketMQ 作为一款优秀的消息队列系统,未来有着广阔的发展前景。开发人员可以关注 RocketMQ 的未来发展,从而更好地应对未来的需求。

虽然 RocketMQ 中创建线程的技巧可以帮助开发人员更好地掌握 RocketMQ 的多线程编程,但是 RocketMQ 还有很多其他方面值得开发人员深入了解和研究。希望本文能够为大家提供一些思路和启示,帮助大家更好地应用 RocketMQ!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永钊源码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值