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

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

8371 次点击
所在节点    程序员
59 条回复
mango111
2023-02-17 12:47:00 +08:00
大厂也没有几个项目在立项时就考虑开源,肯定都是基于业务需求孵化的,只有具备一定完成度了且有空+老板支持才会做开源,其实像耦合 cat ,耦合大象这些做下解耦肯定都不是阻塞的问题,无非就是项目组工作太忙没空搞开源 /项目核心换人了没人牵头了 /老板不挺觉得没产出这些原因
mango111
2023-02-17 12:48:01 +08:00
哦,2020 年出的啊,好像那段时间美团出了个开源项目撕逼的事儿,后来美团基本都不做开源了,嫌麻烦
lmshl
2023-02-17 13:35:43 +08:00
时代变了,大人


icyalala
2023-02-17 13:38:19 +08:00
@mango111 是说 EasyReact 那个事吧
q1angch0u
2023-02-17 13:59:46 +08:00
@Livid 推广
cloudzhou
2023-02-17 14:22:28 +08:00
@lmshl
这个倒是说得太早,尤其是 Java 世界,要原生支持协程且有一段时间。
即便支持协程,对于协程数量的控制还是要需要的,比如有时候消费延后的 kafka ,积累了几十万数据,任何语言启动几十万协程,都不是一个小代价,会系统抖动,影响其他逻辑。

只是技术上我一直倾向一个观点,不要依赖人,依赖算法,而这个 lib 只是提供了一个依赖人去修改的功能,就很鸡肋。
fkdog
2023-02-17 14:34:33 +08:00
全局管控
动态变更
通知报警
数据采集
运行监控
功能扩展
多种模式
容器管理
框架适配
变更审核
动态化插件
多版本适配
================
说是动态线程池,动态修改参数的核心部分就那么几个类。
剩下的都是这些监控、配置、通知一类通用化的解决方案,可以整合到任意一个开源项目里。
隔一段时间就要看到这个 Hippo4j 出来做推广。
可以说是毫无新意。
国产开源项目就没几个能看到有新意的东西,都是些车轱辘倒来倒去。
我下次也可以搞一个动态 Hashmap ,扩容、树化的时候给钉钉微信推送一下消息,然后接进普罗米修斯监控。一个大而全的 HashMap 就出来了,我准备给这个项目起名为 HappyMap 。喜欢大家来 fork 。
ztxcccc
2023-02-17 14:38:32 +08:00
可是美团真的好卡啊
TWorldIsNButThis
2023-02-17 14:49:35 +08:00
@cloudzhou 这还是代码具体怎么写的问题吧,你向线程池提交几十万个 task 不也一样有问题
Tenlearn
2023-02-17 14:55:18 +08:00
@ztxcccc 你手机卡
cloudzhou
2023-02-17 15:22:50 +08:00
@TWorldIsNButThis 是啊,我只是针对上面一个发言,就是协程不是万能的,并发控制一样需要,甚至因为协程更加容易滥用
dqzcwxb
2023-02-17 15:35:19 +08:00
@cloudzhou #26 别说几十万,哪怕是几十亿的 virtual thread 实际使用的线程也只有 cpu 核心数的数量(代码在 jdk19 java.lang.VirtualThread#createDefaultScheduler)
硬要说影响性能那就是这几十亿的对象把内存撑炸了,但是这跟 virtual thread 有什么关系呢?
PythonYXY
2023-02-17 15:35:46 +08:00
@ztxcccc 那也是客户端问题,后端不背这个锅
cloudzhou
2023-02-17 16:26:32 +08:00
@dqzcwxb 你说的都是 *理论上*,好像我们现实中没遇到这场景一样,还是 Go 的服务,够原生协程了吧
现实就是:几十万的协程,就把服务快击垮了,cpu 99% 以上,响应不过来,总有其他短板阿

你要是写一个 hello world ,确实可以不用考虑
lesismal
2023-02-17 16:52:21 +08:00
@cloudzhou
或许我的库能帮助改善 go 的一些状况:github.com/lesismal/nbio
cubecube
2023-02-17 17:16:16 +08:00
@cloudzhou 你说的问题,根本不是线程池&&vt 层面能解决的了。单机计算能力绝对不足,用啥都没用。是架构的问题了。有虚拟线程,的确不需要啥动态线程池了,毫无必要。虚拟线程的资源消耗就和一个 Object 一个数量级,有啥可考虑的。
house600
2023-02-17 17:21:30 +08:00
@cloudzhou 大佬,比起这个项目,我更想知道你是怎么实现的,能分享下吗,比如资源隔离这块,怎么做的
cloudzhou
2023-02-17 17:39:10 +08:00
@lesismal 我知道,你实现了一个 nio ,替代标准库,不过我的观点不在此,我的意思是,真实的业务,在这个网络层面之前,其他已经挂了,还没到这里,问题终究就是需要对资源进行限制

@cubecube 是,我赞成协程不需要池化和复用,只需要控制顶层资源


@house600 这里是这样做的:
executor.execute("processNamespace", () -> handle***(xxx), Opt.withMaxConcurrent(100));
的时候,把 runner 包装一下,执行方法前 processNamespace +1 ,之后 processNamespace -1 ,
通过一个阻塞的变量控制: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html
计数器控制:
https://stackoverflow.com/questions/69760046/java-conditions-with-locks

比如你设置了最多 10 线程,那么当前 10 线程可以,再 +1 = 11 的时候,就会触发等待,需要计数器 -1 才可以继续
cloudzhou
2023-02-17 17:47:59 +08:00
@house600 细节上,做了多个线程池的梯度下降处理,举个例子,定义线程池 poo1 -> pool2 -> pool3 ,最大线程数量从高往低,每个 namespace 定义的最大 pool 占用百分比,分别是 5%,20%,30%,类似这样,一个 namespace 最大占用线程数:
max = poo1*5% + pool2*20% + pool3*30%

除非同时有 20 个 namespace 都占用了那么 5%(那你系统一定有大问题),否则资源最大化同时,又有隔离限制,达到一个平衡
cloudzhou
2023-02-17 17:52:20 +08:00

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

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

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

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

© 2021 V2EX