Release 1.5.0 Documentation

1.5.0
chen.ma 2 years ago
parent e141e5d16f
commit 5fc2906041

@ -10,7 +10,7 @@ sidebar_position: 5
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-1x-starter</artifactId> <artifactId>hippo4j-config-spring-boot-1x-starter</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```

@ -12,7 +12,7 @@ Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心任选其一。
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-starter</artifactId> <artifactId>hippo4j-config-spring-boot-starter</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```
@ -67,8 +67,8 @@ spring:
namespace: xxxx namespace: xxxx
# 配置中心文件格式 # 配置中心文件格式
config-file-type: yml config-file-type: yml
# tomcat、undertow、jetty 三种容器线程池,任选其一 # 支持 tomcat、undertow、jetty 三种容器线程池
undertow: web:
core-pool-size: 100 core-pool-size: 100
maximum-pool-size: 200 maximum-pool-size: 200
keep-alive-time: 1000 keep-alive-time: 1000

@ -33,7 +33,7 @@ Hippo4j 目前已支持的三方框架线程池列表:
<artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq</artifactId> <artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq</artifactId>
<!-- SpringCloud Stream RabbitMQ --> <!-- SpringCloud Stream RabbitMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq</artifactId> <artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```
@ -43,7 +43,7 @@ Hippo4j 目前已支持的三方框架线程池列表:
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter-adapter-all</artifactId> <artifactId>hippo4j-spring-boot-starter-adapter-all</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```

@ -53,7 +53,7 @@ spring:
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter-monitor-micrometer</artifactId> <artifactId>hippo4j-spring-boot-starter-monitor-micrometer</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```

@ -22,7 +22,7 @@ SpringBoot Pom 引入 Hippo4j Starter Jar。
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter</artifactId> <artifactId>hippo4j-spring-boot-starter</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```

@ -10,7 +10,7 @@ sidebar_position: 5
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-1x-starter</artifactId> <artifactId>hippo4j-config-spring-boot-1x-starter</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```

@ -12,7 +12,7 @@ Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心任选其一。
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-starter</artifactId> <artifactId>hippo4j-config-spring-boot-starter</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```
@ -67,8 +67,8 @@ spring:
namespace: xxxx namespace: xxxx
# 配置中心文件格式 # 配置中心文件格式
config-file-type: yml config-file-type: yml
# tomcat、undertow、jetty 三种容器线程池,任选其一 # 支持 tomcat、undertow、jetty 三种容器线程池
undertow: web:
core-pool-size: 100 core-pool-size: 100
maximum-pool-size: 200 maximum-pool-size: 200
keep-alive-time: 1000 keep-alive-time: 1000

@ -33,7 +33,7 @@ Hippo4j 目前已支持的三方框架线程池列表:
<artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq</artifactId> <artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq</artifactId>
<!-- SpringCloud Stream RabbitMQ --> <!-- SpringCloud Stream RabbitMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq</artifactId> <artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```
@ -43,7 +43,7 @@ Hippo4j 目前已支持的三方框架线程池列表:
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter-adapter-all</artifactId> <artifactId>hippo4j-spring-boot-starter-adapter-all</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```

@ -53,7 +53,7 @@ spring:
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter-monitor-micrometer</artifactId> <artifactId>hippo4j-spring-boot-starter-monitor-micrometer</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```

@ -22,7 +22,7 @@ SpringBoot Pom 引入 Hippo4j Starter Jar。
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter</artifactId> <artifactId>hippo4j-spring-boot-starter</artifactId>
<version>1.4.3-upgrade</version> <version>1.5.0</version>
</dependency> </dependency>
``` ```

@ -0,0 +1,8 @@
{
"label": "开发者手册",
"position": 5,
"link": {
"type": "generated-index",
"description": "Hippo4j 留给使用者能够扩展的知识点。"
}
}

@ -0,0 +1,57 @@
---
sidebar_position: 1
---
# 拒绝策略自定义
Hippo4j 通过 SPI 的方式对拒绝策略进行扩展,可以让用户在 Hippo4j 中完成自定义拒绝策略实现。
## Hippo4j Server 拒绝策略扩展
自定义拒绝策略,实现 `CustomRejectedExecutionHandler` 接口,示例如下:
```java
public class ErrorLogRejectedExecutionHandler implements CustomRejectedExecutionHandler {
@Override
public Integer getType() {
return 12;
}
@Override
public RejectedExecutionHandler generateRejected() {
return new CustomErrorLogRejectedExecutionHandler();
}
public static class CustomErrorLogRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
Logger logger = LoggerFactory.getLogger(this.getClass());
logger.error("线程池抛出拒绝策略");
}
}
}
```
创建 `src/main/resources/META-INF/services` 目录,创建 SPI 自定义拒绝策略文件 `cn.hippo4j.common.executor.support.CustomRejectedExecutionHandler`
`cn.hippo4j.common.executor.support.CustomRejectedExecutionHandler` 文件内仅放一行自定义拒绝策略全限定名即可,示例:
```text
cn.hippo4j.example.core.handler.ErrorLogRejectedExecutionHandler
```
创建、修改线程池页面选择 `CustomRejectedPolicy自定义 SPI 策略)`
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220813173907814.png)
拒绝策略触发时,完成上述代码效果,仅打印异常日志提示。
```text
2022-08-01 21:27:49.515 ERROR 48928 --- [ateHandler.test] r$CustomErrorLogRejectedExecutionHandler : 线程池抛出拒绝策略
```
:::note
具体参考 `hippo4j-example/hippo4j-spring-boot-starter-example` 模块。
:::

@ -0,0 +1,50 @@
---
sidebar_position: 0
---
# 内置拒绝策略
内置两种拒绝策略说明:
**RunsOldestTaskPolicy**:添加新任务并由主线程运行最早的任务。
```java
public class RunsOldestTaskPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (executor.isShutdown()) {
return;
}
BlockingQueue<Runnable> workQueue = executor.getQueue();
Runnable firstWork = workQueue.poll();
boolean newTaskAdd = workQueue.offer(r);
if (firstWork != null) {
firstWork.run();
}
if (!newTaskAdd) {
executor.execute(r);
}
}
}
```
**SyncPutQueuePolicy**:主线程把拒绝任务以阻塞的方式添加到队列。
```java
@Slf4j
public class SyncPutQueuePolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (executor.isShutdown()) {
return;
}
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
log.error("Adding Queue task to thread pool failed.", e);
}
}
}
```

@ -0,0 +1,7 @@
{
"label": "快速开始",
"position": 3,
"link": {
"type": "generated-index"
}
}

@ -0,0 +1,5 @@
{
"label": "依赖配置中心",
"position": 2,
"collapsed": true
}

@ -0,0 +1,45 @@
---
sidebar_position: 4
---
# 参数默认配置
曾有多名小伙伴反馈说,项目中线程池一多,配置文件中配置就显得很臃肿。为此 hippo4j-config 开发出了动态线程池默认配置。
```yaml
spring:
dynamic:
thread-pool:
default-executor:
core-pool-size: 4
maximum-pool-size: 6
blocking-queue: ResizableCapacityLinkedBlockingQueue
queue-capacity: 1024
execute-time-out: 1000
keep-alive-time: 9999
rejected-handler: AbortPolicy
active-alarm: 90
capacity-alarm: 85
alarm: true
allow-core-thread-time-out: true
notify:
interval: 5
receives: chen.ma
executors:
- thread-pool-id: message-produce
- thread-pool-id: message-consume
core-pool-size: 80
maximum-pool-size: 100
execute-time-out: 1000
notify:
interval: 6
receives: chen.ma
```
`spring.dynamic.thread-pool.executors` 层级下,仅需要配置 `thread-pool-id`,其余配置从 `spring.dynamic.thread-pool.default-executor` 读取。
如果 `spring.dynamic.thread-pool.executors` 下配置和 `spring.dynamic.thread-pool.default-executor` 冲突,以前者为主。
通过该自定义配置方式,可减少大量重复线程池参数配置项,提高核心配置简洁度。
提示:`spring.dynamic.thread-pool.default-executor` 层级下参数,不提供动态刷新功能。

@ -0,0 +1,122 @@
---
sidebar_position: 3
---
# 线程池监控
## 线程池监控配置
监控前置条件:需要先完成 hippo4j-config 的 [接入工作](/docs/user_docs/getting_started/config/hippo4j-config-start)。
接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。
```xml
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```
添加动态线程池监控相关配置:
```yaml
management:
metrics:
export:
prometheus:
enabled: true
server:
port: 29999 # 可选配置,如果不配置该 port直接使用 ${server.port}
endpoints:
web:
exposure:
include: '*' # 测试使用,开启了所有端点,生产环境不建议 *
spring:
dynamic:
thread-pool:
monitor:
enable: true # 是否开启采集线程池运行时数据
collect-interval: 5000 # 采集线程池运行数据频率
collect-types: micrometer # 采集线程池运行数据的类型。eglog、micrometer。多个可以同时使用默认 micrometer
initial-delay: 10000 # 项目启动后延迟多久进行采集
thread-pool-types: dynamic # 采集线程池的类型。egdynamic、web、adapter。可任意配置默认 dynamic
```
项目启动,访问 `http://localhost:29999/actuator/prometheus` 出现 `dynamic_thread_pool_` 前缀的指标,即为成功。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912220401016.png)
## 配置 Prometheus
通过 Docker 启动 Prometheus 服务。
```shell
docker run -d -p 9090:9090 --name prometheus prom/prometheus
```
添加 Prometheus 抽取数据任务。
```shell
# 进入 prometheus 容器内部
docker exec -it prometheus /bin/sh
# 编辑 prometheus 配置文件
vi /etc/prometheus/prometheus.yml
```
scrape_configs 节点下新添加一个 job如果 Prometheus 是 Docker 方式部署,`{scrape_configs.static_configs.targets}` 需要写本机的 IP。
```yaml
scrape_configs:
- job_name: 'dynamic-thread-pool-job'
scrape_interval: 5s
metrics_path: '/actuator/prometheus'
static_configs:
- targets: [ '127.0.0.1:29999' ]
```
配置成功后 `exit` 退出容器,并进行 Prometheus 容器重启 `docker restart prometheus`
访问 Prometheus 控制台 `http://localhost:9090/graph` 路径,能够展示相关指标即为配置成功。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912221237597.png)
## 配置 Grafana
```shell
docker run -d -p 3000:3000 --name=grafana grafana/grafana
```
访问 Grafana 地址,[http://localhost:3000](http://localhost:3000) 用户名密码:`admin`
Grafana 访问 `http://localhost:3000/datasources` 导入 Prometheus 数据源。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912221646866.png)
> 如果 Prometheus 为 Docker 方式部署HTTP URL 需要为本地 IP比如http://192.168.1.5:9090
关注公众号 `龙台的技术笔记`,回复:`监控`,获取 Hippo4j Grafana DashBoard JSON 配置。
| 公众号 | 回复关键词 |
|:------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------:|
| ![](https://images-machen.oss-cn-beijing.aliyuncs.com/43_65f6020ed111b6bb3808ec338576bd6b.png?x-oss-process=image/resize,h_300,w_400) | ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220327171957444.png?x-oss-process=image/resize,h_300,w_400) |
获取到 JSON 文件后,通过 `http://localhost:3000/dashboard/import` 将 JSON 文件导入至 Grafana DashBoard。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225627272.png)
下拉框内动态选择创建好的 Prometheus 数据源,并点击 `Import`
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225700200.png)
即可使用炫酷的 Hippo4j 动态线程池监控 DashBoard。大家伙儿也可以根据个人喜好进行定制 DashBoard如果觉得有优化点欢迎和我联系贡献。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225813972.png)
如果项目客户端启动多个示例,动态线程池监控效果图如下:
![](https://images-machen.oss-cn-beijing.aliyuncs.com/20220814_hippo4j_monitor.jpg)

@ -0,0 +1,80 @@
---
sidebar_position: 3
---
# 个性化配置
以下所述特性自 hippo4j-config v1.4.2 及以上版本提供,由 hippo4j 核心开发者 [@pizihao](https://github.com/pizihao) 完成相应功能开发。
## 需求背景
**1容器及三方框架线程池自定义启用**
最初设计容器线程池和三方框架线程池的动态变更是和启动无关的。也就是说,启动时不会根据配置文件中相关参数去修改两者对应的线程池配置。
这么设计的初衷是因为,不想让 hippo4j 过多的去介入框架原有的功能。因为容器和三方框架都支持线程池参数的自定义。
也就造成,可能你在配置中心配置了对应的容器和三方框架线程池参数,启动时是无效的。但当修改配置文件任一配置,容器和三方框架线程池配置将生效。
为了更好的用户体验,决定加入启用标识来控制:是否在项目初始化启动时,对容器和三方框架线程池参数进行修改。
**2客户端集群个性化配置**
大家都知道hippo4j-config 是依赖配置中心做线程池配置动态变更。这种模式有一种缺点:改动配置文件后,所有客户端都会变更。
有些小伙伴希望 hippo4j-config 能够像 hippo4j-server 一样,能够针对单独的客户端进行配置变更。
## 容器及三方框架线程池自定义启用
容器及三方框架线程池添加启用配置,为了保持统一,动态线程池配置中也有该参数配置。配置项默认开启。
```yaml
spring:
dynamic:
thread-pool:
tomcat:
enable: true
executors:
- thread-pool-id: message-consume
enable: false
adapter-executors:
- threadPoolKey: 'input'
enable: true
```
## 客户端集群个性化配置
分别在动态线程池、容器线程池以及三方框架线程池配置下增加 `nodes` 配置节点,通过该配置可匹配需要变更的节点。
```yaml
spring:
dynamic:
thread-pool:
tomcat:
nodes: 192.168.1.5:*,192.168.1.6:8080
executors:
- thread-pool-id: message-consume
nodes: 192.168.1.5:*
adapter-executors:
- threadPoolKey: 'input'
nodes: 192.168.1.5:*
```
来一段代码方法中的注释,大家就基本明白如何使用了。
```java
/**
* Matching nodes<br>
* nodes is ip + port.Get 'nodes' in the new Properties,Compare this with the ip + port of Application.<br>
* support prefix pattern matching. e.g: <br>
* <ul>
* <li>192.168.1.5:* -- Matches all ports of 192.168.1.5</li>
* <li>192.168.1.*:2009 -- Matches 2009 port of 192.168.1.*</li>
* <li>* -- all</li>
* <li>empty -- all</li>
* </ul>
* The format of ip + port is ip : port.
*/
```
`nodes` 可与 `enable` 同时使用。如此,基于配置中心的动态线程池实现方式,将能够更方便的支持个性化需求。

@ -0,0 +1,121 @@
---
sidebar_position: 5
---
# 适配SpringBoot1x
目前已支持 Nacos、Apollo 配置中心适配 SpringBoot 1.5.x 版本。
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-1x-starter</artifactId>
<version>1.5.0</version>
</dependency>
```
Nacos SpringBoot 配置如下:
```yaml
spring:
cloud:
nacos:
config:
ext-config:
- data-id: hippo4j-nacos.yaml
group: DEFAULT_GROUP
refresh: true
server-addr: 127.0.0.1:8848
dynamic:
thread-pool:
config-file-type: yml
nacos:
data-id: hippo4j-nacos.yaml
group: DEFAULT_GROUP
```
Apollo SpringBoot 配置如下:
```yaml
apollo:
autoUpdateInjectedSpringProperties: true
bootstrap:
eagerLoad:
enabled: true
enabled: true
namespaces: application
meta: http://127.0.0.1:8080
app:
id: dynamic-threadpool-example
spring:
dynamic:
thread-pool:
apollo:
namespace: application
```
动态线程池通用配置如下:
```yaml
management:
context-path: /actuator
security:
enabled: false
server:
port: 8091
servlet:
context-path: /example
spring:
application:
name: dynamic-threadpool-example
dynamic:
thread-pool:
banner: true
check-state-interval: 5
collect-type: micrometer
config-file-type: properties
enable: true
executors:
- active-alarm: 80
alarm: true
allow-core-thread-time-out: true
blocking-queue: LinkedBlockingQueue
capacity-alarm: 80
core-pool-size: 1
execute-time-out: 1000
keep-alive-time: 6691
maximum-pool-size: 1
notify:
interval: 8
receives: chen.ma
queue-capacity: 1
rejected-handler: AbortPolicy
thread-name-prefix: message-consume
thread-pool-id: message-consume
- active-alarm: 80
alarm: true
allow-core-thread-time-out: true
blocking-queue: LinkedBlockingQueue
capacity-alarm: 80
core-pool-size: 1
execute-time-out: 1000
keep-alive-time: 6691
maximum-pool-size: 1
notify:
interval: 8
receives: chen.ma
queue-capacity: 1
rejected-handler: AbortPolicy
thread-name-prefix: message-produce
thread-pool-id: message-produce
notify-platforms:
- platform: WECHAT
token: ac0426a5-c712-474c-9bff-72b8b8f5caff
profiles:
active: dev
```
具体 Demo 运行请参考以下示例模块,已验证对应线程池动态变更、报警以及运行时监控功能。
- `/hippo4j-config-nacos-spring-boot-1x-starter-example`
- `hippo4j-example/hippo4j-config-apollo-spring-boot-1x-starter-example`

@ -0,0 +1,193 @@
---
sidebar_position: 1
---
# 接入流程
Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心任选其一。
## hippo4j 配置
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>
```
启动类上添加注解 `@EnableDynamicThreadPool`
```java
@SpringBootApplication
@EnableDynamicThreadPool
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
```
SpringBoot 应用配置文件添加:
```yaml
server:
port: 8090
servlet:
context-path: /example
spring:
profiles:
active: dev
dynamic:
thread-pool:
# 是否开启动态线程池
enable: true
# 是否打印 banner
banner: true
# 是否开启线程池数据采集,对接 Micrometer、ES、Log 等
collect: true
# 检查线程池状态,是否达到报警条件,单位毫秒
check-state-interval: 3000
# 通知报警平台,请替换为自己创建的群机器人
notify-platforms:
- platform: 'WECHAT'
token: xxx
- platform: 'DING'
token: xxx
secret: xxx # 加签专属
- platform: 'LARK'
token: xxx
# Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 任选其一
nacos:
data-id: xxx
group: xxx
apollo:
namespace: xxxx
# 配置中心文件格式
config-file-type: yml
# 支持 tomcat、undertow、jetty 三种容器线程池
web:
core-pool-size: 100
maximum-pool-size: 200
keep-alive-time: 1000
# 全局通知配置-是否报警
alarm: true
# 活跃度报警阈值;假设线程池最大线程数 10当线程数达到 8 发起报警
active-alarm: 80
# 容量报警阈值;假设阻塞队列容量 100当容量达到 80 发起报警
capacity-alarm: 80
# 报警间隔,同一线程池下同一报警纬度,在 interval 时间内只会报警一次,单位秒
alarm-interval: 8
# 企业微信填写用户 ID填写其它将无法达到 @ 效果)、钉钉填手机号、飞书填 ou_ 开头唯一 ID
receives: xxx
# 动态线程池列表
executors:
- thread-pool-id: 'message-consume'
# 核心线程数
core-pool-size: 1
# 最大线程数
maximum-pool-size: 1
# 阻塞队列名称,参考 BlockingQueueTypeEnum支持 SPI
blocking-queue: 'LinkedBlockingQueue'
# 阻塞队列大小
queue-capacity: 1
# 执行超时时间,超过此时间发起报警,单位毫秒
execute-time-out: 1000
# 拒绝策略名称,参考 RejectedPolicyTypeEnum支持 SPI
rejected-handler: 'AbortPolicy'
# 线程存活时间,单位秒
keep-alive-time: 1024
# 是否允许核心线程超时
allow-core-thread-time-out: true
# 线程工厂名称前缀
thread-name-prefix: 'message-consume'
# 是否报警
alarm: true
# 活跃度报警阈值;假设线程池最大线程数 10当线程数达到 8 发起报警
active-alarm: 80
# 容量报警阈值;假设阻塞队列容量 100当容量达到 80 发起报警
capacity-alarm: 80
# 通知配置,线程池中通知配置如果存在,则会覆盖全局通知配置
notify:
# 报警间隔,同一线程池下同一报警纬度,在 interval 时间内只会报警一次,单位分钟
interval: 8
# 企业微信填写用户 ID填写其它将无法达到 @ 效果)、钉钉填手机号、飞书填 ou_ 开头唯一 ID
receives: xxx
- thread-pool-id: 'message-produce'
core-pool-size: 1
maximum-pool-size: 1
queue-capacity: 1
execute-time-out: 1000
blocking-queue: 'LinkedBlockingQueue'
rejected-handler: 'AbortPolicy'
keep-alive-time: 1024
allow-core-thread-time-out: true
thread-name-prefix: 'message-consume'
alarm: true
active-alarm: 80
capacity-alarm: 80
notify:
interval: 8
receives: xxx
```
## ThreadPoolExecutor 适配
添加线程池配置类,通过 `@DynamicThreadPool` 注解修饰。`threadPoolId` 为服务端创建的线程池 ID。
```java
package cn.hippo4j.example;
import cn.hippo4j.core.executor.DynamicThreadPool;
import cn.hippo4j.core.executor.support.ThreadPoolBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class ThreadPoolConfig {
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageConsumeDynamicExecutor() {
String threadPoolId = "message-consume";
ThreadPoolExecutor messageConsumeDynamicExecutor = ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
return messageConsumeDynamicExecutor;
}
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageProduceDynamicExecutor() {
String threadPoolId = "message-produce";
ThreadPoolExecutor messageProduceDynamicExecutor = ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
return messageProduceDynamicExecutor;
}
}
```
通过 ThreadPoolBuilder 构建动态线程池,只有 threadFactory、threadPoolId 为必填项,其它参数会从配置中心拉取。
项目中使用上述定义的动态线程池,如下所示:
```java
@Resource
private ThreadPoolExecutor messageConsumeDynamicExecutor;
messageConsumeDynamicExecutor.execute(() -> xxx);
@Resource
private ThreadPoolExecutor messageProduceDynamicExecutor;
messageProduceDynamicExecutor.execute(() -> xxx);
```

@ -0,0 +1,37 @@
---
sidebar_position: 0
---
# 运行模式介绍
1.1.0 版本发布后Hippo4j 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220319154626314.png)
### Hippo4j config
**轻量级动态线程池管理**,依赖 Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 等三方配置中心(任选其一)完成线程池参数动态变更,支持运行时报警、监控等功能。
> 监控功能配置详见:[线程池监控](/docs/user_docs/getting_started/config/hippo4j-config-monitor)
![](https://images-machen.oss-cn-beijing.aliyuncs.com/20220814_hippo4j_monitor.jpg)
### Hippo4j server
**部署 Hippo4j server 服务**,通过可视化 Web 界面完成线程池的创建、变更以及查看,不依赖三方中间件。
相比较 Hippo4j config功能会更强大但同时也引入了一定的复杂性。需要部署一个 Java 服务,以及依赖 MySQL 数据库。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/1644032018254-min.gif)
### 使用总结
| | Hippo4j config | Hippo4j server |
| ---- |-------------------------------------------------------|--------------------------------------------------------|
| 依赖 | Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心(任选其一) | 部署 Hippo4j server内部无依赖中间件 |
| 使用 | 配置中心补充线程池相关参数 | Hippo4j server web 控制台添加线程池记录 |
| 功能 | 包含基础功能:参数动态化、运行时监控、报警等 | 基础功能之外扩展控制台界面、线程池堆栈查看、线程池运行信息实时查看、历史运行信息查看、线程池配置集群个性化等 |
使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 Hippo4j config 使用即可;如果希望更多的功能,可以选择 Hippo4j server。
**两者在进行替换的时候,无需修改业务代码**。

@ -0,0 +1,72 @@
---
sidebar_position: 6
---
# 三方框架线程池适配
Hippo4j 目前已支持的三方框架线程池列表:
- Dubbo
- Hystrix
- RabbitMQ
- RocketMQ
- AlibabaDubbo
- RocketMQSpringCloudStream
- RabbitMQSpringCloudStream
引入 Hippo4j Server 或 Core 的 Maven Jar 坐标后,还需要引入对应的框架适配 Jar
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<!-- Dubbo -->
<artifactId>hippo4j-spring-boot-starter-adapter-dubbo</artifactId>
<!-- Alibaba Dubbo -->
<artifactId>hippo4j-spring-boot-starter-adapter-alibaba-dubbo</artifactId>
<!-- Hystrix -->
<artifactId>hippo4j-spring-boot-starter-adapter-hystrix</artifactId>
<!-- RabbitMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-rabbitmq</artifactId>
<!-- RocketMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-rocketmq</artifactId>
<!-- SpringCloud Stream RocketMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq</artifactId>
<!-- SpringCloud Stream RabbitMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq</artifactId>
<version>1.5.0</version>
</dependency>
```
如果想省事,仅需引入一个全量包,框架底层会根据条件判断加载具体线程池适配器。
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter-adapter-all</artifactId>
<version>1.5.0</version>
</dependency>
```
## Hippo4j Server
Hippo4j Server 仅需要引入上述 Jar 包,即可在 Hippo4j Server 的控制台进行查看及修改三方框架线程池。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220531194810047.png)
## Hippo4j Config
Hippo4j Config 除了依赖上述适配 Jar 包外,还需要在配置中心添加以下配置项。
```yaml
spring:
dynamic:
thread-pool:
# 省略其它配置
adapter-executors:
# threadPoolKey 代表线程池标识
- threadPoolKey: 'input'
# mark 为三方线程池框架类型,参见文初已支持框架集合
mark: 'RocketMQSpringCloudStream'
corePoolSize: 10
maximumPoolSize: 10
```

@ -0,0 +1,5 @@
{
"label": "无中间件依赖",
"position": 3,
"collapsed": true
}

@ -0,0 +1,19 @@
---
sidebar_position: 3
---
# 服务端配置
`hippo4j.core.clean-history-data-enable`
是否开启线程池历史数据清洗,默认开启。
`hippo4j.core.clean-history-data-period`
线程池历史数据保留时间默认值30单位分钟。
服务端会保留这个配置时间的数据,超过这个时间则会被清理。比如按照默认值 30 分钟来说12:00 收集到的数据12:30 就会被清理删除。
`hippo4j.core.monitor.report-type`
客户端监控上报服务端类型可选值http、netty默认 http。服务端开启 netty 配置后,需要在客户端对应开启才可生效。用来应对大量动态线程池监控场景。

@ -0,0 +1,132 @@
---
sidebar_position: 2
---
# 线程池监控
Server 模式默认内置线程池运行时采集和监控功能,如果想要使用 Prometheus + Grafana 的方式可以查看以下内容。
## 线程池监控配置
接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。
```xml
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```
添加动态线程池监控相关配置:
```yaml
management:
metrics:
export:
prometheus:
enabled: true
server:
port: 29999 # 可选配置,如果不配置该 port直接使用 ${server.port}
endpoints:
web:
exposure:
include: '*' # 测试使用,开启了所有端点,生产环境不建议 *
spring:
dynamic:
thread-pool:
monitor:
enable: true # 是否开启采集线程池运行时数据
collect-interval: 5000 # 采集线程池运行数据频率
collect-types: server,micrometer # 采集线程池运行数据的类型。egserver、micrometer。多个可以同时使用默认 server
initial-delay: 10000 # 项目启动后延迟多久进行采集
thread-pool-types: dynamic # 采集线程池的类型。egdynamic、web、adapter。可任意配置默认 dynamic
```
如果使用 `micrometer` 类型的监控指标,需要添加以下依赖。
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter-monitor-micrometer</artifactId>
<version>1.5.0</version>
</dependency>
```
项目启动,访问 `http://localhost:29999/actuator/prometheus` 出现 `dynamic_thread_pool_` 前缀的指标,即为成功。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912220401016.png)
## 配置 Prometheus
通过 Docker 启动 Prometheus 服务。
```shell
docker run -d -p 9090:9090 --name prometheus prom/prometheus
```
添加 Prometheus 抽取数据任务。
```shell
# 进入 prometheus 容器内部
docker exec -it prometheus /bin/sh
# 编辑 prometheus 配置文件
vi /etc/prometheus/prometheus.yml
```
scrape_configs 节点下新添加一个 job如果 Prometheus 是 Docker 方式部署,`{scrape_configs.static_configs.targets}` 需要写本机的 IP。
```yaml
scrape_configs:
- job_name: 'dynamic-thread-pool-job'
scrape_interval: 5s
metrics_path: '/actuator/prometheus'
static_configs:
- targets: [ '127.0.0.1:29999' ]
```
配置成功后 `exit` 退出容器,并进行 Prometheus 容器重启 `docker restart prometheus`
访问 Prometheus 控制台 `http://localhost:9090/graph` 路径,能够展示相关指标即为配置成功。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912221237597.png)
## 配置 Grafana
```shell
docker run -d -p 3000:3000 --name=grafana grafana/grafana
```
访问 Grafana 地址,[http://localhost:3000](http://localhost:3000) 用户名密码:`admin`
Grafana 访问 `http://localhost:3000/datasources` 导入 Prometheus 数据源。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912221646866.png)
> 如果 Prometheus 为 Docker 方式部署HTTP URL 需要为本地 IP比如http://192.168.1.5:9090
关注公众号 `龙台的技术笔记`,回复:`监控`,获取 Hippo4j Grafana DashBoard JSON 配置。
| 公众号 | 回复关键词 |
|:------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------:|
| ![](https://images-machen.oss-cn-beijing.aliyuncs.com/43_65f6020ed111b6bb3808ec338576bd6b.png?x-oss-process=image/resize,h_300,w_400) | ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220327171957444.png?x-oss-process=image/resize,h_300,w_400) |
获取到 JSON 文件后,通过 `http://localhost:3000/dashboard/import` 将 JSON 文件导入至 Grafana DashBoard。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225627272.png)
下拉框内动态选择创建好的 Prometheus 数据源,并点击 `Import`
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225700200.png)
即可使用炫酷的 Hippo4j 动态线程池监控 DashBoard。大家伙儿也可以根据个人喜好进行定制 DashBoard如果觉得有优化点欢迎和我联系贡献。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225813972.png)
如果项目客户端启动多个示例,动态线程池监控效果图如下:
![](https://images-machen.oss-cn-beijing.aliyuncs.com/20220814_hippo4j_monitor.jpg)

@ -0,0 +1,127 @@
---
sidebar_position: 1
---
# 接入流程
部署服务端,参考 [部署手册](/docs/user_docs/ops/hippo4j-server-deploy)。
服务端创建 [租户、项目](/community/faq#租户和项目在-hippo4j-中是什么意思) 和线程池记录。
需要注意,项目 ID 需要与配置文件 `{application.name}` 保持一致。
:::note
租户、项目、线程池 ID 如果由多个词组成,建议以 - 进行分割。比如message-center。
:::
## Hippo4j 配置
SpringBoot Pom 引入 Hippo4j Starter Jar。
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>
```
启动类上添加注解 `@EnableDynamicThreadPool`
```java
@SpringBootApplication
@EnableDynamicThreadPool
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
```
SpringBoot 应用配置文件添加:
```yaml
spring:
profiles:
active: dev
application:
# 服务端创建的项目 id 需要与 application.name 保持一致
name: dynamic-threadpool-example
dynamic:
thread-pool:
# 服务端地址
server-addr: http://localhost:6691
# 用户名
username: admin
# 密码
password: 123456
# 租户 id, 对应 tenant 表
namespace: prescription
# 项目 id, 对应 item 表
item-id: ${spring.application.name}
```
## ThreadPoolExecutor 适配
添加线程池配置类,通过 `@DynamicThreadPool` 注解修饰。`threadPoolId` 为服务端创建的线程池 ID。
```java
package cn.hippo4j.example;
import cn.hippo4j.core.executor.DynamicThreadPool;
import cn.hippo4j.core.executor.support.ThreadPoolBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class ThreadPoolConfig {
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageConsumeDynamicExecutor() {
String threadPoolId = "message-consume";
ThreadPoolExecutor messageConsumeDynamicExecutor = ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
return messageConsumeDynamicExecutor;
}
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageProduceDynamicExecutor() {
String threadPoolId = "message-produce";
ThreadPoolExecutor messageProduceDynamicExecutor = ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
return messageProduceDynamicExecutor;
}
}
```
通过 ThreadPoolBuilder 构建动态线程池,只有 threadFactory、threadPoolId 为必填项,其它参数会从 hippo4j-server 服务拉取。
:::note
创建线程池时建议填充实际的参数。如果在连接 Hippo4j Server 端失败时,会使用填充配置创建线程池。
:::
项目中使用上述定义的动态线程池,如下所示:
```java
@Resource
private ThreadPoolExecutor messageConsumeDynamicExecutor;
messageConsumeDynamicExecutor.execute(() -> xxx);
@Resource
private ThreadPoolExecutor messageProduceDynamicExecutor;
messageProduceDynamicExecutor.execute(() -> xxx);
```

@ -0,0 +1,70 @@
---
sidebar_position: 1
---
# 简介
## 线程池痛点
线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。
如果有在项目中实际使用线程池,相信你可能会遇到以下痛点:
- 线程池随便定义,线程资源过多,造成服务器高负载。
- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。
- 线程池任务执行时间超过平均执行周期,开发人员无法感知。
- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。
- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。
- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。
- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。
- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。
## 什么是 Hippo4j
Hippo4j 通过对 JDK 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。
提供以下功能支持:
- 全局管控 - 管理应用线程池实例。
- 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。
- 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。
- 运行监控 - 实时查看线程池运行时数据,最近半小时线程池运行数据图表展示。
- 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。
- 多种模式 - 内置两种使用模式:[依赖配置中心](https://hippo4j.cn/docs/user_docs/getting_started/config/hippo4j-config-start) 和 [无中间件依赖](https://hippo4j.cn/docs/user_docs/getting_started/server/hippo4j-server-start)。
- 容器管理 - Tomcat、Jetty、Undertow 容器线程池运行时查看和线程数变更。
- 框架适配 - Dubbo、Hystrix、RabbitMQ、RocketMQ 等消费线程池运行时数据查看和线程数变更。
## 快速开始
对于本地演示目的,请参阅 [Quick start](https://hippo4j.cn/docs/user_docs/user_guide/quick-start)
演示环境: [http://console.hippo4j.cn/index.html](http://console.hippo4j.cn/index.html)
## 接入登记
更多接入的公司,欢迎在 [登记地址](https://github.com/opengoofy/hippo4j/issues/13) 登记,登记仅仅为了产品推广。
## 联系我
开源不易,右上角点个 Star 鼓励一下吧!
如果大家想要实时关注 Hippo4j 更新的文章以及分享的干货的话,可以关注我的公众号。
使用过程中有任何问题,或者对项目有什么建议,关注公众号回复:加群,和 1000+ 志同道合的朋友交流讨论。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20230317191041262-mini.png)
## 友情链接
- [[ LiteFlow ]](https://liteflow.yomahub.com/):轻量,快速,稳定可编排的组件式规则引擎。
- [[ Sa-Token ]](https://github.com/dromara/sa-token):一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!
- [[ HertzBeat ]](https://github.com/dromara/hertzbeat):易用友好的云监控系统, 无需 Agent, 强大自定义监控能力。
- [[ JavaGuide ]](https://github.com/Snailclimb/JavaGuide):一份涵盖大部分 Java 程序员所需要掌握的核心知识。
- [[ toBeBetterJavaer ]](https://github.com/itwanger/toBeBetterJavaer):一份通俗易懂、风趣幽默的 Java 学习指南。
## 贡献者
感谢所有为项目作出贡献的开发者。如果有意贡献,参考 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)。

@ -0,0 +1,7 @@
{
"label": "运维指南",
"position": 4,
"link": {
"type": "generated-index"
}
}

@ -0,0 +1,45 @@
---
sidebar_position: 1
---
# 源码包部署
[RELEASE](https://github.com/opengoofy/hippo4j/releases) 页面下载对应版本并进行解压。
## 初始化
修改数据库相关信息。
```txt
/conf/application.properties
```
如果是新运行 Hippo4j数据库执行下述 SQL 脚本即可。
```txt
/conf/hippo4j_manager.sql
```
如果是对已运行 Hippo4j 升级,请查看 `/conf/sql-upgrade` 目录下,是否有目标版本对应的升级脚本。
## 直接运行
Mac Linux 启动执行。
```txt
sh ./bin/startup.sh
```
Windows 启动执行。
```txt
bin/startup.cmd
```
## 访问控制台
启动成功后访问链接。用户名密码admin 123456
```txt
localhost:6691/index.html
```

@ -0,0 +1,50 @@
---
sidebar_position: 2
---
# Docker部署
## 镜像启动
Docker 镜像默认使用内置 H2 数据库,数据持久化到 Docker 容器存储卷中。
```shell
docker run -d -p 6691:6691 --name hippo4j-server hippo4j/hippo4j-server
```
或者,底层存储数据库切换为 MySQL。`DATASOURCE_HOST` 需要切换为本地 IP不能使用 `127.0.0.1``localhost`
```shell
docker run -d -p 6691:6691 --name hippo4j-server \
-e DATASOURCE_MODE=mysql \
-e DATASOURCE_HOST=xxx.xxx.xxx.xxx \
-e DATASOURCE_PORT=3306 \
-e DATASOURCE_DB=hippo4j_manager \
-e DATASOURCE_USERNAME=root \
-e DATASOURCE_PASSWORD=root \
hippo4j/hippo4j-server
```
访问 Server 控制台,路径 `http://localhost:6691/index.html` 默认用户名密码admin / 123456
## 镜像构建
如果想要自定义镜像,可以通过以下命令快速构建 Hippo4j Server
方式一:
```shell
# 进入到 hippo4j-server/hippo4j-bootstrap 工程路径下
mvn clean package -Dskip.spotless.apply=true
# 默认打包是打包的 tag 是 latest
docker build -t hippo4j/hippo4j-server ../hippo4j-bootstrap
```
方式二:
通过 `maven docker plugin`
```shell
# 进入到 hippo4j-server 工程路径下
mvn clean package -DskipTests -Dskip.spotless.apply=true docker:build
```

@ -0,0 +1,7 @@
{
"label": "其它",
"position": 6,
"link": {
"type": "generated-index"
}
}

@ -0,0 +1,23 @@
---
sidebar_position: 5
---
# 推荐公众号
## JavaGuide
专注Java后端学习和大厂面试的公众号
![](https://images-machen.oss-cn-beijing.aliyuncs.com/JavaGuide.png)
## HelloGitHub
HelloGitHub专注于开源社区技术和知识内容分享。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/HelloGitHub.png)
## macrozheng
专注Java技术分享解析优质开源项目。涵盖SpringBoot、SpringCloud、Docker、K8S等实用技术作者Github开源项目mall50K+Star
![](https://images-machen.oss-cn-beijing.aliyuncs.com/macrozheng.png)

@ -0,0 +1,23 @@
---
sidebar_position: 6
---
# 公众号合作
## 推荐须知
hippo4j 作为一款新兴动态线程池框架,开源出来的时间比较晚,目前迫切需要不同的途径进行推广。
如果您是公众号运营者或者开源爱好者,欢迎将 hippo4j 推荐给您的粉丝。
1. 您无需为 hippo4j 专门撰写文案,只需要直接导入 [推荐文章](https://mp.weixin.qq.com/s/JTTwcBEiK_MnFcPTZl3zGA) 即可。
2. 在文章底部或内容中留下项目官网或者 GitHub 仓库链接。
3. 文章需至少 1000+ 的阅读量。
作为推荐回报hippo4j 可以为您:
1. 在框架官方文档 [推荐公众号](/docs/user_docs/other/official-ccounts) 页面处留下您的公众号二维码。
2. 在框架官方交流群里@全体成员推广您的公众号一次,附带介绍语。
3. 您的公众号所有新推文章都可以将链接发送到 hippo4j 交流群中,增加阅读量。
如果您还有除公众号以外的其它途径可以与 hippo4j 相互推荐,欢迎 [加群沟通](/group)。

@ -0,0 +1,246 @@
---
sidebar_position: 3
---
# 问题提问
文档引用自:[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)
## 在提问之前
在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前,请先做到以下事情:
1. 尝试在你准备提问的论坛的旧文章中搜索答案。
2. 尝试上网搜索以找到答案。
3. 尝试阅读手册以找到答案。
4. 尝试阅读常见问题文件FAQ以找到答案。
5. 尝试自己检查或试验以找到答案。
6. 向你身边的强者朋友打听以找到答案。
7. 如果你是程序开发者,请尝试阅读源代码以找到答案。
当你提出问题的时候,请先表明你已经做了上述的努力;这将有助于树立你并不是一个不劳而获且浪费别人的时间的提问者。如果你能一并表达在做了上述努力的过程中所**学到**的东西会更好,因为我们更乐于回答那些表现出能从答案中学习的人的问题。
## 当你提问时
### 慎选提问的论坛
小心选择你要提问的场合。如果你做了下述的事情,你很可能被忽略掉或者被看作失败者:
* 在与主题不合的论坛上贴出你的问题。
* 在探讨进阶技术问题的论坛张贴非常初级的问题;反之亦然。
* 在太多的不同新闻群组上重复转贴同样的问题cross-post
* 向既非熟人也没有义务解决你问题的人发送私人电邮。
因此第一步是找到对的论坛。再说一次Google 和其它搜索引擎还是你的朋友用它们来找到与你遭遇到困难的软硬件问题最相关的网站。通常那儿都有常见问题FAQ、邮件列表及相关说明文件的链接。如果你的努力包括**阅读** FAQ都没有结果网站上也许还有报告 BugBug-reporting的流程或链接如果是这样链过去看看。
### 使用有意义且描述明确的标题
在邮件列表、新闻群组或论坛中,大约 50 字以内的标题是抓住资深专家注意力的好机会。别用喋喋不休的帮帮忙、跪求、急(更别说救命啊!!!!这样让人反感的话,用这种标题会被条件反射式地忽略)来浪费这个机会。不要妄想用你的痛苦程度来打动我们,而应该是在这点空间中使用极简单扼要的描述方式来提出问题。
一个好标题范例是`目标 —— 差异`式的描述,许多技术支持组织就是这样做的。在`目标`部分指出是哪一个或哪一组东西有问题,在`差异`部分则描述与期望的行为不一致的地方。
> 蠢问题:救命啊!我的笔记本电脑不能正常显示了!
> 聪明问题X.org 6.8.1 的鼠标指针会变形,某牌显卡 MV1005 芯片组。
> 更聪明问题X.org 6.8.1 的鼠标指针,在某牌显卡 MV1005 芯片组环境下 - 会变形。
### 使用清晰、正确、精准且合乎语法的语句
我们从经验中发现,粗心的提问者通常也会粗心地写程序与思考(我敢打包票)。回答粗心大意者的问题很不值得,我们宁愿把时间耗在别处。
正确的拼写、标点符号和大小写是很重要的。一般来说,如果你觉得这样做很麻烦,不想在乎这些,那我们也觉得麻烦,不想在乎你的提问。花点额外的精力斟酌一下字句,用不着太僵硬与正式 —— 事实上,黑客文化很看重能准确地使用非正式、俚语和幽默的语句。但它**必须很**准确,而且有迹象表明你是在思考和关注问题。
### 精确地描述问题并言之有物
* 仔细、清楚地描述你的问题或 Bug 的症状。
* 描述问题发生的环境(机器配置、操作系统、应用程序、以及相关的信息),提供经销商的发行版和版本号(如:`Fedora Core 4`、`Slackware 9.1`等)。
* 描述在提问前你是怎样去研究和理解这个问题的。
* 描述在提问前为确定问题而采取的诊断步骤。
* 描述最近做过什么可能相关的硬件或软件变更。
* 尽可能地提供一个可以`重现这个问题的可控环境`的方法。
尽量去揣测一个黑客会怎样反问你,在你提问之前预先将黑客们可能提出的问题回答一遍。
以上几点中,当你报告的是你认为可能在代码中的问题时,给黑客一个可以重现你的问题的环境尤其重要。当你这么做时,你得到有效的回答的机会和速度都会大大的提升。
[Simon Tatham](http://www.chiark.greenend.org.uk/~sgtatham/) 写过一篇名为《[如何有效的报告 Bug](http://www.chiark.greenend.org.uk/~sgtatham/bugs-cn.html)》的出色文章。强力推荐你也读一读。
### 话不在多而在精
你需要提供精确有内容的信息。这并不是要求你简单的把成堆的出错代码或者资料完全转录到你的提问中。如果你有庞大而复杂的测试样例能重现程序挂掉的情境,尽量将它剪裁得越小越好。
这样做的用处至少有三点。
第一,表现出你为简化问题付出了努力,这可以使你得到回答的机会增加;
第二,简化问题使你更有可能得到**有用**的答案;
第三,在精炼你的 bug 报告的过程中,你很可能就自己找到了解决方法或权宜之计。
### 别动辄声称找到 Bug
当你在使用软件中遇到问题,除非你非常、**非常**的有根据,不要动辄声称找到了 Bug。提示除非你能提供解决问题的源代码补丁或者提供回归测试来表明前一版本中行为不正确否则你都多半不够完全确信。这同样适用在网页和文件如果你声称发现了文件的`Bug`,你应该能提供相应位置的修正或替代文件。
请记得,还有其他许多用户没遇到你发现的问题,否则你在阅读文件或搜索网页时就应该发现了(你在抱怨前[已经做了这些,是吧](#在提问之前)?)。这也意味着很有可能是你弄错了而不是软件本身有问题。
编写软件的人总是非常辛苦地使它尽可能完美。如果你声称找到了 Bug也就是在质疑他们的能力即使你是对的也有可能会冒犯到其中某部分人。当你在标题中嚷嚷着有`Bug`时,这尤其严重。
提问时,即使你私下非常确信已经发现一个真正的 Bug最好写得像是**你**做错了什么。如果真的有 Bug你会在回复中看到这点。这样做的话如果真有 Bug维护者就会向你道歉这总比你惹恼别人然后欠别人一个道歉要好一点。
### 低声下气不能代替你的功课
有些人明白他们不该粗鲁或傲慢的提问并要求得到答复,但他们选择另一个极端 —— 低声下气:`我知道我只是个可悲的新手,一个撸瑟,但...`。这既使人困扰,也没有用,尤其是伴随着与实际问题含糊不清的描述时更令人反感。
别用原始灵长类动物的把戏来浪费你我的时间。取而代之的是,尽可能清楚地描述背景条件和你的问题情况。这比低声下气更好地定位了你的位置。
有时网页论坛会设有专为新手提问的版面,如果你真的认为遇到了初学者的问题,到那去就是了,但一样别那么低声下气。
### 描述问题症状而非你的猜测
告诉黑客们你认为问题是怎样造成的并没什么帮助。(如果你的推断如此有效,还用向别人求助吗?),因此要确信你原原本本告诉了他们问题的症状,而不是你的解释和理论;让黑客们来推测和诊断。如果你认为陈述自己的猜测很重要,清楚地说明这只是你的猜测,并描述为什么它们不起作用。
**蠢问题**
> 我在编译内核时接连遇到 SIG11 错误,
> 我怀疑某条飞线搭在主板的走线上了,这种情况应该怎样检查最好?
**聪明问题**
> 我的组装电脑是 FIC-PA2007 主机板搭载 AMD K6/233 CPU威盛 Apollo VP2 芯片组),
> 256MB Corsair PC133 SDRAM 内存,在编译内核时,从开机 20 分钟以后就频频产生 SIG11 错误,
> 但是在头 20 分钟内从没发生过相同的问题。重新启动也没有用,但是关机一晚上就又能工作 20 分钟。
> 所有内存都换过了,没有效果。相关部分的标准编译记录如下…
由于以上这点似乎让许多人觉得难以配合,这里有句话可以提醒你:`所有的诊断专家都来自密苏里州。` 美国国务院的官方座右铭则是:`让我看看`(出自国会议员 Willard D. Vandiver 在 1899 年时的讲话:`我来自一个出产玉米,棉花,牛蒡和民主党人的国家,滔滔雄辩既不能说服我,也不会让我满意。我来自密苏里州,你必须让我看看。` 针对诊断者而言,这并不是一种怀疑,而只是一种真实而有用的需求,以便让他们看到的是与你看到的原始证据尽可能一致的东西,而不是你的猜测与归纳的结论。所以,大方的展示给我们看吧!
### 按发生时间先后列出问题症状
问题发生前的一系列操作,往往就是对找出问题最有帮助的线索。因此,你的说明里应该包含你的操作步骤,以及机器和软件的反应,直到问题发生。在命令行处理的情况下,提供一段操作记录(例如运行脚本工具所生成的),并引用相关的若干行(如 20 行)记录会非常有帮助。
如果挂掉的程序有诊断选项(如 -v 的详述开关),试着选择这些能在记录中增加调试信息的选项。记住,`多`不等于`好`。试着选取适当的调试级别以便提供有用的信息而不是让读者淹没在垃圾中。
如果你的说明很长(如超过四个段落),在开头简述问题,接下来再按时间顺序详述会有所帮助。这样黑客们在读你的记录时就知道该注意哪些内容了。
### 描述目标而不是过程
如果你想弄清楚如何做某事(而不是报告一个 Bug在开头就描述你的目标然后才陈述重现你所卡住的特定步骤。
经常寻求技术帮助的人在心中有个更高层次的目标,而他们在自以为能达到目标的特定道路上被卡住了,然后跑来问该怎么走,但没有意识到这条路本身就有问题。结果要费很大的劲才能搞定。
**蠢问题**
> 我怎样才能从某绘图程序的颜色选择器中取得十六进制的 RGB 值?
**聪明问题**
> 我正试着用替换一幅图片的色码color table成自己选定的色码我现在知道的唯一方法是编辑每个色码区块table slot
> 但却无法从某绘图程序的颜色选择器取得十六进制的 RGB 值。
第二种提问法比较聪明,你可能得到像是```建议采用另一个更合适的工具```的回复。
### 清楚明确的表达你的问题以及需求
漫无边际的提问是近乎无休无止的时间黑洞。最有可能给你有用答案的人通常也正是最忙的人(他们忙是因为要亲自完成大部分工作)。这样的人对无节制的时间黑洞相当厌恶,所以他们也倾向于厌恶那些漫无边际的提问。
如果你明确表述需要回答者做什么(如提供指点、发送一段代码、检查你的补丁、或是其他等等),就最有可能得到有用的答案。因为这会定出一个时间和精力的上限,便于回答者能集中精力来帮你。这么做很棒。
要理解专家们所处的世界,请把专业技能想像为充裕的资源,而回复的时间则是稀缺的资源。你要求他们奉献的时间越少,你越有可能从真正专业而且很忙的专家那里得到解答。
所以,界定一下你的问题,使专家花在辨识你的问题和回答所需要付出的时间减到最少,这技巧对你有用答案相当有帮助 —— 但这技巧通常和简化问题有所区别。因此,问`我想更好地理解 X可否指点一下哪有好一点说明`通常比问`你能解释一下 X 吗?`更好。如果你的代码不能运作,通常请别人看看哪里有问题,比要求别人替你改正要明智得多。
### 礼多人不怪,而且有时还很有帮助
彬彬有礼,多用`请`和`谢谢您的关注`,或`谢谢你的关照`。让大家都知道你对他们花时间免费提供帮助心存感激。
坦白说,这一点并没有比使用清晰、正确、精准且合乎语法和避免使用专用格式重要(也不能取而代之)。黑客们一般宁可读有点唐突但技术上鲜明的 Bug 报告,而不是那种有礼但含糊的报告。(如果这点让你不解,记住我们是按问题能教给我们什么来评价问题的价值的)
然而,如果你有一串的问题待解决,客气一点肯定会增加你得到有用回应的机会。
(我们注意到,自从本指南发布后,从资深黑客那里得到的唯一严重缺陷反馈,就是对预先道谢这一条。一些黑客觉得`先谢了`意味着事后就不用再感谢任何人的暗示。我们的建议是要么先说`先谢了`**然后**事后再对回复者表示感谢,或者换种方式表达感激,譬如用`谢谢你的关注`或`谢谢你的关照`。)
## 不该问的问题
以下是几个经典蠢问题,以及黑客没回答时心中所想的:
问题:[我能在哪找到 X 程序或 X 资源?](#q1)
问题:[我怎样用 X 做 Y](#q2)
问题:[我的程序/设定/SQL 语句没有用](#q3)
问题:[我的 Windows 电脑有问题,你能帮我吗?](#q4)
问题:[我的程序不会动了,我认为系统工具 X 有问题](#q5)
问题:[我在安装 Linux或者 X )时有问题,你能帮我吗?](#q6)
问题:[我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢?](#q7)
---
> 问题:我能在哪找到 X 程序或 X 资源?
回答:就在我找到它的地方啊,白痴 —— 搜索引擎的那一头。天哪!难道还有人不会用 [Google](https://www.google.com) 吗?
> 问题:我怎样用 X 做 Y
回答:如果你想解决的是 Y ,提问时别给出可能并不恰当的方法。这种问题说明提问者不但对 X 完全无知,也对 Y 要解决的问题糊涂,还被特定形势禁锢了思维。最好忽略这种人,等他们把问题搞清楚了再说。
> 问题:我的{程序/设定/SQL 语句}没有用
回答:这不算是问题吧,我对要我问你二十个问题才找得出你真正问题的问题没兴趣 —— 我有更有意思的事要做呢。在看到这类问题的时候,我的反应通常不外如下三种
* 你还有什么要补充的吗?
* 真糟糕,希望你能搞定。
* 这关我屁事?
> 问题:我的 Windows 电脑有问题,你能帮我吗?
回答:能啊,扔掉微软的垃圾,换个像 Linux 或 BSD 的开源操作系统吧。
注意:如果程序有官方版 Windows 或者与 Windows 有互动(如 Samba你**可以**问与 Windows 相关的问题,只是别对问题是由 Windows 操作系统而不是程序本身造成的回复感到惊讶, 因为 Windows 一般来说实在太烂,这种说法通常都是对的。
> 问题:我的程序不会动了,我认为系统工具 X 有问题
回答:你完全有可能是第一个注意到被成千上万用户反复使用的系统调用与函数库文件有明显缺陷的人,更有可能的是你完全没有根据。不同凡响的说法需要不同凡响的证据,当你这样声称时,你必须有清楚而详尽的缺陷说明文件作后盾。
> 问题:我在安装 Linux或者 X )时有问题,你能帮我吗?
回答:不能,我只有亲自在你的电脑上动手才能找到毛病。还是去找你当地的 Linux 使用群组者寻求实际的指导吧(你能在[这儿](http://www.linux.org/groups/index.html)找到用户群组的清单)。
注意:如果安装问题与某 Linux 的发行版有关,在它的邮件列表、论坛或本地用户群组中提问也许是恰当的。此时,应描述问题的准确细节。在此之前,先用 `Linux` 和**所有**被怀疑的硬件作关键词仔细搜索。
> 问题:我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢?
回答:想要这样做,说明了你是个卑鄙小人;想找个黑客帮你,说明你是个白痴!
## 好问题与蠢问题
最后,我将透过举一些例子,来说明怎样聪明的提问;同一个问题的两种问法被放在一起,一种是愚蠢的,另一种才是明智的。
**蠢问题**
> 我从 foo 项目找来的源码没法编译。它怎么这么烂?
他觉得都是别人的错,这个傲慢自大的提问者。
**聪明问题**
> foo 项目代码在 Nulix 6.2 版下无法编译通过。我读过了 FAQ但里面没有提到跟 Nulix 有关的问题。这是我编译过程的记录,我有什么做的不对的地方吗?
提问者已经指明了环境,也读过了 FAQ还列出了错误并且他没有把问题的责任推到别人头上他的问题值得被关注。
**蠢问题**
> 我的主机板有问题了,谁来帮我?
某黑客对这类问题的回答通常是:`好的,还要帮你拍拍背和换尿布吗?`,然后按下删除键。
**聪明问题**
> 我在 S2464 主机板上试过了 X 、 Y 和 Z ,但没什么作用,我又试了 A 、 B 和 C 。请注意当我尝试 C 时的奇怪现象。显然 florbish 正在 grommicking但结果出人意料。通常在 Athlon MP 主机板上引起 grommicking 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题?
## 如果得不到回答
如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。
总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。
你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。

@ -0,0 +1,8 @@
{
"label": "用户指南",
"position": 2,
"link": {
"type": "generated-index",
"description": "帮助想要了解 Hippo4j 的用户快速掌握核心开发理念。"
}
}

@ -0,0 +1,60 @@
---
sidebar_position: 1
---
# 为什么写
[美团线程池文章](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html "美团线程池文章") 介绍中,因为业务对线程池参数没有合理配置,触发过几起生产事故,进而引发了一系列思考。最终决定封装线程池动态参数调整,扩展线程池监控以及消息报警等功能。
在开源平台找了挺多动态线程池项目,从功能性以及健壮性而言,个人感觉不满足企业级应用。
因为对动态线程池比较感兴趣,加上想写一个有意义的项目,所以决定自己来造一个轻量级的轮子。
想给项目起一个简单易记的名字,类似于 Eureka、Nacos、Redis后和朋友商量决定命名**Hippo4j**。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/动态线程池功能架构-1.jpg)
## 它解决了什么问题
线程池在业务系统应该都有使用到,帮助业务流程提升效率以及管理线程,多数场景应用于大量的异步任务处理。
虽然线程池提供了我们许多便利,但也并非尽善尽美,比如下面这些问题就无法很好解决。
- 线程池随便定义,线程资源过多,造成服务器高负载。
- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。
- 线程池任务执行时间超过平均执行周期,开发人员无法感知。
- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。
- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。
- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。
- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。
- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。
Hippo4j 很好解决了这些问题,它将业务中所有线程池统一管理,增强原生线程池系列功能。
## 它有什么特性
应用系统中线程池并不容易管理。参考美团的设计Hippo4j 按照租户、项目、线程池的维度划分。再加上系统权限,让不同的开发、管理人员负责自己系统的线程池操作。
举个例子,小编在一家公司的公共组件团队,团队中负责消息、短链接网关等项目。公共组件是租户,消息或短链接就是项目。
Hippo4j 除去动态修改线程池,还包含实时查看线程池运行时指标、负载报警、配置日志管理等。
- `hippo4j-adapter`:适配对第三方框架中的线程池进行监控,如 Dubbo、RocketMQ、Hystrix 等;
- `hippo4j-auth`:用户、角色、权限等;
- `hippo4j-common`:多个模块公用代码实现;
- `hippo4j-config`:提供线程池准实时参数更新功能;
- `hippo4j-console`:对接前端控制台;
- `hippo4j-core`:核心的依赖,包括配置、核心包装类等;
- `hippo4j-discovery`:提供线程池项目实例注册、续约、下线等功能;
- `hippo4j-example` :示例工程;
- `hippo4j-message` :配置变更以及报警通知发送;
- `hippo4j-monitor` :线程池运行时监控;
- `hippo4j-server` Server 端发布需要的模块聚合;
- `hippo4j-spring-boot`SpringBoot Starter。

@ -0,0 +1,51 @@
---
sidebar_position: 2
---
# 架构设计
简单来说Hippo4j 从部署的角度上分为两种角色Server 端和 Client 端。
Server 端是 Hippo4j 项目打包出的 Java 进程,功能包括用户权限、线程池监控以及执行持久化的动作。
Client 端指的是我们 SpringBoot 应用,通过引入 Hippo4j Starter Jar 包负责与 Server 端进行交互。
比如拉取 Server 端线程池数据、动态更新线程池配置以及采集上报线程池运行时数据等。
## 基础组件
### 配置中心Config
配置中心位于 Server 端,它的主要作用是监控 Server 端线程池配置变更,实时通知到 Client 实例执行线程池变更流程。
代码设计基于 Nacos 1.x 版本的 **长轮询以及异步 Servlet 机制** 实现。
### 注册中心Discovery
负责管理 Client 端(单机或集群)注册到 Server 端的实例,包括不限于**实例注册、续约、过期剔除** 等操作,代码基于 Eureka 源码实现。
上面的配置中心很容易理解,动态线程池参数变更的根本。但是注册中心是用来做什么的?
注册中心管理 Client 端注册的实例,通过这些实例可以 **实时获取线程池的运行时参数信息**。
目前的设计是如此,不排除后续基于 Discovery 做更多的扩展。
### 控制台Console
对接前端项目,包括不限于以下模块管理:
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211107122504126.png)
## 消息通知Notify
Hippo4j 内置了很多需要通知的事件,比如:线程池参数变更通知、线程池活跃度报警、拒绝策略执行报警以及阻塞队列容量报警等。
目前 Notify 已经接入了钉钉、企业微信和飞书后续持续集成邮件、短信等通知渠道并且Notify 模块提供了消息事件的 SPI 方案,可以接受三方自定义的推送。
## Hippo4j-Spring-Boot-Starter
熟悉 SpringBoot 的小伙伴对 Starter 应该不会陌生。Hippo4j 提供以 Starter Jar 包的形式嵌套在应用内,负责与 Server 端完成交互。
## 功能架构
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211105230953626.png)

@ -0,0 +1,75 @@
---
sidebar_position: 4
---
# 通知报警
现阶段已集成钉钉、企业微信、飞书的消息推送,后续会持续接入邮箱、短信和自定义通知渠道。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220904181527453.png)
**通知平台**
- DING钉钉平台
- LARK飞书平台
- WECHAT企业微信。
**通知类型**
- CONFIG线程池配置变更推送
- ALARM线程池运行报警推送。
**Token**
获取 DING、LARK、WECHAT 机器人 Token。
**报警间隔**
- CONFIG 类型通知没有报警间隔;
- ALARM 类型设置报警间隔后,某一节点下的同一线程池指定间隔只会发送一次报警通知。
**接收者**
```tex
多个接收者使用英文逗号 , 分割 (注意不要有空格)
DING填写手机号
WECHART填写user_id会以@的消息发给用户,填写姓名则是普通的@,如:龙台
LARK填写ou_开头用户唯一标识会以@的消息发给用户,填写手机号则是普通的@
```
## 钉钉平台
[钉钉创建群机器人](https://www.dingtalk.com/qidian/help-detail-20781541.html)
| 配置变更 | 报警通知 |
| :---: | :---: |
| ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211013122816688.png) | ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211013113649068.png) |
添加钉钉机器人后,需在机器人配置自定义关键字,才可发送成功。如下所示:
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220530200133377.png?x-oss-process=image/resize,h_500,w_800)
:::tip
如果使用 1.4.3 及以上版本,`警报` 替换为 `告警`
:::
## 企业微信
[企业微信创建群机器人](https://open.work.weixin.qq.com/help2/pc/14931?person_id=1&from=homesearch)
| 配置变更 | 报警通知 |
| :---: | :---: |
| ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211203213443242.png) | ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211203213512019.png) |
## 飞书平台
[飞书创建群机器人](https://www.feishu.cn/hc/zh-CN/articles/360024984973)
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220304081729347.png)
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220304081507907.png)

@ -0,0 +1,40 @@
---
sidebar_position: 3
---
# 快速开始
## 服务启动
使用 Docker 运行服务端,默认使用内置 H2 数据库,数据持久化到 Docker 容器存储卷中。
```shell
docker run -d -p 6691:6691 --name hippo4j-server hippo4j/hippo4j-server
```
> 如果没有 Docker可以使用源码编译的方式启动 [Hippo4j-Server/Hippo4j-Bootstrap](https://github.com/longtai-cn/hippo4j/tree/develop/hippo4j-server/hippo4j-bootstrap) 模块下 ServerApplication 应用类。
启动示例项目,[hippo4j-spring-boot-starter-example](https://github.com/opengoofy/hippo4j/tree/develop/hippo4j-example/hippo4j-spring-boot-starter-example) 模块下 ServerExampleApplication 应用类。
访问 Server 控制台,路径 `http://localhost:6691/index.html`默认用户名密码admin / 123456
## 配置变更
访问控制台动态线程池菜单下线程池实例,修改动态线程池相关参数。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220813173811668.png)
观察 Hippo4j-Example 控制台日志输出,日志输出包括不限于此信息即为成功。
```tex
2022-09-10 00:23:29.783 INFO 50322 --- [change.config_0] c.h.s.s.c.ServerThreadPoolDynamicRefresh : [message-consume] Dynamic thread pool change parameter.
corePoolSize: 2 => 4
maximumPoolSize: 6 => 12
capacity: 1024 => 2048
keepAliveTime: 9999 => 9999
executeTimeOut: 800 => 3000
rejectedType: SyncPutQueuePolicy => RunsOldestTaskPolicy
allowCoreThreadTimeOut: true => true
```
另外,当 Client 集群部署时,可以修改某一个实例,或选择 `全部修改` 按钮,修改所有实例线程池信息。

@ -0,0 +1,8 @@
{
"label": "开发者手册",
"position": 5,
"link": {
"type": "generated-index",
"description": "Hippo4j 留给使用者能够扩展的知识点。"
}
}

@ -0,0 +1,57 @@
---
sidebar_position: 1
---
# 拒绝策略自定义
Hippo4j 通过 SPI 的方式对拒绝策略进行扩展,可以让用户在 Hippo4j 中完成自定义拒绝策略实现。
## Hippo4j Server 拒绝策略扩展
自定义拒绝策略,实现 `CustomRejectedExecutionHandler` 接口,示例如下:
```java
public class ErrorLogRejectedExecutionHandler implements CustomRejectedExecutionHandler {
@Override
public Integer getType() {
return 12;
}
@Override
public RejectedExecutionHandler generateRejected() {
return new CustomErrorLogRejectedExecutionHandler();
}
public static class CustomErrorLogRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
Logger logger = LoggerFactory.getLogger(this.getClass());
logger.error("线程池抛出拒绝策略");
}
}
}
```
创建 `src/main/resources/META-INF/services` 目录,创建 SPI 自定义拒绝策略文件 `cn.hippo4j.common.executor.support.CustomRejectedExecutionHandler`
`cn.hippo4j.common.executor.support.CustomRejectedExecutionHandler` 文件内仅放一行自定义拒绝策略全限定名即可,示例:
```text
cn.hippo4j.example.core.handler.ErrorLogRejectedExecutionHandler
```
创建、修改线程池页面选择 `CustomRejectedPolicy自定义 SPI 策略)`
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220813173907814.png)
拒绝策略触发时,完成上述代码效果,仅打印异常日志提示。
```text
2022-08-01 21:27:49.515 ERROR 48928 --- [ateHandler.test] r$CustomErrorLogRejectedExecutionHandler : 线程池抛出拒绝策略
```
:::note
具体参考 `hippo4j-example/hippo4j-spring-boot-starter-example` 模块。
:::

@ -0,0 +1,50 @@
---
sidebar_position: 0
---
# 内置拒绝策略
内置两种拒绝策略说明:
**RunsOldestTaskPolicy**:添加新任务并由主线程运行最早的任务。
```java
public class RunsOldestTaskPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (executor.isShutdown()) {
return;
}
BlockingQueue<Runnable> workQueue = executor.getQueue();
Runnable firstWork = workQueue.poll();
boolean newTaskAdd = workQueue.offer(r);
if (firstWork != null) {
firstWork.run();
}
if (!newTaskAdd) {
executor.execute(r);
}
}
}
```
**SyncPutQueuePolicy**:主线程把拒绝任务以阻塞的方式添加到队列。
```java
@Slf4j
public class SyncPutQueuePolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (executor.isShutdown()) {
return;
}
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
log.error("Adding Queue task to thread pool failed.", e);
}
}
}
```

@ -0,0 +1,7 @@
{
"label": "快速开始",
"position": 3,
"link": {
"type": "generated-index"
}
}

@ -0,0 +1,5 @@
{
"label": "依赖配置中心",
"position": 2,
"collapsed": true
}

@ -0,0 +1,45 @@
---
sidebar_position: 4
---
# 参数默认配置
曾有多名小伙伴反馈说,项目中线程池一多,配置文件中配置就显得很臃肿。为此 hippo4j-config 开发出了动态线程池默认配置。
```yaml
spring:
dynamic:
thread-pool:
default-executor:
core-pool-size: 4
maximum-pool-size: 6
blocking-queue: ResizableCapacityLinkedBlockingQueue
queue-capacity: 1024
execute-time-out: 1000
keep-alive-time: 9999
rejected-handler: AbortPolicy
active-alarm: 90
capacity-alarm: 85
alarm: true
allow-core-thread-time-out: true
notify:
interval: 5
receives: chen.ma
executors:
- thread-pool-id: message-produce
- thread-pool-id: message-consume
core-pool-size: 80
maximum-pool-size: 100
execute-time-out: 1000
notify:
interval: 6
receives: chen.ma
```
`spring.dynamic.thread-pool.executors` 层级下,仅需要配置 `thread-pool-id`,其余配置从 `spring.dynamic.thread-pool.default-executor` 读取。
如果 `spring.dynamic.thread-pool.executors` 下配置和 `spring.dynamic.thread-pool.default-executor` 冲突,以前者为主。
通过该自定义配置方式,可减少大量重复线程池参数配置项,提高核心配置简洁度。
提示:`spring.dynamic.thread-pool.default-executor` 层级下参数,不提供动态刷新功能。

@ -0,0 +1,122 @@
---
sidebar_position: 3
---
# 线程池监控
## 线程池监控配置
监控前置条件:需要先完成 hippo4j-config 的 [接入工作](/docs/user_docs/getting_started/config/hippo4j-config-start)。
接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。
```xml
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```
添加动态线程池监控相关配置:
```yaml
management:
metrics:
export:
prometheus:
enabled: true
server:
port: 29999 # 可选配置,如果不配置该 port直接使用 ${server.port}
endpoints:
web:
exposure:
include: '*' # 测试使用,开启了所有端点,生产环境不建议 *
spring:
dynamic:
thread-pool:
monitor:
enable: true # 是否开启采集线程池运行时数据
collect-interval: 5000 # 采集线程池运行数据频率
collect-types: micrometer # 采集线程池运行数据的类型。eglog、micrometer。多个可以同时使用默认 micrometer
initial-delay: 10000 # 项目启动后延迟多久进行采集
thread-pool-types: dynamic # 采集线程池的类型。egdynamic、web、adapter。可任意配置默认 dynamic
```
项目启动,访问 `http://localhost:29999/actuator/prometheus` 出现 `dynamic_thread_pool_` 前缀的指标,即为成功。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912220401016.png)
## 配置 Prometheus
通过 Docker 启动 Prometheus 服务。
```shell
docker run -d -p 9090:9090 --name prometheus prom/prometheus
```
添加 Prometheus 抽取数据任务。
```shell
# 进入 prometheus 容器内部
docker exec -it prometheus /bin/sh
# 编辑 prometheus 配置文件
vi /etc/prometheus/prometheus.yml
```
scrape_configs 节点下新添加一个 job如果 Prometheus 是 Docker 方式部署,`{scrape_configs.static_configs.targets}` 需要写本机的 IP。
```yaml
scrape_configs:
- job_name: 'dynamic-thread-pool-job'
scrape_interval: 5s
metrics_path: '/actuator/prometheus'
static_configs:
- targets: [ '127.0.0.1:29999' ]
```
配置成功后 `exit` 退出容器,并进行 Prometheus 容器重启 `docker restart prometheus`
访问 Prometheus 控制台 `http://localhost:9090/graph` 路径,能够展示相关指标即为配置成功。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912221237597.png)
## 配置 Grafana
```shell
docker run -d -p 3000:3000 --name=grafana grafana/grafana
```
访问 Grafana 地址,[http://localhost:3000](http://localhost:3000) 用户名密码:`admin`
Grafana 访问 `http://localhost:3000/datasources` 导入 Prometheus 数据源。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912221646866.png)
> 如果 Prometheus 为 Docker 方式部署HTTP URL 需要为本地 IP比如http://192.168.1.5:9090
关注公众号 `龙台的技术笔记`,回复:`监控`,获取 Hippo4j Grafana DashBoard JSON 配置。
| 公众号 | 回复关键词 |
|:------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------:|
| ![](https://images-machen.oss-cn-beijing.aliyuncs.com/43_65f6020ed111b6bb3808ec338576bd6b.png?x-oss-process=image/resize,h_300,w_400) | ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220327171957444.png?x-oss-process=image/resize,h_300,w_400) |
获取到 JSON 文件后,通过 `http://localhost:3000/dashboard/import` 将 JSON 文件导入至 Grafana DashBoard。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225627272.png)
下拉框内动态选择创建好的 Prometheus 数据源,并点击 `Import`
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225700200.png)
即可使用炫酷的 Hippo4j 动态线程池监控 DashBoard。大家伙儿也可以根据个人喜好进行定制 DashBoard如果觉得有优化点欢迎和我联系贡献。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225813972.png)
如果项目客户端启动多个示例,动态线程池监控效果图如下:
![](https://images-machen.oss-cn-beijing.aliyuncs.com/20220814_hippo4j_monitor.jpg)

@ -0,0 +1,80 @@
---
sidebar_position: 3
---
# 个性化配置
以下所述特性自 hippo4j-config v1.4.2 及以上版本提供,由 hippo4j 核心开发者 [@pizihao](https://github.com/pizihao) 完成相应功能开发。
## 需求背景
**1容器及三方框架线程池自定义启用**
最初设计容器线程池和三方框架线程池的动态变更是和启动无关的。也就是说,启动时不会根据配置文件中相关参数去修改两者对应的线程池配置。
这么设计的初衷是因为,不想让 hippo4j 过多的去介入框架原有的功能。因为容器和三方框架都支持线程池参数的自定义。
也就造成,可能你在配置中心配置了对应的容器和三方框架线程池参数,启动时是无效的。但当修改配置文件任一配置,容器和三方框架线程池配置将生效。
为了更好的用户体验,决定加入启用标识来控制:是否在项目初始化启动时,对容器和三方框架线程池参数进行修改。
**2客户端集群个性化配置**
大家都知道hippo4j-config 是依赖配置中心做线程池配置动态变更。这种模式有一种缺点:改动配置文件后,所有客户端都会变更。
有些小伙伴希望 hippo4j-config 能够像 hippo4j-server 一样,能够针对单独的客户端进行配置变更。
## 容器及三方框架线程池自定义启用
容器及三方框架线程池添加启用配置,为了保持统一,动态线程池配置中也有该参数配置。配置项默认开启。
```yaml
spring:
dynamic:
thread-pool:
tomcat:
enable: true
executors:
- thread-pool-id: message-consume
enable: false
adapter-executors:
- threadPoolKey: 'input'
enable: true
```
## 客户端集群个性化配置
分别在动态线程池、容器线程池以及三方框架线程池配置下增加 `nodes` 配置节点,通过该配置可匹配需要变更的节点。
```yaml
spring:
dynamic:
thread-pool:
tomcat:
nodes: 192.168.1.5:*,192.168.1.6:8080
executors:
- thread-pool-id: message-consume
nodes: 192.168.1.5:*
adapter-executors:
- threadPoolKey: 'input'
nodes: 192.168.1.5:*
```
来一段代码方法中的注释,大家就基本明白如何使用了。
```java
/**
* Matching nodes<br>
* nodes is ip + port.Get 'nodes' in the new Properties,Compare this with the ip + port of Application.<br>
* support prefix pattern matching. e.g: <br>
* <ul>
* <li>192.168.1.5:* -- Matches all ports of 192.168.1.5</li>
* <li>192.168.1.*:2009 -- Matches 2009 port of 192.168.1.*</li>
* <li>* -- all</li>
* <li>empty -- all</li>
* </ul>
* The format of ip + port is ip : port.
*/
```
`nodes` 可与 `enable` 同时使用。如此,基于配置中心的动态线程池实现方式,将能够更方便的支持个性化需求。

@ -0,0 +1,121 @@
---
sidebar_position: 5
---
# 适配SpringBoot1x
目前已支持 Nacos、Apollo 配置中心适配 SpringBoot 1.5.x 版本。
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-1x-starter</artifactId>
<version>1.5.0</version>
</dependency>
```
Nacos SpringBoot 配置如下:
```yaml
spring:
cloud:
nacos:
config:
ext-config:
- data-id: hippo4j-nacos.yaml
group: DEFAULT_GROUP
refresh: true
server-addr: 127.0.0.1:8848
dynamic:
thread-pool:
config-file-type: yml
nacos:
data-id: hippo4j-nacos.yaml
group: DEFAULT_GROUP
```
Apollo SpringBoot 配置如下:
```yaml
apollo:
autoUpdateInjectedSpringProperties: true
bootstrap:
eagerLoad:
enabled: true
enabled: true
namespaces: application
meta: http://127.0.0.1:8080
app:
id: dynamic-threadpool-example
spring:
dynamic:
thread-pool:
apollo:
namespace: application
```
动态线程池通用配置如下:
```yaml
management:
context-path: /actuator
security:
enabled: false
server:
port: 8091
servlet:
context-path: /example
spring:
application:
name: dynamic-threadpool-example
dynamic:
thread-pool:
banner: true
check-state-interval: 5
collect-type: micrometer
config-file-type: properties
enable: true
executors:
- active-alarm: 80
alarm: true
allow-core-thread-time-out: true
blocking-queue: LinkedBlockingQueue
capacity-alarm: 80
core-pool-size: 1
execute-time-out: 1000
keep-alive-time: 6691
maximum-pool-size: 1
notify:
interval: 8
receives: chen.ma
queue-capacity: 1
rejected-handler: AbortPolicy
thread-name-prefix: message-consume
thread-pool-id: message-consume
- active-alarm: 80
alarm: true
allow-core-thread-time-out: true
blocking-queue: LinkedBlockingQueue
capacity-alarm: 80
core-pool-size: 1
execute-time-out: 1000
keep-alive-time: 6691
maximum-pool-size: 1
notify:
interval: 8
receives: chen.ma
queue-capacity: 1
rejected-handler: AbortPolicy
thread-name-prefix: message-produce
thread-pool-id: message-produce
notify-platforms:
- platform: WECHAT
token: ac0426a5-c712-474c-9bff-72b8b8f5caff
profiles:
active: dev
```
具体 Demo 运行请参考以下示例模块,已验证对应线程池动态变更、报警以及运行时监控功能。
- `/hippo4j-config-nacos-spring-boot-1x-starter-example`
- `hippo4j-example/hippo4j-config-apollo-spring-boot-1x-starter-example`

@ -0,0 +1,193 @@
---
sidebar_position: 1
---
# 接入流程
Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心任选其一。
## hippo4j 配置
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>
```
启动类上添加注解 `@EnableDynamicThreadPool`
```java
@SpringBootApplication
@EnableDynamicThreadPool
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
```
SpringBoot 应用配置文件添加:
```yaml
server:
port: 8090
servlet:
context-path: /example
spring:
profiles:
active: dev
dynamic:
thread-pool:
# 是否开启动态线程池
enable: true
# 是否打印 banner
banner: true
# 是否开启线程池数据采集,对接 Micrometer、ES、Log 等
collect: true
# 检查线程池状态,是否达到报警条件,单位毫秒
check-state-interval: 3000
# 通知报警平台,请替换为自己创建的群机器人
notify-platforms:
- platform: 'WECHAT'
token: xxx
- platform: 'DING'
token: xxx
secret: xxx # 加签专属
- platform: 'LARK'
token: xxx
# Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 任选其一
nacos:
data-id: xxx
group: xxx
apollo:
namespace: xxxx
# 配置中心文件格式
config-file-type: yml
# 支持 tomcat、undertow、jetty 三种容器线程池
web:
core-pool-size: 100
maximum-pool-size: 200
keep-alive-time: 1000
# 全局通知配置-是否报警
alarm: true
# 活跃度报警阈值;假设线程池最大线程数 10当线程数达到 8 发起报警
active-alarm: 80
# 容量报警阈值;假设阻塞队列容量 100当容量达到 80 发起报警
capacity-alarm: 80
# 报警间隔,同一线程池下同一报警纬度,在 interval 时间内只会报警一次,单位秒
alarm-interval: 8
# 企业微信填写用户 ID填写其它将无法达到 @ 效果)、钉钉填手机号、飞书填 ou_ 开头唯一 ID
receives: xxx
# 动态线程池列表
executors:
- thread-pool-id: 'message-consume'
# 核心线程数
core-pool-size: 1
# 最大线程数
maximum-pool-size: 1
# 阻塞队列名称,参考 BlockingQueueTypeEnum支持 SPI
blocking-queue: 'LinkedBlockingQueue'
# 阻塞队列大小
queue-capacity: 1
# 执行超时时间,超过此时间发起报警,单位毫秒
execute-time-out: 1000
# 拒绝策略名称,参考 RejectedPolicyTypeEnum支持 SPI
rejected-handler: 'AbortPolicy'
# 线程存活时间,单位秒
keep-alive-time: 1024
# 是否允许核心线程超时
allow-core-thread-time-out: true
# 线程工厂名称前缀
thread-name-prefix: 'message-consume'
# 是否报警
alarm: true
# 活跃度报警阈值;假设线程池最大线程数 10当线程数达到 8 发起报警
active-alarm: 80
# 容量报警阈值;假设阻塞队列容量 100当容量达到 80 发起报警
capacity-alarm: 80
# 通知配置,线程池中通知配置如果存在,则会覆盖全局通知配置
notify:
# 报警间隔,同一线程池下同一报警纬度,在 interval 时间内只会报警一次,单位分钟
interval: 8
# 企业微信填写用户 ID填写其它将无法达到 @ 效果)、钉钉填手机号、飞书填 ou_ 开头唯一 ID
receives: xxx
- thread-pool-id: 'message-produce'
core-pool-size: 1
maximum-pool-size: 1
queue-capacity: 1
execute-time-out: 1000
blocking-queue: 'LinkedBlockingQueue'
rejected-handler: 'AbortPolicy'
keep-alive-time: 1024
allow-core-thread-time-out: true
thread-name-prefix: 'message-consume'
alarm: true
active-alarm: 80
capacity-alarm: 80
notify:
interval: 8
receives: xxx
```
## ThreadPoolExecutor 适配
添加线程池配置类,通过 `@DynamicThreadPool` 注解修饰。`threadPoolId` 为服务端创建的线程池 ID。
```java
package cn.hippo4j.example;
import cn.hippo4j.core.executor.DynamicThreadPool;
import cn.hippo4j.core.executor.support.ThreadPoolBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class ThreadPoolConfig {
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageConsumeDynamicExecutor() {
String threadPoolId = "message-consume";
ThreadPoolExecutor messageConsumeDynamicExecutor = ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
return messageConsumeDynamicExecutor;
}
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageProduceDynamicExecutor() {
String threadPoolId = "message-produce";
ThreadPoolExecutor messageProduceDynamicExecutor = ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
return messageProduceDynamicExecutor;
}
}
```
通过 ThreadPoolBuilder 构建动态线程池,只有 threadFactory、threadPoolId 为必填项,其它参数会从配置中心拉取。
项目中使用上述定义的动态线程池,如下所示:
```java
@Resource
private ThreadPoolExecutor messageConsumeDynamicExecutor;
messageConsumeDynamicExecutor.execute(() -> xxx);
@Resource
private ThreadPoolExecutor messageProduceDynamicExecutor;
messageProduceDynamicExecutor.execute(() -> xxx);
```

@ -0,0 +1,37 @@
---
sidebar_position: 0
---
# 运行模式介绍
1.1.0 版本发布后Hippo4j 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220319154626314.png)
### Hippo4j config
**轻量级动态线程池管理**,依赖 Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 等三方配置中心(任选其一)完成线程池参数动态变更,支持运行时报警、监控等功能。
> 监控功能配置详见:[线程池监控](/docs/user_docs/getting_started/config/hippo4j-config-monitor)
![](https://images-machen.oss-cn-beijing.aliyuncs.com/20220814_hippo4j_monitor.jpg)
### Hippo4j server
**部署 Hippo4j server 服务**,通过可视化 Web 界面完成线程池的创建、变更以及查看,不依赖三方中间件。
相比较 Hippo4j config功能会更强大但同时也引入了一定的复杂性。需要部署一个 Java 服务,以及依赖 MySQL 数据库。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/1644032018254-min.gif)
### 使用总结
| | Hippo4j config | Hippo4j server |
| ---- |-------------------------------------------------------|--------------------------------------------------------|
| 依赖 | Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心(任选其一) | 部署 Hippo4j server内部无依赖中间件 |
| 使用 | 配置中心补充线程池相关参数 | Hippo4j server web 控制台添加线程池记录 |
| 功能 | 包含基础功能:参数动态化、运行时监控、报警等 | 基础功能之外扩展控制台界面、线程池堆栈查看、线程池运行信息实时查看、历史运行信息查看、线程池配置集群个性化等 |
使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 Hippo4j config 使用即可;如果希望更多的功能,可以选择 Hippo4j server。
**两者在进行替换的时候,无需修改业务代码**。

@ -0,0 +1,72 @@
---
sidebar_position: 6
---
# 三方框架线程池适配
Hippo4j 目前已支持的三方框架线程池列表:
- Dubbo
- Hystrix
- RabbitMQ
- RocketMQ
- AlibabaDubbo
- RocketMQSpringCloudStream
- RabbitMQSpringCloudStream
引入 Hippo4j Server 或 Core 的 Maven Jar 坐标后,还需要引入对应的框架适配 Jar
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<!-- Dubbo -->
<artifactId>hippo4j-spring-boot-starter-adapter-dubbo</artifactId>
<!-- Alibaba Dubbo -->
<artifactId>hippo4j-spring-boot-starter-adapter-alibaba-dubbo</artifactId>
<!-- Hystrix -->
<artifactId>hippo4j-spring-boot-starter-adapter-hystrix</artifactId>
<!-- RabbitMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-rabbitmq</artifactId>
<!-- RocketMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-rocketmq</artifactId>
<!-- SpringCloud Stream RocketMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq</artifactId>
<!-- SpringCloud Stream RabbitMQ -->
<artifactId>hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq</artifactId>
<version>1.5.0</version>
</dependency>
```
如果想省事,仅需引入一个全量包,框架底层会根据条件判断加载具体线程池适配器。
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter-adapter-all</artifactId>
<version>1.5.0</version>
</dependency>
```
## Hippo4j Server
Hippo4j Server 仅需要引入上述 Jar 包,即可在 Hippo4j Server 的控制台进行查看及修改三方框架线程池。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220531194810047.png)
## Hippo4j Config
Hippo4j Config 除了依赖上述适配 Jar 包外,还需要在配置中心添加以下配置项。
```yaml
spring:
dynamic:
thread-pool:
# 省略其它配置
adapter-executors:
# threadPoolKey 代表线程池标识
- threadPoolKey: 'input'
# mark 为三方线程池框架类型,参见文初已支持框架集合
mark: 'RocketMQSpringCloudStream'
corePoolSize: 10
maximumPoolSize: 10
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

@ -0,0 +1,5 @@
{
"label": "无中间件依赖",
"position": 3,
"collapsed": true
}

@ -0,0 +1,19 @@
---
sidebar_position: 3
---
# 服务端配置
`hippo4j.core.clean-history-data-enable`
是否开启线程池历史数据清洗,默认开启。
`hippo4j.core.clean-history-data-period`
线程池历史数据保留时间默认值30单位分钟。
服务端会保留这个配置时间的数据,超过这个时间则会被清理。比如按照默认值 30 分钟来说12:00 收集到的数据12:30 就会被清理删除。
`hippo4j.core.monitor.report-type`
客户端监控上报服务端类型可选值http、netty默认 http。服务端开启 netty 配置后,需要在客户端对应开启才可生效。用来应对大量动态线程池监控场景。

@ -0,0 +1,132 @@
---
sidebar_position: 2
---
# 线程池监控
Server 模式默认内置线程池运行时采集和监控功能,如果想要使用 Prometheus + Grafana 的方式可以查看以下内容。
## 线程池监控配置
接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。
```xml
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```
添加动态线程池监控相关配置:
```yaml
management:
metrics:
export:
prometheus:
enabled: true
server:
port: 29999 # 可选配置,如果不配置该 port直接使用 ${server.port}
endpoints:
web:
exposure:
include: '*' # 测试使用,开启了所有端点,生产环境不建议 *
spring:
dynamic:
thread-pool:
monitor:
enable: true # 是否开启采集线程池运行时数据
collect-interval: 5000 # 采集线程池运行数据频率
collect-types: server,micrometer # 采集线程池运行数据的类型。egserver、micrometer。多个可以同时使用默认 server
initial-delay: 10000 # 项目启动后延迟多久进行采集
thread-pool-types: dynamic # 采集线程池的类型。egdynamic、web、adapter。可任意配置默认 dynamic
```
如果使用 `micrometer` 类型的监控指标,需要添加以下依赖。
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter-monitor-micrometer</artifactId>
<version>1.5.0</version>
</dependency>
```
项目启动,访问 `http://localhost:29999/actuator/prometheus` 出现 `dynamic_thread_pool_` 前缀的指标,即为成功。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912220401016.png)
## 配置 Prometheus
通过 Docker 启动 Prometheus 服务。
```shell
docker run -d -p 9090:9090 --name prometheus prom/prometheus
```
添加 Prometheus 抽取数据任务。
```shell
# 进入 prometheus 容器内部
docker exec -it prometheus /bin/sh
# 编辑 prometheus 配置文件
vi /etc/prometheus/prometheus.yml
```
scrape_configs 节点下新添加一个 job如果 Prometheus 是 Docker 方式部署,`{scrape_configs.static_configs.targets}` 需要写本机的 IP。
```yaml
scrape_configs:
- job_name: 'dynamic-thread-pool-job'
scrape_interval: 5s
metrics_path: '/actuator/prometheus'
static_configs:
- targets: [ '127.0.0.1:29999' ]
```
配置成功后 `exit` 退出容器,并进行 Prometheus 容器重启 `docker restart prometheus`
访问 Prometheus 控制台 `http://localhost:9090/graph` 路径,能够展示相关指标即为配置成功。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912221237597.png)
## 配置 Grafana
```shell
docker run -d -p 3000:3000 --name=grafana grafana/grafana
```
访问 Grafana 地址,[http://localhost:3000](http://localhost:3000) 用户名密码:`admin`
Grafana 访问 `http://localhost:3000/datasources` 导入 Prometheus 数据源。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912221646866.png)
> 如果 Prometheus 为 Docker 方式部署HTTP URL 需要为本地 IP比如http://192.168.1.5:9090
关注公众号 `龙台的技术笔记`,回复:`监控`,获取 Hippo4j Grafana DashBoard JSON 配置。
| 公众号 | 回复关键词 |
|:------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------:|
| ![](https://images-machen.oss-cn-beijing.aliyuncs.com/43_65f6020ed111b6bb3808ec338576bd6b.png?x-oss-process=image/resize,h_300,w_400) | ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220327171957444.png?x-oss-process=image/resize,h_300,w_400) |
获取到 JSON 文件后,通过 `http://localhost:3000/dashboard/import` 将 JSON 文件导入至 Grafana DashBoard。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225627272.png)
下拉框内动态选择创建好的 Prometheus 数据源,并点击 `Import`
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225700200.png)
即可使用炫酷的 Hippo4j 动态线程池监控 DashBoard。大家伙儿也可以根据个人喜好进行定制 DashBoard如果觉得有优化点欢迎和我联系贡献。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220912225813972.png)
如果项目客户端启动多个示例,动态线程池监控效果图如下:
![](https://images-machen.oss-cn-beijing.aliyuncs.com/20220814_hippo4j_monitor.jpg)

@ -0,0 +1,127 @@
---
sidebar_position: 1
---
# 接入流程
部署服务端,参考 [部署手册](/docs/user_docs/ops/hippo4j-server-deploy)。
服务端创建 [租户、项目](/community/faq#租户和项目在-hippo4j-中是什么意思) 和线程池记录。
需要注意,项目 ID 需要与配置文件 `{application.name}` 保持一致。
:::note
租户、项目、线程池 ID 如果由多个词组成,建议以 - 进行分割。比如message-center。
:::
## Hippo4j 配置
SpringBoot Pom 引入 Hippo4j Starter Jar。
```xml
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>
```
启动类上添加注解 `@EnableDynamicThreadPool`
```java
@SpringBootApplication
@EnableDynamicThreadPool
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
```
SpringBoot 应用配置文件添加:
```yaml
spring:
profiles:
active: dev
application:
# 服务端创建的项目 id 需要与 application.name 保持一致
name: dynamic-threadpool-example
dynamic:
thread-pool:
# 服务端地址
server-addr: http://localhost:6691
# 用户名
username: admin
# 密码
password: 123456
# 租户 id, 对应 tenant 表
namespace: prescription
# 项目 id, 对应 item 表
item-id: ${spring.application.name}
```
## ThreadPoolExecutor 适配
添加线程池配置类,通过 `@DynamicThreadPool` 注解修饰。`threadPoolId` 为服务端创建的线程池 ID。
```java
package cn.hippo4j.example;
import cn.hippo4j.core.executor.DynamicThreadPool;
import cn.hippo4j.core.executor.support.ThreadPoolBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class ThreadPoolConfig {
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageConsumeDynamicExecutor() {
String threadPoolId = "message-consume";
ThreadPoolExecutor messageConsumeDynamicExecutor = ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
return messageConsumeDynamicExecutor;
}
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageProduceDynamicExecutor() {
String threadPoolId = "message-produce";
ThreadPoolExecutor messageProduceDynamicExecutor = ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
return messageProduceDynamicExecutor;
}
}
```
通过 ThreadPoolBuilder 构建动态线程池,只有 threadFactory、threadPoolId 为必填项,其它参数会从 hippo4j-server 服务拉取。
:::note
创建线程池时建议填充实际的参数。如果在连接 Hippo4j Server 端失败时,会使用填充配置创建线程池。
:::
项目中使用上述定义的动态线程池,如下所示:
```java
@Resource
private ThreadPoolExecutor messageConsumeDynamicExecutor;
messageConsumeDynamicExecutor.execute(() -> xxx);
@Resource
private ThreadPoolExecutor messageProduceDynamicExecutor;
messageProduceDynamicExecutor.execute(() -> xxx);
```

@ -0,0 +1,70 @@
---
sidebar_position: 1
---
# 简介
## 线程池痛点
线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。
如果有在项目中实际使用线程池,相信你可能会遇到以下痛点:
- 线程池随便定义,线程资源过多,造成服务器高负载。
- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。
- 线程池任务执行时间超过平均执行周期,开发人员无法感知。
- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。
- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。
- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。
- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。
- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。
## 什么是 Hippo4j
Hippo4j 通过对 JDK 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。
提供以下功能支持:
- 全局管控 - 管理应用线程池实例。
- 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。
- 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。
- 运行监控 - 实时查看线程池运行时数据,最近半小时线程池运行数据图表展示。
- 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。
- 多种模式 - 内置两种使用模式:[依赖配置中心](https://hippo4j.cn/docs/user_docs/getting_started/config/hippo4j-config-start) 和 [无中间件依赖](https://hippo4j.cn/docs/user_docs/getting_started/server/hippo4j-server-start)。
- 容器管理 - Tomcat、Jetty、Undertow 容器线程池运行时查看和线程数变更。
- 框架适配 - Dubbo、Hystrix、RabbitMQ、RocketMQ 等消费线程池运行时数据查看和线程数变更。
## 快速开始
对于本地演示目的,请参阅 [Quick start](https://hippo4j.cn/docs/user_docs/user_guide/quick-start)
演示环境: [http://console.hippo4j.cn/index.html](http://console.hippo4j.cn/index.html)
## 接入登记
更多接入的公司,欢迎在 [登记地址](https://github.com/opengoofy/hippo4j/issues/13) 登记,登记仅仅为了产品推广。
## 联系我
开源不易,右上角点个 Star 鼓励一下吧!
如果大家想要实时关注 Hippo4j 更新的文章以及分享的干货的话,可以关注我的公众号。
使用过程中有任何问题,或者对项目有什么建议,关注公众号回复:加群,和 1000+ 志同道合的朋友交流讨论。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20230317191041262-mini.png)
## 友情链接
- [[ LiteFlow ]](https://liteflow.yomahub.com/):轻量,快速,稳定可编排的组件式规则引擎。
- [[ Sa-Token ]](https://github.com/dromara/sa-token):一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!
- [[ HertzBeat ]](https://github.com/dromara/hertzbeat):易用友好的云监控系统, 无需 Agent, 强大自定义监控能力。
- [[ JavaGuide ]](https://github.com/Snailclimb/JavaGuide):一份涵盖大部分 Java 程序员所需要掌握的核心知识。
- [[ toBeBetterJavaer ]](https://github.com/itwanger/toBeBetterJavaer):一份通俗易懂、风趣幽默的 Java 学习指南。
## 贡献者
感谢所有为项目作出贡献的开发者。如果有意贡献,参考 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)。

@ -0,0 +1,7 @@
{
"label": "运维指南",
"position": 4,
"link": {
"type": "generated-index"
}
}

@ -0,0 +1,45 @@
---
sidebar_position: 1
---
# 源码包部署
[RELEASE](https://github.com/opengoofy/hippo4j/releases) 页面下载对应版本并进行解压。
## 初始化
修改数据库相关信息。
```txt
/conf/application.properties
```
如果是新运行 Hippo4j数据库执行下述 SQL 脚本即可。
```txt
/conf/hippo4j_manager.sql
```
如果是对已运行 Hippo4j 升级,请查看 `/conf/sql-upgrade` 目录下,是否有目标版本对应的升级脚本。
## 直接运行
Mac Linux 启动执行。
```txt
sh ./bin/startup.sh
```
Windows 启动执行。
```txt
bin/startup.cmd
```
## 访问控制台
启动成功后访问链接。用户名密码admin 123456
```txt
localhost:6691/index.html
```

@ -0,0 +1,50 @@
---
sidebar_position: 2
---
# Docker部署
## 镜像启动
Docker 镜像默认使用内置 H2 数据库,数据持久化到 Docker 容器存储卷中。
```shell
docker run -d -p 6691:6691 --name hippo4j-server hippo4j/hippo4j-server
```
或者,底层存储数据库切换为 MySQL。`DATASOURCE_HOST` 需要切换为本地 IP不能使用 `127.0.0.1``localhost`
```shell
docker run -d -p 6691:6691 --name hippo4j-server \
-e DATASOURCE_MODE=mysql \
-e DATASOURCE_HOST=xxx.xxx.xxx.xxx \
-e DATASOURCE_PORT=3306 \
-e DATASOURCE_DB=hippo4j_manager \
-e DATASOURCE_USERNAME=root \
-e DATASOURCE_PASSWORD=root \
hippo4j/hippo4j-server
```
访问 Server 控制台,路径 `http://localhost:6691/index.html` 默认用户名密码admin / 123456
## 镜像构建
如果想要自定义镜像,可以通过以下命令快速构建 Hippo4j Server
方式一:
```shell
# 进入到 hippo4j-server/hippo4j-bootstrap 工程路径下
mvn clean package -Dskip.spotless.apply=true
# 默认打包是打包的 tag 是 latest
docker build -t hippo4j/hippo4j-server ../hippo4j-bootstrap
```
方式二:
通过 `maven docker plugin`
```shell
# 进入到 hippo4j-server 工程路径下
mvn clean package -DskipTests -Dskip.spotless.apply=true docker:build
```

@ -0,0 +1,7 @@
{
"label": "其它",
"position": 6,
"link": {
"type": "generated-index"
}
}

@ -0,0 +1,23 @@
---
sidebar_position: 5
---
# 推荐公众号
## JavaGuide
专注Java后端学习和大厂面试的公众号
![](https://images-machen.oss-cn-beijing.aliyuncs.com/JavaGuide.png)
## HelloGitHub
HelloGitHub专注于开源社区技术和知识内容分享。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/HelloGitHub.png)
## macrozheng
专注Java技术分享解析优质开源项目。涵盖SpringBoot、SpringCloud、Docker、K8S等实用技术作者Github开源项目mall50K+Star
![](https://images-machen.oss-cn-beijing.aliyuncs.com/macrozheng.png)

@ -0,0 +1,23 @@
---
sidebar_position: 6
---
# 公众号合作
## 推荐须知
hippo4j 作为一款新兴动态线程池框架,开源出来的时间比较晚,目前迫切需要不同的途径进行推广。
如果您是公众号运营者或者开源爱好者,欢迎将 hippo4j 推荐给您的粉丝。
1. 您无需为 hippo4j 专门撰写文案,只需要直接导入 [推荐文章](https://mp.weixin.qq.com/s/JTTwcBEiK_MnFcPTZl3zGA) 即可。
2. 在文章底部或内容中留下项目官网或者 GitHub 仓库链接。
3. 文章需至少 1000+ 的阅读量。
作为推荐回报hippo4j 可以为您:
1. 在框架官方文档 [推荐公众号](/docs/user_docs/other/official-ccounts) 页面处留下您的公众号二维码。
2. 在框架官方交流群里@全体成员推广您的公众号一次,附带介绍语。
3. 您的公众号所有新推文章都可以将链接发送到 hippo4j 交流群中,增加阅读量。
如果您还有除公众号以外的其它途径可以与 hippo4j 相互推荐,欢迎 [加群沟通](/group)。

@ -0,0 +1,246 @@
---
sidebar_position: 3
---
# 问题提问
文档引用自:[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)
## 在提问之前
在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前,请先做到以下事情:
1. 尝试在你准备提问的论坛的旧文章中搜索答案。
2. 尝试上网搜索以找到答案。
3. 尝试阅读手册以找到答案。
4. 尝试阅读常见问题文件FAQ以找到答案。
5. 尝试自己检查或试验以找到答案。
6. 向你身边的强者朋友打听以找到答案。
7. 如果你是程序开发者,请尝试阅读源代码以找到答案。
当你提出问题的时候,请先表明你已经做了上述的努力;这将有助于树立你并不是一个不劳而获且浪费别人的时间的提问者。如果你能一并表达在做了上述努力的过程中所**学到**的东西会更好,因为我们更乐于回答那些表现出能从答案中学习的人的问题。
## 当你提问时
### 慎选提问的论坛
小心选择你要提问的场合。如果你做了下述的事情,你很可能被忽略掉或者被看作失败者:
* 在与主题不合的论坛上贴出你的问题。
* 在探讨进阶技术问题的论坛张贴非常初级的问题;反之亦然。
* 在太多的不同新闻群组上重复转贴同样的问题cross-post
* 向既非熟人也没有义务解决你问题的人发送私人电邮。
因此第一步是找到对的论坛。再说一次Google 和其它搜索引擎还是你的朋友用它们来找到与你遭遇到困难的软硬件问题最相关的网站。通常那儿都有常见问题FAQ、邮件列表及相关说明文件的链接。如果你的努力包括**阅读** FAQ都没有结果网站上也许还有报告 BugBug-reporting的流程或链接如果是这样链过去看看。
### 使用有意义且描述明确的标题
在邮件列表、新闻群组或论坛中,大约 50 字以内的标题是抓住资深专家注意力的好机会。别用喋喋不休的帮帮忙、跪求、急(更别说救命啊!!!!这样让人反感的话,用这种标题会被条件反射式地忽略)来浪费这个机会。不要妄想用你的痛苦程度来打动我们,而应该是在这点空间中使用极简单扼要的描述方式来提出问题。
一个好标题范例是`目标 —— 差异`式的描述,许多技术支持组织就是这样做的。在`目标`部分指出是哪一个或哪一组东西有问题,在`差异`部分则描述与期望的行为不一致的地方。
> 蠢问题:救命啊!我的笔记本电脑不能正常显示了!
> 聪明问题X.org 6.8.1 的鼠标指针会变形,某牌显卡 MV1005 芯片组。
> 更聪明问题X.org 6.8.1 的鼠标指针,在某牌显卡 MV1005 芯片组环境下 - 会变形。
### 使用清晰、正确、精准且合乎语法的语句
我们从经验中发现,粗心的提问者通常也会粗心地写程序与思考(我敢打包票)。回答粗心大意者的问题很不值得,我们宁愿把时间耗在别处。
正确的拼写、标点符号和大小写是很重要的。一般来说,如果你觉得这样做很麻烦,不想在乎这些,那我们也觉得麻烦,不想在乎你的提问。花点额外的精力斟酌一下字句,用不着太僵硬与正式 —— 事实上,黑客文化很看重能准确地使用非正式、俚语和幽默的语句。但它**必须很**准确,而且有迹象表明你是在思考和关注问题。
### 精确地描述问题并言之有物
* 仔细、清楚地描述你的问题或 Bug 的症状。
* 描述问题发生的环境(机器配置、操作系统、应用程序、以及相关的信息),提供经销商的发行版和版本号(如:`Fedora Core 4`、`Slackware 9.1`等)。
* 描述在提问前你是怎样去研究和理解这个问题的。
* 描述在提问前为确定问题而采取的诊断步骤。
* 描述最近做过什么可能相关的硬件或软件变更。
* 尽可能地提供一个可以`重现这个问题的可控环境`的方法。
尽量去揣测一个黑客会怎样反问你,在你提问之前预先将黑客们可能提出的问题回答一遍。
以上几点中,当你报告的是你认为可能在代码中的问题时,给黑客一个可以重现你的问题的环境尤其重要。当你这么做时,你得到有效的回答的机会和速度都会大大的提升。
[Simon Tatham](http://www.chiark.greenend.org.uk/~sgtatham/) 写过一篇名为《[如何有效的报告 Bug](http://www.chiark.greenend.org.uk/~sgtatham/bugs-cn.html)》的出色文章。强力推荐你也读一读。
### 话不在多而在精
你需要提供精确有内容的信息。这并不是要求你简单的把成堆的出错代码或者资料完全转录到你的提问中。如果你有庞大而复杂的测试样例能重现程序挂掉的情境,尽量将它剪裁得越小越好。
这样做的用处至少有三点。
第一,表现出你为简化问题付出了努力,这可以使你得到回答的机会增加;
第二,简化问题使你更有可能得到**有用**的答案;
第三,在精炼你的 bug 报告的过程中,你很可能就自己找到了解决方法或权宜之计。
### 别动辄声称找到 Bug
当你在使用软件中遇到问题,除非你非常、**非常**的有根据,不要动辄声称找到了 Bug。提示除非你能提供解决问题的源代码补丁或者提供回归测试来表明前一版本中行为不正确否则你都多半不够完全确信。这同样适用在网页和文件如果你声称发现了文件的`Bug`,你应该能提供相应位置的修正或替代文件。
请记得,还有其他许多用户没遇到你发现的问题,否则你在阅读文件或搜索网页时就应该发现了(你在抱怨前[已经做了这些,是吧](#在提问之前)?)。这也意味着很有可能是你弄错了而不是软件本身有问题。
编写软件的人总是非常辛苦地使它尽可能完美。如果你声称找到了 Bug也就是在质疑他们的能力即使你是对的也有可能会冒犯到其中某部分人。当你在标题中嚷嚷着有`Bug`时,这尤其严重。
提问时,即使你私下非常确信已经发现一个真正的 Bug最好写得像是**你**做错了什么。如果真的有 Bug你会在回复中看到这点。这样做的话如果真有 Bug维护者就会向你道歉这总比你惹恼别人然后欠别人一个道歉要好一点。
### 低声下气不能代替你的功课
有些人明白他们不该粗鲁或傲慢的提问并要求得到答复,但他们选择另一个极端 —— 低声下气:`我知道我只是个可悲的新手,一个撸瑟,但...`。这既使人困扰,也没有用,尤其是伴随着与实际问题含糊不清的描述时更令人反感。
别用原始灵长类动物的把戏来浪费你我的时间。取而代之的是,尽可能清楚地描述背景条件和你的问题情况。这比低声下气更好地定位了你的位置。
有时网页论坛会设有专为新手提问的版面,如果你真的认为遇到了初学者的问题,到那去就是了,但一样别那么低声下气。
### 描述问题症状而非你的猜测
告诉黑客们你认为问题是怎样造成的并没什么帮助。(如果你的推断如此有效,还用向别人求助吗?),因此要确信你原原本本告诉了他们问题的症状,而不是你的解释和理论;让黑客们来推测和诊断。如果你认为陈述自己的猜测很重要,清楚地说明这只是你的猜测,并描述为什么它们不起作用。
**蠢问题**
> 我在编译内核时接连遇到 SIG11 错误,
> 我怀疑某条飞线搭在主板的走线上了,这种情况应该怎样检查最好?
**聪明问题**
> 我的组装电脑是 FIC-PA2007 主机板搭载 AMD K6/233 CPU威盛 Apollo VP2 芯片组),
> 256MB Corsair PC133 SDRAM 内存,在编译内核时,从开机 20 分钟以后就频频产生 SIG11 错误,
> 但是在头 20 分钟内从没发生过相同的问题。重新启动也没有用,但是关机一晚上就又能工作 20 分钟。
> 所有内存都换过了,没有效果。相关部分的标准编译记录如下…
由于以上这点似乎让许多人觉得难以配合,这里有句话可以提醒你:`所有的诊断专家都来自密苏里州。` 美国国务院的官方座右铭则是:`让我看看`(出自国会议员 Willard D. Vandiver 在 1899 年时的讲话:`我来自一个出产玉米,棉花,牛蒡和民主党人的国家,滔滔雄辩既不能说服我,也不会让我满意。我来自密苏里州,你必须让我看看。` 针对诊断者而言,这并不是一种怀疑,而只是一种真实而有用的需求,以便让他们看到的是与你看到的原始证据尽可能一致的东西,而不是你的猜测与归纳的结论。所以,大方的展示给我们看吧!
### 按发生时间先后列出问题症状
问题发生前的一系列操作,往往就是对找出问题最有帮助的线索。因此,你的说明里应该包含你的操作步骤,以及机器和软件的反应,直到问题发生。在命令行处理的情况下,提供一段操作记录(例如运行脚本工具所生成的),并引用相关的若干行(如 20 行)记录会非常有帮助。
如果挂掉的程序有诊断选项(如 -v 的详述开关),试着选择这些能在记录中增加调试信息的选项。记住,`多`不等于`好`。试着选取适当的调试级别以便提供有用的信息而不是让读者淹没在垃圾中。
如果你的说明很长(如超过四个段落),在开头简述问题,接下来再按时间顺序详述会有所帮助。这样黑客们在读你的记录时就知道该注意哪些内容了。
### 描述目标而不是过程
如果你想弄清楚如何做某事(而不是报告一个 Bug在开头就描述你的目标然后才陈述重现你所卡住的特定步骤。
经常寻求技术帮助的人在心中有个更高层次的目标,而他们在自以为能达到目标的特定道路上被卡住了,然后跑来问该怎么走,但没有意识到这条路本身就有问题。结果要费很大的劲才能搞定。
**蠢问题**
> 我怎样才能从某绘图程序的颜色选择器中取得十六进制的 RGB 值?
**聪明问题**
> 我正试着用替换一幅图片的色码color table成自己选定的色码我现在知道的唯一方法是编辑每个色码区块table slot
> 但却无法从某绘图程序的颜色选择器取得十六进制的 RGB 值。
第二种提问法比较聪明,你可能得到像是```建议采用另一个更合适的工具```的回复。
### 清楚明确的表达你的问题以及需求
漫无边际的提问是近乎无休无止的时间黑洞。最有可能给你有用答案的人通常也正是最忙的人(他们忙是因为要亲自完成大部分工作)。这样的人对无节制的时间黑洞相当厌恶,所以他们也倾向于厌恶那些漫无边际的提问。
如果你明确表述需要回答者做什么(如提供指点、发送一段代码、检查你的补丁、或是其他等等),就最有可能得到有用的答案。因为这会定出一个时间和精力的上限,便于回答者能集中精力来帮你。这么做很棒。
要理解专家们所处的世界,请把专业技能想像为充裕的资源,而回复的时间则是稀缺的资源。你要求他们奉献的时间越少,你越有可能从真正专业而且很忙的专家那里得到解答。
所以,界定一下你的问题,使专家花在辨识你的问题和回答所需要付出的时间减到最少,这技巧对你有用答案相当有帮助 —— 但这技巧通常和简化问题有所区别。因此,问`我想更好地理解 X可否指点一下哪有好一点说明`通常比问`你能解释一下 X 吗?`更好。如果你的代码不能运作,通常请别人看看哪里有问题,比要求别人替你改正要明智得多。
### 礼多人不怪,而且有时还很有帮助
彬彬有礼,多用`请`和`谢谢您的关注`,或`谢谢你的关照`。让大家都知道你对他们花时间免费提供帮助心存感激。
坦白说,这一点并没有比使用清晰、正确、精准且合乎语法和避免使用专用格式重要(也不能取而代之)。黑客们一般宁可读有点唐突但技术上鲜明的 Bug 报告,而不是那种有礼但含糊的报告。(如果这点让你不解,记住我们是按问题能教给我们什么来评价问题的价值的)
然而,如果你有一串的问题待解决,客气一点肯定会增加你得到有用回应的机会。
(我们注意到,自从本指南发布后,从资深黑客那里得到的唯一严重缺陷反馈,就是对预先道谢这一条。一些黑客觉得`先谢了`意味着事后就不用再感谢任何人的暗示。我们的建议是要么先说`先谢了`**然后**事后再对回复者表示感谢,或者换种方式表达感激,譬如用`谢谢你的关注`或`谢谢你的关照`。)
## 不该问的问题
以下是几个经典蠢问题,以及黑客没回答时心中所想的:
问题:[我能在哪找到 X 程序或 X 资源?](#q1)
问题:[我怎样用 X 做 Y](#q2)
问题:[我的程序/设定/SQL 语句没有用](#q3)
问题:[我的 Windows 电脑有问题,你能帮我吗?](#q4)
问题:[我的程序不会动了,我认为系统工具 X 有问题](#q5)
问题:[我在安装 Linux或者 X )时有问题,你能帮我吗?](#q6)
问题:[我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢?](#q7)
---
> 问题:我能在哪找到 X 程序或 X 资源?
回答:就在我找到它的地方啊,白痴 —— 搜索引擎的那一头。天哪!难道还有人不会用 [Google](https://www.google.com) 吗?
> 问题:我怎样用 X 做 Y
回答:如果你想解决的是 Y ,提问时别给出可能并不恰当的方法。这种问题说明提问者不但对 X 完全无知,也对 Y 要解决的问题糊涂,还被特定形势禁锢了思维。最好忽略这种人,等他们把问题搞清楚了再说。
> 问题:我的{程序/设定/SQL 语句}没有用
回答:这不算是问题吧,我对要我问你二十个问题才找得出你真正问题的问题没兴趣 —— 我有更有意思的事要做呢。在看到这类问题的时候,我的反应通常不外如下三种
* 你还有什么要补充的吗?
* 真糟糕,希望你能搞定。
* 这关我屁事?
> 问题:我的 Windows 电脑有问题,你能帮我吗?
回答:能啊,扔掉微软的垃圾,换个像 Linux 或 BSD 的开源操作系统吧。
注意:如果程序有官方版 Windows 或者与 Windows 有互动(如 Samba你**可以**问与 Windows 相关的问题,只是别对问题是由 Windows 操作系统而不是程序本身造成的回复感到惊讶, 因为 Windows 一般来说实在太烂,这种说法通常都是对的。
> 问题:我的程序不会动了,我认为系统工具 X 有问题
回答:你完全有可能是第一个注意到被成千上万用户反复使用的系统调用与函数库文件有明显缺陷的人,更有可能的是你完全没有根据。不同凡响的说法需要不同凡响的证据,当你这样声称时,你必须有清楚而详尽的缺陷说明文件作后盾。
> 问题:我在安装 Linux或者 X )时有问题,你能帮我吗?
回答:不能,我只有亲自在你的电脑上动手才能找到毛病。还是去找你当地的 Linux 使用群组者寻求实际的指导吧(你能在[这儿](http://www.linux.org/groups/index.html)找到用户群组的清单)。
注意:如果安装问题与某 Linux 的发行版有关,在它的邮件列表、论坛或本地用户群组中提问也许是恰当的。此时,应描述问题的准确细节。在此之前,先用 `Linux` 和**所有**被怀疑的硬件作关键词仔细搜索。
> 问题:我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢?
回答:想要这样做,说明了你是个卑鄙小人;想找个黑客帮你,说明你是个白痴!
## 好问题与蠢问题
最后,我将透过举一些例子,来说明怎样聪明的提问;同一个问题的两种问法被放在一起,一种是愚蠢的,另一种才是明智的。
**蠢问题**
> 我从 foo 项目找来的源码没法编译。它怎么这么烂?
他觉得都是别人的错,这个傲慢自大的提问者。
**聪明问题**
> foo 项目代码在 Nulix 6.2 版下无法编译通过。我读过了 FAQ但里面没有提到跟 Nulix 有关的问题。这是我编译过程的记录,我有什么做的不对的地方吗?
提问者已经指明了环境,也读过了 FAQ还列出了错误并且他没有把问题的责任推到别人头上他的问题值得被关注。
**蠢问题**
> 我的主机板有问题了,谁来帮我?
某黑客对这类问题的回答通常是:`好的,还要帮你拍拍背和换尿布吗?`,然后按下删除键。
**聪明问题**
> 我在 S2464 主机板上试过了 X 、 Y 和 Z ,但没什么作用,我又试了 A 、 B 和 C 。请注意当我尝试 C 时的奇怪现象。显然 florbish 正在 grommicking但结果出人意料。通常在 Athlon MP 主机板上引起 grommicking 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题?
## 如果得不到回答
如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。
总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。
你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。

@ -0,0 +1,8 @@
{
"label": "用户指南",
"position": 2,
"link": {
"type": "generated-index",
"description": "帮助想要了解 Hippo4j 的用户快速掌握核心开发理念。"
}
}

@ -0,0 +1,60 @@
---
sidebar_position: 1
---
# 为什么写
[美团线程池文章](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html "美团线程池文章") 介绍中,因为业务对线程池参数没有合理配置,触发过几起生产事故,进而引发了一系列思考。最终决定封装线程池动态参数调整,扩展线程池监控以及消息报警等功能。
在开源平台找了挺多动态线程池项目,从功能性以及健壮性而言,个人感觉不满足企业级应用。
因为对动态线程池比较感兴趣,加上想写一个有意义的项目,所以决定自己来造一个轻量级的轮子。
想给项目起一个简单易记的名字,类似于 Eureka、Nacos、Redis后和朋友商量决定命名**Hippo4j**。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/动态线程池功能架构-1.jpg)
## 它解决了什么问题
线程池在业务系统应该都有使用到,帮助业务流程提升效率以及管理线程,多数场景应用于大量的异步任务处理。
虽然线程池提供了我们许多便利,但也并非尽善尽美,比如下面这些问题就无法很好解决。
- 线程池随便定义,线程资源过多,造成服务器高负载。
- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。
- 线程池任务执行时间超过平均执行周期,开发人员无法感知。
- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。
- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。
- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。
- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。
- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。
Hippo4j 很好解决了这些问题,它将业务中所有线程池统一管理,增强原生线程池系列功能。
## 它有什么特性
应用系统中线程池并不容易管理。参考美团的设计Hippo4j 按照租户、项目、线程池的维度划分。再加上系统权限,让不同的开发、管理人员负责自己系统的线程池操作。
举个例子,小编在一家公司的公共组件团队,团队中负责消息、短链接网关等项目。公共组件是租户,消息或短链接就是项目。
Hippo4j 除去动态修改线程池,还包含实时查看线程池运行时指标、负载报警、配置日志管理等。
- `hippo4j-adapter`:适配对第三方框架中的线程池进行监控,如 Dubbo、RocketMQ、Hystrix 等;
- `hippo4j-auth`:用户、角色、权限等;
- `hippo4j-common`:多个模块公用代码实现;
- `hippo4j-config`:提供线程池准实时参数更新功能;
- `hippo4j-console`:对接前端控制台;
- `hippo4j-core`:核心的依赖,包括配置、核心包装类等;
- `hippo4j-discovery`:提供线程池项目实例注册、续约、下线等功能;
- `hippo4j-example` :示例工程;
- `hippo4j-message` :配置变更以及报警通知发送;
- `hippo4j-monitor` :线程池运行时监控;
- `hippo4j-server` Server 端发布需要的模块聚合;
- `hippo4j-spring-boot`SpringBoot Starter。

@ -0,0 +1,51 @@
---
sidebar_position: 2
---
# 架构设计
简单来说Hippo4j 从部署的角度上分为两种角色Server 端和 Client 端。
Server 端是 Hippo4j 项目打包出的 Java 进程,功能包括用户权限、线程池监控以及执行持久化的动作。
Client 端指的是我们 SpringBoot 应用,通过引入 Hippo4j Starter Jar 包负责与 Server 端进行交互。
比如拉取 Server 端线程池数据、动态更新线程池配置以及采集上报线程池运行时数据等。
## 基础组件
### 配置中心Config
配置中心位于 Server 端,它的主要作用是监控 Server 端线程池配置变更,实时通知到 Client 实例执行线程池变更流程。
代码设计基于 Nacos 1.x 版本的 **长轮询以及异步 Servlet 机制** 实现。
### 注册中心Discovery
负责管理 Client 端(单机或集群)注册到 Server 端的实例,包括不限于**实例注册、续约、过期剔除** 等操作,代码基于 Eureka 源码实现。
上面的配置中心很容易理解,动态线程池参数变更的根本。但是注册中心是用来做什么的?
注册中心管理 Client 端注册的实例,通过这些实例可以 **实时获取线程池的运行时参数信息**。
目前的设计是如此,不排除后续基于 Discovery 做更多的扩展。
### 控制台Console
对接前端项目,包括不限于以下模块管理:
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211107122504126.png)
## 消息通知Notify
Hippo4j 内置了很多需要通知的事件,比如:线程池参数变更通知、线程池活跃度报警、拒绝策略执行报警以及阻塞队列容量报警等。
目前 Notify 已经接入了钉钉、企业微信和飞书后续持续集成邮件、短信等通知渠道并且Notify 模块提供了消息事件的 SPI 方案,可以接受三方自定义的推送。
## Hippo4j-Spring-Boot-Starter
熟悉 SpringBoot 的小伙伴对 Starter 应该不会陌生。Hippo4j 提供以 Starter Jar 包的形式嵌套在应用内,负责与 Server 端完成交互。
## 功能架构
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211105230953626.png)

@ -0,0 +1,74 @@
---
sidebar_position: 4
---
# 通知报警
现阶段已集成钉钉、企业微信、飞书的消息推送,后续会持续接入邮箱、短信和自定义通知渠道。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220904181527453.png)
**通知平台**
- DING钉钉平台
- LARK飞书平台
- WECHAT企业微信。
**通知类型**
- CONFIG线程池配置变更推送
- ALARM线程池运行报警推送。
**Token**
获取 DING、LARK、WECHAT 机器人 Token。
**报警间隔**
- CONFIG 类型通知没有报警间隔;
- ALARM 类型设置报警间隔后,某一节点下的同一线程池指定间隔只会发送一次报警通知。
**接收者**
```tex
多个接收者使用英文逗号 , 分割 (注意不要有空格)
DING填写手机号
WECHART填写user_id会以@的消息发给用户,填写姓名则是普通的@,如:龙台
LARK填写ou_开头用户唯一标识会以@的消息发给用户,填写手机号则是普通的@
```
## 钉钉平台
[钉钉创建群机器人](https://www.dingtalk.com/qidian/help-detail-20781541.html)
| 配置变更 | 报警通知 |
| :---: | :---: |
| ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211013122816688.png) | ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211013113649068.png) |
添加钉钉机器人后,需在机器人配置自定义关键字,才可发送成功。如下所示:
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220530200133377.png?x-oss-process=image/resize,h_500,w_800)
:::tip
如果使用 1.4.3 及以上版本,`警报` 替换为 `告警`
:::
## 企业微信
[企业微信创建群机器人](https://open.work.weixin.qq.com/help2/pc/14931?person_id=1&from=homesearch)
| 配置变更 | 报警通知 |
| :---: | :---: |
| ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211203213443242.png) | ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20211203213512019.png) |
## 飞书平台
[飞书创建群机器人](https://www.feishu.cn/hc/zh-CN/articles/360024984973)
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220304081729347.png)
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220304081507907.png)

@ -0,0 +1,40 @@
---
sidebar_position: 3
---
# 快速开始
## 服务启动
使用 Docker 运行服务端,默认使用内置 H2 数据库,数据持久化到 Docker 容器存储卷中。
```shell
docker run -d -p 6691:6691 --name hippo4j-server hippo4j/hippo4j-server
```
> 如果没有 Docker可以使用源码编译的方式启动 [Hippo4j-Server/Hippo4j-Bootstrap](https://github.com/longtai-cn/hippo4j/tree/develop/hippo4j-server/hippo4j-bootstrap) 模块下 ServerApplication 应用类。
启动示例项目,[hippo4j-spring-boot-starter-example](https://github.com/opengoofy/hippo4j/tree/develop/hippo4j-example/hippo4j-spring-boot-starter-example) 模块下 ServerExampleApplication 应用类。
访问 Server 控制台,路径 `http://localhost:6691/index.html`默认用户名密码admin / 123456
## 配置变更
访问控制台动态线程池菜单下线程池实例,修改动态线程池相关参数。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220813173811668.png)
观察 Hippo4j-Example 控制台日志输出,日志输出包括不限于此信息即为成功。
```tex
2022-09-10 00:23:29.783 INFO 50322 --- [change.config_0] c.h.s.s.c.ServerThreadPoolDynamicRefresh : [message-consume] Dynamic thread pool change parameter.
corePoolSize: 2 => 4
maximumPoolSize: 6 => 12
capacity: 1024 => 2048
keepAliveTime: 9999 => 9999
executeTimeOut: 800 => 3000
rejectedType: SyncPutQueuePolicy => RunsOldestTaskPolicy
allowCoreThreadTimeOut: true => true
```
另外,当 Client 集群部署时,可以修改某一个实例,或选择 `全部修改` 按钮,修改所有实例线程池信息。

@ -0,0 +1,20 @@
{
"tutorialSidebar": [
{
"type": "autogenerated",
"dirName": "."
}
],
"user_docs": [
{
"type": "autogenerated",
"dirName": "user_docs"
}
],
"sponsor": [
{
"type": "autogenerated",
"dirName": "sponsor"
}
]
}

@ -1,4 +1,5 @@
[ [
"1.5.0",
"1.4.3", "1.4.3",
"1.4.2" "1.4.2"
] ]

Loading…
Cancel
Save