美团:某动态线程池框架是官方开源的么?

2023-02-17 08:00:36 +08:00
 machen

大家好,我是马称。

最近,有很多同学在微信上问我这么一个问题:

Hippo4j 动态线程池框架是美团开源的么

类似于这样的问题还挺多,在这里统一回复下:

美团官方并没有开源任何关于动态线程池的框架

美团官方关于对动态线程池框架的唯一产出,来自于大家基本上看过或者有印象的一篇博客。

Java 线程池实现原理及其在美团业务中的实践

如果不了解动态线程池概念的同学可以深入了解下。文章深入浅出,讲的很透彻,同时也是美团罪受欢迎的文章之一。

在此之后,美团官方并没有基于动态线程池这个 IDEA 做任何的产出。不过,开源社区基于美团这篇文章做了很多开源框架,比如笔者开源的 Hippo4j ,以及另外一位开源作者 DynamicTP 框架等。

说完 Hippo4j 是否美团动态线程池开源后,接下来和大家聊两件和平常工作有关并且有意思的事。

美团动态线程池框架为什么没有开源

根据我的想法,如果当初美团推出动态线程池概念后,顺势推出一款开源框架,肯定会“爆火”。

毕竟,对于工作这么多年的开发来说,谁的线上环境还没有被线程池“坑”过呢。

但是,实际却没有按照这种设想发展,我就找了在美团工作的朋友聊了聊,下面根据我的了解说下是怎么回事。

1. 依赖办公软件大象

动态参数通知和线程池运行中报警,都需要通过办公通信软件或者邮件进行通知。

通过上面提到的美团动态线程池文章可知,在线程池变更通知和过载告警功能上,依赖了美团办公通信软件大象。

如果要开源,如何进行改造呢?

基于通知报警方案,其实很好解决,抽象出去一个通知接口以及核心参数,并提供 SPI 加载方式,基本上就能完成开源兼容适配。

2. 依赖监控工具 Cat

美团线程池支持查看内部任务级别的执行情况,进行细粒度任务级别监控。

核心原理是通过 Cat Transaction 打点进行的支持,下图表就是从 Cat 上汇总进行展示。

Cat 这种依赖,不太好替换,因为会对原有业务代码进行侵入。如果说开源方案的话,可能就需要牺牲一部分功能性或者针对动态线程池框架底层实现这一功能。

3. 依赖消息队列 Kafka

通过美团文章中看到线程池框架使用了 Kafka 消息队列,这里暂且当一个存疑点,动态线程池中哪部分业务需要使用 Kafka 呢?

如果说使用动态线程池功能,还需要依赖消息队列,这可能对于大部分场景来说是说不通用的。

如果要进行开源,我的建议和想法是将这里设置为可替换项。也就是说默认不支持 MQ 功能,同时对市场上主流 MQ 进行适配。如果客户端项目想用的话,可根据项目实际选择。

比较常见的是 Seata 和 SkyWalking 的做法,以 SkyWalking 举例,链路数据存储支持 H2 、MySQL 、ElasticSearch 等数据库,让用户根据场景以及服务体量灵活选择。

4. 动态线程池是监控体系中的“小”模块

之前有和美团的一位技术朋友沟通过,为什么美团的动态线程池框架没有开源出来?

他给我的回复是,动态线程池框架只是美团监控体系下一个“小”模块。

而且,根据不可靠消息,似乎内部该框架的实现不止一个,如果有美团的哥们看到可以评论下。

5. 小结

经过上面的分析,在这里我得出一个结论:美团在最初设计开发动态线程池时,似乎就没有打算对外开源。因此才会依赖如此多的组件以及美团内部的产品

上文中所有想法都是笔者主观想法,实际情况有待考究。

如何识别框架是否官方开源

1. 开源仓库

国内公司中开源框架比较多的,基本上都会在 GitHub 命名空间下运维项目。

这里列举一些大厂开源公司对应的官方 GitHub 地址。

  1. 阿里巴巴: https://github.com/alibaba
  2. 腾讯: https://github.com/tencent
  3. 美团: https://github.com/meituan
  4. ......

2. 依赖包地址

在我们导入依赖包的时候,会输入 groupId 、artifactId 、version 三种信息,是否官方开源在 groupId 上基本就能体现出来。

groupId:一般由三部分组成,标识.公司名.项目名,拿 Apache 、Alibaba 两个组织的 groupId 举例子说明。

<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
  <version>${shardingsphere-jdbc-core-spring-boot-starter.version}</version>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  	<version>${spring-cloud-starter-alibaba-nacos-discovery.version}</version>
</dependency>

所以,如果说项目为官方开源,那么通过 groupId 很容易就能辨别出来。

3. 非官方地址就不是官方开源的么

非官方地址就不是官方开源的么,也不一定,并不是所有项目都在公司命名空间下发展。

有些项目是独立于公司创建的命名空间,比如说阿里的 seata 、ant-design 等。

再比如美团一篇比较火的文章,讲的是操作日志如何记录,相信大多数同学也都有看到。

如何优雅地记录操作日志?

GitHub: https://github.com/mouzt/mzt-biz-log

同样不是官方开源,但是开源项目是由文章本人进行维护,代码质量和项目活跃度不输官方维护项目。

什么是 Hippo4j

上面说了很多关于开源的小知识,接下来向大家介绍下笔者开源的动态线程池框架 Hippo4j 。

原理:通过对 JDK 线程池的增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。

Hippo4j 提供了两种模式,一种是 依赖配置中心,另一种是 无中间件依赖,部署个 Jar 包就能带来 Web 端控制台使用。

GitHub: https://github.com/opengoofy/hippo4j

Gitee: https://gitee.com/magestack/hippo4j

1. 线程池痛点

如果有在项目中实际使用线程池,相信你可能会遇到以下痛点:

  • 线程池随便定义,线程资源过多,造成服务器高负载。
  • 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。
  • 线程池任务执行时间超过平均执行周期,开发人员无法感知。
  • 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。
  • 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。
  • 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG 。
  • 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。
  • 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。

2. 功能支持

基于以上痛点,Hippo4j 提供以下线程池功能扩展支持:

  • 全局管控 - 管理应用线程池实例。
  • 动态变更 - 应用运行时动态变更线程池参数,包括但不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。
  • 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。
  • 数据采集 - 支持多种方式采集线程池数据,包括但不限于:日志、内置采集、Prometheus 、InfluxDB 、ElasticSearch 等。
  • 运行监控 - 实时查看线程池运行时数据,自定义时间内线程池运行数据图表展示。
  • 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。
  • 多种模式 - 内置两种使用模式:依赖配置中心无中间件依赖
  • 容器管理 - Tomcat 、Jetty 、Undertow 容器线程池运行时查看和线程数变更。
  • 框架适配 - Dubbo 、Hystrix 、RabbitMQ 、RocketMQ 等消费线程池运行时数据查看和线程数变更。
  • 变更审核 - 提供多种用户角色,普通用户变更线程池参数需要 Admin 用户审核方可生效。
  • 动态化插件 - 内置多种线程池插件,支持用户自定义插件以及运行时扩展。
  • 多版本适配 - 经过实际测试,已支持客户端 SpringBoot 1.5.x => 2.7.5 版本(更高版本未测试)。

3. 小结

截止目前,共计 30+ 公司 线上使用 Hippo4j 管理应用线程池,使用公司中包括支付、电商、快递、保险以及教育等行业。

同时,共有 86 名 开源同学对 Hippo4j 进行了代码贡献,有 10 名 小伙伴持续投入较多精力维护,晋升为 Hippo4j Committer ,得到官方支持 Jetbrains 全家桶 Licenses 。

最后总结

关于动态线程池的热度一直居高不下,本篇文章讲述了美团动态线程池的上下文,以及对为什么没有开源进行了简单分析。

最终得出的结论是:美团最初设计动态线程池时就没有打算开源,所以才会依赖美团相关中间件以及 Kafka 等“重量级”组件

同时针对有些同学说无法分辨框架是否官方开源,笔者针对这个话题做了几项总结输出。

最后,介绍了下 GitHub 开源领域中比较火的项目 Hippo4j ,如果各位同学觉得不错可以持续关注。

GitHub: https://github.com/opengoofy/hippo4j

Gitee: https://gitee.com/magestack/hippo4j

8341 次点击
所在节点    程序员
59 条回复
koujyungenn
2023-02-17 08:22:01 +08:00
协程被广泛使用后,动态线程池是否会失去优势
fackVL
2023-02-17 08:27:20 +08:00
别的不说,文字写得真好
nomagick
2023-02-17 08:29:52 +08:00
我觉得你这个叫任务队列更合适,叫线程池属于碰瓷
machen
2023-02-17 08:45:36 +08:00
@koujyungenn 可能会吧,但这个需要时间来适应
machen
2023-02-17 08:45:51 +08:00
@fackVL thanks
chendy
2023-02-17 09:08:53 +08:00
是不是应该去到推广节点…
julyclyde
2023-02-17 09:13:26 +08:00
所以我有几个问题:
1 马称是谁?
2 你和美团的关系是怎样的?
3 有合适的身份来代表美团宣布“并没有……框架”这件事吗?
4 最后介绍自己的作品才是核心吧,何必搞美团来当引文呢,直接发到 /promotion 节点不就得了?
vishun
2023-02-17 09:14:03 +08:00
印象中以为是美团开源的,然后又在本站搜了下,发现不单单是你这个产品,还有另一个相同功能的产品`DynamicTp`,它在推广时标题是:[美团动态线程池开源框架 DynamicTp]( https://www.v2ex.com/t/903527),可能是受到这个影响的。
machen
2023-02-17 09:18:30 +08:00
@julyclyde 关于发布到 promotion 节点的事情,后续我会注意。另外几个问题,全当你是个大聪明
machen
2023-02-17 09:19:16 +08:00
@chendy 还没玩的太明白,后续会注意的,应该是 promotion 节点吧
liub34177
2023-02-17 09:23:02 +08:00
快成日经贴了,能不呢来点有创意的开源项目
dqzcwxb
2023-02-17 09:24:51 +08:00
@koujyungenn #1 virtual thread 底层是 forkjoinpool,而 virtual thread 官方是不推荐池化的因为没有必要
rapperx2
2023-02-17 09:29:21 +08:00
产品不错,但是没必要搞的花里胡哨的用美团来引文,会让人反感(都把我们当傻子呗,全都不知道你是来推广的)。
Xusually
2023-02-17 09:35:28 +08:00
vvickey
2023-02-17 10:15:42 +08:00
和谐和谐
ufan0
2023-02-17 10:22:19 +08:00
曾经在站内看过一些文章,自己在项目中尝试用过,确实不错,因为我们此前有这些技术实现,替换后效果良好。
也看了一些实现,部分思想、设计以及细节确实令我感到惊讶,努力学习中。

另外,楼主站内资料的 github 地址应该更新了。
cloudzhou
2023-02-17 10:22:21 +08:00
你这个需求,是为了动态修改线程池状态,但是这个反而不是我们更需要的,
我做了类似的一个线程池框架,最终实现是:

---------------------------------------------------

# Executor 基于命名空间的公用线程池

## 背景

- 合理使用 Java 线程池并没有想象那么容易,容易滥用
- 能否合理做到 try{}catch{},log ,reject(CallerRunsPolicy/AbortPolicy/DiscardPolicy)
- 在尽量使用资源和防止滥用取得一个平衡

## 设计

- 全局共用线程池,便于管理全部线程,唯一是要注意资源隔离
- 提供梯度降级线程池处理,最大并发控制
- wait group ( fork -> join 模型)
- 监控上报(以 namespace 作为划分)
- 优先级
- 保持和 java.util.concurrent.Executor 一致接口

## 使用场景

### 1. 需要限制最大并发线程数量

比如:kafka 消费,要防止积累的数据瞬间占用大量线程,必要时异步转同步,阻塞消费(典型场景:***)

```
executor.execute("process***", () -> {
handle***(xxx);
}, Opt.withMaxConcurrent(100));
```

### 2. 定义并发上限之后不同表现

```
executor.execute("lowPriority", new Runnable(){...}, Opt.withRejectPolicy());
// CallerRunsPolicy 转变成为当前线程执行
// AbortPolicy 打印异常并且放弃
// DiscardPolicy 默默丢弃
```

### 3. 低优先级

比如: 可以丢失的异步任务,达到最大线程数之后,将放在低优先级 pool ( 64 线程) 执行,相互竞争

```
executor.execute("lowPriority", new Runnable(){...}, Opt.withLowPriority());
```

### 4. fork -> join 模型

比如: 发出多个异步任务,等待集体完成

```
executor.execute("job1", new Runnable(){...}, Opt.withConcurrentGroup("thread-group"));
executor.execute("job2", new Runnable(){...}, Opt.withConcurrentGroup("thread-group"));
executor.execute("job3", new Runnable(){...}, Opt.withConcurrentGroup("thread-group"));
executor.wait("thread-group")
// job 1-3 done
```

### 5. log / monitor

对于开启的任务,都进行 log 和耗时监控,同时上报 monitor

---------------------------------------------------

搞一个全局线程池(最大可能是 2048/4096 ,...),通过同步原语控制并发数( or 百分比,10%)
来让资源最大化使用,但是同时不至于滥用资源,至于这个控制算法,需要仔细斟酌,这才是核心需要

至于那些资源动态改来改去,最多算是一个 1.0 版本,我们需要的是,让机器帮我们选择,作出决定。
house600
2023-02-17 11:22:26 +08:00
@cloudzhou 虽然没看懂,但赞
house600
2023-02-17 11:23:02 +08:00
像请教下 op ,DynamicTP 和你的产品有哪些区别
byte10
2023-02-17 11:31:55 +08:00
协程会解决这个动态线程池的问题。 动态线程池实际上真的比较鸡肋,本身线程就有最大的线程数,粗略计算下就不会差太多,500 个跟 800 个的性能差距几乎没多少(如果是 500 个跟几千个,可能会有些明显)。如果差距很大,那么本质上该优化下游服务了,或者换一个语言去实现更好。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/916816

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX