RocketMQ 是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时、高可靠的消息发布与订阅服务。

这篇文章,笔者整理了 RocketMQ 源码中创建线程的几点技巧,希望大家读完之后,能够有所收获。

1 创建单线程

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

  • 实现 Runnable 接口
  • 继承 Thread 类

▍一、实现 Runnable 接口

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

▍二、 继承 Thread 类

线程实现类直接继承 Thread ,本质上也是实现 Runnable 接口的 run 方法。

2 单线程抽象类

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

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

  1. 定义线程名;
  2. 启动线程;
  3. 关闭线程。

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

实现类的编程模版类似 :

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

3 线程池原理

线程池是一种基于池化思想管理线程的工具,线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。

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

参数名 作用
corePoolSize 队列没满时,线程最大并发数
maximumPoolSizes 队列满后线程能够达到的最大并发数
keepAliveTime 空闲线程过多久被回收的时间限制
unit keepAliveTime 的时间单位
workQueue 阻塞的队列类型
threadPoolFactory 改变线程的名称、线程组、优先级、守护进程状态
RejectedExecutionHandler 超出 maximumPoolSizes + workQueue 时,任务会交给RejectedExecutionHandler来处理

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

  1. 如果 workerCount < corePoolSize,创建并启动一个线程来执行新提交的任务。
  2. 如果 workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  3. 如果 workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  4. 如果 workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

4 线程池封装

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

当服务端 Broker 接收到发送消息命令时,都会有单独的线程池 sendMessageExecutor 来处理这种命令请求。

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

  1. 核心线程数和最大线程数相同 ,数量是:cpu核数和4比较后的最小值;
  2. 空闲线程的回收的时间限制,默认1分钟;
  3. 发送消息队列,有界队列,默认10000;
  4. 线程工厂 ThreadFactoryImpl ,定义了线程名前缀:SendMessageThread_ 。

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

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

5 线程名很重要

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

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

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

▍一、日志文件

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

  • 查看 ERROR 日志,追溯到执行线程, 要是线程池隔离做的好,基本可以判断出哪种业务场景出了问题;
  • 通过查看线程打印的日志,推断线程调度是否正常,比如有的定时任务线程打印了开始,没有打印结束,推论当前线程可能已经挂掉或者阻塞。

▍二、堆栈记录

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

jstack -l 进程pid

笔者查看线程堆栈,一般关注如下几点:

  1. 当前 jvm 进程中的线程数量和线程分类是否在预期的范围内;
  2. 系统接口超时或者定时任务停止的异常场景下 ,分析堆栈中是否有锁未释放,或者线程一直等待网络通讯响应;
  3. 分析 jvm 进程中哪个线程占用的 CPU 最高。

6 总结

本文是RocketMQ 系列文章的开篇,和朋友们简单聊聊 RocketMQ 源码里创建线程的技巧。

  1. 单线程抽象类 ServiceThread

    使用者只需要实现业务逻辑以及定义线程名即可 ,不需要写冗余的代码。

  2. 线程池封装

    适当封装,定义线程工厂,并合理配置线程池参数。

  3. 线程名很重要

    文件日志,堆栈记录配合线程名能大大提升解决问题的效率。

RocketMQ 的多线程编程技巧很多,比如线程通讯,并发控制,线程模型等等,后续的文章会一一为大家展现。


如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

读完 RocketMQ 源码,我学会了如何优雅的创建线程的更多相关文章

  1. JDK源码分析之concurrent包(二) -- 线程池ThreadPoolExecutor

    上一篇我们简单描述了Executor框架的结构,本篇正式开始并发包中部分源码的解读. 我们知道,目前主流的商用虚拟机在线程的实现上可能会有所差别.但不管如何实现,在开启和关闭线程时一定会耗费很多CPU ...

  2. RocketMQ源码详解 | Broker篇 · 其三:CommitLog、索引、消费队列

    概述 上一章中,已经介绍了 Broker 的文件系统的各个层次与部分细节,本章将继续了解在逻辑存储层的三个文件 CommitLog.IndexFile.ConsumerQueue 的一些细节.文章最后 ...

  3. ROCKETMQ源码分析笔记1:tools

    rocketmq源码解析笔记 大家好,先安利一下自己,本人男,35岁,已婚.目前就职于小资生活(北京),职位是开发总监. 姓名DaneBrown 好了.我保证本文绝不会太监!转载时请附上以上安利信息. ...

  4. RocketMQ源码 — 六、 RocketMQ高可用(1)

    高可用究竟指的是什么?请参考:关于高可用的系统 RocketMQ做了以下的事情来保证系统的高可用 多master部署,防止单点故障 消息冗余(主从结构),防止消息丢失 故障恢复(本篇暂不讨论) 那么问 ...

  5. RocketMQ 源码学习笔记————Producer 是怎么将消息发送至 Broker 的?

    目录 RocketMQ 源码学习笔记----Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest ...

  6. RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?

    目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest Roc ...

  7. RocketMQ 源码分析 —— Message 发送与接收

    1.概述 Producer 发送消息.主要是同步发送消息源码,涉及到 异步/Oneway发送消息,事务消息会跳过. Broker 接收消息.(存储消息在<RocketMQ 源码分析 —— Mes ...

  8. RocketMQ源码分析之从官方示例窥探:RocketMQ事务消息实现基本思想

    摘要: RocketMQ源码分析之从官方示例窥探RocketMQ事务消息实现基本思想. 在阅读本文前,若您对RocketMQ技术感兴趣,请加入RocketMQ技术交流群 RocketMQ4.3.0版本 ...

  9. 记一次RocketMQ源码导入IDEA过程

    首先,下载源码,可以官网下载source包,也可以从GitHub上直接拉下来导入IDEA.如果是官网下载的source zip包,直接作为当前project的module导入,这里不赘述太多,只强调一 ...

随机推荐

  1. 聊聊Adapter模式

    今天我们聊一个最简单的设计模式,适配器Adapter.跟以往一样,我们还是从一个例子出发. 一个例子 最开始的结构 假设我们有个数据分析软件,其中包含了数据收集器和数据分析器,数据收集器基于XML格式 ...

  2. RPA 抖音机器人汇总

    一.RPA 抖音小店催好评机器人 RPA机器人详情: 1.配置好抖音小店订单号与催好评话术 2.启动抖音小店催好评机器人 3.机器人打开飞鸽客服软件 4.自动搜索订单号,自动发送催好评内容 5.机器人 ...

  3. 用Python制作爆款视频,没想到吧?

    前几天小编在抖音上刷到一个慢慢变老的视频,播放量居然有 30W+,当时就在想这视频 Python 可不可以做?经过一番搜索,小编找到了腾讯云的人脸年龄变化 API,上面介绍说只要用户上传一张人脸图片, ...

  4. Cf #782 (Div. 2)

    A. Red Versus Blue 题意 共有 n 个连续字符 ,其中有 a 个  R ,b 个 B (a+b=n),问怎么排列使 R 的最大连续个数最小,输出一种可能排列 思路 b 个B可以把a个 ...

  5. 【ASP.NET Core】自定义的配置源

    本文的主题是简单说说如何实现 IConfigurationSource.IConfigurationProvider 接口来自定义一个配置信息的来源,后面老周给的示例是实现用 CSV 文件进行应用配置 ...

  6. Android刷第三方Recovery &获取root权限

    一.基础环境 Make sure your computer has working adb and fastboot. Setup instructions can be found here. E ...

  7. Java + Selenium + OpenCV解决自动化测试中的滑块验证

    最近工作过程中,一个常用的被测网站突然增加了滑块验证环节,导致整个自动化项目失效了. 为了解决这个滑块验证问题,在网上查阅了一些资料后,总结并实现了解决方案,现记录如下. 1.滑块验证思路 被测对象的 ...

  8. Solution -「线段树」题目集合

    T1 无聊的数列 来自:Link flag 帖先从水题入手. 首先分析题目,它是以等差数列为原型进行的修改.等差数列一大性质就是其差分数列的值除第一项以外均相等. 于是不难想到使用差分数列进行维护. ...

  9. DTS搭载全新自研内核,突破两地三中心架构的关键技术|腾讯云数据库

    随着企业规模的扩大,对数据库可用性要求越来越高,更多企业采用两地三中心.异地多活的架构,以提高数据库的异常事件应对能力. 在数据库领域,我们常听的"两地三中心"."异地多 ...

  10. Java学习(一)MarkDown语法

    Java学习(一)MarkDown语法 一.标题语法 一级标题 一级标题前添加一个#号 二级标题 二级标题前添加两个#号 三级标题 三级标题前添加三个#号 ... 二.字体 1.粗体 hello wo ...