Banner就是控制台打印的东西,比如说springboot
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.0.7)
比如说claude code,可惜现在变成这样了

* ▐▛███▜▌ * Claude Code v2.0.76
* ▝▜█████▛▘ * Sonnet 4.5 · API Usage Billing
* ▘▘ ▝▝ * ~\Desktop\
oneThread 的 Banner 组件采用了模块化设计,通过自动配置机制实现无侵入集成:

Text to ASCII Art Generator (TAAG)实现banner实现

- 入口点:CommonAutoConfiguration(通用自动配置类)。这是 Spring Boot 自动装配的机制,负责将 Banner 组件注册到 Spring 容器中。
- 核心类:OneThreadBannerHandler。
- 它实现了 InitializingBean 接口。这意味着当 Bean 的属性设置完成后,Spring 会自动调用 afterPropertiesSet() 方法。Banner 的打印逻辑就写在这个方法里,确保在应用启动早期执行。
- 它依赖 BuildProperties。这是一个 Spring Boot 提供的类,用于读取构建信息(如版本号)。
新建 OneThreadBannerHandler.java 类。
package com.nageoffer.onethread.spring.config;
import cn.hutool.core.util.StrUtil;
import org.springframework.boot.ansi.AnsiColor;
import org.springframework.boot.ansi.AnsiOutput;
import org.springframework.boot.ansi.AnsiStyle;
import org.springframework.boot.info.BuildProperties;
import org.springframework.beans.factory.InitializingBean;
public class OneThreadBannerHandler implements InitializingBean {
private static final String ONE_THREAD_DASHBOARD = "Dashboard"; // 示例
private static final String ONE_THREAD_SITE = "Site"; // 示例
private static final String DYNAMIC_THREAD_POOL = "Dynamic Thread Pool";
private static final int STRAP_LINE_SIZE = 50; // 根据实际Banner宽度调整
private final String version;
public OneThreadBannerHandler(BuildProperties buildProperties) {
this.version = buildProperties != null ? buildProperties.getVersion() : "";
}
@Override
public void afterPropertiesSet() throws Exception {
String banner = """
░██░██ ░██ ░██ ░██
\s
░███████ ░██░██ ░██████ ░██ ░███████ ░███████ ░███████ ░███████ ░███████ ░██ ░██████ ░██
░██ ░██ ░██░██ ░██ ░██░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██
░██ ░██ ░██░██ ░███████ ░██░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░███████ ░██
░██ ░██ ░██░██░██ ░██ ░██░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██░██ ░██ ░██
░███████ ░██░██ ░█████░██ ░██ ░███████ ░███████ ░███████ ░███████ ░███████ ░██ ░█████░██ ░██
\s
\s
\s
"""; // 此处填入文档中的 ASCII 字符
String bannerVersion = StrUtil.isNotEmpty(version) ? " (v" + version + ")" : "no version.";
// 计算 Padding
StringBuilder padding = new StringBuilder();
while (padding.length() < STRAP_LINE_SIZE - (bannerVersion.length() + DYNAMIC_THREAD_POOL.length())) {
padding.append(" ");
}
System.out.println(AnsiOutput.toString(
AnsiColor.GREEN, banner,
AnsiColor.DEFAULT, DYNAMIC_THREAD_POOL,
AnsiColor.DEFAULT, padding.toString(),
AnsiStyle.FAINT, bannerVersion,
"\n" // 根据需要添加换行或链接
));
}
}
pomxml里面加这个
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<!-- 2. 新增:Spring Boot 插件(只用于生成构建信息) -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<goals>
<!-- 核心配置:生成 META-INF/build-info.properties -->
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
基于Nacos开发oneThread控制台模块
在深入了解 DashBoard-Dev 的设计之前,我们先来分析一下市场上动态线程池的两种主流架构模式。
1. 配置中心模式
配置中心模式是一种相对轻量的架构方案:

2. 独立服务模式
独立服务模式提供了更强的功能性,但架构相对复杂:

3. oneThread 混合架构
oneThread 采用了一种创新的混合架构,既保持了配置中心模式的轻量特性,又提供了独立服务模式的可视化能力:

这种混合架构的核心优势在于:
- 数据源统一 :所有配置变更都通过配置中心进行,保证数据一致性。
- 依赖最小化 :业务应用只需依赖配置中心,无需额外中间件。
- 可视化增强 :DashBoard-Dev 提供友好的管理界面。
- 架构灵活 :可以选择性部署 Dashboard,不影响核心功能。
抽象 Nacos API 操作
在 oneThread 框架中,Nacos 代理客户端(NacosProxyClient)扮演着关键角色。它负责与 Nacos 服务端进行交互,实现配置的查询、更新和服务实例的发现。通过封装Nacos的RESTAPI,NacosProxyClient提供了一组面向业务的高层接口 ,使得框架的其他层能够以统一的方式操作 Nacos,而无需关心底层实现细节。
com.nageoffer.onethread.dashboard.dev.server.remote.client.NacosProxyClient

1. 核心方法设计
NacosProxyClient 包含四个核心方法,分别对应不同的 Nacos REST API 操作:
- listConfig方法 :查询命名空间下配置文件集合。
publicList<NacosConfigRespDTO>listConfig(String namespace){// ... 省略实现细节}
- getConfig方法 :查询配置明细信息。
publicNacosConfigDetailRespDTOgetConfig(String namespace,String dataId,String group){// ... 省略实现细节}
- publishConfig方法 :发布配置。
publicvoidpublishConfig(String namespace,String dataId,String group,StringMD5,String id,String content,String type){// ... 省略实现细节}
- service方法 :查询服务明细。
publicNacosServiceListRespDTOservice(String namespace,String service){// ... 省略实现细节}
2. 参数处理与命名空间隔离
NacosProxyClient 在处理参数时,特别注意了命名空间的隔离问题。在 Nacos 中,命名空间是用于区分不同环境(如开发、测试、生产)的隔离单元。通过判断namespace是否为"public",NacosProxyClient能够正确处理不同命名空间下的配置请求 。
// 在listConfig方法中form("tenant",Objects.equals(namespace,"public")?"": namespace)
// 在 service方法中form("namespaceId",Objects.equals(namespace,"public")?"": namespace)
这种设计使得 oneThread 框架能够支持多环境部署,实现配置的环境隔离 ,大大提高了框架的灵活性和安全性。
如果是 public 需要传递空字符串,为了适配 Nacos 的接口参数处理。
3. YAML 配置解析与绑定
NacosProxyClient 在查询配置后,需要将配置内容解析为 Java 对象。oneThread框架使用YAML格式存储线程池配置,通过yamlConfigParser进行解析,再通过Spring的Binder绑定到DashBoardConfigProperties对象 。
// 在 threadPoolManagerService 中Map<Object, Object> configInfoMap = yamlConfigParser.doParse(config.getContent());ConfigurationPropertySource sources =newMapConfigurationPropertySource(configInfoMap);Binder binder =newBinder(sources);DashBoardConfigProperties refresherProperties = binder.bind("onethread",Bindable.of(DashBoardConfigProperties.class)).orElseThrow(()->newIllegalArgumentException("配置绑定失败"));
DashBoard-Dev 服务的功能与实现机制
DashBoard-Dev 服务是 oneThread 框架的管理入口,它提供了一套 REST API,使得运维人员能够通过 HTTP 接口查询和更新线程池配置,监控线程池运行状态,并设置阈值告警。DashBoard-Dev服务的核心功能包括配置管理、服务发现、状态监控和告警设置 。
在 DashBoard-Dev 中,如果进行功能拆分,可以划分为以下几个模块:
- 项目模块
- Grafana 模块
- 用户模块
- 线程池管理 & 实例
- Web 线程池管理 & 实例
其中,前三个模块主要是为了支撑控制台的搭建,设计相对简单,大家了解即可。
真正的核心在于 线程池管理 和 Web线程池管理 ,二者的逻辑基本一致。下面将以 线程池 为例展开说明。
1. 管理配置 API
DashBoard-Dev 服务通过 ThreadPoolManagerController 提供配置管理 API,允许用户查询和更新线程池配置。
查询线程池集合API :
@GetMapping("/api/onethread-dashboard/thread-pools")publicResult<List<ThreadPoolDetailRespDTO>>listThreadPool(ThreadPoolListReqDTO requestParam){returnResults.success(threadPoolManagerService.listThreadPool(requestParam));}
更新线程池API :
@PutMapping("/api/onethread-dashboard/thread-pool")publicResult<Void>updateGlobalThreadPool(@RequestBody@ValidThreadPoolUpdateReqDTO requestParam){
threadPoolManagerService.updateGlobalThreadPool(requestParam);returnResults.success();}
这些 API 调用 threadPoolManagerService 的相应方法,通过 NacosProxyClient 与 Nacos 交互,实现配置的查询和更新。
2. 服务发现与配置聚合
DashBoard-Dev 服务通过 NacosProxyClient 的 service 方法查询服务实例列表,然后通过 HTTP 调用各实例的监控接口,收集线程池运行时状态。
服务发现与配置聚合流程 :
- 1.根据命名空间和服务名查询 Nacos 服务实例列表;
- 2.对每个服务实例发起 HTTP 请求,获取线程池运行时状态;
- 3.将各实例的状态数据聚合,形成全局的线程池监控视图。
// 在 threadPoolManagerService 的 listThreadPool 方法中List<String> namespaces =newArrayList<>(oneThreadProperties.getNamespaces());// 处理namespace过滤// 并行拉取各namespace的配置List<Map.Entry<String, NacosConfigRespDTO>> tasks = namespaces
.parallelStream().flatMap(ns ->{List<NacosConfigRespDTO> cfgs = nacosProxyClient.listConfig(ns);// ... 省略过滤逻辑}).collect(Collectors.toList());
// 并行处理任务:解析YAML -> 绑定配置 -> 查询服务实例数 -> 拼装返回return tasks.parallelStream().map(entry ->{String namespace = entry.getKey();NacosConfigRespDTO config = entry.getValue();
// 解析YAMLMap<Object, Object> configInfoMap = yamlConfigParser.doParse(config.getContent());ConfigurationPropertySource sources =newMapConfigurationPropertySource(configInfoMap);Binder binder =newBinder(sources);
// 绑定onethread配置DashBoardConfigProperties refresherProperties = binder.bind("onethread",Bindable.of(DashBoardConfigProperties.class)).orElseThrow(()->newIllegalArgumentException("配置绑定失败"));
// 查询当前服务在Nacos的实例数NacosServiceListRespDTO service = nacosProxyClient.service(namespace, config.getAppName());
refresherProperties.getExecutors().forEach(each ->{
each.setNamespace(namespace);
each.setServiceName(config.getAppName());
each.setDataId(config.getDataId());
each.setGroup(config.getGroup());
each.setInstanceCount(service.getInstanceCount());});
return refresherProperties.getExecutors();}).flatMap(List::stream).collect(Collectors.toList());
这一实现利用了Java8的StreamAPI和并行流,提高了配置查询和状态收集的效率 。特别是对于大规模部署的系统,这种并行处理机制能够显著减少管理界面的响应时间。
3. 线程池动态调整实现
DashBoard-Dev 服务通过 updateGlobalThreadPool 方法实现线程池参数的动态调整。这一过程涉及到配置的查询、修改、解析和发布,最终实现线程池参数的在线更新 。
线程池参数热更新流程 :
- 1.DashBoard-Dev 服务通过 NacosProxyClient 更新线程池配置;
- 2.Nacos 配置中心将配置变更推送给所有监听该配置的业务应用;
- 3.业务应用通过 oneThread SpringBoot Starter 配置监听器捕获配置变更;
- 4.业务应用解析新配置,更新本地线程池参数。
@SneakyThrows@OverridepublicvoidupdateGlobalThreadPool(ThreadPoolUpdateReqDTO requestParam){// 查询原配置NacosConfigDetailRespDTO configDetail = nacosProxyClient.getConfig(requestParam.getNamespace(), requestParam.getDataId(), requestParam.getGroup());String originalContent = configDetail.getContent();
// 解析原配置Map<Object, Object> configInfoMap = yamlConfigParser.doParse(originalContent);ConfigurationPropertySource source =newMapConfigurationPropertySource(configInfoMap);
// 绑定到Java对象DashBoardConfigProperties onethread = binder.bind("onethread",Bindable.of(DashBoardConfigProperties.class)).orElseThrow(()->newRuntimeException("绑定失败"));
// 修改线程池参数
onethread.getExecutors().stream().filter(e -> e.getThreadPoolId().equals(requestParam.getThreadPoolId())).findFirst().ifPresent(e ->{
e.setCorePoolSize(requestParam.getCorePoolSize());
e.setMaximumPoolSize(requestParam.getMaximumPoolSize());
e.setKeepAliveTime(requestParam.getKeepAliveTime());
e.setQueueCapacity(requestParam.getQueueCapacity());// ... 省略其他参数设置});
// 将Java对象转回YAML格式YAMLFactory factory =newYAMLFactory();
factory.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
factory.enable(YAMLGenerator.Feature.MINIMIZEQuOTES);
ObjectMapper objectMapper =newObjectMapper(factory);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setPropertyNamingStrategy PropertyNamingStrategies.KEBAB_CASE);
String yamlStr = objectMapper.writeValueAsString(Collections.singletonMap("onethread", onethread));
// 发布新配置
nacosProxyClient.publishConfig(requestParam.getNamespace(), requestParam.getDataId(), requestParam.getGroup(), configDetail.getAppName(), configDetail.getId(), configDetail.getMD5(), yamlStr,"yaml");}
这一实现流程的关键在于获取原配置的id和md5值 ,以确保配置更新的原子性和一致性。在 Nacos 中,每个配置都有唯一的 id 和 md5 值,通过携带这些值进行配置更新,可以避免多人同时修改配置时的版本冲突问题。
4. 线程池实例监控指标
线程池监控能力由 onethread-dashboard-dev-spring-boot-starter 提供,它暴露了标准的 Web 接口。 底层实现依赖业务应用在 Nacos 上注册的 IP地址 ,Dashboard 通过该地址直接调用目标应用的接口获取线程池状态。
例如,getRuntimeState 方法会拼接业务应用的网络地址并发起请求:
@OverridepublicThreadPoolStateRespDTOgetRuntimeState(String threadPoolId,String networkAddress){String resultStr =HttpUtil.get("http://"+ networkAddress +"/dynamic/thread-pool/"+ threadPoolId);Result<ThreadPoolStateRespDTO> result =JSON.parseObject(resultStr,newTypeReference<>(){});return result.getData();}
其中 networkAddress 参数来源于线程池列表接口,调用时直接透传。
在 onethread-dashboard-dev-spring-boot-starter 内置的控制器中,提供了两类接口:
- 轻量级指标接口 :用于线程池列表展示,快速获取基础运行信息;
- 完整运行时接口 :用于详情页面,展示线程池的全面状态。
代码如下所示:
@RestController@RequiredArgsConstructorpublicclassDynamicThreadPoolController{
privatefinalDynamicThreadPoolService dynamicThreadPoolService;
/**
* 获取线程池的轻量级运行指标
*/@GetMapping("/dynamic/thread-pool/{threadPoolId}/basic-metrics")publicResult<ThreadPoolDashBoardDevBaseMetricsRespDTO>getBasicMetrics(@PathVariableString threadPoolId){returnResults.success(dynamicThreadPoolService.getBasicMetrics(threadPoolId));}
/**
* 获取线程池的完整运行时状态
*/@GetMapping("/dynamic/thread-pool/{threadPoolId}")publicResult<ThreadPoolDashBoardDevRespDTO>getRuntimeInfo(@PathVariableString threadPoolId){returnResults.success(dynamicThreadPoolService.getRuntimeInfo(threadPoolId));}}
5. 线程池管理和实例有什么区别?
在 线程池管理 中,分为 线程池列表 和 线程池实例 两个维度:
- 线程池列表 “代表当前的静态线程池配置,配置变更会作用于 所有应用实例 。可以理解为全局的线程池配置管理。
- 线程池实例 :展示了某个线程池在不同应用实例上的运行情况。支持查看每个实例的详细运行参数,并且可以针对单个实例进行参数调整。
单实例变更场景 :通常用于集群整体线程池配置没有问题,但某个实例出现了特殊情况(如遇到流量激增)。此时无需修改全局配置,只需扩容或调整该实例的线程池参数即可。
| 维度 | 线程池列表 | 线程池实例 |
|---|---|---|
| 定义 | 静态的线程池配置清单 | 某个线程池在具体应用实例上的运行情况 |
| 作用范围 | 变更作用于 所有应用实例 | 可以针对 单个实例 调整参数 |
| 用途 | 全局统一的线程池配置管理 | 针对单实例的运行状态监控与调整 |
| 可查看内容 | 配置项(核心线程数、最大线程数、队列大小等) | 每个实例的实时运行指标与配置详情 |
| 典型场景 | 统一配置、批量修改 | 某个实例异常,需要扩容或单独调整参数 |
文末总结
oneThread 动态线程池框架的 DashBoard-Dev 服务,通过巧妙的架构设计,成功地平衡了系统复杂度与功能完整性。它既保持了基于配置中心架构的轻量特性,又提供了强大的可视化管理能力,为线程池治理提供了一套完整的解决方案。
核心设计亮点总结 :
- 1.架构创新 :采用配置中心 + 可视化代理的混合模式,兼顾轻量与功能性。
- 2.实现精巧 :通过 Nacos HTTP API 代理,实现了无侵入的配置管理。
- 3.性能优化 :大量使用并行处理和异步编程,提升了数据获取和处理效率。
- 4.扩展友好 :预留了多个扩展点,支持多配置中心和自定义监控指标。
这种设计思路对于其他中间件的可视化管理也具有很好的借鉴意义,特别是在如何平衡架构复杂度与功能完整性方面,算是为大家提供了一个开阔性的案例吧。

Comments NOTHING