diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..8cb2470c --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +coverage: + status: + project: + default: + target: auto + threshold: 0% + informational: true + patch: + default: + informational: true +ignore: + - "hippo4j-example/.*" + - "docs/.*" + - "dev-support/.*" diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index eb15bc5e..6e54c6c7 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ # These are supported funding model platforms -custom: ['https://hippo4j.cn/docs/community/sponsor'] +custom: ['https://hippo4j.cn/community/sponsor'] diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 7fc0ffe4..3d18f858 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -8,7 +8,7 @@ about: 提交问题缺陷帮助我们更好的改进 在开始报告错误之前,请确保认真查看了以下步骤: - 搜索打开和关闭的 [GitHub 问题](https://github.com/opengoofy/hippo4j/issues) -- 阅读 [常见问题文档](https://hippo4j.cn/docs/user_docs/other/issue) +- 阅读 [常见问题文档](https://hippo4j.cn/community/faq) 请在提交问题之前回答这些问题,谢谢。 diff --git a/.github/ISSUE_TEMPLATE/question-report.md b/.github/ISSUE_TEMPLATE/question-report.md index 87d00572..9284b976 100644 --- a/.github/ISSUE_TEMPLATE/question-report.md +++ b/.github/ISSUE_TEMPLATE/question-report.md @@ -8,7 +8,7 @@ about: 文档或讨论中未回答的使用问题 在开始报告问题之前,请确保认真查看了以下步骤: - 搜索打开和关闭的 [GitHub 问题](https://github.com/opengoofy/hippo4j/issues) -- 阅读 [常见问题文档](https://hippo4j.cn/docs/user_docs/other/issue) +- 阅读 [常见问题文档](https://hippo4j.cn/community/faq) 请在提交问题之前回答这些问题,谢谢。 diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index 0aa809f5..907fdeba 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -3,4 +3,4 @@ Fixes #ISSUSE_ID Changes proposed in this pull request: - -> Check mailbox configuration when submitting. [Contributor Guide](https://hippo4j.cn/docs/community/contributor) +> Check mailbox configuration when submitting. [Contributor Guide](https://hippo4j.cn/community/contributor-guide) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4495376..9b07e4d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,3 +65,25 @@ jobs: - uses: actions/checkout@v3 - name: Build with Maven run: echo y | mvn clean install -Dskip.gpg=true -Dspotless.apply.skip=true -Dmaven.javadoc.skip=true + + test-coverage: + if: github.repository == 'opengoofy/hippo4j' + name: Test coverage report + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Cache Maven Repos + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: hippo4j-maven-third-party-${{ hashFiles('**/pom.xml') }} + restore-keys: | + hippo4j-maven-third-party- + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 8 + - name: Test with Maven + run: echo y | mvn -T1C clean install -Dskip.gpg=true -Dspotless.apply.skip=true -Dskip.jacoco.plugin=false + - name: Upload to Codecov + run: bash <(curl -s https://codecov.io/bash) diff --git a/.gitignore b/.gitignore index 2aaf1d4a..d3bc67d0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ +!**/node_modules/ +!**/dist/ ### STS ### .apt_generated diff --git a/README-EN.md b/README-EN.md index 297625a9..5e699edf 100644 --- a/README-EN.md +++ b/README-EN.md @@ -1,8 +1,8 @@ -image +image # Dynamic and observable thread pool framework -[![Gitee](https://gitee.com/itmachen/hippo4j/badge/star.svg?theme=gvp)](https://gitee.com/itmachen/hippo4j) [![GitHub](https://img.shields.io/github/stars/opengoofy/hippo4j)](https://github.com/opengoofy/hippo4j) [![Docker Pulls](https://img.shields.io/docker/pulls/hippo4j/hippo4j-server.svg)](https://store.docker.com/community/images/hippo4j/hippo4j-server) [![Contributors](https://img.shields.io/github/contributors/opengoofy/hippo4j?color=3ba272)](https://github.com/opengoofy/hippo4j/graphs/contributors) [![License](https://img.shields.io/github/license/opengoofy/hippo4j?color=5470c6)](https://github.com/opengoofy/hippo4j/blob/develop/LICENSE) +[![Gitee](https://gitee.com/magegoofy/hippo4j/badge/star.svg?theme=gvp)](https://gitee.com/magegoofy/hippo4j) [![GitHub](https://img.shields.io/github/stars/opengoofy/hippo4j?color=5470c6)](https://github.com/opengoofy/hippo4j) [![Contributors](https://img.shields.io/github/contributors/opengoofy/hippo4j?color=3ba272)](https://github.com/opengoofy/hippo4j/graphs/contributors) [![Docker Pulls](https://img.shields.io/docker/pulls/hippo4j/hippo4j-server.svg?label=docker%20pulls&color=fac858)](https://store.docker.com/community/images/hippo4j/hippo4j-server) [![codecov](https://codecov.io/gh/opengoofy/hippo4j/branch/develop/graph/badge.svg?token=WBUVJN107I)](https://codecov.io/gh/opengoofy/hippo4j) ------- @@ -54,3 +54,7 @@ More companies with access are welcome to register at [registration address](htt ## Contributors Thanks to all the developers who contributed to the project. If interested in contributing, refer to [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). + + + + diff --git a/README.md b/README.md index a935d666..9591aa0f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ -image -中文 | [English](https://github.com/opengoofy/hippo4j/blob/develop/README-EN.md) +# 动态可观测线程池 -# 动态可观测线程池框架,提高线上运行保障能力 +image -[![Gitee](https://gitee.com/itmachen/hippo4j/badge/star.svg?theme=gvp)](https://gitee.com/itmachen/hippo4j) [![GitHub](https://img.shields.io/github/stars/opengoofy/hippo4j)](https://github.com/opengoofy/hippo4j) [![Docker Pulls](https://img.shields.io/docker/pulls/hippo4j/hippo4j-server.svg)](https://store.docker.com/community/images/hippo4j/hippo4j-server) [![Contributors](https://img.shields.io/github/contributors/opengoofy/hippo4j?color=3ba272)](https://github.com/opengoofy/hippo4j/graphs/contributors) [![License](https://img.shields.io/github/license/opengoofy/hippo4j?color=5470c6)](https://github.com/opengoofy/hippo4j/blob/develop/LICENSE) +通过对 JDK 线程池的增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。 + +[![GitHub stars](https://img.shields.io/github/stars/opengoofy/hippo4j.svg?style=for-the-badge&label=Stars&logo=github)](https://github.com/opengoofy/hippo4j) [![Contributors](https://img.shields.io/github/contributors/opengoofy/hippo4j.svg?style=for-the-badge&label=Contributors&logo=appveyor)](https://github.com/opengoofy/hippo4j) + +[![Gitee](https://gitee.com/magestack/hippo4j/badge/star.svg?theme=gvp)](https://gitee.com/magestack/hippo4j) [![Docker Pulls](https://img.shields.io/docker/pulls/hippo4j/hippo4j-server.svg?label=docker%20pulls&color=fac858)](https://store.docker.com/community/images/hippo4j/hippo4j-server) [![codecov](https://codecov.io/gh/opengoofy/hippo4j/branch/develop/graph/badge.svg?token=WBUVJN107I)](https://codecov.io/gh/opengoofy/hippo4j) [![EN doc](https://img.shields.io/badge/readme-English-orange.svg)](https://github.com/opengoofy/hippo4j/blob/develop/README-EN.md) ------- @@ -24,21 +27,23 @@ - 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。 - 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。 -## 什么是 Hippo-4J - -Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。 +## 什么是 Hippo4j 提供以下功能支持: - 全局管控 - 管理应用线程池实例。 -- 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。 +- 动态变更 - 应用运行时动态变更线程池参数,包括但不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。 - 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。 -- 运行监控 - 实时查看线程池运行时数据,最近半小时线程池运行数据图表展示。 +- 数据采集 - 支持多种方式采集线程池数据,包括但不限于:日志、内置采集、Prometheus、InfluxDB、ElasticSearch 等。 +- 运行监控 - 实时查看线程池运行时数据,自定义时间内线程池运行数据图表展示。 - 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。 - 多种模式 - 内置两种使用模式:[依赖配置中心](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 等消费线程池运行时数据查看和线程数变更。 +- 变更审核 - 提供多种用户角色,普通用户变更线程池参数需要 Admin 用户审核方可生效。 +- 动态化插件 - 内置多种线程池插件,支持用户自定义插件以及运行时扩展。 +- 多版本适配 - 经过实际测试,已支持客户端 SpringBoot 1.5.x => 2.7.5 版本(更高版本未测试)。 ## 快速开始 @@ -56,6 +61,18 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池 扫码添加微信,备注:hippo4j,邀您加入群聊。若图片加载不出来,访问 [官网站点](https://hippo4j.cn/docs/user_docs/other/group)。 +## 深入原理 + +如果您公司没有使用 Hippo4j 场景的话,我也建议去阅读下项目的底层原理,主要有以下几个原因: + +- 为了提高代码质量以及后续的扩展行为,运用多种设计模式实现高内聚、低耦合。 + +- 框架底层依赖 Spring 框架运行,并在源码中大量使用 Spring 相关功能。 +- 运用 JUC 并发包下多种工具保障多线程运行安全,通过实际场景理解并发编程。 +- 借鉴主流开源框架 Nacos、Eureka 实现轻量级配置中心和注册中心功能。 +- 自定义 RPC 框架实现,封装 Netty 完成客户端/服务端网络通信优化。 +- 通过 CheckStyle、Spotless 等插件规范代码编写,保障高质量代码行为和代码样式。 + ## 友情链接 - [[ Sa-Token ]](https://github.com/dromara/sa-token):一个轻量级 java 权限认证框架,让鉴权变得简单、优雅! @@ -63,560 +80,24 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池 - [[ HertzBeat ]](https://github.com/dromara/hertzbeat):易用友好的云监控系统, 无需 Agent, 强大自定义监控能力。 - [[ JavaGuide ]](https://github.com/Snailclimb/JavaGuide):一份涵盖大部分 Java 程序员所需要掌握的核心知识。 - [[ toBeBetterJavaer ]](https://github.com/itwanger/toBeBetterJavaer):一份通俗易懂、风趣幽默的 Java 学习指南。 +- [[ Jpom ]](https://gitee.com/dromara/Jpom):简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件。 + +## 刚果商城 + +👉 [工作经验不满五年程序员,如何通过项目提高个人技术能力?](https://sourl.cn/VERS4y) + +image ## 贡献者 感谢所有为项目作出贡献的开发者。如果有意贡献,参考 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - itmachen -
- 小马哥 -
-
- - shining-stars-lk -
- Lucky 8 -
-
- - weihubeats -
- Weihubeats -
-
- - pirme -
- 李金来 -
-
- - shanjianq -
- Shanjianq -
-
- - hippo4jbot -
- Hippo4jbot[bot] -
-
- - iwangjie -
- 王杰 -
-
- - BigXin0109 -
- BigXin0109 -
-
- - pizihao -
- Pizihao -
-
- - Gdk666 -
- Null -
-
- - xqxyxchy -
- Null -
-
- - road2master -
- Lijx -
-
- - maxisvest -
- Null -
-
- - baymax55 -
- Baymax55 -
-
- - zhuanghaozhe -
- 庄昊哲 -
-
- - liulinfei121 -
- Null -
-
- - Atmanuclear -
- Null -
-
- - hippo4j -
- Hippo4j -
-
- - imyzt -
- 杨镇涛 -
-
- - Tliutao -
- Liutao -
-
- - monsterxxp -
- Null -
-
- - voilaf -
- Null -
-
- - wulangcode -
- WuLang -
-
- - gywanghai -
- 二师兄 -
-
- - skyemin -
- Null -
-
- - Redick01 -
- Redick Liu -
-
- - xiaochengxuyuan -
- Sean Wu -
-
- - HKMV -
- Serenity -
-
- - gewuwo -
- 格悟沃 -
-
- - hushtian -
- Null -
-
- - jinlingmei -
- Null -
-
- - linlinjie -
- Null -
-
- - selectbook -
- Leping Huang -
-
- - soulmz -
- Soulzz -
-
- - tomsun28 -
- Tomsun28 -
-
- - backbay2-yzg -
- 游祖光 -
-
- - puppet4 -
- Tudo -
-
- - yanrongzhen -
- 严荣振 -
-
- - 2EXP -
- Null -
-
- - onesimplecoder -
- Alic -
-
- - CalebZYC -
- Null -
-
- - Hibernate5666 -
- Cheng Xihong -
-
- - smartdj -
- DJ -
-
- - dmego -
- Dmego -
-
- - dousp -
- Douspeng -
-
- - hl1248 -
- Lucas -
-
- - gentlelynn -
- Lynn -
-
- - alexhaoxuan -
- Alexli -
-
- - qizhongju -
- Bug搬运工 -
-
- - san4j -
- San4j -
-
- - zhenyed -
- Zhenye -
-
- - dongming0920 -
- Null -
-
- - f497196689 -
- Fengjing -
-
- - Snailclimb -
- Guide -
-
- - hbw1994 -
- Null -
-
- - hncboy -
- Null -
-
- - stronglong -
- Itermis -
-
- - janey668 -
- Null -
-
- - klsq94 -
- Hui Cao -
-
- - kongyanbo-cx -
- Null -
-
- - lishiyu -
- Null -
-
- - Nhxz -
- Nhxz -
-
- - op-lht -
- Op-lht -
-
- - wangjie-github -
- Wangjie -
-
- - wangyi123456 -
- Null -
-
- - Williamren97 -
- William Ren -
-
- - wzw8795 -
- Null -
-
- - huaxianchao -
- Null -
-
- - yangzhiw -
- Opentanent -
-
- - yhc777 -
- Null -
-
- - zhaiweij -
- Zhaiweij -
-
- - zhaojinchao95 -
- Zhaojinchao -
-
- - zj1997 -
- Null -
-
- - li-xiao-shuang -
- 李晓双 Li Xiao Shuang -
-
- - oreoft -
- 没有气的汽水 -
-
- - wo883721 -
- Xinhao -
-
- - Createsequence -
- 黄成兴 -
-
+ + +## 鸣谢 + +Hippo4j 社区收到 Jetbrains 多份 Licenses,并已分配项目 [活跃开发者](https://hippo4j.cn/team),非常感谢 Jetbrains 对开源社区的支持。 + +![JetBrains Logo (Main) logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg) diff --git a/dev-support/hippo4j_checkstyle_suppression.xml b/dev-support/hippo4j_checkstyle_suppression.xml index ab55dae0..765a781b 100644 --- a/dev-support/hippo4j_checkstyle_suppression.xml +++ b/dev-support/hippo4j_checkstyle_suppression.xml @@ -25,4 +25,8 @@ + + + + diff --git a/docs/blog/authors.yml b/docs/blog/authors.yml index 0edbc8d9..ffffe8d4 100644 --- a/docs/blog/authors.yml +++ b/docs/blog/authors.yml @@ -1,5 +1,5 @@ xiaomage: name: 小马哥 title: hippo4j 作者 - url: https://github.com/mabaiwan + url: https://github.com/magegoofy image_url: https://avatars.githubusercontent.com/u/77398366?v=4 diff --git a/docs/docs/community/_category_.json b/docs/community/_category_.json similarity index 100% rename from docs/docs/community/_category_.json rename to docs/community/_category_.json diff --git a/docs/community/contributor.md b/docs/community/contributor.md new file mode 100644 index 00000000..f40d60b1 --- /dev/null +++ b/docs/community/contributor.md @@ -0,0 +1,16 @@ +--- +id: contributor-guide +sidebar_position: 1 +title: 贡献指南 +--- + +Git Commit Log 尽量使用英文。 + +Pull Request 尽量保持单一,不同语义的代码贡献应拆分多个 Pull Request。 + +为了让您的 GitHub ID 显示在 Contributor 列表中,别忘了以下设置: + +```shell +git config --global user.name "username" +git config --global user.email "GitHub 账号邮箱" +``` diff --git a/docs/docs/community/dev_convention/_category_.json b/docs/community/dev_convention/_category_.json similarity index 100% rename from docs/docs/community/dev_convention/_category_.json rename to docs/community/dev_convention/_category_.json diff --git a/docs/community/dev_convention/code.md b/docs/community/dev_convention/code.md new file mode 100644 index 00000000..ce3d6018 --- /dev/null +++ b/docs/community/dev_convention/code.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 2 +title: 代码规约 +--- + +1. 代码提交前,执行 `mvn spotless:apply` 保证代码格式符合规范。 +2. 代码中不要出现无意义的空行。 diff --git a/docs/community/dev_convention/document.md b/docs/community/dev_convention/document.md new file mode 100644 index 00000000..7f0d65a2 --- /dev/null +++ b/docs/community/dev_convention/document.md @@ -0,0 +1,226 @@ +--- +sidebar_position: 1 +title: 文档规约 +--- + +文档引用自:[中文文案排版指北](https://github.com/sparanoid/chinese-copywriting-guidelines) + +## 空格 + +> 「有研究顯示,打字的時候不喜歡在中文和英文之間加空格的人,感情路都走得很辛苦,有七成的比例會在 34 歲的時候跟自己不愛的人結婚,而其餘三成的人最後只能把遺產留給自己的貓。畢竟愛情跟書寫都需要適時地留白。 +> +> 與大家共勉之。」——[vinta/paranoid-auto-spacing](https://github.com/vinta/pangu.js) + +### 中英文之間需要增加空格 + +正確: + +> 在 LeanCloud 上,數據儲存是圍繞 `AVObject` 進行的。 + +錯誤: + +> 在LeanCloud上,數據儲存是圍繞`AVObject`進行的。 + +> 在 LeanCloud上,數據儲存是圍繞`AVObject` 進行的。 + +完整的正確用法: + +> 在 LeanCloud 上,數據儲存是圍繞 `AVObject` 進行的。每個 `AVObject` 都包含了與 JSON 兼容的 key-value 對應的數據。數據是 schema-free 的,你不需要在每個 `AVObject` 上提前指定存在哪些键,只要直接設定對應的 key-value 即可。 + +例外:「豆瓣FM」等產品名詞,按照官方所定義的格式書寫。 + +### 中文與數字之間需要增加空格 + +正確: + +> 今天出去買菜花了 5000 元。 + +錯誤: + +> 今天出去買菜花了 5000元。 + +> 今天出去買菜花了5000元。 + +### 數字與單位之間需要增加空格 + +正確: + +> 我家的光纖入屋寬頻有 10 Gbps,SSD 一共有 20 TB。 + +錯誤: + +> 我家的光纖入屋寬頻有 10Gbps,SSD 一共有 20TB。 + +例外:度數/百分比與數字之間不需要增加空格: + +正確: + +> 角度為 90° 的角,就是直角。 + +> 新 MacBook Pro 有 15% 的 CPU 性能提升。 + +錯誤: + +> 角度為 90 ° 的角,就是直角。 + +> 新 MacBook Pro 有 15 % 的 CPU 性能提升。 + +### 全形標點與其他字符之間不加空格 + +正確: + +> 剛剛買了一部 iPhone,好開心! + +錯誤: + +> 剛剛買了一部 iPhone ,好開心! + +> 剛剛買了一部 iPhone, 好開心! + +### `text-spacing` to the rescue? + +CSS Text Module Level 4 的 [`text-spacing`](https://www.w3.org/TR/css-text-4/#text-spacing-property) 和 Microsoft 的 [`-ms-text-autospace`](https://msdn.microsoft.com/library/ms531164(v=vs.85).aspx) 可以實現自動為中英文之間增加空白。不過目前並未普及,另外在其他應用場景,例如 macOS、iOS、Windows 等用戶介面目前並不存在這個特性,所以請繼續保持隨手加空格的習慣。 + +## 標點符號 + +### 不重複使用標點符號 + +即使中國大陸的標點符號用法允許重複使用標點符號,但是這麼做會破壞句子的美觀性。 + +正確: + +> 德國隊竟然戰勝了巴西隊! + +> 她竟然對你說「喵」?! + +錯誤: + +> 德國隊竟然戰勝了巴西隊!! + +> 德國隊竟然戰勝了巴西隊!!!!!!!! + +> 她竟然對你說「喵」??!! + +> 她竟然對你說「喵」?!?!??!! + +## 全形和半形 + +不明白什麼是全形(全角)與半形(半角)符號?請查看維基百科條目『[全形和半形](https://zh.wikipedia.org/wiki/%E5%85%A8%E5%BD%A2%E5%92%8C%E5%8D%8A%E5%BD%A2)』。 + +### 使用全形中文標點 + +正確: + +> 嗨!你知道嘛?今天前台的小妹跟我說「喵」了哎! + +> 核磁共振成像(NMRI)是什麼原理都不知道?JFGI! + +錯誤: + +> 嗨! 你知道嘛? 今天前台的小妹跟我說 "喵" 了哎! + +> 嗨!你知道嘛?今天前台的小妹跟我說"喵"了哎! + +> 核磁共振成像 (NMRI) 是什麼原理都不知道? JFGI! + +> 核磁共振成像(NMRI)是什麼原理都不知道?JFGI! + +### 數字使用半形字符 + +正確: + +> 這件蛋糕只賣 1000 元。 + +錯誤: + +> 這件蛋糕只賣 1000 元。 + +例外:在設計稿、宣傳海報中如出現極少量數字的情形時,為方便文字對齊,是可以使用全形數字的。 + +### 遇到完整的英文整句、特殊名詞,其內容使用半形標點 + +正確: + +> 賈伯斯那句話是怎麼說的?「Stay hungry, stay foolish.」 + +> 推薦你閱讀《Hackers & Painters: Big Ideas from the Computer Age》,非常的有趣。 + +錯誤: + +> 賈伯斯那句話是怎麼說的?「Stay hungry,stay foolish。」 + +> 推薦你閱讀《Hackers&Painters:Big Ideas from the Computer Age》,非常的有趣。 + +## 名詞 + +### 專有名詞使用正確的大小寫 + +大小寫相關用法原屬於英文書寫範疇,不屬於本 wiki 討論內容,在這裡只對部分易錯用法進行簡述。 + +正確: + +> 使用 GitHub 登錄 + +> 我們的客戶有 GitHub、Foursquare、Microsoft Corporation、Google、Facebook, Inc.。 + +錯誤: + +> 使用 github 登錄 + +> 使用 GITHUB 登錄 + +> 使用 Github 登錄 + +> 使用 gitHub 登錄 + +> 使用 gイんĤЦ8 登錄 + +> 我們的客戶有 github、foursquare、microsoft corporation、google、facebook, inc.。 + +> 我們的客戶有 GITHUB、FOURSQUARE、MICROSOFT CORPORATION、GOOGLE、FACEBOOK, INC.。 + +> 我們的客戶有 Github、FourSquare、MicroSoft Corporation、Google、FaceBook, Inc.。 + +> 我們的客戶有 gitHub、fourSquare、microSoft Corporation、google、faceBook, Inc.。 + +> 我們的客戶有 gイんĤЦ8、キouЯƧquムгє、๓เςг๏ร๏Ŧt ς๏гק๏гคtเ๏ภn、900913、ƒ4ᄃëв๏๏к, IПᄃ.。 + +注意:當網頁中需要配合整體視覺風格而出現全部大寫/小寫的情形,HTML 中請使用標準的大小寫規範進行書寫;並通過 `text-transform: uppercase;`/`text-transform: lowercase;` 對表現形式進行定義。 + +### 不要使用不道地的縮寫 + +正確: + +> 我們需要一位熟悉 TypeScript、HTML5,至少理解一種框架(如 React、Next.js)的前端開發者。 + +錯誤: + +> 我們需要一位熟悉 Ts、h5,至少理解一種框架(如 RJS、nextjs)的 FED。 + +## 爭議 + +以下用法略帶有個人色彩,即:無論是否遵循下述規則,從語法的角度來講都是**正確**的。 + +### 超連結之間增加空格 + +用法: + +> 請 [提交一個 issue](#) 並分配给相關同事。 + +> 訪問我們網站的最新動態,請 [點擊這裡](#) 進行訂閱! + +對比用法: + +> 請[提交一個 issue](#) 並分配给相關同事。 + +> 訪問我們網站的最新動態,請[點擊這裡](#)進行訂閱! + +### 簡體中文使用直角引號 + +用法: + +> 「老师,『有条不紊』的『紊』是什么意思?」 + +對比用法: + +> “老师,‘有条不紊’的‘紊’是什么意思?” diff --git a/docs/docs/user_docs/other/issue.md b/docs/community/faq.md similarity index 89% rename from docs/docs/user_docs/other/issue.md rename to docs/community/faq.md index 11f48a12..fe7b20ec 100644 --- a/docs/docs/user_docs/other/issue.md +++ b/docs/community/faq.md @@ -1,10 +1,11 @@ --- -sidebar_position: 2 +sidebar_position: 3 +title: 常见问题 --- # 常见问题 -- 租户和项目在 Hippo4J 中是什么意思 +- 租户和项目在 Hippo4j 中是什么意思 - 控制台线程池管理和线程池实例的区别 - 示例项目为什么会有跨域请求 - 更新代码后运行时服务端SQL报错 @@ -16,16 +17,15 @@ sidebar_position: 2 - 线程池实例中修改队列容量参数问题 - 控制台 SocketTimeoutException: connect timed out -## 租户和项目在 Hippo4J 中是什么意思 +## 租户和项目在 Hippo4j 中是什么意思 -Hippo4J 按照租户、项目、线程池的维度划分。 +Hippo4j 按照租户、项目、线程池的维度划分。 举个例子,小编在一家公司的公共组件团队,团队中负责消息、短链接网关等项目。公共组件是租户,消息或短链接就是项目。 ## 控制台线程池管理和线程池实例的区别 -在线程池管理中修改线程池参数,客户端并不能实时感知到并变更参数,需要重启客户端。而线程池实例中去对具体的实例修改参数时,客户端无需重启,可以实时感知到参数变化。如果二者针对同一线程 -池的参数配置不同,则在重启客户端时,客户端会去拉去线程池管理中的参数配置。 +在线程池管理中修改线程池参数,客户端并不能实时感知到并变更参数,需要重启客户端。而线程池实例中去对具体的实例修改参数时,客户端无需重启,可以实时感知到参数变化。如果二者针对同一线程池的参数配置不同,则在重启客户端时,客户端会去拉去线程池管理中的参数配置。 二者对应的定位:线程池管理中的配置是常态化配置。而线程池实例里的配置变更像是一种临时修改,比如突发的流量激增等场景,并不具备普适性。 @@ -43,13 +43,13 @@ Hippo4J 按照租户、项目、线程池的维度划分。 如果更新代码运行功能出错,大概率是因为项目新增或修改了表结构。如版本升级迭代涉及数据库表变更,会额外提供 SQL 变更文件。 -如若第一次使用,初始化 SQL 脚本地址:[hippo4j_manager.sql](https://github.com/longtai-cn/hippo4j/blob/develop/hippo4j-server/conf/hippo4j_manager.sql)。 +如若第一次使用,初始化 SQL 脚本地址:[Hippo4j_manager.sql](https://github.com/longtai-cn/hippo4j/blob/develop/hippo4j-server/conf/hippo4j_manager.sql)。 > 友情提示:每次执行数据库表或数据变更时,一定要保持提前备份的好习惯。 ## 生产环境如何不启用动态线程池 -测试环境已经引入 Hippo4J,暂时不打算上线生产环境。 +测试环境已经引入 Hippo4j,暂时不打算上线生产环境。 生产环境指定配置 `spring.dynamic.thread-pool.enable=false`,测试环境和生产环境配置就会隔离。 @@ -57,9 +57,9 @@ Hippo4J 按照租户、项目、线程池的维度划分。 不会。Client 端包含对 Server 端的健康检查机制,Server 端不可用时会停止交互,检查到可用时重新建立连接交互。 -## Hippo4J 的发布方式是怎样的?如何选择正确的版本 +## Hippo4j 的发布方式是怎样的?如何选择正确的版本 -Hippo4J 发布时可能会涉及到两端发布,分别是 Server 和 Starter。如无特殊说明,**每一次的版本升级将兼容上一版本代码**。 +Hippo4j 发布时可能会涉及到两端发布,分别是 Server 和 Starter。如无特殊说明,**每一次的版本升级将兼容上一版本代码**。 - 如涉及 Server 发布,会在 [发布列表页面](https://github.com/longtai-cn/hippo4j/releases) 创建最新的发行版本; - 如涉及 Starter 发布,将直接推送 Starter Jar 至中央仓库,Server 包版本不变。 @@ -67,7 +67,7 @@ Hippo4J 发布时可能会涉及到两端发布,分别是 Server 和 Starter ## 群机器人接受不到通知报警 如果是钉钉机器人,需在机器人配置自定义关键字,才可发送成功。如下所示: - + ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220530200133377.png?x-oss-process=image/resize,h_500,w_800) 如果使用 hippo4j-server,请检查在 hippo4j-server 添加的报警通知记录,是否在客户端项目启动前,因为客户端只有在启动时会去 hippo4j-server 拉取报警通知记录。 diff --git a/docs/community/sponsor.md b/docs/community/sponsor.md new file mode 100644 index 00000000..f216fc3b --- /dev/null +++ b/docs/community/sponsor.md @@ -0,0 +1,22 @@ +--- +sidebar_position: 4 +title: 支持开源 +--- + +如果您正在使用这个项目并感觉良好,或者是想支持我继续开发,通过以下二维码一次性捐款。 + +在这里承诺将,将收到的所有赞助支持资金完全公开化,且后续资金用途仅 Hippo4j 项目的运转。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/IMG_6719_2.jpg?x-oss-process=image/resize,h_180,w_180) + +感谢给予支持的朋友,您的支持是 Hippo4j 社区前进的动力 🎉 + +| | ID | 赞赏金额 | 时间 | 备注 | +|-----|--|-------|------------|--------------------| +| 1 | 六月飞雪 | 30.00 | 2021-12-30 | 代码设计很优雅的一款框架,继续加油! | +| 2 | 孙大圣 | 26.6 | 2022-03-23 | 学习一下😁😁 | +| 3 | Easy 点 | 66.00 | 2022-04-09 | 好货好技术当加赏 | +| 4 | 捷克 | 30.00 | 2022-05-21 | 非常不错的框架,点赞 | +| 5 | 吃猫的饼干 | 88.00 | 2022-08-21 | 👍 | +| 6 | 不忘初心· | 66.00 | 2022-10-28 | Nice! | +| 7 | 时刻· | 6.00 | 2022-10-30 | -! | diff --git a/docs/docs/user_docs/other/update-log.md b/docs/community/update-log.md similarity index 84% rename from docs/docs/user_docs/other/update-log.md rename to docs/community/update-log.md index fc7b2fda..3985ed9c 100644 --- a/docs/docs/user_docs/other/update-log.md +++ b/docs/community/update-log.md @@ -1,8 +1,53 @@ --- -sidebar_position: 4 +title: 更新日志 +sidebar_position: 5 --- -# 更新日志 +## 1.4.3 (Nov 06, 2022) + +这是一个功能增强版本,修复了少量 BUG。建议按照当前版本升级。具体信息可查看 Release 标签地址:[1.4.3](https://github.com/opengoofy/hippo4j/milestone/13?closed=1) + +**Use Change** + +- 重构线程池监控,配置层级和命名改变 +- 如果使用钉钉报警,关键字【警报】修改为【告警】 + +**Feature** + +- 重构 Spring 后置处理器创建动态线程池逻辑 +- 官网开启多版本化功能 +- 官网支持国际化,en-US +- 适配线程池延迟初始化 @wulangcode +- 添加 Codecov 相关代码覆盖率指标 +- 项目优雅关闭时停止运行状态采集 + +**Refactor** + +- DynamicThreadPoolExecutor 重构,增加插件扩展逻辑 @Createsequence +- 重构线程池监控,新增容器和三方框架线程池监控 +- 重构服务端包目录,聚合 hippo4j-server 相关 module + +**Bug** + +- dubbo 线程池无法获取运行信息 @iwangjie +- 线程池检查活跃度报警取值错误 @maxisvest +- 动态线程池修改多次后队列提示信息丢失 +- docker部署 mysql启动报错H2驱动 +- docker-startup.sh的mysql配置多个“-” @Malcolmli +- 动态注册线程池队列容量赋值错误 +- 飞书超时类型告警不存在 Trace 信息时发送错误 @mageeric + +**Optimize** + +- 修改报警文案,【警报】修改为【告警】 @wulangcode +- 自动选择H2数据库的存储路径 @iwangjie +- 服务端在客户端后面启动,依旧支持长轮训 @wulangcode +- 配置未发生变更时,长轮询返回 304 @wulangcode +- discovery服务Lease类中判断过期时间需要多等一个duration @w-jirong +- 优化 ThreadPoolBuilder#maxPoolNum 核心线程不得大于最大线程 @wulangcode +- hippo4j console ui 迁移至本项目 +- 查询 Web 线程池列表添加框架标识 +- 优化 H2 初始化逻辑 ## 1.4.2 (Oct 18, 2022) diff --git a/docs/docs/community/contributor.md b/docs/docs/community/contributor.md deleted file mode 100644 index f0185ad3..00000000 --- a/docs/docs/community/contributor.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -sidebar_position: 1 ---- - -# 贡献指南 - -Git Commit Log 尽量使用英文。 - -Pull Request 尽量保持单一,不同语义的代码贡献应拆分多个 Pull Request。 - -为了让您的 GitHub ID 显示在 Contributor 列表中,别忘了以下设置: - -```shell -git config --global user.name "username" -git config --global user.email "GitHub 账号邮箱" -``` - -## 贡献者列表 - -您可以在 [Hippo4J](https://github.com/opengoofy/hippo4j/graphs/contributors) 和 [Hippo4J Console](https://github.com/opengoofy/hippo4j-console) 的贡献列表中找到全部的贡献者名单。 - - - - diff --git a/docs/docs/community/sponsor.md b/docs/docs/community/sponsor.md deleted file mode 100644 index 51127f15..00000000 --- a/docs/docs/community/sponsor.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -sidebar_position: 4 ---- - -# 支持开源 - -如果您正在使用这个项目并感觉良好,或者是想支持我继续开发。通过以下二维码 一次性捐款,我多半会买一杯咖啡或茶~ - -> [GitHub](https://github.com/opengoofy/hippo4j) 或 [Gitee](https://gitee.com/agentart/hippo4j) Star 才是最大动力~ - -![](https://images-machen.oss-cn-beijing.aliyuncs.com/IMG_6719_2.jpg?x-oss-process=image/resize,h_180,w_180) - -感谢给予支持的朋友,您的支持是我前进的动力 🎉 - -| | ID | 赞赏金额 | 时间 | 备注 | -|-----|--------|-------|------------|--------------------| -| 1 | 六月飞雪 | 30.00 | 2021-12-30 | 代码设计很优雅的一款框架,继续加油! | -| 2 | 孙大圣 | 26.6 | 2022-03-23 | 学习一下😁😁 | -| 3 | Easy 点 | 66.00 | 2022-04-09 | 好货好技术当加赏 | -| 4 | 捷克 | 30.00 | 2022-05-21 | 非常不错的框架,点赞 | -| 5 | 吃猫的饼干 | 88.00 | 2022-08-21 | 👍 | diff --git a/docs/docs/user_docs/getting_started/config/hippo4j-config-monitor.md b/docs/docs/user_docs/getting_started/config/hippo4j-config-monitor.md index 72528638..c030dc10 100644 --- a/docs/docs/user_docs/getting_started/config/hippo4j-config-monitor.md +++ b/docs/docs/user_docs/getting_started/config/hippo4j-config-monitor.md @@ -39,7 +39,12 @@ management: spring: dynamic: thread-pool: - collect-type: micrometer + monitor: + enable: true # 是否开启采集线程池运行时数据 + collect-interval: 5000 # 采集线程池运行数据频率 + collect-types: micrometer # 采集线程池运行数据的类型。eg:log、micrometer。多个可以同时使用,默认 micrometer + initial-delay: 10000 # 项目启动后延迟多久进行采集 + thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、web、adapter。可任意配置,默认 dynamic ``` 项目启动,访问 `http://localhost:29999/actuator/prometheus` 出现 `dynamic_thread_pool_` 前缀的指标,即为成功。 diff --git a/docs/docs/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md b/docs/docs/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md index 693dd341..709e882a 100644 --- a/docs/docs/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md +++ b/docs/docs/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md @@ -10,7 +10,7 @@ sidebar_position: 5 cn.hippo4j hippo4j-config-spring-boot-1x-starter - 1.4.2 + 1.4.3-upgrade ``` diff --git a/docs/docs/user_docs/getting_started/config/hippo4j-config-start.md b/docs/docs/user_docs/getting_started/config/hippo4j-config-start.md index 33f658c2..e6a222a6 100644 --- a/docs/docs/user_docs/getting_started/config/hippo4j-config-start.md +++ b/docs/docs/user_docs/getting_started/config/hippo4j-config-start.md @@ -4,7 +4,7 @@ sidebar_position: 1 # 接入流程 -Nacos、Apollo、Zookeeper、ETCD、Polaris 配置中心任选其一。 +Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心任选其一。 ## hippo4j 配置 @@ -12,7 +12,7 @@ Nacos、Apollo、Zookeeper、ETCD、Polaris 配置中心任选其一。 cn.hippo4j hippo4j-config-spring-boot-starter - 1.4.2 + 1.4.3-upgrade ``` @@ -59,7 +59,7 @@ spring: secret: xxx # 加签专属 - platform: 'LARK' token: xxx - # Nacos、Apollo、Zookeeper、ETCD、Polaris 任选其一 + # Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 任选其一 nacos: data-id: xxx group: xxx diff --git a/docs/docs/user_docs/getting_started/difference.md b/docs/docs/user_docs/getting_started/difference.md index 4e07d71b..6bf56962 100644 --- a/docs/docs/user_docs/getting_started/difference.md +++ b/docs/docs/user_docs/getting_started/difference.md @@ -4,34 +4,34 @@ sidebar_position: 0 # 运行模式介绍 -1.1.0 版本发布后,Hippo-4J 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。 +1.1.0 版本发布后,Hippo4j 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。 ![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220319154626314.png) -### hippo4j-config +### Hippo4j config -**轻量级动态线程池管理**,依赖 Nacos、Apollo、Zookeeper、ETCD、Polaris 等三方配置中心(任选其一)完成线程池参数动态变更,支持运行时报警、监控等功能。 +**轻量级动态线程池管理**,依赖 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 -**部署 hippo4j-server 服务**,通过可视化 Web 界面完成线程池的创建、变更以及查看,不依赖三方中间件。 +**部署 Hippo4j server 服务**,通过可视化 Web 界面完成线程池的创建、变更以及查看,不依赖三方中间件。 -相比较 hippo4j-config,功能会更强大,但同时也引入了一定的复杂性。需要部署一个 Java 服务,以及依赖 MySQL 数据库。 +相比较 Hippo4j config,功能会更强大,但同时也引入了一定的复杂性。需要部署一个 Java 服务,以及依赖 MySQL 数据库。 ![](https://images-machen.oss-cn-beijing.aliyuncs.com/1644032018254-min.gif) ### 使用总结 -| | hippo4j-config | hippo4j-server | -| ---- | ---------------------------------------------------- | ------------------------------------------------------------ | -| 依赖 | Nacos、Apollo、Zookeeper、ETCD、Polaris 配置中心(任选其一) | 部署 Hippo-4J Server(内部无依赖中间件) | -| 使用 | 配置中心补充线程池相关参数 | Hippo-4J Server Web 控制台添加线程池记录 | -| 功能 | 包含基础功能:参数动态化、运行时监控、报警等 | 基础功能之外扩展控制台界面、线程池堆栈查看、线程池运行信息实时查看、历史运行信息查看、线程池配置集群个性化等 | +| | Hippo4j config | Hippo4j server | +| ---- |-------------------------------------------------------|--------------------------------------------------------| +| 依赖 | Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心(任选其一) | 部署 Hippo4j server(内部无依赖中间件) | +| 使用 | 配置中心补充线程池相关参数 | Hippo4j server web 控制台添加线程池记录 | +| 功能 | 包含基础功能:参数动态化、运行时监控、报警等 | 基础功能之外扩展控制台界面、线程池堆栈查看、线程池运行信息实时查看、历史运行信息查看、线程池配置集群个性化等 | -使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 hippo4j-config 使用即可;如果希望更多的功能,可以选择 hippo4j-server。 +使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 Hippo4j config 使用即可;如果希望更多的功能,可以选择 Hippo4j server。 **两者在进行替换的时候,无需修改业务代码**。 diff --git a/docs/docs/user_docs/getting_started/hippo4j-adapter.md b/docs/docs/user_docs/getting_started/hippo4j-adapter.md index 030367e1..378713e8 100644 --- a/docs/docs/user_docs/getting_started/hippo4j-adapter.md +++ b/docs/docs/user_docs/getting_started/hippo4j-adapter.md @@ -33,7 +33,7 @@ Hippo4J 目前已支持的三方框架线程池列表: hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq - 1.4.2 + 1.4.3-upgrade ``` @@ -43,7 +43,7 @@ Hippo4J 目前已支持的三方框架线程池列表: cn.hippo4j hippo4j-spring-boot-starter-adapter-all - 1.4.2 + 1.4.3-upgrade ``` diff --git a/docs/docs/user_docs/getting_started/server/hippo4j-server-config.md b/docs/docs/user_docs/getting_started/server/hippo4j-server-config.md index 24bf9a13..e1ef853f 100644 --- a/docs/docs/user_docs/getting_started/server/hippo4j-server-config.md +++ b/docs/docs/user_docs/getting_started/server/hippo4j-server-config.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 3 --- # 服务端配置 diff --git a/docs/docs/user_docs/getting_started/server/hippo4j-server-monitor.md b/docs/docs/user_docs/getting_started/server/hippo4j-server-monitor.md new file mode 100644 index 00000000..4a81a47b --- /dev/null +++ b/docs/docs/user_docs/getting_started/server/hippo4j-server-monitor.md @@ -0,0 +1,132 @@ +--- +sidebar_position: 2 +--- + +# 线程池监控 + +Server 模式默认内置线程池运行时采集和监控功能,如果想要使用 Prometheus + Grafana 的方式可以查看以下内容。 + +## 线程池监控配置 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```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 # 采集线程池运行数据的类型。eg:server、micrometer。多个可以同时使用,默认 server + initial-delay: 10000 # 项目启动后延迟多久进行采集 + thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、web、adapter。可任意配置,默认 dynamic +``` + +如果使用 `micrometer` 类型的监控指标,需要添加以下依赖。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-monitor-micrometer + 1.4.3-upgrade + +``` + +项目启动,访问 `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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/docs/user_docs/getting_started/server/hippo4j-server-start.md b/docs/docs/user_docs/getting_started/server/hippo4j-server-start.md index 64b94ee3..5bf3db1c 100644 --- a/docs/docs/user_docs/getting_started/server/hippo4j-server-start.md +++ b/docs/docs/user_docs/getting_started/server/hippo4j-server-start.md @@ -1,12 +1,12 @@ --- -sidebar_position: 3 +sidebar_position: 1 --- # 接入流程 部署服务端,参考 [部署手册](/docs/user_docs/ops/hippo4j-server-deploy)。 -服务端创建 [租户、项目](/docs/user_docs/other/issue#租户和项目在-hippo4j-中是什么意思) 和线程池记录。 +服务端创建 [租户、项目](/community/faq#租户和项目在-hippo4j-中是什么意思) 和线程池记录。 需要注意,项目 ID 需要与配置文件 `{application.name}` 保持一致。 @@ -22,7 +22,7 @@ SpringBoot Pom 引入 Hippo4j Starter Jar。 cn.hippo4j hippo4j-spring-boot-starter - 1.4.2 + 1.4.3-upgrade ``` diff --git a/docs/docs/user_docs/intro.md b/docs/docs/user_docs/intro.md index 4bfd0d78..fd2549c8 100644 --- a/docs/docs/user_docs/intro.md +++ b/docs/docs/user_docs/intro.md @@ -4,70 +4,63 @@ sidebar_position: 1 # 简介 -## 动态可观测线程池框架 +## 线程池痛点 -Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。 - -- 🏗 全局管控 - 管理应用线程池实例。 +线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。 -- ⚡️ 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。 +如果有在项目中实际使用线程池,相信你可能会遇到以下痛点: -- 🐳 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。 +- 线程池随便定义,线程资源过多,造成服务器高负载。 -- 👀 运行监控 - 实时查看线程池运行时数据,最近半小时线程池运行数据图表展示。 +- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。 +- 线程池任务执行时间超过平均执行周期,开发人员无法感知。 +- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。 +- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。 +- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。 +- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。 +- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。 -- 👐 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。 +## 什么是 Hippo-4J -- 👯‍♀️ 多种模式 - 内置两种使用模式:[依赖配置中心](https://hippo4j.cn/docs/user_docs/getting-started/config/hippo4j-config-start) 和 [无中间件依赖](https://hippo4j.cn/docs/user_docs/getting-started/server/hippo4j-server-start)。 +Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。 -- 🛠 容器管理 - Tomcat、Jetty、Undertow 容器线程池运行时查看和线程数变更。 +提供以下功能支持: -- 🌈 中间件适配 - Apache RocketMQ、Dubbo、RabbitMQ、Hystrix 消费线程池运行时数据查看和线程数变更。 +- 全局管控 - 管理应用线程池实例。 -> 看完有收获,GitHub 右上角帮忙点个小星星,开源作者为爱发电也不容易 🤣 +- 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。 +- 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。 +- 运行监控 - 实时查看线程池运行时数据,最近半小时线程池运行数据图表展示。 +- 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。 +- 多种模式 - 内置两种使用模式:[依赖配置中心](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](docs/user_docs/getting_started/server/hippo4j-server-start) - -演示环境: -- [http://console.hippo4j.cn/index.html](http://console.hippo4j.cn/index.html) -- 用户/密码:hippo4j/hippo4j - -## 联系我 - -![image](https://user-images.githubusercontent.com/77398366/169202380-6c068acd-700a-41fa-8823-e01c92bb5e88.png) +对于本地演示目的,请参阅 [Quick start](https://hippo4j.cn/docs/user_docs/user_guide/quick-start) -## 开发者 +演示环境: [http://console.hippo4j.cn/index.html](http://console.hippo4j.cn/index.html) -感谢所有为 Hippo-4J 做出贡献的开发者! +## 接入登记 - +更多接入的公司,欢迎在 [登记地址](https://github.com/opengoofy/hippo4j/issues/13) 登记,登记仅仅为了产品推广。 -## 我们的荣誉 - -Hippo-4J 获得了一些宝贵的荣誉,这属于每一位对 Hippo-4J 做出过贡献的成员,谢谢各位的付出。 - -![](https://user-images.githubusercontent.com/77398366/170607238-7308c9be-1d63-46a6-852c-eef2e4cf7405.JPG) +## 联系我 -## Stars 趋势 +![](https://user-images.githubusercontent.com/77398366/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) -![](https://starchart.cc/longtai-cn/hippo4j.svg) +扫码添加微信,备注:hippo4j,邀您加入群聊。若图片加载不出来,访问 [官网站点](https://hippo4j.cn/docs/user_docs/other/group)。 ## 友情链接 - [[ LiteFlow ]](https://liteflow.yomahub.com/):轻量,快速,稳定可编排的组件式规则引擎。 - [[ Sa-Token ]](https://github.com/dromara/sa-token):一个轻量级 java 权限认证框架,让鉴权变得简单、优雅! - -- [[ HertzBeat ]](https://github.com/dromara/hertzbeat):易用友好的云监控系统, 无需 Agent, 强大自定义监控能力。 - +- [[ HertzBeat ]](https://github.com/dromara/hertzbeat):易用友好的云监控系统, 无需 Agent, 强大自定义监控能力。 - [[ JavaGuide ]](https://github.com/Snailclimb/JavaGuide):一份涵盖大部分 Java 程序员所需要掌握的核心知识。 +- [[ toBeBetterJavaer ]](https://github.com/itwanger/toBeBetterJavaer):一份通俗易懂、风趣幽默的 Java 学习指南。 -- [[ toBeBetterJavaer ]](https://github.com/itwanger/toBeBetterJavaer):一份通俗易懂、风趣幽默的Java学习指南,内容涵盖Java基础、Java并发编程等核心知识点。 - -## 鸣谢 - -Hippo4J 项目基于或参考以下项目:[Nacos](https://github.com/alibaba/nacos)、[Eureka](https://github.com/Netflix/Eureka)。 +## 贡献者 -感谢 JetBrains 提供的免费开源 License +感谢所有为项目作出贡献的开发者。如果有意贡献,参考 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)。 diff --git a/docs/docs/user_docs/ops/server-docker.md b/docs/docs/user_docs/ops/server-docker.md index d0a738ce..141c597a 100644 --- a/docs/docs/user_docs/ops/server-docker.md +++ b/docs/docs/user_docs/ops/server-docker.md @@ -16,6 +16,7 @@ docker run -d -p 6691:6691 --name hippo4j-server hippo4j/hippo4j-server ```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 \ diff --git a/docs/docs/user_docs/user_guide/notify.md b/docs/docs/user_docs/user_guide/notify.md index 216840e9..924d5a7e 100644 --- a/docs/docs/user_docs/user_guide/notify.md +++ b/docs/docs/user_docs/user_guide/notify.md @@ -41,7 +41,6 @@ WECHART:填写user_id会以@的消息发给用户,填写姓名则是普通 LARK:填写ou_开头用户唯一标识会以@的消息发给用户,填写手机号则是普通的@ ``` - ## 钉钉平台 [钉钉创建群机器人](https://www.dingtalk.com/qidian/help-detail-20781541.html) @@ -54,6 +53,10 @@ LARK:填写ou_开头用户唯一标识会以@的消息发给用户,填写手 ![](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) diff --git a/docs/docs/user_docs/user_guide/quick-start.md b/docs/docs/user_docs/user_guide/quick-start.md index cce9e577..5e2e7035 100644 --- a/docs/docs/user_docs/user_guide/quick-start.md +++ b/docs/docs/user_docs/user_guide/quick-start.md @@ -14,7 +14,7 @@ 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) 模块下 Hippo4JServerExampleApplication 应用类。 +启动示例项目,[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 diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index c83cb4a2..73d439b3 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -6,7 +6,7 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula'); /** @type {import('@docusaurus/types').Config} */ const config = { - title: 'HIPPO-4J', + title: 'Hippo4j', tagline: '动态可观测线程池框架,为业务系统提高线上运行保障能力', url: 'https://hippo4j.cn', baseUrl: '/', @@ -22,10 +22,20 @@ const config = { // Even if you don't use internalization, you can use this field to set useful // metadata like html lang. For example, if your site is Chinese, you may want // to replace "en" with "zh-Hans". - i18n: { - defaultLocale: 'zh-CN', - locales: ['zh-CN'], + i18n: { + defaultLocale: "en", + locales: ["en", "zh"], + localeConfigs: { + en: { + label: "English", + direction: "ltr", + }, + zh: { + label: "简体中文", + direction: "ltr", + }, }, + }, presets: [ [ @@ -52,6 +62,18 @@ const config = { }), ], ], + + plugins: [ + [ + "@docusaurus/plugin-content-docs", + { + id: "community", + path: "community", + routeBasePath: "community", + sidebarPath: require.resolve("./sidebarsCommunity.js"), + }, + ], + ], themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ @@ -60,14 +82,15 @@ const config = { announcementBar: { id: 'announcementBar-1', // Increment on change // content: `⭐️ If you like hippo4j, give it a star on Gitee, thanks.`, - content: `⭐️ 如果您喜欢 hippo4j,请在 GiteeGitHub 上给它一个 star,谢谢!`, + // content: `⭐️ 如果您喜欢 hippo4j,请在 GiteeGitHub 上给它一个 star,谢谢!`, + content: `⭐️ 开源不易,hippo4j 如果对您工作有帮助,请在 GitHub 上给它一个 🌟`, // content: `👉 《小马哥的代码实战课》官方知识星球来啦!!!`, }, navbar: { - title: 'HIPPO-4J', + title: '', logo: { alt: 'HIPPO-4J 动态可观测线程池框架', - src: 'img/web.png', + src: 'img/hippo4j.png', }, items: [ { @@ -78,12 +101,13 @@ const config = { label: '文档', }, { - type: 'docSidebar', - docId: 'contributor', - position: 'left', - sidebarId: 'community', - label: '社区', + to: "/community/contributor-guide", + label: "社区", + position: "left", + activeBaseRegex: `/community/`, }, + { to: "/team", label: "团队", position: "left" }, + { to: "/users", label: "用户", position: "left" }, /*{to: '/blog', label: '博客', position: 'left'},*/ { href: 'http://console.hippo4j.cn/index.html', @@ -91,10 +115,15 @@ const config = { position: 'left', }, { - href: 'https://xiaomage.info/knowledge-planet', - label: '🥇代码实战课', + href: 'https://sourl.cn/Fyp7y4', + label: '👉 刚果商城', position: 'left', }, + { + type: 'docsVersionDropdown', + position: 'right', + dropdownActiveClassDisabled: true, + }, {type: 'localeDropdown', position: 'right'}, /*{ @@ -123,20 +152,28 @@ const config = { title: 'Docs', items: [ { - label: '简介', + label: 'Intro', to: '/docs/user_docs/intro', }, + { + label: 'Config Mode', + to: '/docs/user_docs/getting_started/config/hippo4j-config-start', + }, + { + label: 'Server Mode', + to: '/docs/user_docs/getting_started/server/hippo4j-server-start', + }, ], }, { title: 'Community', items: [ { - label: '加群沟通', + label: 'Group', href: 'https://hippo4j.cn/docs/user_docs/other/group', }, { - label: '微信公众号', + label: 'WeChat', href: 'https://mp.weixin.qq.com/s/diVHYvwiuYH9aWpZDPc27g', }, ], @@ -146,15 +183,24 @@ const config = { items: [ { label: 'Gitee', - href: 'https://gitee.com/itmachen/hippo4j', + href: 'https://gitee.com/magestack/hippo4j', }, { label: 'GitHub', href: 'https://github.com/opengoofy/hippo4j', }, + ], + }, + { + title: 'Links', + items: [ + { + label: '书源', + href: 'https://bookyuan.cn/', + }, { - label: '公司登记', - href: 'https://github.com/opengoofy/hippo4j/issues/13', + label: '推广合作', + href: 'https://hippo4j.cn/docs/user_docs/other/operation', }, ], }, diff --git a/docs/i18n/zh/code.json b/docs/i18n/zh/code.json new file mode 100644 index 00000000..e037b95c --- /dev/null +++ b/docs/i18n/zh/code.json @@ -0,0 +1,260 @@ +{ + "theme.ErrorPageContent.title": { + "message": "页面已崩溃。", + "description": "The title of the fallback page when the page crashed" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "重试", + "description": "The label of the button to try again when the page crashed" + }, + "theme.NotFound.title": { + "message": "找不到页面", + "description": "The title of the 404 page" + }, + "theme.NotFound.p1": { + "message": "我们找不到您要找的页面。", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "请联系原始链接来源网站的所有者,并告知他们链接已损坏。", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.admonition.note": { + "message": "备注", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "提示", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.admonition.danger": { + "message": "危险", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "信息", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.caution": { + "message": "警告", + "description": "The default label used for the Caution admonition (:::caution)" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "回到顶部", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "博文列表分页导航", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "较新的博文", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "较旧的博文", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.archive.title": { + "message": "历史博文", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "历史博文", + "description": "The page & hero description of the blog archive page" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "博文分页导航", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "较新一篇", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "较旧一篇", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.colorToggle.ariaLabel": { + "message": "切换浅色/暗黑模式(当前为{mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "暗黑模式", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "浅色模式", + "description": "The name for the light color mode" + }, + "theme.blog.post.plurals": { + "message": "{count} 篇博文", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} 含有标签「{tagName}」", + "description": "The title of the page for a blog tag" + }, + "theme.tags.tagsPageLink": { + "message": "查看所有标签", + "description": "The label of the link targeting the tag list page" + }, + "theme.docs.breadcrumbs.home": { + "message": "主页面", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "页面路径", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.DocCard.categoryDescription": { + "message": "{count} 个项目", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "文档分页导航", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "上一页", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "下一页", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "{count} 篇文档带有标签", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged}「{tagName}」", + "description": "The title of the page for a docs tag" + }, + "theme.docs.versionBadge.label": { + "message": "版本:{versionLabel}" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "此为 {siteTitle} {versionLabel} 版尚未发行的文档。", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "此为 {siteTitle} {versionLabel} 版的文档,现已不再积极维护。", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "最新的文档请参阅 {latestVersionLink} ({versionLabel})。", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "最新版本", + "description": "The label used for the latest version suggestion link label" + }, + "theme.common.editThisPage": { + "message": "编辑此页", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "标题的直接链接", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": "于 {date} ", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": "由 {user} ", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "最后{byUser}{atDate}更新", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "选择版本", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.common.skipToMainContent": { + "message": "跳到主要内容", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsListLabel": { + "message": "标签:", + "description": "The label alongside a tag list" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "关闭", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "最近博文导航", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "复制成功", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "复制代码到剪贴板", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "复制", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "切换自动换行", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { + "message": "打开/收起侧边栏菜单「{label}」", + "description": "The ARIA label to toggle the collapsible sidebar category" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "选择语言", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "本页总览", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "收起侧边栏", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "收起侧边栏", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.blog.post.readingTime.plurals": { + "message": "阅读需 {readingTime} 分钟", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.post.readMore": { + "message": "阅读更多", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "阅读 {title} 的全文", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← 回到主菜单", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "展开侧边栏", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "展开侧边栏", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.tags.tagsPageTitle": { + "message": "标签", + "description": "The title of the tag list page" + }, + "Community": { + "message": "社区" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-blog/2022-06-06-hippo4j/index.md b/docs/i18n/zh/docusaurus-plugin-content-blog/2022-06-06-hippo4j/index.md new file mode 100644 index 00000000..6f2d833b --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-blog/2022-06-06-hippo4j/index.md @@ -0,0 +1,145 @@ +--- +slug: Hippo-4J发布1.3.0版本 +title: Hippo-4J发布1.3.0版本 +authors: [xiaomage] +tags: [hippo4j, release, 1.3.0] +--- + +大家好,我是 **小马哥**。 + +Hippo-4J 距离上一个版本 1.2.1 已经过去一个月的时间。在此期间,由 **8 位贡献者** 提交了 **170+ commits**,正式发布 **1.3.0** 版本。 + +注:这是一个 **兼容历史版本** 的重大升级。 + +## HIPPO-4J 1.3.0 + +### Feature + +1. 添加 RabbitMQ 线程池监控及动态变更 +2. 添加 RocketMQ 线程池监控及动态变更 +3. 添加 Dubbo 线程池监控及动态变更 +4. 添加 SpringCloud Stream RocketMQ 消费线程池监控及动态变更 + +### Refactor + +1. 重构容器线程池查询及修改功能 +2. 优化配置中心触发监听后,所执行的数据变更逻辑 + +### Optimize + +1. 前端控制台删除无用组件 +2. 服务端页面字段未显示中文 +3. 控制台 UI 优化 +4. 修改线程池实例后实时刷新列表参数 +5. 容器线程池编辑仅限 Admin 权限 +6. SpringBoot Starter 变更包路径 + +### BUG + +1. 修复 SpringBoot Nacos 动态刷新不生效 +2. 报警配置 alarm=false 不配置通知报警平台和接收人报错 + +## 三方框架线程池适配 + +Hippo-4J 1.3.0 最大的功能发布就是开发出了 **适配三方框架的基础框架**。 + +目前已完成 **Dubbo、RabbitMQ、RocketMQ、RocketMQSpringCloudStream** 的线程池适配,后续还会接入 **Kafka、Hystrix** 等框架或中间件的线程池适配。 + +### 引入适配三方框架 Jar 包 + +引入 Hippo-4J server 或 core 的 maven jar 坐标后,还需要引入对应的框架适配 jar: + +```xml + + cn.hippo4j + + hippo4j-spring-boot-starter-adapter-dubbo + + hippo4j-spring-boot-starter-adapter-rabbitmq + + hippo4j-spring-boot-starter-adapter-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq + 1.3.0 + +``` + +如果想觉得引入多个 jar 包繁琐,可以仅需引入一个全量包,Hippo-4J 框架底层会根据各中间件的条件,判断加载具体线程池适配器。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-adapter-all + 1.3.0 + +``` + +### HIPPO-4J Server + +Hippo-4J server 引入上述适配 jar 包后,即可在 Hippo-4J server 的控制台进行查看及修改三方框架线程池。 + +![图1 线程池适配列表](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220531194810047.png) + + + +点击编辑即可修改该 Java 应用对应的框架底层线程池。 + +![图2 修改三方线程池](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220605152549732.png) + + + +点击 **全部修改** 按钮可以修改当前组下所有应用实例的线程池配置。 + +修改成功后,应用控制台打印以下日志,即为修改成功。 + +```java +[input] RocketMQ consumption thread pool parameter change. coreSize :: 1 => 10, maximumSize :: 1 => 10 +``` + +### HIPPO-4J Core + +Hippo-4J core 除了依赖上述适配 Jar 包外,还需要在配置中心添加以下配置项。 + +```yaml +spring: + dynamic: + thread-pool: + # 省略其它配置 + adapter-executors: + # threadPoolKey 代表线程池标识 + - threadPoolKey: 'input' + # mark 为三方线程池框架类型,参见文初已支持框架集合 + mark: 'RocketMQSpringCloudStream' + corePoolSize: 10 + maximumPoolSize: 10 +``` + +## Gitee GVP + +Hippo-4J 获得了一些宝贵的荣誉,这属于每一位对 Hippo-4J 做出过贡献的成员。 + +![图3 GVP 证书](https://images-machen.oss-cn-beijing.aliyuncs.com/170607238-7308c9be-1d63-46a6-852c-eef2e4cf7405.jpeg) + + + +感谢所有为 Hippo-4J 做出贡献的开发者! + +https://github.com/opengoofy/hippo4j/graphs/contributors + +![图4 Hippo-4J 开发者](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220605151136276.png) + + + +## 最后总结 + +开源不易,如果各位小伙伴看了 Hippo-4J 框架后有所收获,希望能帮忙在 Github、Gitee 点个 star,谢谢。 + +**Github**:https://github.com/opengoofy/hippo4j + +**Gitee**:https://gitee.com/mabaiwancn/hippo4j + +目前已有 **10+** 公司在生产环境使用 Hippo-4J,如果贵公司使用了 Hippo-4J,请在下方 Issue 登记,谢谢。 + +**Issue**:https://github.com/opengoofy/hippo4j/issues/13 + +登记使用不会对公司有任何影响,仅为了扩大 Hippo-4J 影响力,帮助它能走得更远。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-blog/authors.yml b/docs/i18n/zh/docusaurus-plugin-content-blog/authors.yml new file mode 100644 index 00000000..ffffe8d4 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-blog/authors.yml @@ -0,0 +1,5 @@ +xiaomage: + name: 小马哥 + title: hippo4j 作者 + url: https://github.com/magegoofy + image_url: https://avatars.githubusercontent.com/u/77398366?v=4 diff --git a/docs/i18n/zh/docusaurus-plugin-content-blog/options.json b/docs/i18n/zh/docusaurus-plugin-content-blog/options.json new file mode 100644 index 00000000..9239ff70 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-blog/options.json @@ -0,0 +1,14 @@ +{ + "title": { + "message": "Blog", + "description": "The title for the blog used in SEO" + }, + "description": { + "message": "Blog", + "description": "The description for the blog used in SEO" + }, + "sidebar.title": { + "message": "Recent posts", + "description": "The label for the left sidebar" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs-community/current.json b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current.json new file mode 100644 index 00000000..52732047 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current.json @@ -0,0 +1,94 @@ +{ + "version.label": { + "message": "Next", + "description": "The label for version current" + }, + "sidebar.tutorialSidebar.category.社区": { + "message": "社区", + "description": "The label for category 社区 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.贡献规约": { + "message": "贡献规约", + "description": "The label for category 贡献规约 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.user_docs": { + "message": "user_docs", + "description": "The label for category user_docs in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.用户指南": { + "message": "用户指南", + "description": "The label for category 用户指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.用户指南.link.generated-index.description": { + "message": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。", + "description": "The generated-index page description for category 用户指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.快速开始": { + "message": "快速开始", + "description": "The label for category 快速开始 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.依赖配置中心": { + "message": "依赖配置中心", + "description": "The label for category 依赖配置中心 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.无中间件依赖": { + "message": "无中间件依赖", + "description": "The label for category 无中间件依赖 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.运维指南": { + "message": "运维指南", + "description": "The label for category 运维指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.开发者手册": { + "message": "开发者手册", + "description": "The label for category 开发者手册 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.开发者手册.link.generated-index.description": { + "message": "Hippo4J 留给使用者能够扩展的知识点。", + "description": "The generated-index page description for category 开发者手册 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.其它": { + "message": "其它", + "description": "The label for category 其它 in sidebar tutorialSidebar" + }, + "sidebar.user_docs.category.用户指南": { + "message": "用户指南", + "description": "The label for category 用户指南 in sidebar user_docs" + }, + "sidebar.user_docs.category.用户指南.link.generated-index.description": { + "message": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。", + "description": "The generated-index page description for category 用户指南 in sidebar user_docs" + }, + "sidebar.user_docs.category.快速开始": { + "message": "快速开始", + "description": "The label for category 快速开始 in sidebar user_docs" + }, + "sidebar.user_docs.category.依赖配置中心": { + "message": "依赖配置中心", + "description": "The label for category 依赖配置中心 in sidebar user_docs" + }, + "sidebar.user_docs.category.无中间件依赖": { + "message": "无中间件依赖", + "description": "The label for category 无中间件依赖 in sidebar user_docs" + }, + "sidebar.user_docs.category.运维指南": { + "message": "运维指南", + "description": "The label for category 运维指南 in sidebar user_docs" + }, + "sidebar.user_docs.category.开发者手册": { + "message": "开发者手册", + "description": "The label for category 开发者手册 in sidebar user_docs" + }, + "sidebar.user_docs.category.开发者手册.link.generated-index.description": { + "message": "Hippo4J 留给使用者能够扩展的知识点。", + "description": "The generated-index page description for category 开发者手册 in sidebar user_docs" + }, + "sidebar.user_docs.category.其它": { + "message": "其它", + "description": "The label for category 其它 in sidebar user_docs" + }, + "sidebar.community.category.贡献规约": { + "message": "贡献规约", + "description": "The label for category 贡献规约 in sidebar community" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/_category_.json new file mode 100644 index 00000000..bcd256c9 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "社区", + "position": 1, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/contributor.md b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/contributor.md new file mode 100644 index 00000000..dd77fa3d --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/contributor.md @@ -0,0 +1,16 @@ +--- +id: contributor-guide +sidebar_position: 1 +title: 贡献指南 +--- + +Git Commit Log 尽量使用英文。 + +Pull Request 尽量保持单一,不同语义的代码贡献应拆分多个 Pull Request。 + +为了让您的 GitHub ID 显示在 Contributor 列表中,别忘了以下设置: + +```shell +git config --global user.name "username" +git config --global user.email "GitHub 账号邮箱" +``` \ No newline at end of file diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/dev_convention/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/dev_convention/_category_.json new file mode 100644 index 00000000..cb875453 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/dev_convention/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "贡献规约", + "position": 2, + "link": { + "type": "generated-index" + } +} diff --git a/docs/docs/community/dev_convention/code.md b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/dev_convention/code.md similarity index 100% rename from docs/docs/community/dev_convention/code.md rename to docs/i18n/zh/docusaurus-plugin-content-docs-community/current/dev_convention/code.md diff --git a/docs/docs/community/dev_convention/document.md b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/dev_convention/document.md similarity index 100% rename from docs/docs/community/dev_convention/document.md rename to docs/i18n/zh/docusaurus-plugin-content-docs-community/current/dev_convention/document.md diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/faq.md b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/faq.md new file mode 100644 index 00000000..fe7b20ec --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/faq.md @@ -0,0 +1,90 @@ +--- +sidebar_position: 3 +title: 常见问题 +--- + +# 常见问题 + +- 租户和项目在 Hippo4j 中是什么意思 +- 控制台线程池管理和线程池实例的区别 +- 示例项目为什么会有跨域请求 +- 更新代码后运行时服务端SQL报错 +- 生产环境如何不启用动态线程池 +- Server 端宕机会影响 Client 运行么 +- Hippo4J 的发布方式是怎样的?如何选择正确的版本 +- 群机器人接受不到通知报警 +- 设置线程池参数优先级问题 +- 线程池实例中修改队列容量参数问题 +- 控制台 SocketTimeoutException: connect timed out + +## 租户和项目在 Hippo4j 中是什么意思 + +Hippo4j 按照租户、项目、线程池的维度划分。 + +举个例子,小编在一家公司的公共组件团队,团队中负责消息、短链接网关等项目。公共组件是租户,消息或短链接就是项目。 + +## 控制台线程池管理和线程池实例的区别 + +在线程池管理中修改线程池参数,客户端并不能实时感知到并变更参数,需要重启客户端。而线程池实例中去对具体的实例修改参数时,客户端无需重启,可以实时感知到参数变化。如果二者针对同一线程池的参数配置不同,则在重启客户端时,客户端会去拉去线程池管理中的参数配置。 + +二者对应的定位:线程池管理中的配置是常态化配置。而线程池实例里的配置变更像是一种临时修改,比如突发的流量激增等场景,并不具备普适性。 + +## 示例项目为什么会有跨域请求 + +~~正常大家在部署时,服务端项目和客户端都在同一网络下,进行内网通信,是没有问题的。~~ + +~~因为示例项目中,服务端部署在外网,而客户端注册到服务端 IP 是内网的,所以不通。~~ + +~~涉及功能:线程池实例-查看、编辑,容器线程池。~~ + +1.2.0 版本后,服务端访问客户端已变成,浏览器访问服务端,服务端转发客户端的形式完成调用,跨域问题已解决。 + +## 更新代码后运行时服务端SQL报错 + +如果更新代码运行功能出错,大概率是因为项目新增或修改了表结构。如版本升级迭代涉及数据库表变更,会额外提供 SQL 变更文件。 + +如若第一次使用,初始化 SQL 脚本地址:[Hippo4j_manager.sql](https://github.com/longtai-cn/hippo4j/blob/develop/hippo4j-server/conf/hippo4j_manager.sql)。 + +> 友情提示:每次执行数据库表或数据变更时,一定要保持提前备份的好习惯。 + +## 生产环境如何不启用动态线程池 + +测试环境已经引入 Hippo4j,暂时不打算上线生产环境。 + +生产环境指定配置 `spring.dynamic.thread-pool.enable=false`,测试环境和生产环境配置就会隔离。 + +## Server 端宕机会影响 Client 运行么 + +不会。Client 端包含对 Server 端的健康检查机制,Server 端不可用时会停止交互,检查到可用时重新建立连接交互。 + +## Hippo4j 的发布方式是怎样的?如何选择正确的版本 + +Hippo4j 发布时可能会涉及到两端发布,分别是 Server 和 Starter。如无特殊说明,**每一次的版本升级将兼容上一版本代码**。 + +- 如涉及 Server 发布,会在 [发布列表页面](https://github.com/longtai-cn/hippo4j/releases) 创建最新的发行版本; +- 如涉及 Starter 发布,将直接推送 Starter Jar 至中央仓库,Server 包版本不变。 + +## 群机器人接受不到通知报警 + +如果是钉钉机器人,需在机器人配置自定义关键字,才可发送成功。如下所示: + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220530200133377.png?x-oss-process=image/resize,h_500,w_800) + +如果使用 hippo4j-server,请检查在 hippo4j-server 添加的报警通知记录,是否在客户端项目启动前,因为客户端只有在启动时会去 hippo4j-server 拉取报警通知记录。 + +重启客户端项目,会重新拉取最新报警推送配置,问题解决。 + +## 设置线程池参数优先级问题 + +- 当使用 `@DynamicThreadPool` 进行修饰的方法中和在管理界面设置中同时存在的话,则管理界面设置的优先级最高; +- 如果连接 server 端失败的话,使用 `@DynamicThreadPool` 进行修饰设置的优先级最高。 + +## 线程池实例中修改队列容量参数问题 + +在线程池管理中添加时,只有当选择队列类型为 `ResizableCapacityLinkedBlockingQueue` 时,后续再进行修改容量大小时才会实时的刷新修改成功。 + +## 控制台 SocketTimeoutException: connect timed out + +控制台中触发的某些操作涉及到 hippo4j-server 调用客户端项目。如果 hippo4j-server 部署在测试环境,而客户端项目为本地启动,则会触发该问题。 + +为什么编辑线程池参数不报错?因为线程池的动态变更是客户端主动发起连接,和服务端保持了一个长轮询,所以不存在服务端主动调用客户端行为。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/sponsor.md b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/sponsor.md new file mode 100644 index 00000000..7108dc6e --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/sponsor.md @@ -0,0 +1,23 @@ +--- +sidebar_position: 4 +--- + +# 支持开源 + +如果您正在使用这个项目并感觉良好,或者是想支持我继续开发,通过以下二维码一次性捐款。 + +在这里承诺将,将收到的所有赞助支持资金完全公开化,且后续资金用途仅 Hippo4j 项目的运转。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/IMG_6719_2.jpg?x-oss-process=image/resize,h_180,w_180) + +感谢给予支持的朋友,您的支持是 Hippo4j 社区前进的动力 🎉 + +| | ID | 赞赏金额 | 时间 | 备注 | +|-----|--|-------|------------|--------------------| +| 1 | 六月飞雪 | 30.00 | 2021-12-30 | 代码设计很优雅的一款框架,继续加油! | +| 2 | 孙大圣 | 26.6 | 2022-03-23 | 学习一下😁😁 | +| 3 | Easy 点 | 66.00 | 2022-04-09 | 好货好技术当加赏 | +| 4 | 捷克 | 30.00 | 2022-05-21 | 非常不错的框架,点赞 | +| 5 | 吃猫的饼干 | 88.00 | 2022-08-21 | 👍 | +| 6 | 不忘初心· | 66.00 | 2022-10-28 | Nice! | +| 7 | 时刻· | 6.00 | 2022-10-30 | -! | diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/update-log.md b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/update-log.md new file mode 100644 index 00000000..3985ed9c --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs-community/current/update-log.md @@ -0,0 +1,344 @@ +--- +title: 更新日志 +sidebar_position: 5 +--- + +## 1.4.3 (Nov 06, 2022) + +这是一个功能增强版本,修复了少量 BUG。建议按照当前版本升级。具体信息可查看 Release 标签地址:[1.4.3](https://github.com/opengoofy/hippo4j/milestone/13?closed=1) + +**Use Change** + +- 重构线程池监控,配置层级和命名改变 +- 如果使用钉钉报警,关键字【警报】修改为【告警】 + +**Feature** + +- 重构 Spring 后置处理器创建动态线程池逻辑 +- 官网开启多版本化功能 +- 官网支持国际化,en-US +- 适配线程池延迟初始化 @wulangcode +- 添加 Codecov 相关代码覆盖率指标 +- 项目优雅关闭时停止运行状态采集 + +**Refactor** + +- DynamicThreadPoolExecutor 重构,增加插件扩展逻辑 @Createsequence +- 重构线程池监控,新增容器和三方框架线程池监控 +- 重构服务端包目录,聚合 hippo4j-server 相关 module + +**Bug** + +- dubbo 线程池无法获取运行信息 @iwangjie +- 线程池检查活跃度报警取值错误 @maxisvest +- 动态线程池修改多次后队列提示信息丢失 +- docker部署 mysql启动报错H2驱动 +- docker-startup.sh的mysql配置多个“-” @Malcolmli +- 动态注册线程池队列容量赋值错误 +- 飞书超时类型告警不存在 Trace 信息时发送错误 @mageeric + +**Optimize** + +- 修改报警文案,【警报】修改为【告警】 @wulangcode +- 自动选择H2数据库的存储路径 @iwangjie +- 服务端在客户端后面启动,依旧支持长轮训 @wulangcode +- 配置未发生变更时,长轮询返回 304 @wulangcode +- discovery服务Lease类中判断过期时间需要多等一个duration @w-jirong +- 优化 ThreadPoolBuilder#maxPoolNum 核心线程不得大于最大线程 @wulangcode +- hippo4j console ui 迁移至本项目 +- 查询 Web 线程池列表添加框架标识 +- 优化 H2 初始化逻辑 + +## 1.4.2 (Oct 18, 2022) + +这是一个功能增强版本,修复了少量 BUG。建议按照当前版本升级。具体信息可查看 Release 标签地址:[1.4.2](https://github.com/opengoofy/hippo4j/milestone/12?closed=1) + +**Feature** + +- 强制指定客户端注册的 ip + port +- 支持 spring-cloud-tencent Polaris 线程池动态更新 @weihubeats +- 服务启动时加载 MySQL、H2 数据库初始化语句 +- Adapter 初始化覆盖核心参数 @pizihao +- Server 端新增是否开启认证模式 @baymax55 + +**Refactor** + +- 替换底层网络工具类 OkHttp @yanrongzhen +- 全局移除 commons-lang3 工具包依赖 @yanrongzhen +- 去除三方工具类依赖 @pizihao +- 全局移除 Guava 工具包依赖 @road2master +- DockerFile 基于 H2 数据库重新构建 @BigXin0109 + +**Bug** + +- Dubbo 2.7.15 无法获取线程池引用 @iwangjie +- 动态线程池报警参数颠倒 @jinlingmei + +**Optimize** + +- 线程池实例运行数据采集,如果线程池id不存在,且长度超长,会报异常 @Gdk666 +- 项目中动态线程池数量为空时,存在 CPU 空转情况 +- 客户端注册服务端失败,输出服务端返回信息 @wulangcode +- 调整数据库项目 id 和线程池 id 字段长度 +- 增加代码检查工具 maven-checkstyle-plugin +- 调整控制台监控图表颜色展示 + +## 1.4.1 (Sep 12, 2022) + +这是一个功能增强版本,修复了若干 BUG。建议按照当前版本升级。具体信息可查看 Release 标签地址:[1.4.1](https://github.com/opengoofy/hippo4j/milestone/11?closed=1) + +**Feature** + +- 支持 H2 数据库 @weihubeats +- 动态线程池配置变更时,支持单个、多个或全部节点变 @pizihao +- 增加线程池活跃度和容量报警可选择关闭 +- @DynamicThreadPool 线程池不存在则创建 @shanjianq +- 支持 ETCD 配置中心动态调整参数 @weihubeats +- 创建动态线程池支持 spring 线程池 @BigXin0109 +- 线程池实例变更增加执行超时时间 +- 线程池相关查询页面增加阻塞队列属性 +- 定义动态线程池时,抽象默认配置 +- 提供 ExecutorContext 封装上下文细节 @road2master +- Docker 制作服务端镜像,帮助开发者快速启动 @BigXin0109 +- RabbitMQ 适配器增加多个 MQ 数据源 @weihubeats + +**Bug** + +- 动态线程池设置关闭时启动报错 @dousp +- ExecutorTtlWrapper 类型的 Executor 不生效 @BigXin0109 +- Undertow 获取 WebServer 类型参数异常 @shining-stars-lk +- 修复线程池核心、最大线程数校验限制 +- ByteConvertUtil#getPrintSize 单位转换错误 @onesimplecoder +- 创建线程池单选框选择错误 +- ReflectUtil#getFieldsDirectly missing fields @BigXin0109 +- 本地代码中设置的 capacity 无效 @BigXin0109 +- 服务端线程池超时时间存在拆箱空指针异常 @oreoft +- 未读取服务端返回执行超时时间属性 +- ResizableCapacityLinkedBlockingQueue#put 当前元素数量大于 capacity 未阻塞 + +**Optimize** + +- 长轮询任务判断逻辑优化 @shining-stars-lk +- 线程池存在实例不允许删除线程池 @shanjianq +- 优化租户、项目列表展示排版 +- 通知报警模块项目和线程池下拉查询排序修改 +- 动态线程池拒绝策略触发,以异步的方式报警 +- 优化框架中线程池工厂产生的线程名称 @road2master + +## 1.4.0 (Aug 16, 2022) + +`hippo4j server` 兼容历史低版本,`hippo4j config` 中部分属性名进行了调整,请参考 [hippo4j config 快速开始](https://hippo4j.cn/docs/user_docs/getting-started/hippo4j-core-start)。 + +注意事项: +1. 如果是对已运行 hippo4j server 升级,执行 `/conf/sql-upgrade` 目录下对应的升级脚本。 +2. 需客户端在 1.4.0 及以上版本才可在 hippo4j server 设置线程执行超时时间属性。 + +**Feature** + +- 添加 Alibaba Dubbo 线程池监控及动态变更 +- hippo4j server 支持任务执行超时时间动态修改 +- 阿里 TTL 框架线程池适配 +- 添加动态线程池自动注册功能 +- 订阅回调线程池参数变更 +- 动态线程池监控增加 SPI 自定义功能 +- hippo4j server 支持多种线程池监控方式,例如 Prometheus +- 通知相关参数添加动态变更功能 + +**Bug** + +- 线程池变更:executeTimeOut 变更极端情况下会出现异常 +- 用户登录时候,如果输入了不存在的用户名,后台报空指针异常 +- 修复了对 spring-boot 服务中 tomcat 线程池的兼容问题 +- 排除 Tomcat Jar 使用 Undertow 启动报错 + +**Optimize** + +- hippo4j-core-spring-boot-starter 模块修改名称为 hippo4j-config-spring-boot-starter +- 拆分容器线程池子页面:Tomcat、Undertow、Jetty +- 服务端访问客户端时对 URL 转码 +- MyBatisPlus 修改全局填充方法优化 +- 控制台线程池列表下拉框默认正序 +- 控制台线程池实例菜单,对于非可修改容量队列外,不允许修改队列容量 +- 动态线程池控制台功能变更 +- 租户和项目列表分页查询按照创建时间倒序展示 +- 线程池监控页面图表 UI 优化 +- 设置 maven-gpg-plugin 插件默认不执行 +- 前端控制台相关搜索条件添加必填提示 +- hippo4j 消息通知 & 报警抽象优化 +- 配置中心未配置线程池启动报错 +- 控制台线程池报警 UI 以及功能优化 +- Web、框架线程池编辑弹框 UI 优化 +- 线程池添加、编辑页面 UI 优化 +- 线程池运行详情页前端 UI 优化 + +**Refactor** + +- 删除自定义日志组件 +- 线程池监控功能重构 +- hippo4j core 配置中心生效判断重构 +- 配置变更通知 & 报警通知重构 +- Web 容器线程池适配迁移 hippo4j-adapter + +## 1.3.1 (July 17, 2022) + +注:这是一个兼容历史版本的小范围升级。 + +**Feature** + +- 控制台新增线程池功能设置为 Admin 权限 +- 添加 Hystrix 线程池监控及动态变更 +- 添加 Netty 上传动态线程池监控数据方式 +- 添加 GitHub Actions CI 流程 +- 添加 Spring Kafka 示例项目 +- Tomcat 版本号 >= 9.0.55 线程池适配 + +**Refactor** + +- 更多线程池拆分子目录页面 + +**Optimize** + +- hippo4j core 添加 banner 打印 +- 优化可变更容量阻塞队列名称 + +**BUG** + +- Apollo 配置修改延迟了一个版本 +- Spring Boot 环境下使用 hippo4j-core 接入,配置中心使用 nacos;启动时提示 ConfigService not found + +查看 1.3.1 版本发布:https://github.com/mabaiwan/hippo4j/milestone/9 + +## 1.3.0 (June 06, 2022) + +1.3.0 发布 **适配三方框架的基础框架**。 + +目前已完成 **Dubbo、RabbitMQ、RocketMQ、RocketMQSpringCloudStream** 的线程池适配,后续还会接入 **Kafka、Hystrix** 等框架或中间件的线程池适配。 + +注:这是一个兼容历史版本的重大升级。 + +**Feature** + +- 添加 RabbitMQ 线程池监控及动态变更 +- 添加 RocketMQ 线程池监控及动态变更 +- 添加 Dubbo 线程池监控及动态变更 +- 添加 SpringCloud Stream RocketMQ 消费线程池监控及动态变更 + +**Refactor** + +- 重构容器线程池查询及修改功能 +- 优化配置中心触发监听后,所执行的数据变更逻辑 + +**Optimize** + +- 前端控制台删除无用组件 +- 服务端页面字段未显示中文 +- 控制台 UI 优化 +- 修改线程池实例后实时刷新列表参数 +- 容器线程池编辑仅限 Admin 权限 +- SpringBoot Starter 变更包路径 + +**BUG** + +- 修复 SpringBoot Nacos 动态刷新不生效 +- 报警配置 alarm=false 不配置通知报警平台和接收人报错 + +## 1.2.1 (May 07, 2022) + +**BugFix** + +- apollo 动态配置不生效 +- 修复 hippo4j-core 后置处理器创建线程池问题 +- 重构 hippo4j-core spring 后置处理器逻辑 +- 优化ThreadPoolNotifyAlarmHandler下的空指针异常 +- 修复线程池核心、最大线程数变更问题 +- startup.cmd 未正常读取 conf 配置文件 + +**Optimize** + +- 配置文件中字段歧义 +- 修改代码中历史网址 +- InstanceInfo 的 groupKey 参数重复设置 +- ConfigFileTypeEnum 枚举字段添加注释 +- 线程资源通过线程池创建,不允许自行显示创建线程 +- Guava 版本升级至 30.0-jre 及以上版本 +- SystemClock 替换 System.currentTimeMillis() +- 添加代码格式化插件 Spotless +- 修改线程池文案 + +## 1.2.0 (Mar 13, 2022) + +**Feature** + +- hippo4j-core线程池资源对接 Prometheus 监控 +- hippo4j-core 支持 Zookeeper +- hippo4j-core 支持 Apollo + +**Optimize** + +- 适配非 Web SpringBoot 项目使用 Hippo4J +- 优化报警通知 +- 修复在 JDK 小版本中的兼容性问题 + +**BugFix** + +- server 端查看容器线程池,参数为 null +- 重构线程池查看及容器线程池查看等交互 +- 修复引入 hippo4j-spring-boot-starter 后,运行单元测试报错 +- 修复可能出现的空指针异常 + +## 1.1.0 (Mar 13, 2022) + +Hippo4J 线程池框架 1.1.0 RELEASE 版本,添加了 Hippo4J-Core(依赖配置中心的动态线程池). + +**Feature** + +- 删除 DynamicThreadPoolExecutor 内代码实现,仅通过线程池扩展点进行扩展 +- 通过动态代理实现线程池拒绝策略执行次数统计 +- 抽象通知报警消息模块 +- 抽象 hippo4j 核心组件,不依赖 server 端即可完成动态调参、监控、报警等功能 +- 前端删除线程池按钮添加 Admin 权限 +- 添加线程池任务运行超长报警 +- 容器线程池支持 Undertow +- 容器线程池支持 Jetty +- 重构服务端异常体系 + +**Optimize** + +- 前端项目 Token 失效跳转登录页 +- 优化 Server 启动脚本日志输出 +- 优化前端按钮权限控制粒度 +- 优化线程池报警推送文案 +- 前端弹框样式优化 +- 适配低版本 SpringBoot Bind +- 优化消息通知模块 + +**BugFix** + +- Duplicate entry 'xxx' for key 'uk_configinfo_datagrouptenant' + +## 1.0.0 (Feb 01, 2022) + +**Feature** + +- 线程池运行堆栈查看 +- 扩展 Web 容器线程池动态调参、监控 + +**Optimize** + +- 删除高版本 SpringBoot Api +- ListableBeanFactory#findAnnotationOnBean SpringBoot 低版本适配 +- 优化客户端关闭时调用服务端钩子函数 +- 线程池实例参数弹框添加实例 ID 和线程池状态 +- 补充线程池替换 Hippo4J 文档 +- 1.5.x springboot 引入hippo4j-spring-boot-starter配置项,bean初始化失败 +- 优化线程池参数编辑合理性校验 +- BaseInstanceRegistry 读写锁重构 + +**BugFix** + +- 本地项目线程池实例缓存无法精确清理 +- 线程池实例页面多实例不同 Active 展示错误 +- 创建动态线程池逻辑判断修复 +- 创建动态线程池增强参数未设置 +- 控制消息推送报警频率的方法有并发安全的问题 +- tomcat线程池上下文获取失败 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current.json b/docs/i18n/zh/docusaurus-plugin-content-docs/current.json new file mode 100644 index 00000000..52732047 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current.json @@ -0,0 +1,94 @@ +{ + "version.label": { + "message": "Next", + "description": "The label for version current" + }, + "sidebar.tutorialSidebar.category.社区": { + "message": "社区", + "description": "The label for category 社区 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.贡献规约": { + "message": "贡献规约", + "description": "The label for category 贡献规约 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.user_docs": { + "message": "user_docs", + "description": "The label for category user_docs in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.用户指南": { + "message": "用户指南", + "description": "The label for category 用户指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.用户指南.link.generated-index.description": { + "message": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。", + "description": "The generated-index page description for category 用户指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.快速开始": { + "message": "快速开始", + "description": "The label for category 快速开始 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.依赖配置中心": { + "message": "依赖配置中心", + "description": "The label for category 依赖配置中心 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.无中间件依赖": { + "message": "无中间件依赖", + "description": "The label for category 无中间件依赖 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.运维指南": { + "message": "运维指南", + "description": "The label for category 运维指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.开发者手册": { + "message": "开发者手册", + "description": "The label for category 开发者手册 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.开发者手册.link.generated-index.description": { + "message": "Hippo4J 留给使用者能够扩展的知识点。", + "description": "The generated-index page description for category 开发者手册 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.其它": { + "message": "其它", + "description": "The label for category 其它 in sidebar tutorialSidebar" + }, + "sidebar.user_docs.category.用户指南": { + "message": "用户指南", + "description": "The label for category 用户指南 in sidebar user_docs" + }, + "sidebar.user_docs.category.用户指南.link.generated-index.description": { + "message": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。", + "description": "The generated-index page description for category 用户指南 in sidebar user_docs" + }, + "sidebar.user_docs.category.快速开始": { + "message": "快速开始", + "description": "The label for category 快速开始 in sidebar user_docs" + }, + "sidebar.user_docs.category.依赖配置中心": { + "message": "依赖配置中心", + "description": "The label for category 依赖配置中心 in sidebar user_docs" + }, + "sidebar.user_docs.category.无中间件依赖": { + "message": "无中间件依赖", + "description": "The label for category 无中间件依赖 in sidebar user_docs" + }, + "sidebar.user_docs.category.运维指南": { + "message": "运维指南", + "description": "The label for category 运维指南 in sidebar user_docs" + }, + "sidebar.user_docs.category.开发者手册": { + "message": "开发者手册", + "description": "The label for category 开发者手册 in sidebar user_docs" + }, + "sidebar.user_docs.category.开发者手册.link.generated-index.description": { + "message": "Hippo4J 留给使用者能够扩展的知识点。", + "description": "The generated-index page description for category 开发者手册 in sidebar user_docs" + }, + "sidebar.user_docs.category.其它": { + "message": "其它", + "description": "The label for category 其它 in sidebar user_docs" + }, + "sidebar.community.category.贡献规约": { + "message": "贡献规约", + "description": "The label for category 贡献规约 in sidebar community" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/_category_.json new file mode 100644 index 00000000..11f12432 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "开发者手册", + "position": 5, + "link": { + "type": "generated-index", + "description": "Hippo4J 留给使用者能够扩展的知识点。" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/rejected-policy-custom.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/rejected-policy-custom.md new file mode 100644 index 00000000..1f2cf694 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/rejected-policy-custom.md @@ -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` 模块。 +::: diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/rejected-policy-info.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/rejected-policy-info.md new file mode 100644 index 00000000..d5c68138 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/dev_manual/rejected-policy-info.md @@ -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 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); + } + } +} +``` \ No newline at end of file diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/_category_.json new file mode 100644 index 00000000..72b1e151 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "快速开始", + "position": 3, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/_category_.json new file mode 100644 index 00000000..78b3d9c4 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "依赖配置中心", + "position": 2, + "collapsed": true +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-default.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-default.md new file mode 100644 index 00000000..9c9e0fc9 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-default.md @@ -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` 层级下参数,不提供动态刷新功能。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-monitor.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-monitor.md new file mode 100644 index 00000000..c030dc10 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-monitor.md @@ -0,0 +1,122 @@ +--- +sidebar_position: 3 +--- + +# 线程池监控 + +## 线程池监控配置 + +监控前置条件:需要先完成 hippo4j-config 的 [接入工作](/docs/user_docs/getting_started/config/hippo4j-config-start)。 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```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 # 采集线程池运行数据的类型。eg:log、micrometer。多个可以同时使用,默认 micrometer + initial-delay: 10000 # 项目启动后延迟多久进行采集 + thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-more.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-more.md new file mode 100644 index 00000000..2511b4f3 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-more.md @@ -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
+ * nodes is ip + port.Get 'nodes' in the new Properties,Compare this with the ip + port of Application.
+ * support prefix pattern matching. e.g:
+ *
    + *
  • 192.168.1.5:* -- Matches all ports of 192.168.1.5
  • + *
  • 192.168.1.*:2009 -- Matches 2009 port of 192.168.1.*
  • + *
  • * -- all
  • + *
  • empty -- all
  • + *
+ * The format of ip + port is ip : port. + */ +``` + +`nodes` 可与 `enable` 同时使用。如此,基于配置中心的动态线程池实现方式,将能够更方便的支持个性化需求。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md new file mode 100644 index 00000000..709e882a --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md @@ -0,0 +1,121 @@ +--- +sidebar_position: 5 +--- + +# 适配SpringBoot1x + +目前已支持 Nacos、Apollo 配置中心适配 SpringBoot 1.5.x 版本。 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-1x-starter + 1.4.3-upgrade + +``` + +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` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-start.md new file mode 100644 index 00000000..e6a222a6 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/config/hippo4j-config-start.md @@ -0,0 +1,193 @@ +--- +sidebar_position: 1 +--- + +# 接入流程 + +Nacos、Apollo、Zookeeper、ETCD、Polaris、Consul 配置中心任选其一。 + +## hippo4j 配置 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-starter + 1.4.3-upgrade + +``` + +启动类上添加注解 `@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 三种容器线程池,任选其一 + undertow: + 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); +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/difference.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/difference.md new file mode 100644 index 00000000..6bf56962 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/difference.md @@ -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。 + +**两者在进行替换的时候,无需修改业务代码**。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/hippo4j-adapter.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/hippo4j-adapter.md new file mode 100644 index 00000000..378713e8 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/hippo4j-adapter.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 6 +--- + +# 三方框架线程池适配 + +Hippo4J 目前已支持的三方框架线程池列表: + +- Dubbo +- Hystrix +- RabbitMQ +- RocketMQ +- AlibabaDubbo +- RocketMQSpringCloudStream +- RabbitMQSpringCloudStream + +引入 Hippo4J Server 或 Core 的 Maven Jar 坐标后,还需要引入对应的框架适配 Jar: + +```xml + + cn.hippo4j + + hippo4j-spring-boot-starter-adapter-dubbo + + hippo4j-spring-boot-starter-adapter-alibaba-dubbo + + hippo4j-spring-boot-starter-adapter-hystrix + + hippo4j-spring-boot-starter-adapter-rabbitmq + + hippo4j-spring-boot-starter-adapter-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq + 1.4.3-upgrade + +``` + +如果想省事,仅需引入一个全量包,框架底层会根据条件判断加载具体线程池适配器。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-adapter-all + 1.4.3-upgrade + +``` + +## 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 +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/docsVersionDropdown.png b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/docsVersionDropdown.png new file mode 100644 index 00000000..97e41646 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/docsVersionDropdown.png differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/grafana-monitor.jpg b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/grafana-monitor.jpg new file mode 100644 index 00000000..336bd980 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/grafana-monitor.jpg differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/localeDropdown.png b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/localeDropdown.png new file mode 100644 index 00000000..e257edc1 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/img/localeDropdown.png differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/_category_.json new file mode 100644 index 00000000..4063c17f --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "无中间件依赖", + "position": 3, + "collapsed": true +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-config.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-config.md new file mode 100644 index 00000000..e1ef853f --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-config.md @@ -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 配置后,需要在客户端对应开启才可生效。用来应对大量动态线程池监控场景。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-monitor.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-monitor.md new file mode 100644 index 00000000..4a81a47b --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-monitor.md @@ -0,0 +1,132 @@ +--- +sidebar_position: 2 +--- + +# 线程池监控 + +Server 模式默认内置线程池运行时采集和监控功能,如果想要使用 Prometheus + Grafana 的方式可以查看以下内容。 + +## 线程池监控配置 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```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 # 采集线程池运行数据的类型。eg:server、micrometer。多个可以同时使用,默认 server + initial-delay: 10000 # 项目启动后延迟多久进行采集 + thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、web、adapter。可任意配置,默认 dynamic +``` + +如果使用 `micrometer` 类型的监控指标,需要添加以下依赖。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-monitor-micrometer + 1.4.3-upgrade + +``` + +项目启动,访问 `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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-start.md new file mode 100644 index 00000000..5bf3db1c --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/getting_started/server/hippo4j-server-start.md @@ -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 + + cn.hippo4j + hippo4j-spring-boot-starter + 1.4.3-upgrade + +``` + +启动类上添加注解 `@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); +``` + diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/intro.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/intro.md new file mode 100644 index 00000000..fd2549c8 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/intro.md @@ -0,0 +1,66 @@ +--- +sidebar_position: 1 +--- + +# 简介 + +## 线程池痛点 + +线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。 + +如果有在项目中实际使用线程池,相信你可能会遇到以下痛点: + +- 线程池随便定义,线程资源过多,造成服务器高负载。 + +- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。 +- 线程池任务执行时间超过平均执行周期,开发人员无法感知。 +- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。 +- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。 +- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。 +- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。 +- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。 + +## 什么是 Hippo-4J + +Hippo-4J 通过对 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) 登记,登记仅仅为了产品推广。 + +## 联系我 + +![](https://user-images.githubusercontent.com/77398366/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) + +扫码添加微信,备注:hippo4j,邀您加入群聊。若图片加载不出来,访问 [官网站点](https://hippo4j.cn/docs/user_docs/other/group)。 + +## 友情链接 + +- [[ 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)。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/_category_.json new file mode 100644 index 00000000..d5b7d1e8 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "运维指南", + "position": 4, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/hippo4j-server-deploy.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/hippo4j-server-deploy.md new file mode 100644 index 00000000..f78457ba --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/hippo4j-server-deploy.md @@ -0,0 +1,45 @@ +--- +sidebar_position: 1 +--- + +# 源码包部署 + +[RELEASE](https://github.com/opengoofy/hippo4j/releases) 页面下载对应版本并进行解压。 + +## 初始化 + +修改数据库相关信息。 + +```txt +/conf/application.properties +``` + +如果是新运行 Hippo-4J,数据库执行下述 SQL 脚本即可。 + +```txt +/conf/hippo4j_manager.sql +``` + +如果是对已运行 Hippo-4J 升级,请查看 `/conf/sql-upgrade` 目录下,是否有目标版本对应的升级脚本。 + +## 直接运行 + +Mac Linux 启动执行。 + +```txt +sh ./bin/startup.sh +``` + +Windows 启动执行。 + +```txt +bin/startup.cmd +``` + +## 访问控制台 + +启动成功后,访问链接。用户名密码:admin 123456 + +```txt +localhost:6691/index.html +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/server-docker.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/server-docker.md new file mode 100644 index 00000000..141c597a --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/ops/server-docker.md @@ -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 +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/_category_.json new file mode 100644 index 00000000..a74d7432 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "其它", + "position": 6, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/group.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/group.md new file mode 100644 index 00000000..e75e96be --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/group.md @@ -0,0 +1,9 @@ +--- +sidebar_position: 1 +--- + +# 加群沟通 + +扫码添加微信,备注:`hippo4j`,邀您加入群聊。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/official-ccounts.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/official-ccounts.md new file mode 100644 index 00000000..61a1a5c6 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/official-ccounts.md @@ -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开源项目mall(50K+Star)。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/macrozheng.png) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/operation.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/operation.md new file mode 100644 index 00000000..ba9ed7f0 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/operation.md @@ -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 相互推荐,欢迎 [加群沟通](/docs/user_docs/other/group)。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/question.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/question.md new file mode 100644 index 00000000..49fd4d77 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/other/question.md @@ -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)都没有结果,网站上也许还有报告 Bug(Bug-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 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题? + +## 如果得不到回答 + +如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。 + +总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。 + +你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/_category_.json new file mode 100644 index 00000000..6dc10c33 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "用户指南", + "position": 2, + "link": { + "type": "generated-index", + "description": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/frame.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/frame.md new file mode 100644 index 00000000..47f568fa --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/frame.md @@ -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。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/framework.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/framework.md new file mode 100644 index 00000000..226ddf61 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/framework.md @@ -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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/notify.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/notify.md new file mode 100644 index 00000000..d4ca2bfd --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/notify.md @@ -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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/quick-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/quick-start.md new file mode 100644 index 00000000..5e2e7035 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/current/user_docs/user_guide/quick-start.md @@ -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 集群部署时,可以修改某一个实例,或选择 `全部修改` 按钮,修改所有实例线程池信息。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/_category_.json new file mode 100644 index 00000000..11f12432 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "开发者手册", + "position": 5, + "link": { + "type": "generated-index", + "description": "Hippo4J 留给使用者能够扩展的知识点。" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/rejected-policy-custom.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/rejected-policy-custom.md new file mode 100644 index 00000000..1f2cf694 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/rejected-policy-custom.md @@ -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` 模块。 +::: diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/rejected-policy-info.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/rejected-policy-info.md new file mode 100644 index 00000000..d5c68138 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/dev_manual/rejected-policy-info.md @@ -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 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); + } + } +} +``` \ No newline at end of file diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/_category_.json new file mode 100644 index 00000000..72b1e151 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "快速开始", + "position": 3, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/_category_.json new file mode 100644 index 00000000..78b3d9c4 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "依赖配置中心", + "position": 2, + "collapsed": true +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-default.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-default.md new file mode 100644 index 00000000..9c9e0fc9 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-default.md @@ -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` 层级下参数,不提供动态刷新功能。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-monitor.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-monitor.md new file mode 100644 index 00000000..72528638 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-monitor.md @@ -0,0 +1,117 @@ +--- +sidebar_position: 3 +--- + +# 线程池监控 + +## 线程池监控配置 + +监控前置条件:需要先完成 hippo4j-config 的 [接入工作](/docs/user_docs/getting_started/config/hippo4j-config-start)。 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```yaml +management: + metrics: + export: + prometheus: + enabled: true + server: + port: 29999 # 可选配置,如果不配置该 port,直接使用 ${server.port} + endpoints: + web: + exposure: + include: '*' # 测试使用,开启了所有端点,生产环境不建议 * +spring: + dynamic: + thread-pool: + collect-type: micrometer +``` + +项目启动,访问 `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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-more.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-more.md new file mode 100644 index 00000000..2511b4f3 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-more.md @@ -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
+ * nodes is ip + port.Get 'nodes' in the new Properties,Compare this with the ip + port of Application.
+ * support prefix pattern matching. e.g:
+ *
    + *
  • 192.168.1.5:* -- Matches all ports of 192.168.1.5
  • + *
  • 192.168.1.*:2009 -- Matches 2009 port of 192.168.1.*
  • + *
  • * -- all
  • + *
  • empty -- all
  • + *
+ * The format of ip + port is ip : port. + */ +``` + +`nodes` 可与 `enable` 同时使用。如此,基于配置中心的动态线程池实现方式,将能够更方便的支持个性化需求。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md new file mode 100644 index 00000000..693dd341 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md @@ -0,0 +1,121 @@ +--- +sidebar_position: 5 +--- + +# 适配SpringBoot1x + +目前已支持 Nacos、Apollo 配置中心适配 SpringBoot 1.5.x 版本。 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-1x-starter + 1.4.2 + +``` + +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` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-start.md new file mode 100644 index 00000000..33f658c2 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-start.md @@ -0,0 +1,193 @@ +--- +sidebar_position: 1 +--- + +# 接入流程 + +Nacos、Apollo、Zookeeper、ETCD、Polaris 配置中心任选其一。 + +## hippo4j 配置 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-starter + 1.4.2 + +``` + +启动类上添加注解 `@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 任选其一 + nacos: + data-id: xxx + group: xxx + apollo: + namespace: xxxx + # 配置中心文件格式 + config-file-type: yml + # tomcat、undertow、jetty 三种容器线程池,任选其一 + undertow: + 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); +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/difference.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/difference.md new file mode 100644 index 00000000..4e07d71b --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/difference.md @@ -0,0 +1,37 @@ +--- +sidebar_position: 0 +--- + +# 运行模式介绍 + +1.1.0 版本发布后,Hippo-4J 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220319154626314.png) + +### hippo4j-config + +**轻量级动态线程池管理**,依赖 Nacos、Apollo、Zookeeper、ETCD、Polaris 等三方配置中心(任选其一)完成线程池参数动态变更,支持运行时报警、监控等功能。 + +> 监控功能配置详见:[线程池监控](/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 配置中心(任选其一) | 部署 Hippo-4J Server(内部无依赖中间件) | +| 使用 | 配置中心补充线程池相关参数 | Hippo-4J Server Web 控制台添加线程池记录 | +| 功能 | 包含基础功能:参数动态化、运行时监控、报警等 | 基础功能之外扩展控制台界面、线程池堆栈查看、线程池运行信息实时查看、历史运行信息查看、线程池配置集群个性化等 | + +使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 hippo4j-config 使用即可;如果希望更多的功能,可以选择 hippo4j-server。 + +**两者在进行替换的时候,无需修改业务代码**。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/hippo4j-adapter.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/hippo4j-adapter.md new file mode 100644 index 00000000..030367e1 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/hippo4j-adapter.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 6 +--- + +# 三方框架线程池适配 + +Hippo4J 目前已支持的三方框架线程池列表: + +- Dubbo +- Hystrix +- RabbitMQ +- RocketMQ +- AlibabaDubbo +- RocketMQSpringCloudStream +- RabbitMQSpringCloudStream + +引入 Hippo4J Server 或 Core 的 Maven Jar 坐标后,还需要引入对应的框架适配 Jar: + +```xml + + cn.hippo4j + + hippo4j-spring-boot-starter-adapter-dubbo + + hippo4j-spring-boot-starter-adapter-alibaba-dubbo + + hippo4j-spring-boot-starter-adapter-hystrix + + hippo4j-spring-boot-starter-adapter-rabbitmq + + hippo4j-spring-boot-starter-adapter-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq + 1.4.2 + +``` + +如果想省事,仅需引入一个全量包,框架底层会根据条件判断加载具体线程池适配器。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-adapter-all + 1.4.2 + +``` + +## 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 +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/docsVersionDropdown.png b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/docsVersionDropdown.png new file mode 100644 index 00000000..97e41646 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/docsVersionDropdown.png differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/grafana-monitor.jpg b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/grafana-monitor.jpg new file mode 100644 index 00000000..336bd980 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/grafana-monitor.jpg differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/localeDropdown.png b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/localeDropdown.png new file mode 100644 index 00000000..e257edc1 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/img/localeDropdown.png differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/_category_.json new file mode 100644 index 00000000..4063c17f --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "无中间件依赖", + "position": 3, + "collapsed": true +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-config.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-config.md new file mode 100644 index 00000000..24bf9a13 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-config.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 4 +--- + +# 服务端配置 + +`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 配置后,需要在客户端对应开启才可生效。用来应对大量动态线程池监控场景。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-start.md new file mode 100644 index 00000000..eec2af48 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-start.md @@ -0,0 +1,127 @@ +--- +sidebar_position: 3 +--- + +# 接入流程 + +部署服务端,参考 [部署手册](/docs/user_docs/ops/hippo4j-server-deploy)。 + +服务端创建 [租户、项目](/community/faq#租户和项目在-hippo4j-中是什么意思) 和线程池记录。 + +需要注意,项目 ID 需要与配置文件 `{application.name}` 保持一致。 + +:::note +租户、项目、线程池 ID 如果由多个词组成,建议以 - 进行分割。比如:message-center。 +::: + +## Hippo4J 配置 + +SpringBoot Pom 引入 Hippo4j Starter Jar。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter + 1.4.2 + +``` + +启动类上添加注解 `@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); +``` + diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/intro.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/intro.md new file mode 100644 index 00000000..eea7c946 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/intro.md @@ -0,0 +1,65 @@ +--- +sidebar_position: 1 +--- + +# 简介 + +## 线程池痛点 + +线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。 + +如果有在项目中实际使用线程池,相信你可能会遇到以下痛点: + +- 线程池随便定义,线程资源过多,造成服务器高负载。 + +- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。 +- 线程池任务执行时间超过平均执行周期,开发人员无法感知。 +- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。 +- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。 +- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。 +- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。 +- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。 + +## 什么是 Hippo-4J + +Hippo-4J 通过对 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) 登记,登记仅仅为了产品推广。 + +## 联系我 + +![](https://user-images.githubusercontent.com/77398366/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) + +扫码添加微信,备注:hippo4j,邀您加入群聊。若图片加载不出来,访问 [官网站点](https://hippo4j.cn/docs/user_docs/other/group)。 + +## 友情链接 +- [[ 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)。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/_category_.json new file mode 100644 index 00000000..d5b7d1e8 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "运维指南", + "position": 4, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/hippo4j-server-deploy.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/hippo4j-server-deploy.md new file mode 100644 index 00000000..f78457ba --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/hippo4j-server-deploy.md @@ -0,0 +1,45 @@ +--- +sidebar_position: 1 +--- + +# 源码包部署 + +[RELEASE](https://github.com/opengoofy/hippo4j/releases) 页面下载对应版本并进行解压。 + +## 初始化 + +修改数据库相关信息。 + +```txt +/conf/application.properties +``` + +如果是新运行 Hippo-4J,数据库执行下述 SQL 脚本即可。 + +```txt +/conf/hippo4j_manager.sql +``` + +如果是对已运行 Hippo-4J 升级,请查看 `/conf/sql-upgrade` 目录下,是否有目标版本对应的升级脚本。 + +## 直接运行 + +Mac Linux 启动执行。 + +```txt +sh ./bin/startup.sh +``` + +Windows 启动执行。 + +```txt +bin/startup.cmd +``` + +## 访问控制台 + +启动成功后,访问链接。用户名密码:admin 123456 + +```txt +localhost:6691/index.html +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/server-docker.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/server-docker.md new file mode 100644 index 00000000..141c597a --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/ops/server-docker.md @@ -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 +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/_category_.json new file mode 100644 index 00000000..a74d7432 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "其它", + "position": 6, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/group.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/group.md new file mode 100644 index 00000000..e75e96be --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/group.md @@ -0,0 +1,9 @@ +--- +sidebar_position: 1 +--- + +# 加群沟通 + +扫码添加微信,备注:`hippo4j`,邀您加入群聊。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/official-ccounts.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/official-ccounts.md new file mode 100644 index 00000000..61a1a5c6 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/official-ccounts.md @@ -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开源项目mall(50K+Star)。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/macrozheng.png) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/operation.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/operation.md new file mode 100644 index 00000000..ba9ed7f0 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/operation.md @@ -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 相互推荐,欢迎 [加群沟通](/docs/user_docs/other/group)。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/question.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/question.md new file mode 100644 index 00000000..49fd4d77 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/other/question.md @@ -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)都没有结果,网站上也许还有报告 Bug(Bug-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 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题? + +## 如果得不到回答 + +如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。 + +总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。 + +你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/_category_.json new file mode 100644 index 00000000..6dc10c33 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "用户指南", + "position": 2, + "link": { + "type": "generated-index", + "description": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/frame.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/frame.md new file mode 100644 index 00000000..47f568fa --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/frame.md @@ -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。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/framework.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/framework.md new file mode 100644 index 00000000..226ddf61 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/framework.md @@ -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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/notify.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/notify.md new file mode 100644 index 00000000..216840e9 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/notify.md @@ -0,0 +1,71 @@ +--- +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) + +## 企业微信 + +[企业微信创建群机器人](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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/quick-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/quick-start.md new file mode 100644 index 00000000..5e2e7035 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.2/user_docs/user_guide/quick-start.md @@ -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 集群部署时,可以修改某一个实例,或选择 `全部修改` 按钮,修改所有实例线程池信息。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/_category_.json new file mode 100644 index 00000000..11f12432 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "开发者手册", + "position": 5, + "link": { + "type": "generated-index", + "description": "Hippo4J 留给使用者能够扩展的知识点。" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/rejected-policy-custom.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/rejected-policy-custom.md new file mode 100644 index 00000000..1f2cf694 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/rejected-policy-custom.md @@ -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` 模块。 +::: diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/rejected-policy-info.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/rejected-policy-info.md new file mode 100644 index 00000000..d5c68138 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/dev_manual/rejected-policy-info.md @@ -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 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); + } + } +} +``` \ No newline at end of file diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/_category_.json new file mode 100644 index 00000000..72b1e151 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "快速开始", + "position": 3, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/_category_.json new file mode 100644 index 00000000..78b3d9c4 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "依赖配置中心", + "position": 2, + "collapsed": true +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-default.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-default.md new file mode 100644 index 00000000..9c9e0fc9 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-default.md @@ -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` 层级下参数,不提供动态刷新功能。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-monitor.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-monitor.md new file mode 100644 index 00000000..c030dc10 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-monitor.md @@ -0,0 +1,122 @@ +--- +sidebar_position: 3 +--- + +# 线程池监控 + +## 线程池监控配置 + +监控前置条件:需要先完成 hippo4j-config 的 [接入工作](/docs/user_docs/getting_started/config/hippo4j-config-start)。 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```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 # 采集线程池运行数据的类型。eg:log、micrometer。多个可以同时使用,默认 micrometer + initial-delay: 10000 # 项目启动后延迟多久进行采集 + thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-more.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-more.md new file mode 100644 index 00000000..2511b4f3 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-more.md @@ -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
+ * nodes is ip + port.Get 'nodes' in the new Properties,Compare this with the ip + port of Application.
+ * support prefix pattern matching. e.g:
+ *
    + *
  • 192.168.1.5:* -- Matches all ports of 192.168.1.5
  • + *
  • 192.168.1.*:2009 -- Matches 2009 port of 192.168.1.*
  • + *
  • * -- all
  • + *
  • empty -- all
  • + *
+ * The format of ip + port is ip : port. + */ +``` + +`nodes` 可与 `enable` 同时使用。如此,基于配置中心的动态线程池实现方式,将能够更方便的支持个性化需求。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md new file mode 100644 index 00000000..709e882a --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md @@ -0,0 +1,121 @@ +--- +sidebar_position: 5 +--- + +# 适配SpringBoot1x + +目前已支持 Nacos、Apollo 配置中心适配 SpringBoot 1.5.x 版本。 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-1x-starter + 1.4.3-upgrade + +``` + +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` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-start.md new file mode 100644 index 00000000..51907ae7 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-start.md @@ -0,0 +1,193 @@ +--- +sidebar_position: 1 +--- + +# 接入流程 + +Nacos、Apollo、Zookeeper、ETCD、Polaris 配置中心任选其一。 + +## hippo4j 配置 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-starter + 1.4.3-upgrade + +``` + +启动类上添加注解 `@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 任选其一 + nacos: + data-id: xxx + group: xxx + apollo: + namespace: xxxx + # 配置中心文件格式 + config-file-type: yml + # tomcat、undertow、jetty 三种容器线程池,任选其一 + undertow: + 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); +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/difference.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/difference.md new file mode 100644 index 00000000..4e07d71b --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/difference.md @@ -0,0 +1,37 @@ +--- +sidebar_position: 0 +--- + +# 运行模式介绍 + +1.1.0 版本发布后,Hippo-4J 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220319154626314.png) + +### hippo4j-config + +**轻量级动态线程池管理**,依赖 Nacos、Apollo、Zookeeper、ETCD、Polaris 等三方配置中心(任选其一)完成线程池参数动态变更,支持运行时报警、监控等功能。 + +> 监控功能配置详见:[线程池监控](/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 配置中心(任选其一) | 部署 Hippo-4J Server(内部无依赖中间件) | +| 使用 | 配置中心补充线程池相关参数 | Hippo-4J Server Web 控制台添加线程池记录 | +| 功能 | 包含基础功能:参数动态化、运行时监控、报警等 | 基础功能之外扩展控制台界面、线程池堆栈查看、线程池运行信息实时查看、历史运行信息查看、线程池配置集群个性化等 | + +使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 hippo4j-config 使用即可;如果希望更多的功能,可以选择 hippo4j-server。 + +**两者在进行替换的时候,无需修改业务代码**。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/hippo4j-adapter.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/hippo4j-adapter.md new file mode 100644 index 00000000..378713e8 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/hippo4j-adapter.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 6 +--- + +# 三方框架线程池适配 + +Hippo4J 目前已支持的三方框架线程池列表: + +- Dubbo +- Hystrix +- RabbitMQ +- RocketMQ +- AlibabaDubbo +- RocketMQSpringCloudStream +- RabbitMQSpringCloudStream + +引入 Hippo4J Server 或 Core 的 Maven Jar 坐标后,还需要引入对应的框架适配 Jar: + +```xml + + cn.hippo4j + + hippo4j-spring-boot-starter-adapter-dubbo + + hippo4j-spring-boot-starter-adapter-alibaba-dubbo + + hippo4j-spring-boot-starter-adapter-hystrix + + hippo4j-spring-boot-starter-adapter-rabbitmq + + hippo4j-spring-boot-starter-adapter-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq + 1.4.3-upgrade + +``` + +如果想省事,仅需引入一个全量包,框架底层会根据条件判断加载具体线程池适配器。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-adapter-all + 1.4.3-upgrade + +``` + +## 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 +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/docsVersionDropdown.png b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/docsVersionDropdown.png new file mode 100644 index 00000000..97e41646 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/docsVersionDropdown.png differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/grafana-monitor.jpg b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/grafana-monitor.jpg new file mode 100644 index 00000000..336bd980 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/grafana-monitor.jpg differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/localeDropdown.png b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/localeDropdown.png new file mode 100644 index 00000000..e257edc1 Binary files /dev/null and b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/img/localeDropdown.png differ diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/_category_.json new file mode 100644 index 00000000..4063c17f --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "无中间件依赖", + "position": 3, + "collapsed": true +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-config.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-config.md new file mode 100644 index 00000000..e1ef853f --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-config.md @@ -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 配置后,需要在客户端对应开启才可生效。用来应对大量动态线程池监控场景。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-monitor.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-monitor.md new file mode 100644 index 00000000..4a81a47b --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-monitor.md @@ -0,0 +1,132 @@ +--- +sidebar_position: 2 +--- + +# 线程池监控 + +Server 模式默认内置线程池运行时采集和监控功能,如果想要使用 Prometheus + Grafana 的方式可以查看以下内容。 + +## 线程池监控配置 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```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 # 采集线程池运行数据的类型。eg:server、micrometer。多个可以同时使用,默认 server + initial-delay: 10000 # 项目启动后延迟多久进行采集 + thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、web、adapter。可任意配置,默认 dynamic +``` + +如果使用 `micrometer` 类型的监控指标,需要添加以下依赖。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-monitor-micrometer + 1.4.3-upgrade + +``` + +项目启动,访问 `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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-start.md new file mode 100644 index 00000000..5bf3db1c --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-start.md @@ -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 + + cn.hippo4j + hippo4j-spring-boot-starter + 1.4.3-upgrade + +``` + +启动类上添加注解 `@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); +``` + diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/intro.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/intro.md new file mode 100644 index 00000000..eea7c946 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/intro.md @@ -0,0 +1,65 @@ +--- +sidebar_position: 1 +--- + +# 简介 + +## 线程池痛点 + +线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。 + +如果有在项目中实际使用线程池,相信你可能会遇到以下痛点: + +- 线程池随便定义,线程资源过多,造成服务器高负载。 + +- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。 +- 线程池任务执行时间超过平均执行周期,开发人员无法感知。 +- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。 +- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。 +- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。 +- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。 +- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。 + +## 什么是 Hippo-4J + +Hippo-4J 通过对 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) 登记,登记仅仅为了产品推广。 + +## 联系我 + +![](https://user-images.githubusercontent.com/77398366/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) + +扫码添加微信,备注:hippo4j,邀您加入群聊。若图片加载不出来,访问 [官网站点](https://hippo4j.cn/docs/user_docs/other/group)。 + +## 友情链接 +- [[ 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)。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/_category_.json new file mode 100644 index 00000000..d5b7d1e8 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "运维指南", + "position": 4, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/hippo4j-server-deploy.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/hippo4j-server-deploy.md new file mode 100644 index 00000000..f78457ba --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/hippo4j-server-deploy.md @@ -0,0 +1,45 @@ +--- +sidebar_position: 1 +--- + +# 源码包部署 + +[RELEASE](https://github.com/opengoofy/hippo4j/releases) 页面下载对应版本并进行解压。 + +## 初始化 + +修改数据库相关信息。 + +```txt +/conf/application.properties +``` + +如果是新运行 Hippo-4J,数据库执行下述 SQL 脚本即可。 + +```txt +/conf/hippo4j_manager.sql +``` + +如果是对已运行 Hippo-4J 升级,请查看 `/conf/sql-upgrade` 目录下,是否有目标版本对应的升级脚本。 + +## 直接运行 + +Mac Linux 启动执行。 + +```txt +sh ./bin/startup.sh +``` + +Windows 启动执行。 + +```txt +bin/startup.cmd +``` + +## 访问控制台 + +启动成功后,访问链接。用户名密码:admin 123456 + +```txt +localhost:6691/index.html +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/server-docker.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/server-docker.md new file mode 100644 index 00000000..141c597a --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/ops/server-docker.md @@ -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 +``` diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/_category_.json new file mode 100644 index 00000000..a74d7432 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "其它", + "position": 6, + "link": { + "type": "generated-index" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/group.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/group.md new file mode 100644 index 00000000..e75e96be --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/group.md @@ -0,0 +1,9 @@ +--- +sidebar_position: 1 +--- + +# 加群沟通 + +扫码添加微信,备注:`hippo4j`,邀您加入群聊。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/official-ccounts.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/official-ccounts.md new file mode 100644 index 00000000..61a1a5c6 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/official-ccounts.md @@ -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开源项目mall(50K+Star)。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/macrozheng.png) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/operation.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/operation.md new file mode 100644 index 00000000..ba9ed7f0 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/operation.md @@ -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 相互推荐,欢迎 [加群沟通](/docs/user_docs/other/group)。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/question.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/question.md new file mode 100644 index 00000000..49fd4d77 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/other/question.md @@ -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)都没有结果,网站上也许还有报告 Bug(Bug-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 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题? + +## 如果得不到回答 + +如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。 + +总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。 + +你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/_category_.json b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/_category_.json new file mode 100644 index 00000000..6dc10c33 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "用户指南", + "position": 2, + "link": { + "type": "generated-index", + "description": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。" + } +} diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/frame.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/frame.md new file mode 100644 index 00000000..47f568fa --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/frame.md @@ -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。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/framework.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/framework.md new file mode 100644 index 00000000..226ddf61 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/framework.md @@ -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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/notify.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/notify.md new file mode 100644 index 00000000..d4ca2bfd --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/notify.md @@ -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) diff --git a/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/quick-start.md b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/quick-start.md new file mode 100644 index 00000000..5e2e7035 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-docs/version-1.4.3/user_docs/user_guide/quick-start.md @@ -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 集群部署时,可以修改某一个实例,或选择 `全部修改` 按钮,修改所有实例线程池信息。 diff --git a/docs/i18n/zh/docusaurus-plugin-content-pages/markdown-page.md b/docs/i18n/zh/docusaurus-plugin-content-pages/markdown-page.md new file mode 100644 index 00000000..9756c5b6 --- /dev/null +++ b/docs/i18n/zh/docusaurus-plugin-content-pages/markdown-page.md @@ -0,0 +1,7 @@ +--- +title: Markdown page example +--- + +# Markdown page example + +You don't need React to write simple standalone pages. diff --git a/docs/docs/community/developer.md b/docs/i18n/zh/docusaurus-plugin-content-pages/team.md similarity index 51% rename from docs/docs/community/developer.md rename to docs/i18n/zh/docusaurus-plugin-content-pages/team.md index b468d82d..821d1e9a 100644 --- a/docs/docs/community/developer.md +++ b/docs/i18n/zh/docusaurus-plugin-content-pages/team.md @@ -1,8 +1,12 @@ --- -sidebar_position: 2 +sidebar_position: 1 --- -# 核心开发者 +## Hippo4j 团队 + +非常感谢通过开源对 Hippo4j 贡献的开发者,项目始终保持高质量编码,以社区的力量去发展项目,并以项目反馈社区成员。我们期待对开源有兴趣的小伙伴加入团队,与社区共同成长。 + +## 成员 @@ -13,10 +17,10 @@ sidebar_position: 2 - + - - + + @@ -61,4 +65,45 @@ sidebar_position: 2 + + + + + + + + + + + + + +
联系方式
马称itmachen小马哥的技术专栏mageeric小马哥的技术专栏 machen@apache.org
- 17855368071@163.com
黄成兴CreatesequenceCreatesequence's Blog841396397@qq.com
黎金星road2masterroad2master's Bloglijinxingerm@gmail.com
+ +## 贡献者 + + + + + +## 成为提交者 + +持续对 Hippo4j 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 +个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) +或以上,以及若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 + +:::note +会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR +即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) +::: + +成为核心开发者后,会为该 GitHub 账号获取 Jetbrains 全家桶 Licenses,有效期为 1 年。不用再为破解而烦恼,感受随时升级的快乐。 + +:::note +距离到期两周前,会再次申请 Jetbrains Licenses。有点类似于无限续约的感觉,在此感谢 Jetbrains 公司对开源的支持。 +::: + +截至 `2022-10-30` 当天,已为登记的 7 位核心开发者成功申请 Licenses。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20221030134303060.png) diff --git a/docs/docs/community/powered-by.md b/docs/i18n/zh/docusaurus-plugin-content-pages/users.md similarity index 53% rename from docs/docs/community/powered-by.md rename to docs/i18n/zh/docusaurus-plugin-content-pages/users.md index 4771d17e..91c5d028 100644 --- a/docs/docs/community/powered-by.md +++ b/docs/i18n/zh/docusaurus-plugin-content-pages/users.md @@ -1,24 +1,17 @@ --- -sidebar_position: 3 +sidebar_position: 1 +title: 采用公司 --- -# 采用公司 +## 谁在使用 Hippo4j -## 登记 - -欢迎采用了 Hippo4J 的公司在此登记,非常感谢大家对 Hippo4J 的关注和支持,这是我们前进最大的动力。 - -请按公司名 + 首页的格式在 [此处](https://github.com/opengoofy/hippo4j/issues/13) 登记。 - -## 谁在使用 Hippo4J - -共计 19+ 家公司生产接入 Hippo4J。按照公司登记时间排序。 +共计 29+ 家公司生产接入 Hippo4j。按照公司登记时间排序。 - [身边云](https://serviceshare.com) -- [Medbanks](https://www.medbanks.cn) +- [思派健康科技](https://www.medbanks.cn) - [北京智合联创科技有限公司](http://www.zhlc.com.cn) - [神州数码](http://www.digitalchina.com) -- [payermax](https://www.payermax.com/) +- [PayerMax](https://www.payermax.com/) - [轻松到家](http://www.uyess.com/index.html) - [某商业银行股份有限公司](https://github.com/opengoofy/hippo4j/issues/13) - [某国际物流信息股份有限公司](https://github.com/opengoofy/hippo4j/issues/13) @@ -33,3 +26,19 @@ sidebar_position: 3 - [深圳航天信息有限公司](http://sz.aisino.com/) - [新东方教育科技集团](https://www.xdf.cn/) - [远眺网络科技有限公司](https://www.yuantiaokj.com/) +- [浙江吉利控股集团有限公司](https://www.geely.com/) +- [三立人(深圳)科技有限公司-焦内](https://www.bananain.com/) +- [上海天猫好房添玑网络服务有限公司](http://www.eju.com/) +- [郑州力通水务](http://www.zzltsw.com/) +- [北京自如信息科技有限公司](https://www.ziroom.com/) +- [北京易车信息科技有限公司](https://www.yiche.com/) +- [中新软件(上海)有限公司](http://www.5000m.com) +- [深圳市上马网络技术有限公司](https://www.shangmanet.com) +- [兰亭集势(成都)科技有限公司](https://www.lightinthebox.com/) +- [杭州数理大数据有限公司](https://www.data4truth.com/#/) + +## 登记 + +欢迎采用了 Hippo4j 的公司在此登记,非常感谢大家对 Hippo4j 的关注和支持,这是我们前进最大的动力。 + +请按公司名 + 首页的格式在 [此处](https://github.com/opengoofy/hippo4j/issues/13) 登记。 diff --git a/docs/i18n/zh/docusaurus-theme-classic/footer.json b/docs/i18n/zh/docusaurus-theme-classic/footer.json new file mode 100644 index 00000000..781b05cb --- /dev/null +++ b/docs/i18n/zh/docusaurus-theme-classic/footer.json @@ -0,0 +1,42 @@ +{ + "link.title.Docs": { + "message": "Docs", + "description": "The title of the footer links column with title=Docs in the footer" + }, + "link.title.Community": { + "message": "Community", + "description": "The title of the footer links column with title=Community in the footer" + }, + "link.title.More": { + "message": "More", + "description": "The title of the footer links column with title=More in the footer" + }, + "link.item.label.简介": { + "message": "简介", + "description": "The label of footer link with label=简介 linking to /docs/user_docs/intro" + }, + "link.item.label.加群沟通": { + "message": "加群沟通", + "description": "The label of footer link with label=加群沟通 linking to https://hippo4j.cn/docs/user_docs/other/group" + }, + "link.item.label.微信公众号": { + "message": "微信公众号", + "description": "The label of footer link with label=微信公众号 linking to https://mp.weixin.qq.com/s/diVHYvwiuYH9aWpZDPc27g" + }, + "link.item.label.Gitee": { + "message": "Gitee", + "description": "The label of footer link with label=Gitee linking to https://gitee.com/itmachen/hippo4j" + }, + "link.item.label.GitHub": { + "message": "GitHub", + "description": "The label of footer link with label=GitHub linking to https://github.com/opengoofy/hippo4j" + }, + "link.item.label.公司登记": { + "message": "公司登记", + "description": "The label of footer link with label=公司登记 linking to https://github.com/opengoofy/hippo4j/issues/13" + }, + "copyright": { + "message": "Copyright © 2021-2022 小马哥版权所有 京ICP备2021038095号-2\n", + "description": "The footer copyright" + } +} diff --git a/docs/i18n/zh/docusaurus-theme-classic/navbar.json b/docs/i18n/zh/docusaurus-theme-classic/navbar.json new file mode 100644 index 00000000..3e38678a --- /dev/null +++ b/docs/i18n/zh/docusaurus-theme-classic/navbar.json @@ -0,0 +1,22 @@ +{ + "title": { + "message": "", + "description": "The title in the navbar" + }, + "item.label.文档": { + "message": "文档", + "description": "Navbar item with label 文档" + }, + "item.label.社区": { + "message": "社区", + "description": "Navbar item with label 社区" + }, + "item.label.控制台样例": { + "message": "控制台样例", + "description": "Navbar item with label 控制台样例" + }, + "item.label.🥇代码实战课": { + "message": "🥇代码实战课", + "description": "Navbar item with label 🥇代码实战课" + } +} diff --git a/docs/sidebars.js b/docs/sidebars.js index 112da355..a22fc6a3 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -22,12 +22,12 @@ const sidebars = { dirName: 'user_docs', }, ], - community: [ + /*community: [ { type: 'autogenerated', dirName: 'community', }, - ], + ],*/ sponsor: [ { diff --git a/docs/sidebarsCommunity.js b/docs/sidebarsCommunity.js new file mode 100755 index 00000000..676888c2 --- /dev/null +++ b/docs/sidebarsCommunity.js @@ -0,0 +1,15 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + community: [ + { + type: 'autogenerated', + dirName: '.', + }, + ], +}; diff --git a/docs/src/components/HomepageFeatures/index.js b/docs/src/components/HomepageFeatures/index.js index 59e97a95..10b5d288 100644 --- a/docs/src/components/HomepageFeatures/index.js +++ b/docs/src/components/HomepageFeatures/index.js @@ -8,7 +8,7 @@ const FeatureList = [ Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, description: ( <> - 应用运行时动态变更线程池参数,包括不限于核心、最大线程、阻塞队列大小和拒绝策略等;支持应用集群线程池配置差异化 + 应用运行时动态变更线程池参数,包括不限于核心、最大线程、阻塞队列大小和拒绝策略等,支持应用集群下不同节点线程池配置差异化 ), }, @@ -26,7 +26,7 @@ const FeatureList = [ Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, description: ( <> - 支持自定义时长的线程池运行数据采集存储,并提供可视化大屏监控运行指标;同时也支持 Prometheus 采集监控,帮助排查以及确定线程池问题 + 支持自定义时长线程池运行数据采集存储,同时也支持 Prometheus、InfluxDB 等采集监控,通过 Grafana 或内置监控页面提供可视化大屏监控运行指标 ), }, diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js index 557e5928..a1bdfacc 100644 --- a/docs/src/pages/index.js +++ b/docs/src/pages/index.js @@ -1,6 +1,7 @@ import React from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; +import Translate from '@docusaurus/Translate'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Layout from '@theme/Layout'; import HomepageFeatures from '@site/src/components/HomepageFeatures'; diff --git a/docs/src/pages/team.md b/docs/src/pages/team.md new file mode 100644 index 00000000..5d852b71 --- /dev/null +++ b/docs/src/pages/team.md @@ -0,0 +1,116 @@ +--- +sidebar_position: 1 +--- + +## Hippo4j 团队 + +非常感谢通过开源对 Hippo4j 贡献的开发者,项目始终保持高质量编码,以社区的力量去发展项目,并以项目反馈社区成员。我们期待对开源有兴趣的小伙伴加入团队,与社区共同成长。 + +## 成员 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
姓名GitHub ID博客地址联系方式
马称mageeric小马哥的技术专栏machen@apache.org
陆宽shining-stars-lk宽仔的代码之路1031900093@qq.com
王杰iwangjie-wangchenmo1025@gmail.com
魏虎weihubeatsweihubeatsweihubeats@163.com
李剑鑫BigXin0109Only丶Big1064730540@qq.com
刘文浩pizihaopizihaohao3073liu@163.com
叶炜shanjianq-17855368071@163.com
黄成兴CreatesequenceCreatesequence's Blog841396397@qq.com
吴浪wulangcode-sanliangitch@foxmail.com
黎金星road2masterroad2master's Bloglijinxingerm@gmail.com
+ +## 贡献者 + + + + + +## 成为提交者 + +持续对 Hippo4j 进行贡献, 粗略评估,完成 10 次 PR 贡献即可成为核心开发者。 其中包括完成 2 +个 [good pro issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+pro+issue%22) +或以上,以及若干个 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。 + +:::note +会根据 PR 质量提供个性化评估,有可能一个或两个质量较高 PR +即可成为核心开发者。参考:[重构 DynamicThreadPoolExecutor 功能扩展逻辑](https://github.com/opengoofy/hippo4j/pull/854) +::: + +成为核心开发者后,会为该 GitHub 账号获取 Jetbrains 全家桶 Licenses,有效期为 1 年。不用再为破解而烦恼,感受随时升级的快乐。 + +:::note +距离到期两周前,会再次申请 Jetbrains Licenses。有点类似于无限续约的感觉,在此感谢 Jetbrains 公司对开源的支持。 +::: + +截至 `2022-10-30` 当天,已为登记的 7 位核心开发者成功申请 Licenses。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20221030134303060.png) diff --git a/docs/src/pages/users.md b/docs/src/pages/users.md new file mode 100644 index 00000000..91c5d028 --- /dev/null +++ b/docs/src/pages/users.md @@ -0,0 +1,44 @@ +--- +sidebar_position: 1 +title: 采用公司 +--- + +## 谁在使用 Hippo4j + +共计 29+ 家公司生产接入 Hippo4j。按照公司登记时间排序。 + +- [身边云](https://serviceshare.com) +- [思派健康科技](https://www.medbanks.cn) +- [北京智合联创科技有限公司](http://www.zhlc.com.cn) +- [神州数码](http://www.digitalchina.com) +- [PayerMax](https://www.payermax.com/) +- [轻松到家](http://www.uyess.com/index.html) +- [某商业银行股份有限公司](https://github.com/opengoofy/hippo4j/issues/13) +- [某国际物流信息股份有限公司](https://github.com/opengoofy/hippo4j/issues/13) +- [萨科(深圳)科技有限公司](https://www.lbdj.com/) +- [广东天枢新能源科技有限公司](https://gd-tianshu.com/) +- [FitTime](http://fittime.com/) +- [百强国际物流](https://github.com/opengoofy/hippo4j/issues/13) +- [海南某深圳分公司](https://github.com/opengoofy/hippo4j/issues/13) +- [众合云科(51社保)](https://home.101hr.com/) +- [好货云店](https://pc.haohuoyundian.com/) +- [斗象科技](https://www.tophant.com/) +- [深圳航天信息有限公司](http://sz.aisino.com/) +- [新东方教育科技集团](https://www.xdf.cn/) +- [远眺网络科技有限公司](https://www.yuantiaokj.com/) +- [浙江吉利控股集团有限公司](https://www.geely.com/) +- [三立人(深圳)科技有限公司-焦内](https://www.bananain.com/) +- [上海天猫好房添玑网络服务有限公司](http://www.eju.com/) +- [郑州力通水务](http://www.zzltsw.com/) +- [北京自如信息科技有限公司](https://www.ziroom.com/) +- [北京易车信息科技有限公司](https://www.yiche.com/) +- [中新软件(上海)有限公司](http://www.5000m.com) +- [深圳市上马网络技术有限公司](https://www.shangmanet.com) +- [兰亭集势(成都)科技有限公司](https://www.lightinthebox.com/) +- [杭州数理大数据有限公司](https://www.data4truth.com/#/) + +## 登记 + +欢迎采用了 Hippo4j 的公司在此登记,非常感谢大家对 Hippo4j 的关注和支持,这是我们前进最大的动力。 + +请按公司名 + 首页的格式在 [此处](https://github.com/opengoofy/hippo4j/issues/13) 登记。 diff --git a/docs/static/img/hippo4j.png b/docs/static/img/hippo4j.png new file mode 100644 index 00000000..66ab4b25 Binary files /dev/null and b/docs/static/img/hippo4j.png differ diff --git a/docs/static/img/web.png b/docs/static/img/web.png deleted file mode 100644 index 6c9d3b00..00000000 Binary files a/docs/static/img/web.png and /dev/null differ diff --git a/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/_category_.json b/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/_category_.json new file mode 100644 index 00000000..11f12432 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "开发者手册", + "position": 5, + "link": { + "type": "generated-index", + "description": "Hippo4J 留给使用者能够扩展的知识点。" + } +} diff --git a/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/rejected-policy-custom.md b/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/rejected-policy-custom.md new file mode 100644 index 00000000..1f2cf694 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/rejected-policy-custom.md @@ -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` 模块。 +::: diff --git a/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/rejected-policy-info.md b/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/rejected-policy-info.md new file mode 100644 index 00000000..d5c68138 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/dev_manual/rejected-policy-info.md @@ -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 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); + } + } +} +``` \ No newline at end of file diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/_category_.json b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/_category_.json new file mode 100644 index 00000000..72b1e151 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "快速开始", + "position": 3, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/_category_.json b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/_category_.json new file mode 100644 index 00000000..78b3d9c4 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "依赖配置中心", + "position": 2, + "collapsed": true +} diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-default.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-default.md new file mode 100644 index 00000000..9c9e0fc9 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-default.md @@ -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` 层级下参数,不提供动态刷新功能。 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-monitor.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-monitor.md new file mode 100644 index 00000000..72528638 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-monitor.md @@ -0,0 +1,117 @@ +--- +sidebar_position: 3 +--- + +# 线程池监控 + +## 线程池监控配置 + +监控前置条件:需要先完成 hippo4j-config 的 [接入工作](/docs/user_docs/getting_started/config/hippo4j-config-start)。 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```yaml +management: + metrics: + export: + prometheus: + enabled: true + server: + port: 29999 # 可选配置,如果不配置该 port,直接使用 ${server.port} + endpoints: + web: + exposure: + include: '*' # 测试使用,开启了所有端点,生产环境不建议 * +spring: + dynamic: + thread-pool: + collect-type: micrometer +``` + +项目启动,访问 `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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-more.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-more.md new file mode 100644 index 00000000..2511b4f3 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-more.md @@ -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
+ * nodes is ip + port.Get 'nodes' in the new Properties,Compare this with the ip + port of Application.
+ * support prefix pattern matching. e.g:
+ *
    + *
  • 192.168.1.5:* -- Matches all ports of 192.168.1.5
  • + *
  • 192.168.1.*:2009 -- Matches 2009 port of 192.168.1.*
  • + *
  • * -- all
  • + *
  • empty -- all
  • + *
+ * The format of ip + port is ip : port. + */ +``` + +`nodes` 可与 `enable` 同时使用。如此,基于配置中心的动态线程池实现方式,将能够更方便的支持个性化需求。 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md new file mode 100644 index 00000000..693dd341 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md @@ -0,0 +1,121 @@ +--- +sidebar_position: 5 +--- + +# 适配SpringBoot1x + +目前已支持 Nacos、Apollo 配置中心适配 SpringBoot 1.5.x 版本。 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-1x-starter + 1.4.2 + +``` + +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` diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-start.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-start.md new file mode 100644 index 00000000..33f658c2 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/config/hippo4j-config-start.md @@ -0,0 +1,193 @@ +--- +sidebar_position: 1 +--- + +# 接入流程 + +Nacos、Apollo、Zookeeper、ETCD、Polaris 配置中心任选其一。 + +## hippo4j 配置 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-starter + 1.4.2 + +``` + +启动类上添加注解 `@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 任选其一 + nacos: + data-id: xxx + group: xxx + apollo: + namespace: xxxx + # 配置中心文件格式 + config-file-type: yml + # tomcat、undertow、jetty 三种容器线程池,任选其一 + undertow: + 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); +``` diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/difference.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/difference.md new file mode 100644 index 00000000..4e07d71b --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/difference.md @@ -0,0 +1,37 @@ +--- +sidebar_position: 0 +--- + +# 运行模式介绍 + +1.1.0 版本发布后,Hippo-4J 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220319154626314.png) + +### hippo4j-config + +**轻量级动态线程池管理**,依赖 Nacos、Apollo、Zookeeper、ETCD、Polaris 等三方配置中心(任选其一)完成线程池参数动态变更,支持运行时报警、监控等功能。 + +> 监控功能配置详见:[线程池监控](/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 配置中心(任选其一) | 部署 Hippo-4J Server(内部无依赖中间件) | +| 使用 | 配置中心补充线程池相关参数 | Hippo-4J Server Web 控制台添加线程池记录 | +| 功能 | 包含基础功能:参数动态化、运行时监控、报警等 | 基础功能之外扩展控制台界面、线程池堆栈查看、线程池运行信息实时查看、历史运行信息查看、线程池配置集群个性化等 | + +使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 hippo4j-config 使用即可;如果希望更多的功能,可以选择 hippo4j-server。 + +**两者在进行替换的时候,无需修改业务代码**。 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/hippo4j-adapter.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/hippo4j-adapter.md new file mode 100644 index 00000000..030367e1 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/hippo4j-adapter.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 6 +--- + +# 三方框架线程池适配 + +Hippo4J 目前已支持的三方框架线程池列表: + +- Dubbo +- Hystrix +- RabbitMQ +- RocketMQ +- AlibabaDubbo +- RocketMQSpringCloudStream +- RabbitMQSpringCloudStream + +引入 Hippo4J Server 或 Core 的 Maven Jar 坐标后,还需要引入对应的框架适配 Jar: + +```xml + + cn.hippo4j + + hippo4j-spring-boot-starter-adapter-dubbo + + hippo4j-spring-boot-starter-adapter-alibaba-dubbo + + hippo4j-spring-boot-starter-adapter-hystrix + + hippo4j-spring-boot-starter-adapter-rabbitmq + + hippo4j-spring-boot-starter-adapter-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq + 1.4.2 + +``` + +如果想省事,仅需引入一个全量包,框架底层会根据条件判断加载具体线程池适配器。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-adapter-all + 1.4.2 + +``` + +## 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 +``` diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/docsVersionDropdown.png b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/docsVersionDropdown.png new file mode 100644 index 00000000..97e41646 Binary files /dev/null and b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/docsVersionDropdown.png differ diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/grafana-monitor.jpg b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/grafana-monitor.jpg new file mode 100644 index 00000000..336bd980 Binary files /dev/null and b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/grafana-monitor.jpg differ diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/localeDropdown.png b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/localeDropdown.png new file mode 100644 index 00000000..e257edc1 Binary files /dev/null and b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/img/localeDropdown.png differ diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/_category_.json b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/_category_.json new file mode 100644 index 00000000..4063c17f --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "无中间件依赖", + "position": 3, + "collapsed": true +} diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-config.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-config.md new file mode 100644 index 00000000..24bf9a13 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-config.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 4 +--- + +# 服务端配置 + +`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 配置后,需要在客户端对应开启才可生效。用来应对大量动态线程池监控场景。 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-start.md b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-start.md new file mode 100644 index 00000000..eec2af48 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/getting_started/server/hippo4j-server-start.md @@ -0,0 +1,127 @@ +--- +sidebar_position: 3 +--- + +# 接入流程 + +部署服务端,参考 [部署手册](/docs/user_docs/ops/hippo4j-server-deploy)。 + +服务端创建 [租户、项目](/community/faq#租户和项目在-hippo4j-中是什么意思) 和线程池记录。 + +需要注意,项目 ID 需要与配置文件 `{application.name}` 保持一致。 + +:::note +租户、项目、线程池 ID 如果由多个词组成,建议以 - 进行分割。比如:message-center。 +::: + +## Hippo4J 配置 + +SpringBoot Pom 引入 Hippo4j Starter Jar。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter + 1.4.2 + +``` + +启动类上添加注解 `@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); +``` + diff --git a/docs/versioned_docs/version-1.4.2/user_docs/intro.md b/docs/versioned_docs/version-1.4.2/user_docs/intro.md new file mode 100644 index 00000000..b0d75d4d --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/intro.md @@ -0,0 +1,68 @@ +--- +sidebar_position: 1 +--- + +# 简介 + +## 线程池痛点 + +线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。 + +在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。 + +如果有在项目中实际使用线程池,相信你可能会遇到以下痛点: + +- 线程池随便定义,线程资源过多,造成服务器高负载。 + +- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。 +- 线程池任务执行时间超过平均执行周期,开发人员无法感知。 +- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。 +- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。 +- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。 +- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。 +- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。 + +## 什么是 Hippo-4J + +Hippo-4J 通过对 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) 登记,登记仅仅为了产品推广。 + +## 联系我 + +![](https://user-images.githubusercontent.com/77398366/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) + +扫码添加微信,备注:hippo4j,邀您加入群聊。若图片加载不出来,访问 [官网站点](https://hippo4j.cn/docs/user_docs/other/group)。 + +## 友情链接 + +- [[ 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)。 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/ops/_category_.json b/docs/versioned_docs/version-1.4.2/user_docs/ops/_category_.json new file mode 100644 index 00000000..d5b7d1e8 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/ops/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "运维指南", + "position": 4, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-1.4.2/user_docs/ops/hippo4j-server-deploy.md b/docs/versioned_docs/version-1.4.2/user_docs/ops/hippo4j-server-deploy.md new file mode 100644 index 00000000..f78457ba --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/ops/hippo4j-server-deploy.md @@ -0,0 +1,45 @@ +--- +sidebar_position: 1 +--- + +# 源码包部署 + +[RELEASE](https://github.com/opengoofy/hippo4j/releases) 页面下载对应版本并进行解压。 + +## 初始化 + +修改数据库相关信息。 + +```txt +/conf/application.properties +``` + +如果是新运行 Hippo-4J,数据库执行下述 SQL 脚本即可。 + +```txt +/conf/hippo4j_manager.sql +``` + +如果是对已运行 Hippo-4J 升级,请查看 `/conf/sql-upgrade` 目录下,是否有目标版本对应的升级脚本。 + +## 直接运行 + +Mac Linux 启动执行。 + +```txt +sh ./bin/startup.sh +``` + +Windows 启动执行。 + +```txt +bin/startup.cmd +``` + +## 访问控制台 + +启动成功后,访问链接。用户名密码:admin 123456 + +```txt +localhost:6691/index.html +``` diff --git a/docs/versioned_docs/version-1.4.2/user_docs/ops/server-docker.md b/docs/versioned_docs/version-1.4.2/user_docs/ops/server-docker.md new file mode 100644 index 00000000..141c597a --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/ops/server-docker.md @@ -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 +``` diff --git a/docs/versioned_docs/version-1.4.2/user_docs/other/_category_.json b/docs/versioned_docs/version-1.4.2/user_docs/other/_category_.json new file mode 100644 index 00000000..a74d7432 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/other/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "其它", + "position": 6, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-1.4.2/user_docs/other/group.md b/docs/versioned_docs/version-1.4.2/user_docs/other/group.md new file mode 100644 index 00000000..e75e96be --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/other/group.md @@ -0,0 +1,9 @@ +--- +sidebar_position: 1 +--- + +# 加群沟通 + +扫码添加微信,备注:`hippo4j`,邀您加入群聊。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) diff --git a/docs/versioned_docs/version-1.4.2/user_docs/other/official-ccounts.md b/docs/versioned_docs/version-1.4.2/user_docs/other/official-ccounts.md new file mode 100644 index 00000000..61a1a5c6 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/other/official-ccounts.md @@ -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开源项目mall(50K+Star)。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/macrozheng.png) diff --git a/docs/versioned_docs/version-1.4.2/user_docs/other/operation.md b/docs/versioned_docs/version-1.4.2/user_docs/other/operation.md new file mode 100644 index 00000000..ba9ed7f0 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/other/operation.md @@ -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 相互推荐,欢迎 [加群沟通](/docs/user_docs/other/group)。 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/other/question.md b/docs/versioned_docs/version-1.4.2/user_docs/other/question.md new file mode 100644 index 00000000..49fd4d77 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/other/question.md @@ -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)都没有结果,网站上也许还有报告 Bug(Bug-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 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题? + +## 如果得不到回答 + +如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。 + +总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。 + +你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/user_guide/_category_.json b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/_category_.json new file mode 100644 index 00000000..6dc10c33 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "用户指南", + "position": 2, + "link": { + "type": "generated-index", + "description": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。" + } +} diff --git a/docs/versioned_docs/version-1.4.2/user_docs/user_guide/frame.md b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/frame.md new file mode 100644 index 00000000..47f568fa --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/frame.md @@ -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。 diff --git a/docs/versioned_docs/version-1.4.2/user_docs/user_guide/framework.md b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/framework.md new file mode 100644 index 00000000..226ddf61 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/framework.md @@ -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) diff --git a/docs/versioned_docs/version-1.4.2/user_docs/user_guide/notify.md b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/notify.md new file mode 100644 index 00000000..216840e9 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/notify.md @@ -0,0 +1,71 @@ +--- +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) + +## 企业微信 + +[企业微信创建群机器人](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) diff --git a/docs/versioned_docs/version-1.4.2/user_docs/user_guide/quick-start.md b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/quick-start.md new file mode 100644 index 00000000..5e2e7035 --- /dev/null +++ b/docs/versioned_docs/version-1.4.2/user_docs/user_guide/quick-start.md @@ -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 集群部署时,可以修改某一个实例,或选择 `全部修改` 按钮,修改所有实例线程池信息。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/_category_.json b/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/_category_.json new file mode 100644 index 00000000..11f12432 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "开发者手册", + "position": 5, + "link": { + "type": "generated-index", + "description": "Hippo4J 留给使用者能够扩展的知识点。" + } +} diff --git a/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/rejected-policy-custom.md b/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/rejected-policy-custom.md new file mode 100644 index 00000000..1f2cf694 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/rejected-policy-custom.md @@ -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` 模块。 +::: diff --git a/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/rejected-policy-info.md b/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/rejected-policy-info.md new file mode 100644 index 00000000..d5c68138 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/dev_manual/rejected-policy-info.md @@ -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 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); + } + } +} +``` \ No newline at end of file diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/_category_.json b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/_category_.json new file mode 100644 index 00000000..72b1e151 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "快速开始", + "position": 3, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/_category_.json b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/_category_.json new file mode 100644 index 00000000..78b3d9c4 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "依赖配置中心", + "position": 2, + "collapsed": true +} diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-default.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-default.md new file mode 100644 index 00000000..9c9e0fc9 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-default.md @@ -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` 层级下参数,不提供动态刷新功能。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-monitor.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-monitor.md new file mode 100644 index 00000000..c030dc10 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-monitor.md @@ -0,0 +1,122 @@ +--- +sidebar_position: 3 +--- + +# 线程池监控 + +## 线程池监控配置 + +监控前置条件:需要先完成 hippo4j-config 的 [接入工作](/docs/user_docs/getting_started/config/hippo4j-config-start)。 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```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 # 采集线程池运行数据的类型。eg:log、micrometer。多个可以同时使用,默认 micrometer + initial-delay: 10000 # 项目启动后延迟多久进行采集 + thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-more.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-more.md new file mode 100644 index 00000000..2511b4f3 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-more.md @@ -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
+ * nodes is ip + port.Get 'nodes' in the new Properties,Compare this with the ip + port of Application.
+ * support prefix pattern matching. e.g:
+ *
    + *
  • 192.168.1.5:* -- Matches all ports of 192.168.1.5
  • + *
  • 192.168.1.*:2009 -- Matches 2009 port of 192.168.1.*
  • + *
  • * -- all
  • + *
  • empty -- all
  • + *
+ * The format of ip + port is ip : port. + */ +``` + +`nodes` 可与 `enable` 同时使用。如此,基于配置中心的动态线程池实现方式,将能够更方便的支持个性化需求。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md new file mode 100644 index 00000000..709e882a --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-springboot1x-adapter.md @@ -0,0 +1,121 @@ +--- +sidebar_position: 5 +--- + +# 适配SpringBoot1x + +目前已支持 Nacos、Apollo 配置中心适配 SpringBoot 1.5.x 版本。 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-1x-starter + 1.4.3-upgrade + +``` + +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` diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-start.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-start.md new file mode 100644 index 00000000..51907ae7 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/config/hippo4j-config-start.md @@ -0,0 +1,193 @@ +--- +sidebar_position: 1 +--- + +# 接入流程 + +Nacos、Apollo、Zookeeper、ETCD、Polaris 配置中心任选其一。 + +## hippo4j 配置 + +```xml + + cn.hippo4j + hippo4j-config-spring-boot-starter + 1.4.3-upgrade + +``` + +启动类上添加注解 `@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 任选其一 + nacos: + data-id: xxx + group: xxx + apollo: + namespace: xxxx + # 配置中心文件格式 + config-file-type: yml + # tomcat、undertow、jetty 三种容器线程池,任选其一 + undertow: + 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); +``` diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/difference.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/difference.md new file mode 100644 index 00000000..4e07d71b --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/difference.md @@ -0,0 +1,37 @@ +--- +sidebar_position: 0 +--- + +# 运行模式介绍 + +1.1.0 版本发布后,Hippo-4J 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/image-20220319154626314.png) + +### hippo4j-config + +**轻量级动态线程池管理**,依赖 Nacos、Apollo、Zookeeper、ETCD、Polaris 等三方配置中心(任选其一)完成线程池参数动态变更,支持运行时报警、监控等功能。 + +> 监控功能配置详见:[线程池监控](/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 配置中心(任选其一) | 部署 Hippo-4J Server(内部无依赖中间件) | +| 使用 | 配置中心补充线程池相关参数 | Hippo-4J Server Web 控制台添加线程池记录 | +| 功能 | 包含基础功能:参数动态化、运行时监控、报警等 | 基础功能之外扩展控制台界面、线程池堆栈查看、线程池运行信息实时查看、历史运行信息查看、线程池配置集群个性化等 | + +使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 hippo4j-config 使用即可;如果希望更多的功能,可以选择 hippo4j-server。 + +**两者在进行替换的时候,无需修改业务代码**。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/hippo4j-adapter.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/hippo4j-adapter.md new file mode 100644 index 00000000..378713e8 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/hippo4j-adapter.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 6 +--- + +# 三方框架线程池适配 + +Hippo4J 目前已支持的三方框架线程池列表: + +- Dubbo +- Hystrix +- RabbitMQ +- RocketMQ +- AlibabaDubbo +- RocketMQSpringCloudStream +- RabbitMQSpringCloudStream + +引入 Hippo4J Server 或 Core 的 Maven Jar 坐标后,还需要引入对应的框架适配 Jar: + +```xml + + cn.hippo4j + + hippo4j-spring-boot-starter-adapter-dubbo + + hippo4j-spring-boot-starter-adapter-alibaba-dubbo + + hippo4j-spring-boot-starter-adapter-hystrix + + hippo4j-spring-boot-starter-adapter-rabbitmq + + hippo4j-spring-boot-starter-adapter-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rocketmq + + hippo4j-spring-boot-starter-adapter-spring-cloud-stream-rabbitmq + 1.4.3-upgrade + +``` + +如果想省事,仅需引入一个全量包,框架底层会根据条件判断加载具体线程池适配器。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-adapter-all + 1.4.3-upgrade + +``` + +## 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 +``` diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/docsVersionDropdown.png b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/docsVersionDropdown.png new file mode 100644 index 00000000..97e41646 Binary files /dev/null and b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/docsVersionDropdown.png differ diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/grafana-monitor.jpg b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/grafana-monitor.jpg new file mode 100644 index 00000000..336bd980 Binary files /dev/null and b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/grafana-monitor.jpg differ diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/localeDropdown.png b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/localeDropdown.png new file mode 100644 index 00000000..e257edc1 Binary files /dev/null and b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/img/localeDropdown.png differ diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/_category_.json b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/_category_.json new file mode 100644 index 00000000..4063c17f --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "无中间件依赖", + "position": 3, + "collapsed": true +} diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-config.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-config.md new file mode 100644 index 00000000..e1ef853f --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-config.md @@ -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 配置后,需要在客户端对应开启才可生效。用来应对大量动态线程池监控场景。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-monitor.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-monitor.md new file mode 100644 index 00000000..4a81a47b --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-monitor.md @@ -0,0 +1,132 @@ +--- +sidebar_position: 2 +--- + +# 线程池监控 + +Server 模式默认内置线程池运行时采集和监控功能,如果想要使用 Prometheus + Grafana 的方式可以查看以下内容。 + +## 线程池监控配置 + +接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。 + +```xml + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +添加动态线程池监控相关配置: + +```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 # 采集线程池运行数据的类型。eg:server、micrometer。多个可以同时使用,默认 server + initial-delay: 10000 # 项目启动后延迟多久进行采集 + thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、web、adapter。可任意配置,默认 dynamic +``` + +如果使用 `micrometer` 类型的监控指标,需要添加以下依赖。 + +```xml + + cn.hippo4j + hippo4j-spring-boot-starter-monitor-micrometer + 1.4.3-upgrade + +``` + +项目启动,访问 `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) + +即可使用炫酷的 Hippo-4J 动态线程池监控 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) diff --git a/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-start.md b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-start.md new file mode 100644 index 00000000..5bf3db1c --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/getting_started/server/hippo4j-server-start.md @@ -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 + + cn.hippo4j + hippo4j-spring-boot-starter + 1.4.3-upgrade + +``` + +启动类上添加注解 `@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); +``` + diff --git a/docs/versioned_docs/version-1.4.3/user_docs/intro.md b/docs/versioned_docs/version-1.4.3/user_docs/intro.md new file mode 100644 index 00000000..fd2549c8 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/intro.md @@ -0,0 +1,66 @@ +--- +sidebar_position: 1 +--- + +# 简介 + +## 线程池痛点 + +线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。 + +如果有在项目中实际使用线程池,相信你可能会遇到以下痛点: + +- 线程池随便定义,线程资源过多,造成服务器高负载。 + +- 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。 +- 线程池任务执行时间超过平均执行周期,开发人员无法感知。 +- 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。 +- 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。 +- 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。 +- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。 +- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。 + +## 什么是 Hippo-4J + +Hippo-4J 通过对 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) 登记,登记仅仅为了产品推广。 + +## 联系我 + +![](https://user-images.githubusercontent.com/77398366/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) + +扫码添加微信,备注:hippo4j,邀您加入群聊。若图片加载不出来,访问 [官网站点](https://hippo4j.cn/docs/user_docs/other/group)。 + +## 友情链接 + +- [[ 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)。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/ops/_category_.json b/docs/versioned_docs/version-1.4.3/user_docs/ops/_category_.json new file mode 100644 index 00000000..d5b7d1e8 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/ops/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "运维指南", + "position": 4, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-1.4.3/user_docs/ops/hippo4j-server-deploy.md b/docs/versioned_docs/version-1.4.3/user_docs/ops/hippo4j-server-deploy.md new file mode 100644 index 00000000..f78457ba --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/ops/hippo4j-server-deploy.md @@ -0,0 +1,45 @@ +--- +sidebar_position: 1 +--- + +# 源码包部署 + +[RELEASE](https://github.com/opengoofy/hippo4j/releases) 页面下载对应版本并进行解压。 + +## 初始化 + +修改数据库相关信息。 + +```txt +/conf/application.properties +``` + +如果是新运行 Hippo-4J,数据库执行下述 SQL 脚本即可。 + +```txt +/conf/hippo4j_manager.sql +``` + +如果是对已运行 Hippo-4J 升级,请查看 `/conf/sql-upgrade` 目录下,是否有目标版本对应的升级脚本。 + +## 直接运行 + +Mac Linux 启动执行。 + +```txt +sh ./bin/startup.sh +``` + +Windows 启动执行。 + +```txt +bin/startup.cmd +``` + +## 访问控制台 + +启动成功后,访问链接。用户名密码:admin 123456 + +```txt +localhost:6691/index.html +``` diff --git a/docs/versioned_docs/version-1.4.3/user_docs/ops/server-docker.md b/docs/versioned_docs/version-1.4.3/user_docs/ops/server-docker.md new file mode 100644 index 00000000..141c597a --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/ops/server-docker.md @@ -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 +``` diff --git a/docs/versioned_docs/version-1.4.3/user_docs/other/_category_.json b/docs/versioned_docs/version-1.4.3/user_docs/other/_category_.json new file mode 100644 index 00000000..a74d7432 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/other/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "其它", + "position": 6, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-1.4.3/user_docs/other/group.md b/docs/versioned_docs/version-1.4.3/user_docs/other/group.md new file mode 100644 index 00000000..e75e96be --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/other/group.md @@ -0,0 +1,9 @@ +--- +sidebar_position: 1 +--- + +# 加群沟通 + +扫码添加微信,备注:`hippo4j`,邀您加入群聊。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png) diff --git a/docs/versioned_docs/version-1.4.3/user_docs/other/official-ccounts.md b/docs/versioned_docs/version-1.4.3/user_docs/other/official-ccounts.md new file mode 100644 index 00000000..61a1a5c6 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/other/official-ccounts.md @@ -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开源项目mall(50K+Star)。 + +![](https://images-machen.oss-cn-beijing.aliyuncs.com/macrozheng.png) diff --git a/docs/versioned_docs/version-1.4.3/user_docs/other/operation.md b/docs/versioned_docs/version-1.4.3/user_docs/other/operation.md new file mode 100644 index 00000000..ba9ed7f0 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/other/operation.md @@ -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 相互推荐,欢迎 [加群沟通](/docs/user_docs/other/group)。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/other/question.md b/docs/versioned_docs/version-1.4.3/user_docs/other/question.md new file mode 100644 index 00000000..49fd4d77 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/other/question.md @@ -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)都没有结果,网站上也许还有报告 Bug(Bug-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 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题? + +## 如果得不到回答 + +如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。 + +总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。 + +你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/user_guide/_category_.json b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/_category_.json new file mode 100644 index 00000000..6dc10c33 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "用户指南", + "position": 2, + "link": { + "type": "generated-index", + "description": "帮助想要了解 Hippo4J 的用户快速掌握核心开发理念。" + } +} diff --git a/docs/versioned_docs/version-1.4.3/user_docs/user_guide/frame.md b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/frame.md new file mode 100644 index 00000000..47f568fa --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/frame.md @@ -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。 diff --git a/docs/versioned_docs/version-1.4.3/user_docs/user_guide/framework.md b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/framework.md new file mode 100644 index 00000000..226ddf61 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/framework.md @@ -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) diff --git a/docs/versioned_docs/version-1.4.3/user_docs/user_guide/notify.md b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/notify.md new file mode 100644 index 00000000..924d5a7e --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/notify.md @@ -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) diff --git a/docs/versioned_docs/version-1.4.3/user_docs/user_guide/quick-start.md b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/quick-start.md new file mode 100644 index 00000000..5e2e7035 --- /dev/null +++ b/docs/versioned_docs/version-1.4.3/user_docs/user_guide/quick-start.md @@ -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 集群部署时,可以修改某一个实例,或选择 `全部修改` 按钮,修改所有实例线程池信息。 diff --git a/docs/versioned_sidebars/version-1.4.2-sidebars.json b/docs/versioned_sidebars/version-1.4.2-sidebars.json new file mode 100644 index 00000000..98d1c255 --- /dev/null +++ b/docs/versioned_sidebars/version-1.4.2-sidebars.json @@ -0,0 +1,26 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ], + "user_docs": [ + { + "type": "autogenerated", + "dirName": "user_docs" + } + ], + "community": [ + { + "type": "autogenerated", + "dirName": "community" + } + ], + "sponsor": [ + { + "type": "autogenerated", + "dirName": "sponsor" + } + ] +} diff --git a/docs/versioned_sidebars/version-1.4.3-sidebars.json b/docs/versioned_sidebars/version-1.4.3-sidebars.json new file mode 100644 index 00000000..98d1c255 --- /dev/null +++ b/docs/versioned_sidebars/version-1.4.3-sidebars.json @@ -0,0 +1,26 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ], + "user_docs": [ + { + "type": "autogenerated", + "dirName": "user_docs" + } + ], + "community": [ + { + "type": "autogenerated", + "dirName": "community" + } + ], + "sponsor": [ + { + "type": "autogenerated", + "dirName": "sponsor" + } + ] +} diff --git a/docs/versions.json b/docs/versions.json new file mode 100644 index 00000000..91c99418 --- /dev/null +++ b/docs/versions.json @@ -0,0 +1,4 @@ +[ + "1.4.3", + "1.4.2" +] diff --git a/hippo4j-adapter/hippo4j-adapter-alibaba-dubbo/pom.xml b/hippo4j-adapter/hippo4j-adapter-alibaba-dubbo/pom.xml index e2413208..bf041aa2 100644 --- a/hippo4j-adapter/hippo4j-adapter-alibaba-dubbo/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-alibaba-dubbo/pom.xml @@ -9,19 +9,17 @@ hippo4j-adapter-alibaba-dubbo - - 2.6.12 - - cn.hippo4j hippo4j-adapter-base + ${project.version} com.alibaba dubbo ${alibaba-dubbo.version} + true diff --git a/hippo4j-adapter/hippo4j-adapter-alibaba-dubbo/src/main/java/cn/hippo4j/adapter/alibaba/dubbo/AlibabaDubboThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-alibaba-dubbo/src/main/java/cn/hippo4j/adapter/alibaba/dubbo/AlibabaDubboThreadPoolAdapter.java index fed0f6ad..914f6def 100644 --- a/hippo4j-adapter/hippo4j-adapter-alibaba-dubbo/src/main/java/cn/hippo4j/adapter/alibaba/dubbo/AlibabaDubboThreadPoolAdapter.java +++ b/hippo4j-adapter/hippo4j-adapter-alibaba-dubbo/src/main/java/cn/hippo4j/adapter/alibaba/dubbo/AlibabaDubboThreadPoolAdapter.java @@ -65,7 +65,7 @@ public class AlibabaDubboThreadPoolAdapter implements ThreadPoolAdapter, Applica @Override public List getThreadPoolStates() { List threadPoolAdapterStates = new ArrayList<>(); - DUBBO_PROTOCOL_EXECUTOR.forEach((kel, val) -> threadPoolAdapterStates.add(getThreadPoolState(String.valueOf(val)))); + DUBBO_PROTOCOL_EXECUTOR.forEach((key, val) -> threadPoolAdapterStates.add(getThreadPoolState(String.valueOf(key)))); return threadPoolAdapterStates; } diff --git a/hippo4j-adapter/hippo4j-adapter-base/src/main/java/cn/hippo4j/adapter/base/ThreadPoolAdapterBeanContainer.java b/hippo4j-adapter/hippo4j-adapter-base/src/main/java/cn/hippo4j/adapter/base/ThreadPoolAdapterBeanContainer.java index 92d555f2..a87b4743 100644 --- a/hippo4j-adapter/hippo4j-adapter-base/src/main/java/cn/hippo4j/adapter/base/ThreadPoolAdapterBeanContainer.java +++ b/hippo4j-adapter/hippo4j-adapter-base/src/main/java/cn/hippo4j/adapter/base/ThreadPoolAdapterBeanContainer.java @@ -29,7 +29,7 @@ import java.util.concurrent.ConcurrentHashMap; public class ThreadPoolAdapterBeanContainer implements InitializingBean { /** - * Store three-party thread pool framework bean instances. + * Store three-party thread pool framework bean instances */ public static final Map THREAD_POOL_ADAPTER_BEAN_CONTAINER = new ConcurrentHashMap<>(); diff --git a/hippo4j-adapter/hippo4j-adapter-base/src/main/java/cn/hippo4j/adapter/base/ThreadPoolAdapterRegisterAction.java b/hippo4j-adapter/hippo4j-adapter-base/src/main/java/cn/hippo4j/adapter/base/ThreadPoolAdapterRegisterAction.java index 0d380f15..0dda7fa3 100644 --- a/hippo4j-adapter/hippo4j-adapter-base/src/main/java/cn/hippo4j/adapter/base/ThreadPoolAdapterRegisterAction.java +++ b/hippo4j-adapter/hippo4j-adapter-base/src/main/java/cn/hippo4j/adapter/base/ThreadPoolAdapterRegisterAction.java @@ -28,7 +28,7 @@ public interface ThreadPoolAdapterRegisterAction { /** * Get thread pool adapter cache configs. * - * @param threadPoolAdapterMap + * @param threadPoolAdapterMap thread-pool adapter map * @return List */ List getThreadPoolAdapterCacheConfigs(Map threadPoolAdapterMap); @@ -36,8 +36,7 @@ public interface ThreadPoolAdapterRegisterAction { /** * Do register. * - * @param cacheConfigList - * @return + * @param cacheConfigList cache config list */ void doRegister(List cacheConfigList); } diff --git a/hippo4j-adapter/hippo4j-adapter-dubbo/pom.xml b/hippo4j-adapter/hippo4j-adapter-dubbo/pom.xml index 1b975be4..4ad5ab2f 100644 --- a/hippo4j-adapter/hippo4j-adapter-dubbo/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-dubbo/pom.xml @@ -9,14 +9,11 @@ hippo4j-adapter-dubbo - - 3.0.5 - - cn.hippo4j hippo4j-adapter-base + ${project.version} org.apache.dubbo diff --git a/hippo4j-adapter/hippo4j-adapter-dubbo/src/main/java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-dubbo/src/main/java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java index f11d8ee1..b1d12ed6 100644 --- a/hippo4j-adapter/hippo4j-adapter-dubbo/src/main/java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java +++ b/hippo4j-adapter/hippo4j-adapter-dubbo/src/main/java/cn/hippo4j/adapter/dubbo/DubboThreadPoolAdapter.java @@ -69,7 +69,7 @@ public class DubboThreadPoolAdapter implements ThreadPoolAdapter, ApplicationLis @Override public List getThreadPoolStates() { List threadPoolAdapterStates = new ArrayList<>(); - DUBBO_PROTOCOL_EXECUTOR.forEach((kel, val) -> threadPoolAdapterStates.add(getThreadPoolState(String.valueOf(val)))); + DUBBO_PROTOCOL_EXECUTOR.forEach((key, val) -> threadPoolAdapterStates.add(getThreadPoolState(String.valueOf(key)))); return threadPoolAdapterStates; } @@ -114,7 +114,7 @@ public class DubboThreadPoolAdapter implements ThreadPoolAdapter, ApplicationLis ConcurrentMap executorServiceMap = data.get(poolKey); executorServiceMap.forEach((key, value) -> DUBBO_PROTOCOL_EXECUTOR.put(String.valueOf(key), (ThreadPoolExecutor) value)); } catch (Exception ex) { - log.error("Failed to get Dubbo {}.X protocol thread pool", isLegacyVersion ? "2" : "3", ex); + log.error("Failed to get Dubbo {} protocol thread pool", Version.getVersion(), ex); } } } diff --git a/hippo4j-adapter/hippo4j-adapter-kafka/pom.xml b/hippo4j-adapter/hippo4j-adapter-dubbox/pom.xml similarity index 78% rename from hippo4j-adapter/hippo4j-adapter-kafka/pom.xml rename to hippo4j-adapter/hippo4j-adapter-dubbox/pom.xml index f193cc75..7404c1f3 100644 --- a/hippo4j-adapter/hippo4j-adapter-kafka/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-dubbox/pom.xml @@ -7,12 +7,13 @@ hippo4j-adapter ${revision} - hippo4j-adapter-kafka + hippo4j-adapter-dubbox cn.hippo4j - hippo4j-adapter-base + hippo4j-adapter-dubbo + ${project.version} diff --git a/hippo4j-server/hippo4j-auth/src/main/java/cn/hippo4j/auth/mapper/RoleMapper.java b/hippo4j-adapter/hippo4j-adapter-dubbox/src/main/java/cn/hippo4j/adapter/dubbo/DubboxThreadPoolAdapter.java similarity index 76% rename from hippo4j-server/hippo4j-auth/src/main/java/cn/hippo4j/auth/mapper/RoleMapper.java rename to hippo4j-adapter/hippo4j-adapter-dubbox/src/main/java/cn/hippo4j/adapter/dubbo/DubboxThreadPoolAdapter.java index abff1ad5..5207bbac 100644 --- a/hippo4j-server/hippo4j-auth/src/main/java/cn/hippo4j/auth/mapper/RoleMapper.java +++ b/hippo4j-adapter/hippo4j-adapter-dubbox/src/main/java/cn/hippo4j/adapter/dubbo/DubboxThreadPoolAdapter.java @@ -15,15 +15,18 @@ * limitations under the License. */ -package cn.hippo4j.auth.mapper; +package cn.hippo4j.adapter.dubbo; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.hippo4j.auth.model.RoleInfo; -import org.apache.ibatis.annotations.Mapper; +import lombok.extern.slf4j.Slf4j; /** - * Role mapper. + * Dubbox thread-pool adapter. */ -@Mapper -public interface RoleMapper extends BaseMapper { +@Slf4j +public class DubboxThreadPoolAdapter extends DubboThreadPoolAdapter { + + @Override + public String mark() { + return "Dubbox"; + } } diff --git a/hippo4j-adapter/hippo4j-adapter-hystrix/pom.xml b/hippo4j-adapter/hippo4j-adapter-hystrix/pom.xml index 8da68229..55553850 100644 --- a/hippo4j-adapter/hippo4j-adapter-hystrix/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-hystrix/pom.xml @@ -13,11 +13,13 @@ cn.hippo4j hippo4j-adapter-base + ${project.version} org.springframework.cloud spring-cloud-starter-netflix-hystrix ${spring-cloud-starter-netflix-hystrix.version} + true diff --git a/hippo4j-adapter/hippo4j-adapter-hystrix/src/main/java/cn/hippo4j/adapter/hystrix/HystrixThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-hystrix/src/main/java/cn/hippo4j/adapter/hystrix/HystrixThreadPoolAdapter.java index ef5002f7..78a50765 100644 --- a/hippo4j-adapter/hippo4j-adapter-hystrix/src/main/java/cn/hippo4j/adapter/hystrix/HystrixThreadPoolAdapter.java +++ b/hippo4j-adapter/hippo4j-adapter-hystrix/src/main/java/cn/hippo4j/adapter/hystrix/HystrixThreadPoolAdapter.java @@ -17,7 +17,11 @@ package cn.hippo4j.adapter.hystrix; -import cn.hippo4j.adapter.base.*; +import cn.hippo4j.adapter.base.ThreadPoolAdapter; +import cn.hippo4j.adapter.base.ThreadPoolAdapterCacheConfig; +import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter; +import cn.hippo4j.adapter.base.ThreadPoolAdapterRegisterAction; +import cn.hippo4j.adapter.base.ThreadPoolAdapterState; import cn.hippo4j.common.config.ApplicationContextHolder; import cn.hippo4j.common.toolkit.CollectionUtil; import com.netflix.hystrix.HystrixThreadPool; @@ -48,9 +52,9 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL private static final String THREAD_POOLS_FIELD = "threadPools"; - private final Map HYSTRIX_CONSUME_EXECUTOR = new HashMap<>(); + private final Map hystrixConsumeExecutor = new HashMap<>(); - private ThreadPoolAdapterScheduler threadPoolAdapterScheduler; + private final ThreadPoolAdapterScheduler threadPoolAdapterScheduler; public HystrixThreadPoolAdapter(ThreadPoolAdapterScheduler threadPoolAdapterScheduler) { this.threadPoolAdapterScheduler = threadPoolAdapterScheduler; @@ -64,7 +68,7 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL @Override public ThreadPoolAdapterState getThreadPoolState(String identify) { ThreadPoolAdapterState result = new ThreadPoolAdapterState(); - ThreadPoolExecutor threadPoolExecutor = HYSTRIX_CONSUME_EXECUTOR.get(identify); + ThreadPoolExecutor threadPoolExecutor = hystrixConsumeExecutor.get(identify); if (threadPoolExecutor != null) { result.setThreadPoolKey(identify); result.setCoreSize(threadPoolExecutor.getCorePoolSize()); @@ -78,14 +82,14 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL @Override public List getThreadPoolStates() { List threadPoolAdapterStates = new ArrayList<>(); - HYSTRIX_CONSUME_EXECUTOR.forEach((kel, val) -> threadPoolAdapterStates.add(getThreadPoolState(kel))); + hystrixConsumeExecutor.forEach((kel, val) -> threadPoolAdapterStates.add(getThreadPoolState(kel))); return threadPoolAdapterStates; } @Override public boolean updateThreadPool(ThreadPoolAdapterParameter threadPoolAdapterParameter) { String threadPoolKey = threadPoolAdapterParameter.getThreadPoolKey(); - ThreadPoolExecutor threadPoolExecutor = HYSTRIX_CONSUME_EXECUTOR.get(threadPoolKey); + ThreadPoolExecutor threadPoolExecutor = hystrixConsumeExecutor.get(threadPoolKey); if (threadPoolExecutor == null) { log.warn("[{}] Hystrix thread pool not found.", threadPoolKey); return false; @@ -135,7 +139,7 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL threadPoolField.setAccessible(true); ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) threadPoolField.get(hystrixThreadPoolDefault); - HYSTRIX_CONSUME_EXECUTOR.put(key, threadPoolExecutor); + hystrixConsumeExecutor.put(key, threadPoolExecutor); } } } @@ -179,7 +183,7 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL private int taskIntervalSeconds; - public HystrixThreadPoolRefreshTask(ScheduledExecutorService scheduler, int taskIntervalSeconds) { + HystrixThreadPoolRefreshTask(ScheduledExecutorService scheduler, int taskIntervalSeconds) { this.scheduler = scheduler; this.taskIntervalSeconds = taskIntervalSeconds; } @@ -196,6 +200,9 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL } } + /** + * Thread Pool Adapter Register Task + */ class ThreadPoolAdapterRegisterTask implements Runnable { private ScheduledExecutorService scheduler; @@ -208,9 +215,9 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL private List cacheConfigList = new ArrayList<>(); - public ThreadPoolAdapterRegisterTask(ScheduledExecutorService scheduler, int taskIntervalSeconds, - Map threadPoolAdapterMap, - ThreadPoolAdapterRegisterAction threadPoolAdapterRegisterAction) { + ThreadPoolAdapterRegisterTask(ScheduledExecutorService scheduler, int taskIntervalSeconds, + Map threadPoolAdapterMap, + ThreadPoolAdapterRegisterAction threadPoolAdapterRegisterAction) { this.scheduler = scheduler; this.taskIntervalSeconds = taskIntervalSeconds; this.threadPoolAdapterMap = threadPoolAdapterMap; diff --git a/hippo4j-adapter/hippo4j-adapter-hystrix/src/main/java/cn/hippo4j/adapter/hystrix/ThreadPoolAdapterScheduler.java b/hippo4j-adapter/hippo4j-adapter-hystrix/src/main/java/cn/hippo4j/adapter/hystrix/ThreadPoolAdapterScheduler.java index d71cf267..143b285e 100644 --- a/hippo4j-adapter/hippo4j-adapter-hystrix/src/main/java/cn/hippo4j/adapter/hystrix/ThreadPoolAdapterScheduler.java +++ b/hippo4j-adapter/hippo4j-adapter-hystrix/src/main/java/cn/hippo4j/adapter/hystrix/ThreadPoolAdapterScheduler.java @@ -18,6 +18,7 @@ package cn.hippo4j.adapter.hystrix; import cn.hippo4j.common.design.builder.ThreadFactoryBuilder; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ScheduledExecutorService; @@ -31,6 +32,7 @@ public class ThreadPoolAdapterScheduler { private static final int TASK_INTERVAL_SECONDS = 10; + @Getter private final ScheduledExecutorService scheduler; public ThreadPoolAdapterScheduler() { @@ -41,10 +43,11 @@ public class ThreadPoolAdapterScheduler { .build()); } - public ScheduledExecutorService getScheduler() { - return scheduler; - } - + /** + * Gt task interval seconds. + * + * @return task interval seconds + */ public int getTaskIntervalSeconds() { return TASK_INTERVAL_SECONDS; } diff --git a/hippo4j-adapter/hippo4j-adapter-rabbitmq/pom.xml b/hippo4j-adapter/hippo4j-adapter-rabbitmq/pom.xml index d477e026..9de38874 100644 --- a/hippo4j-adapter/hippo4j-adapter-rabbitmq/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-rabbitmq/pom.xml @@ -13,10 +13,12 @@ cn.hippo4j hippo4j-adapter-base + ${project.version} org.springframework.boot spring-boot-starter-amqp + true diff --git a/hippo4j-adapter/hippo4j-adapter-rabbitmq/src/main/java/cn/hippo4j/adapter/rabbitmq/RabbitMQThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-rabbitmq/src/main/java/cn/hippo4j/adapter/rabbitmq/RabbitMQThreadPoolAdapter.java index ccaab2b1..86b34f7e 100644 --- a/hippo4j-adapter/hippo4j-adapter-rabbitmq/src/main/java/cn/hippo4j/adapter/rabbitmq/RabbitMQThreadPoolAdapter.java +++ b/hippo4j-adapter/hippo4j-adapter-rabbitmq/src/main/java/cn/hippo4j/adapter/rabbitmq/RabbitMQThreadPoolAdapter.java @@ -27,7 +27,11 @@ import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationListener; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; @@ -42,11 +46,11 @@ public class RabbitMQThreadPoolAdapter implements ThreadPoolAdapter, Application private static final String RABBITMQ = "RabbitMQ"; - private static final String FiledName = "executorService"; + private static final String FILED_NAME = "executorService"; private final Map abstractConnectionFactoryMap; - private final Map RABBITMQ_THREAD_POOL_TASK_EXECUTOR = new HashMap<>(); + private final Map rabbitmqThreadPoolTaskExecutor = new HashMap<>(); @Override public String mark() { @@ -56,7 +60,7 @@ public class RabbitMQThreadPoolAdapter implements ThreadPoolAdapter, Application @Override public ThreadPoolAdapterState getThreadPoolState(String identify) { ThreadPoolAdapterState threadPoolAdapterState = new ThreadPoolAdapterState(); - ThreadPoolExecutor threadPoolTaskExecutor = RABBITMQ_THREAD_POOL_TASK_EXECUTOR.get(identify); + ThreadPoolExecutor threadPoolTaskExecutor = rabbitmqThreadPoolTaskExecutor.get(identify); threadPoolAdapterState.setThreadPoolKey(identify); if (Objects.nonNull(threadPoolTaskExecutor)) { threadPoolAdapterState.setCoreSize(threadPoolTaskExecutor.getCorePoolSize()); @@ -68,7 +72,7 @@ public class RabbitMQThreadPoolAdapter implements ThreadPoolAdapter, Application @Override public List getThreadPoolStates() { List adapterStateList = new ArrayList<>(); - RABBITMQ_THREAD_POOL_TASK_EXECUTOR.forEach( + rabbitmqThreadPoolTaskExecutor.forEach( (key, val) -> adapterStateList.add(getThreadPoolState(key))); return adapterStateList; } @@ -76,7 +80,7 @@ public class RabbitMQThreadPoolAdapter implements ThreadPoolAdapter, Application @Override public boolean updateThreadPool(ThreadPoolAdapterParameter threadPoolAdapterParameter) { String threadPoolKey = threadPoolAdapterParameter.getThreadPoolKey(); - ThreadPoolExecutor threadPoolTaskExecutor = RABBITMQ_THREAD_POOL_TASK_EXECUTOR.get(threadPoolKey); + ThreadPoolExecutor threadPoolTaskExecutor = rabbitmqThreadPoolTaskExecutor.get(threadPoolKey); if (Objects.nonNull(threadPoolTaskExecutor)) { int originalCoreSize = threadPoolTaskExecutor.getCorePoolSize(); int originalMaximumPoolSize = threadPoolTaskExecutor.getMaximumPoolSize(); @@ -95,17 +99,16 @@ public class RabbitMQThreadPoolAdapter implements ThreadPoolAdapter, Application @Override public void onApplicationEvent(ApplicationStartedEvent event) { abstractConnectionFactoryMap.forEach((beanName, abstractConnectionFactor) -> { - ExecutorService executor = (ExecutorService) ReflectUtil.getFieldValue(abstractConnectionFactor, FiledName); + ExecutorService executor = (ExecutorService) ReflectUtil.getFieldValue(abstractConnectionFactor, FILED_NAME); if (Objects.nonNull(executor)) { if (executor instanceof ThreadPoolExecutor) { ThreadPoolExecutor threadPoolTaskExecutor = (ThreadPoolExecutor) executor; - RABBITMQ_THREAD_POOL_TASK_EXECUTOR.put(beanName, threadPoolTaskExecutor); + rabbitmqThreadPoolTaskExecutor.put(beanName, threadPoolTaskExecutor); log.info("Rabbitmq executor name {}", beanName); } else { log.warn("Custom thread pools only support ThreadPoolExecutor"); } } - }); } } diff --git a/hippo4j-adapter/hippo4j-adapter-rocketmq/pom.xml b/hippo4j-adapter/hippo4j-adapter-rocketmq/pom.xml index 5b9451b3..70210369 100644 --- a/hippo4j-adapter/hippo4j-adapter-rocketmq/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-rocketmq/pom.xml @@ -13,11 +13,13 @@ cn.hippo4j hippo4j-adapter-base + ${project.version} org.apache.rocketmq rocketmq-spring-boot-starter ${rocketmq.version} + true diff --git a/hippo4j-adapter/hippo4j-adapter-rocketmq/src/main/java/cn/hippo4j/adapter/rocketmq/RocketMQThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-rocketmq/src/main/java/cn/hippo4j/adapter/rocketmq/RocketMQThreadPoolAdapter.java index 88e2a869..e5541703 100644 --- a/hippo4j-adapter/hippo4j-adapter-rocketmq/src/main/java/cn/hippo4j/adapter/rocketmq/RocketMQThreadPoolAdapter.java +++ b/hippo4j-adapter/hippo4j-adapter-rocketmq/src/main/java/cn/hippo4j/adapter/rocketmq/RocketMQThreadPoolAdapter.java @@ -43,7 +43,7 @@ import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMI @Slf4j public class RocketMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener { - private final Map ROCKET_MQ_CONSUME_EXECUTOR = new HashMap<>(); + private final Map rocketmqConsumeExecutor = new HashMap<>(); @Override public String mark() { @@ -53,7 +53,7 @@ public class RocketMQThreadPoolAdapter implements ThreadPoolAdapter, Application @Override public ThreadPoolAdapterState getThreadPoolState(String identify) { ThreadPoolAdapterState result = new ThreadPoolAdapterState(); - ThreadPoolExecutor rocketMQConsumeExecutor = ROCKET_MQ_CONSUME_EXECUTOR.get(identify); + ThreadPoolExecutor rocketMQConsumeExecutor = rocketmqConsumeExecutor.get(identify); if (rocketMQConsumeExecutor != null) { result.setThreadPoolKey(identify); result.setCoreSize(rocketMQConsumeExecutor.getCorePoolSize()); @@ -67,7 +67,7 @@ public class RocketMQThreadPoolAdapter implements ThreadPoolAdapter, Application @Override public List getThreadPoolStates() { List adapterStateList = new ArrayList<>(); - ROCKET_MQ_CONSUME_EXECUTOR.forEach( + rocketmqConsumeExecutor.forEach( (key, val) -> adapterStateList.add(getThreadPoolState(key))); return adapterStateList; } @@ -75,7 +75,7 @@ public class RocketMQThreadPoolAdapter implements ThreadPoolAdapter, Application @Override public boolean updateThreadPool(ThreadPoolAdapterParameter threadPoolAdapterParameter) { String threadPoolKey = threadPoolAdapterParameter.getThreadPoolKey(); - ThreadPoolExecutor rocketMQConsumeExecutor = ROCKET_MQ_CONSUME_EXECUTOR.get(threadPoolKey); + ThreadPoolExecutor rocketMQConsumeExecutor = rocketmqConsumeExecutor.get(threadPoolKey); if (rocketMQConsumeExecutor != null) { int originalCoreSize = rocketMQConsumeExecutor.getCorePoolSize(); int originalMaximumPoolSize = rocketMQConsumeExecutor.getMaximumPoolSize(); @@ -101,7 +101,7 @@ public class RocketMQThreadPoolAdapter implements ThreadPoolAdapter, Application if (defaultMQPushConsumer != null) { ConsumeMessageService consumeMessageService = defaultMQPushConsumer.getDefaultMQPushConsumerImpl().getConsumeMessageService(); ThreadPoolExecutor consumeExecutor = (ThreadPoolExecutor) ReflectUtil.getFieldValue(consumeMessageService, "consumeExecutor"); - ROCKET_MQ_CONSUME_EXECUTOR.put(container.getConsumerGroup(), consumeExecutor); + rocketmqConsumeExecutor.put(container.getConsumerGroup(), consumeExecutor); } } } catch (Exception ex) { diff --git a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-kafka/pom.xml b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-kafka/pom.xml deleted file mode 100644 index 25be8e39..00000000 --- a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-kafka/pom.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - 4.0.0 - - cn.hippo4j - hippo4j-adapter - ${revision} - - hippo4j-adapter-spring-cloud-stream-kafka - - - - cn.hippo4j - hippo4j-adapter-base - - - diff --git a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-kafka/src/main/java/cn/hippo4j/adapter/springcloud/stream/kafka/SpringCloudStreamKafkaThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-kafka/src/main/java/cn/hippo4j/adapter/springcloud/stream/kafka/SpringCloudStreamKafkaThreadPoolAdapter.java deleted file mode 100644 index 4cc49356..00000000 --- a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-kafka/src/main/java/cn/hippo4j/adapter/springcloud/stream/kafka/SpringCloudStreamKafkaThreadPoolAdapter.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package cn.hippo4j.adapter.springcloud.stream.kafka; - -import cn.hippo4j.adapter.base.ThreadPoolAdapter; -import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter; -import cn.hippo4j.adapter.base.ThreadPoolAdapterState; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.context.ApplicationListener; - -import java.util.List; - -/** - * Spring cloud stream kafka thread-pool adapter. - */ -@Slf4j -public class SpringCloudStreamKafkaThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener { - - @Override - public String mark() { - return "KafkaSpringCloudStream"; - } - - @Override - public ThreadPoolAdapterState getThreadPoolState(String identify) { - return null; - } - - @Override - public List getThreadPoolStates() { - return null; - } - - @Override - public boolean updateThreadPool(ThreadPoolAdapterParameter threadPoolAdapterParameter) { - return false; - } - - @Override - public void onApplicationEvent(ApplicationStartedEvent event) { - - } -} diff --git a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rabbitmq/pom.xml b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rabbitmq/pom.xml index ffce64e8..3c4e1be6 100644 --- a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rabbitmq/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rabbitmq/pom.xml @@ -13,11 +13,13 @@ cn.hippo4j hippo4j-adapter-base + ${project.version} org.springframework.cloud spring-cloud-starter-stream-rabbit ${spring-cloud-starter-stream-rabbitmq.version} + true diff --git a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rabbitmq/src/main/java/cn/hippo4j/adapter/springcloud/stream/rabbitmq/SpringCloudStreamRabbitMQThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rabbitmq/src/main/java/cn/hippo4j/adapter/springcloud/stream/rabbitmq/SpringCloudStreamRabbitMQThreadPoolAdapter.java index cb04cf6b..d5b3326c 100644 --- a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rabbitmq/src/main/java/cn/hippo4j/adapter/springcloud/stream/rabbitmq/SpringCloudStreamRabbitMQThreadPoolAdapter.java +++ b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rabbitmq/src/main/java/cn/hippo4j/adapter/springcloud/stream/rabbitmq/SpringCloudStreamRabbitMQThreadPoolAdapter.java @@ -34,17 +34,22 @@ import org.springframework.cloud.stream.binding.InputBindingLifecycle; import org.springframework.context.ApplicationListener; import org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMITER; /** - * Spring cloud stream rabbimq thread-pool adapter. + * Spring cloud stream rabbit-mq thread-pool adapter. */ @Slf4j public class SpringCloudStreamRabbitMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener { - private final Map ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR = new HashMap<>(); + private final Map rocketMqSpringCloudStreamConsumeExecutor = new HashMap<>(); @Override public String mark() { @@ -54,7 +59,7 @@ public class SpringCloudStreamRabbitMQThreadPoolAdapter implements ThreadPoolAda @Override public ThreadPoolAdapterState getThreadPoolState(String identify) { ThreadPoolAdapterState result = new ThreadPoolAdapterState(); - AbstractMessageListenerContainer messageListenerContainer = ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.get(identify); + AbstractMessageListenerContainer messageListenerContainer = rocketMqSpringCloudStreamConsumeExecutor.get(identify); if (messageListenerContainer != null) { result.setThreadPoolKey(identify); if (messageListenerContainer instanceof SimpleMessageListenerContainer) { @@ -80,7 +85,7 @@ public class SpringCloudStreamRabbitMQThreadPoolAdapter implements ThreadPoolAda @Override public List getThreadPoolStates() { List adapterStateList = new ArrayList<>(); - ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.forEach( + rocketMqSpringCloudStreamConsumeExecutor.forEach( (key, val) -> adapterStateList.add(getThreadPoolState(key))); return adapterStateList; } @@ -88,9 +93,9 @@ public class SpringCloudStreamRabbitMQThreadPoolAdapter implements ThreadPoolAda @Override public boolean updateThreadPool(ThreadPoolAdapterParameter threadPoolAdapterParameter) { String threadPoolKey = threadPoolAdapterParameter.getThreadPoolKey(); - AbstractMessageListenerContainer messageListenerContainer = ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.get(threadPoolKey); + AbstractMessageListenerContainer messageListenerContainer = rocketMqSpringCloudStreamConsumeExecutor.get(threadPoolKey); if (messageListenerContainer != null) { - synchronized (ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR) { + synchronized (rocketMqSpringCloudStreamConsumeExecutor) { Integer corePoolSize = threadPoolAdapterParameter.getCorePoolSize(); Integer maximumPoolSize = threadPoolAdapterParameter.getMaximumPoolSize(); if (messageListenerContainer instanceof SimpleMessageListenerContainer) { @@ -148,7 +153,7 @@ public class SpringCloudStreamRabbitMQThreadPoolAdapter implements ThreadPoolAda Object lifecycle = ReflectUtil.getFieldValue(defaultBinding, "lifecycle"); if (lifecycle instanceof AmqpInboundChannelAdapter) { AbstractMessageListenerContainer rabbitMQListenerContainer = (AbstractMessageListenerContainer) ReflectUtil.getFieldValue(lifecycle, "messageListenerContainer"); - ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.put(bindingName, rabbitMQListenerContainer); + rocketMqSpringCloudStreamConsumeExecutor.put(bindingName, rabbitMQListenerContainer); } } } catch (Exception ex) { diff --git a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rocketmq/pom.xml b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rocketmq/pom.xml index 1227e7bf..95e96968 100644 --- a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rocketmq/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rocketmq/pom.xml @@ -13,11 +13,13 @@ cn.hippo4j hippo4j-adapter-base + ${project.version} com.alibaba.cloud spring-cloud-starter-stream-rocketmq ${spring-cloud-starter-stream-rocketmq.version} + true diff --git a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rocketmq/src/main/java/cn/hippo4j/adapter/springcloud/stream/rocketmq/SpringCloudStreamRocketMQThreadPoolAdapter.java b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rocketmq/src/main/java/cn/hippo4j/adapter/springcloud/stream/rocketmq/SpringCloudStreamRocketMQThreadPoolAdapter.java index b71d7aac..60d35416 100644 --- a/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rocketmq/src/main/java/cn/hippo4j/adapter/springcloud/stream/rocketmq/SpringCloudStreamRocketMQThreadPoolAdapter.java +++ b/hippo4j-adapter/hippo4j-adapter-spring-cloud-stream-rocketmq/src/main/java/cn/hippo4j/adapter/springcloud/stream/rocketmq/SpringCloudStreamRocketMQThreadPoolAdapter.java @@ -35,7 +35,12 @@ import org.springframework.cloud.stream.binder.DefaultBinding; import org.springframework.cloud.stream.binding.InputBindingLifecycle; import org.springframework.context.ApplicationListener; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.ThreadPoolExecutor; import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMITER; @@ -46,7 +51,7 @@ import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMI @Slf4j public class SpringCloudStreamRocketMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener { - private final Map ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR = new HashMap<>(); + private final Map rocketMqSpringCloudStreamConsumeExecutor = new HashMap<>(); @Override public String mark() { @@ -56,7 +61,7 @@ public class SpringCloudStreamRocketMQThreadPoolAdapter implements ThreadPoolAda @Override public ThreadPoolAdapterState getThreadPoolState(String identify) { ThreadPoolAdapterState result = new ThreadPoolAdapterState(); - ThreadPoolExecutor rocketMQConsumeExecutor = ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.get(identify); + ThreadPoolExecutor rocketMQConsumeExecutor = rocketMqSpringCloudStreamConsumeExecutor.get(identify); if (rocketMQConsumeExecutor != null) { result.setThreadPoolKey(identify); result.setCoreSize(rocketMQConsumeExecutor.getCorePoolSize()); @@ -70,7 +75,7 @@ public class SpringCloudStreamRocketMQThreadPoolAdapter implements ThreadPoolAda @Override public List getThreadPoolStates() { List adapterStateList = new ArrayList<>(); - ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.forEach( + rocketMqSpringCloudStreamConsumeExecutor.forEach( (key, val) -> adapterStateList.add(getThreadPoolState(key))); return adapterStateList; } @@ -78,7 +83,7 @@ public class SpringCloudStreamRocketMQThreadPoolAdapter implements ThreadPoolAda @Override public boolean updateThreadPool(ThreadPoolAdapterParameter threadPoolAdapterParameter) { String threadPoolKey = threadPoolAdapterParameter.getThreadPoolKey(); - ThreadPoolExecutor rocketMQConsumeExecutor = ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.get(threadPoolKey); + ThreadPoolExecutor rocketMQConsumeExecutor = rocketMqSpringCloudStreamConsumeExecutor.get(threadPoolKey); if (rocketMQConsumeExecutor != null) { int originalCoreSize = rocketMQConsumeExecutor.getCorePoolSize(); int originalMaximumPoolSize = rocketMQConsumeExecutor.getMaximumPoolSize(); @@ -111,7 +116,7 @@ public class SpringCloudStreamRocketMQThreadPoolAdapter implements ThreadPoolAda DefaultMQPushConsumerImpl defaultMQPushConsumerImpl = consumer.getDefaultMQPushConsumerImpl(); ConsumeMessageConcurrentlyService consumeMessageService = (ConsumeMessageConcurrentlyService) defaultMQPushConsumerImpl.getConsumeMessageService(); ThreadPoolExecutor consumeExecutor = (ThreadPoolExecutor) ReflectUtil.getFieldValue(consumeMessageService, "consumeExecutor"); - ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.put(bindingName, consumeExecutor); + rocketMqSpringCloudStreamConsumeExecutor.put(bindingName, consumeExecutor); } } catch (Exception ex) { log.error("Failed to get input-bindings thread pool.", ex); diff --git a/hippo4j-adapter/hippo4j-adapter-web/pom.xml b/hippo4j-adapter/hippo4j-adapter-web/pom.xml index 8563db72..b6e98183 100644 --- a/hippo4j-adapter/hippo4j-adapter-web/pom.xml +++ b/hippo4j-adapter/hippo4j-adapter-web/pom.xml @@ -31,10 +31,6 @@ spring-boot-starter-undertow true - - org.projectlombok - lombok - org.springframework.boot spring-boot-starter @@ -42,6 +38,7 @@ cn.hippo4j hippo4j-core + ${project.version} diff --git a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/AbstractWebThreadPoolService.java b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/AbstractWebThreadPoolService.java index 771d6699..bafdf678 100644 --- a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/AbstractWebThreadPoolService.java +++ b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/AbstractWebThreadPoolService.java @@ -34,12 +34,12 @@ import java.util.concurrent.Executor; public abstract class AbstractWebThreadPoolService implements WebThreadPoolService, ApplicationRunner { /** - * Thread pool executor. + * Thread pool executor */ protected volatile Executor executor; /** - * Get web thread pool by server. + * Get web thread pool by server * * @param webServer * @return diff --git a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/TomcatWebThreadPoolHandler.java b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/TomcatWebThreadPoolHandler.java index c15cf7a6..33a4d626 100644 --- a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/TomcatWebThreadPoolHandler.java +++ b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/TomcatWebThreadPoolHandler.java @@ -31,7 +31,6 @@ import org.springframework.boot.web.server.WebServer; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.Date; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; @@ -47,14 +46,14 @@ public class TomcatWebThreadPoolHandler extends AbstractWebThreadPoolService { private final AtomicBoolean cacheFlag = new AtomicBoolean(Boolean.FALSE); - private static String EXCEPTION_MESSAGE; + private static String exceptionMessage; private final AbstractThreadPoolRuntime webThreadPoolRunStateHandler; @Override protected Executor getWebThreadPoolByServer(WebServer webServer) { if (cacheFlag.get()) { - log.warn("Exception getting Tomcat thread pool. Exception message: {}", EXCEPTION_MESSAGE); + log.warn("Exception getting Tomcat thread pool. Exception message: {}", exceptionMessage); return null; } Executor tomcatExecutor = null; @@ -62,8 +61,8 @@ public class TomcatWebThreadPoolHandler extends AbstractWebThreadPoolService { tomcatExecutor = ((TomcatWebServer) webServer).getTomcat().getConnector().getProtocolHandler().getExecutor(); } catch (Exception ex) { cacheFlag.set(Boolean.TRUE); - EXCEPTION_MESSAGE = ex.getMessage(); - log.error("Failed to get Tomcat thread pool. Message: {}", EXCEPTION_MESSAGE); + exceptionMessage = ex.getMessage(); + log.error("Failed to get Tomcat thread pool. Message: {}", exceptionMessage); } return tomcatExecutor; } diff --git a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebIpAndPortHolder.java b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebIpAndPortHolder.java index 04de2355..687e3eee 100644 --- a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebIpAndPortHolder.java +++ b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebIpAndPortHolder.java @@ -24,6 +24,7 @@ import cn.hippo4j.common.toolkit.StringUtil; import cn.hippo4j.core.toolkit.inet.InetUtils; import lombok.NoArgsConstructor; import org.springframework.boot.web.server.WebServer; +import org.springframework.core.env.ConfigurableEnvironment; import java.util.Arrays; import java.util.Objects; @@ -35,12 +36,12 @@ import java.util.concurrent.atomic.AtomicReference; @NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) public class WebIpAndPortHolder { - private static boolean SUPPORT_VERSION = false; + private static boolean supportVersion = false; static { try { Class.forName("org.springframework.boot.web.server.WebServer"); - SUPPORT_VERSION = true; + supportVersion = true; } catch (Exception ignored) { } } @@ -48,17 +49,27 @@ public class WebIpAndPortHolder { /** * Application ip and application post */ - protected static AtomicReference WEB_IP_AND_PORT = new AtomicReference<>(); + protected static AtomicReference webIpAndPort = new AtomicReference<>(); public static final String ALL = "*"; protected static final String SEPARATOR = ","; + /** + * get port for Environment + */ + protected static final String PORT_KEY = "server.port"; + + /** + * if port is null, use this + */ + protected static final int PORT = 8080; + protected static void initIpAndPort() { - if (!SUPPORT_VERSION) { + if (!supportVersion) { return; } - WEB_IP_AND_PORT.compareAndSet(null, getWebIpAndPortInfo()); + webIpAndPort.compareAndSet(null, getWebIpAndPortInfo()); } private static WebIpAndPortInfo getWebIpAndPortInfo() { @@ -66,12 +77,20 @@ public class WebIpAndPortHolder { InetUtils.HostInfo loopBackHostInfo = inetUtils.findFirstNonLoopBackHostInfo(); Assert.notNull(loopBackHostInfo, "Unable to get the application IP address"); String ip = loopBackHostInfo.getIpAddress(); - WebThreadPoolHandlerChoose webThreadPoolHandlerChoose = ApplicationContextHolder.getBean(WebThreadPoolHandlerChoose.class); - WebThreadPoolService webThreadPoolService = webThreadPoolHandlerChoose.choose(); - // When get the port at startup, can get the message: "port xxx was already in use" or use two ports - WebServer webServer = webThreadPoolService.getWebServer(); - String port = String.valueOf(webServer.getPort()); - return new WebIpAndPortInfo(ip, port); + + ConfigurableEnvironment environment = ApplicationContextHolder.getBean(ConfigurableEnvironment.class); + Integer port = environment.getProperty(PORT_KEY, Integer.TYPE); + port = Objects.isNull(port) ? PORT : port; + + if (port == 0) { + WebThreadPoolHandlerChoose webThreadPoolHandlerChoose = ApplicationContextHolder.getBean(WebThreadPoolHandlerChoose.class); + WebThreadPoolService webThreadPoolService = webThreadPoolHandlerChoose.choose(); + // When get the port at startup, can get the message: "port xxx was already in use" or use two ports + WebServer webServer = webThreadPoolService.getWebServer(); + port = webServer.getPort(); + } + + return new WebIpAndPortInfo(ip, String.valueOf(port)); } /** @@ -80,10 +99,10 @@ public class WebIpAndPortHolder { * @return Web ip and port info */ public static WebIpAndPortInfo getWebIpAndPort() { - if (WEB_IP_AND_PORT.get() == null) { + if (webIpAndPort.get() == null) { initIpAndPort(); } - return WebIpAndPortHolder.WEB_IP_AND_PORT.get(); + return WebIpAndPortHolder.webIpAndPort.get(); } /** @@ -104,4 +123,4 @@ public class WebIpAndPortHolder { .filter(Objects::nonNull) .anyMatch(each -> each.check(webIpAndPort.getIpSegment(), webIpAndPort.getPort())); } -} \ No newline at end of file +} diff --git a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebThreadPoolHandlerChoose.java b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebThreadPoolHandlerChoose.java index f88f4d54..1d94d379 100644 --- a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebThreadPoolHandlerChoose.java +++ b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebThreadPoolHandlerChoose.java @@ -30,7 +30,7 @@ public class WebThreadPoolHandlerChoose { /** * Choose the web thread pool service bean. * - * @return + * @return web thread pool service bean */ public WebThreadPoolService choose() { WebThreadPoolService webThreadPoolService; diff --git a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebThreadPoolRunStateHandler.java b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebThreadPoolRunStateHandler.java index 2727e52c..a30c16c8 100644 --- a/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebThreadPoolRunStateHandler.java +++ b/hippo4j-adapter/hippo4j-adapter-web/src/main/java/cn/hippo4j/adapter/web/WebThreadPoolRunStateHandler.java @@ -35,9 +35,9 @@ public class WebThreadPoolRunStateHandler extends AbstractThreadPoolRuntime { long used = MemoryUtil.heapMemoryUsed(); long max = MemoryUtil.heapMemoryMax(); String memoryProportion = StringUtil.newBuilder( - "已分配: ", + "Allocation: ", ByteConvertUtil.getPrintSize(used), - " / 最大可用: ", + " / Maximum available: ", ByteConvertUtil.getPrintSize(max)); poolRunStateInfo.setCurrentLoad(poolRunStateInfo.getCurrentLoad() + "%"); poolRunStateInfo.setPeakLoad(poolRunStateInfo.getPeakLoad() + "%"); diff --git a/hippo4j-adapter/pom.xml b/hippo4j-adapter/pom.xml index d1c0624a..e65296d8 100644 --- a/hippo4j-adapter/pom.xml +++ b/hippo4j-adapter/pom.xml @@ -13,13 +13,12 @@ hippo4j-adapter-base hippo4j-adapter-dubbo + hippo4j-adapter-dubbox hippo4j-adapter-alibaba-dubbo - hippo4j-adapter-kafka hippo4j-adapter-rabbitmq hippo4j-adapter-rocketmq hippo4j-adapter-hystrix hippo4j-adapter-spring-cloud-stream-rocketmq - hippo4j-adapter-spring-cloud-stream-kafka hippo4j-adapter-spring-cloud-stream-rabbitmq hippo4j-adapter-web diff --git a/hippo4j-common/pom.xml b/hippo4j-common/pom.xml index 36d6d3b1..5f56c500 100644 --- a/hippo4j-common/pom.xml +++ b/hippo4j-common/pom.xml @@ -57,10 +57,15 @@ com.github.ben-manes.caffeine caffeine + ${caffeine.version} com.github.dozermapper dozer-core + + org.springframework.boot + spring-boot-configuration-processor + diff --git a/hippo4j-message/src/main/java/cn/hippo4j/message/request/base/NotifyRequest.java b/hippo4j-common/src/main/java/cn/hippo4j/common/api/NotifyRequest.java similarity index 95% rename from hippo4j-message/src/main/java/cn/hippo4j/message/request/base/NotifyRequest.java rename to hippo4j-common/src/main/java/cn/hippo4j/common/api/NotifyRequest.java index 13e0da92..9b2b71ee 100644 --- a/hippo4j-message/src/main/java/cn/hippo4j/message/request/base/NotifyRequest.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/api/NotifyRequest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.message.request.base; +package cn.hippo4j.common.api; /** * Notify request. diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolCheckAlarm.java b/hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolCheckAlarm.java new file mode 100644 index 00000000..4b3a3f2a --- /dev/null +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolCheckAlarm.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.common.api; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * Thread-pol check alarm. + * + *

Dynamic thread pool check and send logic wait for refactoring, + * Try not to rely on this component for custom extensions, because it is undefined. + */ +public interface ThreadPoolCheckAlarm extends CommandLineRunner { + + /** + * Get a none thread pool check alarm. + * + * @return {@link ThreadPoolCheckAlarm} + * @see NoneThreadPoolCheckAlarm + */ + static ThreadPoolCheckAlarm none() { + return NoneThreadPoolCheckAlarm.INSTANCE; + } + + /** + * Check pool capacity alarm. + * + * @param threadPoolId thread-pool id + * @param threadPoolExecutor thread-pool executor + */ + void checkPoolCapacityAlarm(String threadPoolId, ThreadPoolExecutor threadPoolExecutor); + + /** + * Check pool activity alarm. + * + * @param threadPoolId thread-pool id + * @param threadPoolExecutor thread-pool executor + */ + void checkPoolActivityAlarm(String threadPoolId, ThreadPoolExecutor threadPoolExecutor); + + /** + * Async send rejected alarm. + * + * @param threadPoolId thread-pool id + */ + void asyncSendRejectedAlarm(String threadPoolId); + + /** + * Async send execute time-out alarm. + * + * @param threadPoolId thread-pool id + * @param executeTime execute time + * @param executeTimeOut execute time-out + * @param threadPoolExecutor thread-pool executor + */ + void asyncSendExecuteTimeOutAlarm(String threadPoolId, long executeTime, long executeTimeOut, ThreadPoolExecutor threadPoolExecutor); + + /** + * None implementation of {@link ThreadPoolCheckAlarm}. + * + * @see #none() + */ + @Slf4j + @NoArgsConstructor(access = AccessLevel.PRIVATE) + class NoneThreadPoolCheckAlarm implements ThreadPoolCheckAlarm { + + /** + * Default singleton. + */ + private static final NoneThreadPoolCheckAlarm INSTANCE = new NoneThreadPoolCheckAlarm(); + + /** + * Check pool capacity alarm. + * + * @param threadPoolId thread-pool id + * @param threadPoolExecutor thread-pool executor + */ + @Override + public void checkPoolCapacityAlarm(String threadPoolId, ThreadPoolExecutor threadPoolExecutor) { + log.info("Ignore check pool capacity alarm for ExecuteService '{}'", threadPoolId); + } + + /** + * Check pool activity alarm. + * + * @param threadPoolId thread-pool id + * @param threadPoolExecutor thread-pool executor + */ + @Override + public void checkPoolActivityAlarm(String threadPoolId, ThreadPoolExecutor threadPoolExecutor) { + log.info("Ignore check pool activity alarm for ExecuteService '{}'", threadPoolId); + } + + /** + * Async send rejected alarm. + * + * @param threadPoolId thread-pool id + */ + @Override + public void asyncSendRejectedAlarm(String threadPoolId) { + log.info("Ignore async send rejected alarm for ExecuteService '{}'", threadPoolId); + } + + /** + * Async send execute time-out alarm. + * + * @param threadPoolId thread-pool id + * @param executeTime execute time + * @param executeTimeOut execute time-out + * @param threadPoolExecutor thread-pool executor + */ + @Override + public void asyncSendExecuteTimeOutAlarm(String threadPoolId, long executeTime, long executeTimeOut, ThreadPoolExecutor threadPoolExecutor) { + log.info("Ignore async send execute time out alarm for ExecuteService '{}'", threadPoolId); + } + + @Override + public void run(String... args) throws Exception { + // do nothing + } + } +} diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolConfigChange.java b/hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolConfigChange.java new file mode 100644 index 00000000..8086240e --- /dev/null +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolConfigChange.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.common.api; + +/** + * Thread-pool config change. + */ +public interface ThreadPoolConfigChange { + + /** + * Send pool config change. + * + * @param requestParam request param + */ + void sendPoolConfigChange(T requestParam); +} diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/constant/ConfigModifyTypeConstants.java b/hippo4j-common/src/main/java/cn/hippo4j/common/constant/ConfigModifyTypeConstants.java index 84a8bf61..deb4465c 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/constant/ConfigModifyTypeConstants.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/constant/ConfigModifyTypeConstants.java @@ -17,6 +17,9 @@ package cn.hippo4j.common.constant; +/** + * Config modify type constants. + */ public class ConfigModifyTypeConstants { /** diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/constant/Constants.java b/hippo4j-common/src/main/java/cn/hippo4j/common/constant/Constants.java index 8c225e33..32b79cce 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/constant/Constants.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/constant/Constants.java @@ -38,6 +38,8 @@ public class Constants { public static final String DEFAULT_NAMESPACE_ID = "public"; + public static final String ADMIN_USER = "admin"; + public static final String ENCODE = "UTF-8"; public static final String NULL = ""; @@ -109,4 +111,6 @@ public class Constants { public static final String EXECUTE_TIMEOUT_TRACE = "executeTimeoutTrace"; public static final int HTTP_EXECUTE_TIMEOUT = 5000; + + public static final String CLIENT_VERSION = "Client-Version"; } diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/constant/HttpResponseCode.java b/hippo4j-common/src/main/java/cn/hippo4j/common/constant/HttpResponseCode.java index e063e6a8..72e9df94 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/constant/HttpResponseCode.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/constant/HttpResponseCode.java @@ -22,87 +22,202 @@ package cn.hippo4j.common.constant; * * @author Rongzhen Yan */ -public interface HttpResponseCode { - - int SC_CONTINUE = 100; - - int SC_SWITCHING_PROTOCOLS = 101; - - int SC_OK = 200; - - int SC_CREATED = 201; - - int SC_ACCEPTED = 202; - - int SC_NON_AUTHORITATIVE_INFORMATION = 203; - - int SC_NO_CONTENT = 204; - - int SC_RESET_CONTENT = 205; - - int SC_PARTIAL_CONTENT = 206; - - int SC_MULTIPLE_CHOICES = 300; - - int SC_MOVED_PERMANENTLY = 301; - - int SC_MOVED_TEMPORARILY = 302; - - int SC_FOUND = 302; - - int SC_SEE_OTHER = 303; - - int SC_NOT_MODIFIED = 304; - - int SC_USE_PROXY = 305; - - int SC_TEMPORARY_REDIRECT = 307; - - int SC_BAD_REQUEST = 400; - - int SC_UNAUTHORIZED = 401; - - int SC_PAYMENT_REQUIRED = 402; - - int SC_FORBIDDEN = 403; - - int SC_NOT_FOUND = 404; - - int SC_METHOD_NOT_ALLOWED = 405; - - int SC_NOT_ACCEPTABLE = 406; - - int SC_PROXY_AUTHENTICATION_REQUIRED = 407; - - int SC_REQUEST_TIMEOUT = 408; - - int SC_CONFLICT = 409; - - int SC_GONE = 410; - - int SC_LENGTH_REQUIRED = 411; - - int SC_PRECONDITION_FAILED = 412; - - int SC_REQUEST_ENTITY_TOO_LARGE = 413; - - int SC_REQUEST_URI_TOO_LONG = 414; - - int SC_UNSUPPORTED_MEDIA_TYPE = 415; - - int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; - - int SC_EXPECTATION_FAILED = 417; - - int SC_INTERNAL_SERVER_ERROR = 500; - - int SC_NOT_IMPLEMENTED = 501; - - int SC_BAD_GATEWAY = 502; - - int SC_SERVICE_UNAVAILABLE = 503; - - int SC_GATEWAY_TIMEOUT = 504; - - int SC_HTTP_VERSION_NOT_SUPPORTED = 505; +public class HttpResponseCode { + + /* 2XX: generally "OK" */ + + /** + * HTTP Status-Code 200: OK. + */ + public static final int SC_OK = 200; + + /** + * HTTP Status-Code 201: Created. + */ + public static final int SC_HTTP_CREATED = 201; + + /** + * HTTP Status-Code 202: Accepted. + */ + public static final int SC_HTTP_ACCEPTED = 202; + + /** + * HTTP Status-Code 203: Non-Authoritative Information. + */ + public static final int SC_HTTP_NOT_AUTHORITATIVE = 203; + + /** + * HTTP Status-Code 204: No Content. + */ + public static final int SC_HTTP_NO_CONTENT = 204; + + /** + * HTTP Status-Code 205: Reset Content. + */ + public static final int SC_HTTP_RESET = 205; + + /** + * HTTP Status-Code 206: Partial Content. + */ + public static final int SC_HTTP_PARTIAL = 206; + + /* 3XX: relocation/redirect */ + + /** + * HTTP Status-Code 300: Multiple Choices. + */ + public static final int SC_HTTP_MULT_CHOICE = 300; + + /** + * HTTP Status-Code 301: Moved Permanently. + */ + public static final int SC_HTTP_MOVED_PERM = 301; + + /** + * HTTP Status-Code 302: Temporary Redirect. + */ + public static final int SC_HTTP_MOVED_TEMP = 302; + + /** + * HTTP Status-Code 303: See Other. + */ + public static final int SC_HTTP_SEE_OTHER = 303; + + /** + * HTTP Status-Code 304: Not Modified. + */ + public static final int SC_HTTP_NOT_MODIFIED = 304; + + /** + * HTTP Status-Code 305: Use Proxy. + */ + public static final int SC_HTTP_USE_PROXY = 305; + + /** + * HTTP 1.1 Status-Code 307: Temporary Redirect. + */ + public static final int SC_HTTP_TEMP_REDIRECT = 307; + + /** + * HTTP 1.1 Status-Code 308: Permanent Redirect + */ + public static final int SC_HTTP_PERMANENT_REDIRECT = 308; + + /* 4XX: client error */ + + /** + * HTTP Status-Code 400: Bad Request. + */ + public static final int SC_HTTP_BAD_REQUEST = 400; + + /** + * HTTP Status-Code 401: Unauthorized. + */ + public static final int SC_HTTP_UNAUTHORIZED = 401; + + /** + * HTTP Status-Code 402: Payment Required. + */ + public static final int SC_HTTP_PAYMENT_REQUIRED = 402; + + /** + * HTTP Status-Code 403: Forbidden. + */ + public static final int SC_HTTP_FORBIDDEN = 403; + + /** + * HTTP Status-Code 404: Not Found. + */ + public static final int SC_HTTP_NOT_FOUND = 404; + + /** + * HTTP Status-Code 405: Method Not Allowed. + */ + public static final int SC_HTTP_BAD_METHOD = 405; + + /** + * HTTP Status-Code 406: Not Acceptable. + */ + public static final int SC_HTTP_NOT_ACCEPTABLE = 406; + + /** + * HTTP Status-Code 407: Proxy Authentication Required. + */ + public static final int SC_HTTP_PROXY_AUTH = 407; + + /** + * HTTP Status-Code 408: Request Time-Out. + */ + public static final int SC_HTTP_CLIENT_TIMEOUT = 408; + + /** + * HTTP Status-Code 409: Conflict. + */ + public static final int SC_HTTP_CONFLICT = 409; + + /** + * HTTP Status-Code 410: Gone. + */ + public static final int SC_HTTP_GONE = 410; + + /** + * HTTP Status-Code 411: Length Required. + */ + public static final int SC_HTTP_LENGTH_REQUIRED = 411; + + /** + * HTTP Status-Code 412: Precondition Failed. + */ + public static final int SC_HTTP_PRECON_FAILED = 412; + + /** + * HTTP Status-Code 413: Request Entity Too Large. + */ + public static final int SC_HTTP_ENTITY_TOO_LARGE = 413; + + /** + * HTTP Status-Code 414: Request-URI Too Large. + */ + public static final int SC_HTTP_REQ_TOO_LONG = 414; + + /** + * HTTP Status-Code 415: Unsupported Media Type. + */ + public static final int SC_HTTP_UNSUPPORTED_TYPE = 415; + + /* 5XX: server error */ + + /** + * HTTP Status-Code 500: Internal Server Error. + */ + public static final int SC_HTTP_INTERNAL_ERROR = 500; + + /** + * HTTP Status-Code 501: Not Implemented. + */ + public static final int SC_HTTP_NOT_IMPLEMENTED = 501; + + /** + * HTTP Status-Code 502: Bad Gateway. + */ + public static final int SC_HTTP_BAD_GATEWAY = 502; + + /** + * HTTP Status-Code 503: Service Unavailable. + */ + public static final int SC_HTTP_UNAVAILABLE = 503; + + /** + * HTTP Status-Code 504: Gateway Timeout. + */ + public static final int SC_HTTP_GATEWAY_TIMEOUT = 504; + + /** + * HTTP Status-Code 505: HTTP Version Not Supported. + */ + public static final int SC_HTTP_VERSION = 505; + + public static boolean isOk(int code) { + return (code >= SC_OK && code < SC_HTTP_MULT_CHOICE) || code == SC_HTTP_NOT_MODIFIED; + } } diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/executor/ThreadPoolManager.java b/hippo4j-common/src/main/java/cn/hippo4j/common/executor/ThreadPoolManager.java index b38d104f..aa3be5bd 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/executor/ThreadPoolManager.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/executor/ThreadPoolManager.java @@ -23,12 +23,11 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; /** * Thread-pool manager. */ -public class ThreadPoolManager { +public final class ThreadPoolManager { private Map>> resourcesManager; @@ -36,8 +35,6 @@ public class ThreadPoolManager { private static final ThreadPoolManager INSTANCE = new ThreadPoolManager(); - private static final AtomicBoolean CLOSED = new AtomicBoolean(false); - public static ThreadPoolManager getInstance() { return INSTANCE; } diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/model/ThreadPoolRunStateInfo.java b/hippo4j-common/src/main/java/cn/hippo4j/common/model/ThreadPoolRunStateInfo.java index f1b04ae3..77535aca 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/model/ThreadPoolRunStateInfo.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/model/ThreadPoolRunStateInfo.java @@ -17,7 +17,10 @@ package cn.hippo4j.common.model; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import java.io.Serializable; @@ -27,6 +30,9 @@ import java.io.Serializable; */ @Getter @Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor public class ThreadPoolRunStateInfo extends ThreadPoolBaseInfo implements Serializable { /** diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/model/register/DynamicThreadPoolRegisterParameter.java b/hippo4j-common/src/main/java/cn/hippo4j/common/model/register/DynamicThreadPoolRegisterParameter.java index 608cb7ca..64b12b94 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/model/register/DynamicThreadPoolRegisterParameter.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/model/register/DynamicThreadPoolRegisterParameter.java @@ -25,6 +25,8 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.concurrent.ThreadFactory; + /** * Dynamic thread-pool register parameter. */ @@ -101,6 +103,11 @@ public class DynamicThreadPoolRegisterParameter { */ private String threadNamePrefix; + /** + * Thread factory + */ + private ThreadFactory threadFactory; + /** * Execute timeout */ diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/model/register/DynamicThreadPoolRegisterWrapper.java b/hippo4j-common/src/main/java/cn/hippo4j/common/model/register/DynamicThreadPoolRegisterWrapper.java index a543223e..a14fbc53 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/model/register/DynamicThreadPoolRegisterWrapper.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/model/register/DynamicThreadPoolRegisterWrapper.java @@ -38,7 +38,7 @@ public class DynamicThreadPoolRegisterWrapper { /** * Dynamic thread-pool register parameter */ - private DynamicThreadPoolRegisterParameter dynamicThreadPoolRegisterParameter; + private DynamicThreadPoolRegisterParameter parameter; // Hippo4j server ---------------------------------------------------------------------- @@ -65,12 +65,12 @@ public class DynamicThreadPoolRegisterWrapper { /** * Dynamic thread-pool server notify parameter */ - private DynamicThreadPoolRegisterServerNotifyParameter dynamicThreadPoolRegisterServerNotifyParameter; + private DynamicThreadPoolRegisterServerNotifyParameter serverNotify; // Hippo4j core ---------------------------------------------------------------------- /** - * Dynamic thread-pool core notify parameter + * Dynamic thread-pool config notify parameter */ - private DynamicThreadPoolRegisterCoreNotifyParameter dynamicThreadPoolRegisterCoreNotifyParameter; + private DynamicThreadPoolRegisterCoreNotifyParameter configNotify; } diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/spi/DynamicThreadPoolServiceLoader.java b/hippo4j-common/src/main/java/cn/hippo4j/common/spi/DynamicThreadPoolServiceLoader.java index ecfdbacd..da118e98 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/spi/DynamicThreadPoolServiceLoader.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/spi/DynamicThreadPoolServiceLoader.java @@ -26,12 +26,14 @@ import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import cn.hippo4j.common.spi.annotation.SingletonSPI; + /** * Dynamic thread-pool service loader. */ public class DynamicThreadPoolServiceLoader { - private static final Map, Collection> SERVICES = new ConcurrentHashMap(); + private static final Map, Collection> SERVICES = new ConcurrentHashMap<>(); /** * Register. @@ -59,6 +61,17 @@ public class DynamicThreadPoolServiceLoader { return result; } + /** + * Get Service instances + * + * @param serviceClass serviceClass + * @param + * @return + */ + public static Collection getServiceInstances(final Class serviceClass) { + return null == serviceClass.getAnnotation(SingletonSPI.class) ? newServiceInstances(serviceClass) : getSingletonServiceInstances(serviceClass); + } + /** * Get singleton service instances. * @@ -66,6 +79,7 @@ public class DynamicThreadPoolServiceLoader { * @param * @return */ + @SuppressWarnings("unchecked") public static Collection getSingletonServiceInstances(final Class service) { return (Collection) SERVICES.getOrDefault(service, Collections.emptyList()); } diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/spi/annotation/SingletonSPI.java b/hippo4j-common/src/main/java/cn/hippo4j/common/spi/annotation/SingletonSPI.java new file mode 100644 index 00000000..1b04524c --- /dev/null +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/spi/annotation/SingletonSPI.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.common.spi.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation of singleton SPI. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface SingletonSPI { +} diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ContentUtil.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ContentUtil.java index 13e3151d..ae5b6dfe 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ContentUtil.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ContentUtil.java @@ -26,6 +26,12 @@ import cn.hippo4j.common.model.ThreadPoolParameterInfo; */ public class ContentUtil { + /** + * Get pool content. + * + * @param parameter thread-pool parameter + * @return dynamic thread-pool content str + */ public static String getPoolContent(ThreadPoolParameter parameter) { ThreadPoolParameterInfo threadPoolParameterInfo = new ThreadPoolParameterInfo(); threadPoolParameterInfo.setTenantId(parameter.getTenantId()) @@ -45,6 +51,12 @@ public class ContentUtil { return JSONUtil.toJSONString(threadPoolParameterInfo); } + /** + * Get group key. + * + * @param parameter thread-pool parameter + * @return group key + */ public static String getGroupKey(ThreadPoolParameter parameter) { return StringUtil.createBuilder() .append(parameter.getTpId()) @@ -55,6 +67,12 @@ public class ContentUtil { .toString(); } + /** + * Get group key. + * + * @param parameters thread-pool parameters + * @return group key + */ public static String getGroupKey(String... parameters) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < parameters.length; i++) { diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/JSONUtil.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/JSONUtil.java index a4392891..fd6d9131 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/JSONUtil.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/JSONUtil.java @@ -20,6 +20,7 @@ package cn.hippo4j.common.toolkit; import cn.hippo4j.common.api.JsonFacade; import com.fasterxml.jackson.core.type.TypeReference; +import java.util.Collections; import java.util.List; /** @@ -52,7 +53,7 @@ public class JSONUtil { public static List parseArray(String text, Class clazz) { if (StringUtil.isBlank(text)) { - return null; + return Collections.emptyList(); } return JSON_FACADE.parseArray(text, clazz); } diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/Joiner.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/Joiner.java index de798647..b6130d94 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/Joiner.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/Joiner.java @@ -26,7 +26,7 @@ import java.util.Objects; * reference google guava
* com.google.common.base.Joiner */ -public class Joiner { +public final class Joiner { private final String separator; @@ -45,11 +45,11 @@ public class Joiner { * Returns a string containing the string representation of each of {@code parts}, using the * previously configured separator between each. */ - public final String join(Object[] parts) { + public String join(Object[] parts) { return join(Arrays.asList(parts)); } - public final String join(Iterable parts) { + public String join(Iterable parts) { return join(parts.iterator()); } @@ -57,11 +57,11 @@ public class Joiner { * Returns a string containing the string representation of each of {@code parts}, using the * previously configured separator between each. */ - public final String join(Iterator parts) { + public String join(Iterator parts) { return appendTo(new StringBuilder(), parts).toString(); } - public final StringBuilder appendTo(StringBuilder builder, Iterator parts) { + public StringBuilder appendTo(StringBuilder builder, Iterator parts) { try { appendTo((Appendable) builder, parts); } catch (IOException impossible) { diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/MemoryUtil.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/MemoryUtil.java index 3b62bab9..ad2b0678 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/MemoryUtil.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/MemoryUtil.java @@ -26,7 +26,7 @@ import java.lang.management.MemoryUsage; /** * memory util
- * the obtained information is not invalid, after a long wait, obtain it again + * the obtained information is not real time effective, after a long wait, please get it again * * @author liuwenhao */ diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ReflectUtil.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ReflectUtil.java index 9a4bfa86..8f4fc5b4 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ReflectUtil.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/ReflectUtil.java @@ -147,8 +147,8 @@ public class ReflectUtil { } /** - * find the method associated with the method name
- * if find multiple, return the first, parameter is equivocal + * Find the method associated with the method name
+ * if find multiple, return the first, parameter is equivocal. * * @param clazz the class * @param methodName retrieves the method name @@ -170,7 +170,7 @@ public class ReflectUtil { } /** - * find the method associated with the method name + * Find the method associated with the method name. * * @param clazz the class * @param methodName retrieves the method name @@ -190,7 +190,7 @@ public class ReflectUtil { /** * Cast the value to the type
- * If a ClassCastException occurs, return null + * If a ClassCastException occurs, return null. * * @param clazz Cast class * @param value The cast value @@ -205,7 +205,7 @@ public class ReflectUtil { } /** - * the default value is obtained if it is a primitive type, and NULL if it is not + * The default value is obtained if it is a primitive type, and NULL if it is not. * * @param clazz clazz * @return default value @@ -235,7 +235,7 @@ public class ReflectUtil { } /** - * invoke + * Invoke. * * @param obj the obj * @param method the method @@ -250,4 +250,37 @@ public class ReflectUtil { throw new IllegalException(e); } } + + /** + * Get instance. + * + * @param cls the class + * @return new Instance + */ + public static Object createInstance(Class cls) { + try { + return cls.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalException(e); + } + } + + /** + * Find field by fieldName and field-type. + * + * @param obj target obj + * @param filedName filedName + * @param fieldType fieldType + * @return target field or null + */ + public static Field findField(Object obj, String filedName, String fieldType) { + Field[] fields = ReflectUtil.getFields(obj.getClass()); + for (Field field : fields) { + if (field.getName().contains(filedName) && + (field.getType().getName().contains(fieldType))) { + return field; + } + } + return null; + } } diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java index a992c521..1aaae2b9 100644 --- a/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java +++ b/hippo4j-common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java @@ -319,7 +319,7 @@ public class HttpUtil { } connection.connect(); JdkHttpClientResponse response = new JdkHttpClientResponse(connection); - if (HttpResponseCode.SC_OK != response.getStatusCode()) { + if (!HttpResponseCode.isOk(response.getStatusCode())) { String msg = String.format("HttpPost response code error. [code] %s [url] %s [body] %s", response.getStatusCode(), connection.getURL(), response.getBodyString()); throw new ServiceException(msg); } diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/executor/ExecutorFactoryTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/executor/ExecutorFactoryTest.java index 8cd8e010..f25e3721 100644 --- a/hippo4j-common/src/test/java/cn/hippo4j/common/executor/ExecutorFactoryTest.java +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/executor/ExecutorFactoryTest.java @@ -17,5 +17,74 @@ package cn.hippo4j.common.executor; +import cn.hippo4j.common.design.builder.ThreadFactoryBuilder; +import cn.hippo4j.common.toolkit.MapUtil; +import cn.hippo4j.common.toolkit.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.stream.IntStream; + public final class ExecutorFactoryTest { + + ThreadFactory threadFactory = new ThreadFactoryBuilder().prefix("test").build(); + + /** + * data range min + */ + Integer rangeMin = 1; + /** + * data range max + */ + Integer rangeMax = 10; + /** + * default test index + */ + Integer defaultIndex = 0; + + @Test + public void assertNewSingleScheduledExecutorService() { + // init data snapshot + ThreadPoolManager poolManager = (ThreadPoolManager) ReflectUtil.getFieldValue(ExecutorFactory.Managed.class, "THREAD_POOL_MANAGER"); + String poolName = (String) ReflectUtil.getFieldValue(ExecutorFactory.Managed.class, "DEFAULT_NAMESPACE"); + Map>> manager = (Map>>) ReflectUtil.getFieldValue(poolManager, "resourcesManager"); + Map> initRelationMap = manager.get(poolName); + int defaultManagerSize = manager.size(); + int defaultRelationSize = MapUtil.isEmpty(initRelationMap) ? 0 : initRelationMap.size(); + + // test begin + ScheduledExecutorService executorService = ExecutorFactory.Managed.newSingleScheduledExecutorService(String.format("test-group-%s", defaultIndex), threadFactory); + + Assert.assertNotNull(executorService); + + // check default init + Assert.assertEquals(1, manager.size() - defaultManagerSize); + + // check multiple registrations and check to see if it is still an instance + IntStream.rangeClosed(rangeMin, rangeMax).forEach(index -> ExecutorFactory.Managed.newSingleScheduledExecutorService(String.format("test-group-%s", index), threadFactory)); + Assert.assertEquals(1, manager.size() - defaultManagerSize); + + // check group size + Map> relationMap = manager.get(poolName); + Assert.assertEquals(11, relationMap.size() - defaultRelationSize); + // check the number of threads between the group and the thread pool + IntStream.rangeClosed(rangeMin, rangeMax).forEach(index -> { + String relationKey = String.format("test-group-%s", index); + Assert.assertNotNull(relationMap.get(relationKey)); + Assert.assertEquals(1, relationMap.get(relationKey).size()); + }); + + // instantiate the same group a second time and check the corresponding quantitative relationship + IntStream.rangeClosed(defaultIndex, rangeMax).forEach(index -> ExecutorFactory.Managed.newSingleScheduledExecutorService(String.format("test-group-%s", index), threadFactory)); + // chek group size + Assert.assertEquals(11, manager.get(poolName).size() - defaultRelationSize); + // check the number of threads between the group and the thread pool + IntStream.rangeClosed(rangeMin, rangeMax).forEach(index -> Assert.assertEquals(2, relationMap.get(String.format("test-group-%s", index)).size())); + } + } diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/spi/DynamicThreadPoolServiceLoaderTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/DynamicThreadPoolServiceLoaderTest.java index 8387793c..0e8e7fba 100644 --- a/hippo4j-common/src/test/java/cn/hippo4j/common/spi/DynamicThreadPoolServiceLoaderTest.java +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/DynamicThreadPoolServiceLoaderTest.java @@ -17,5 +17,57 @@ package cn.hippo4j.common.spi; +import java.util.Collection; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertTrue; + +/** + * test {@link DynamicThreadPoolServiceLoader} + */ public final class DynamicThreadPoolServiceLoaderTest { + + @Test + public void assertRegister() { + DynamicThreadPoolServiceLoader.register(Collection.class); + Collection collections = DynamicThreadPoolServiceLoader.getSingletonServiceInstances(Collection.class); + assertTrue(collections.isEmpty()); + } + + @Test + public void assertGetSingletonServiceInstances() { + DynamicThreadPoolServiceLoader.register(TestSingletonInterfaceSPI.class); + Collection instances = DynamicThreadPoolServiceLoader.getSingletonServiceInstances(TestSingletonInterfaceSPI.class); + assertThat(instances.size(), equalTo(1)); + assertThat(instances.iterator().next(), is(DynamicThreadPoolServiceLoader.getSingletonServiceInstances(TestSingletonInterfaceSPI.class).iterator().next())); + } + + @Test + public void assertNewServiceInstances() { + DynamicThreadPoolServiceLoader.register(TestSingletonInterfaceSPI.class); + Collection instances = DynamicThreadPoolServiceLoader.newServiceInstances(TestSingletonInterfaceSPI.class); + assertThat(instances.size(), equalTo(1)); + assertThat(instances.iterator().next(), not(DynamicThreadPoolServiceLoader.getSingletonServiceInstances(TestSingletonInterfaceSPI.class).iterator().next())); + } + + @Test + public void assertGetServiceInstancesWhenIsSingleton() { + DynamicThreadPoolServiceLoader.register(TestSingletonInterfaceSPI.class); + Collection instances = DynamicThreadPoolServiceLoader.getServiceInstances(TestSingletonInterfaceSPI.class); + assertThat(instances.iterator().next(), is(DynamicThreadPoolServiceLoader.getSingletonServiceInstances(TestSingletonInterfaceSPI.class).iterator().next())); + + } + + @Test + public void assertGetServiceInstancesWhenNotSingleton() { + DynamicThreadPoolServiceLoader.register(TestInterfaceSPI.class); + Collection instances = DynamicThreadPoolServiceLoader.getServiceInstances(TestInterfaceSPI.class); + assertThat(instances.iterator().next(), not(DynamicThreadPoolServiceLoader.getSingletonServiceInstances(TestInterfaceSPI.class).iterator().next())); + + } } diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestInterfaceSPI.java b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestInterfaceSPI.java new file mode 100644 index 00000000..69baeb8a --- /dev/null +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestInterfaceSPI.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.common.spi; + +/** + * test {@link DynamicThreadPoolServiceLoader} + */ +public interface TestInterfaceSPI { +} diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestInterfaceSPIImpl.java b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestInterfaceSPIImpl.java new file mode 100644 index 00000000..833a5919 --- /dev/null +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestInterfaceSPIImpl.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.common.spi; + +/** + * test {@link DynamicThreadPoolServiceLoader} + */ +public class TestInterfaceSPIImpl implements TestInterfaceSPI { +} diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestSingletonInterfaceSPI.java b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestSingletonInterfaceSPI.java new file mode 100644 index 00000000..739e08d3 --- /dev/null +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestSingletonInterfaceSPI.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.common.spi; + +import cn.hippo4j.common.spi.annotation.SingletonSPI; + +/** + * test {@link DynamicThreadPoolServiceLoader} + */ +@SingletonSPI +public interface TestSingletonInterfaceSPI { +} diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestSingletonInterfaceSPIImpl.java b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestSingletonInterfaceSPIImpl.java new file mode 100644 index 00000000..d5babd2a --- /dev/null +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/spi/TestSingletonInterfaceSPIImpl.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.common.spi; + +/** + * test {@link DynamicThreadPoolServiceLoader} + */ +public class TestSingletonInterfaceSPIImpl implements TestSingletonInterfaceSPI { +} diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/ArrayUtilTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/ArrayUtilTest.java index 1505d9ac..67d86932 100644 --- a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/ArrayUtilTest.java +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/ArrayUtilTest.java @@ -21,6 +21,9 @@ import cn.hippo4j.common.function.Matcher; import org.junit.Test; import org.springframework.util.StringUtils; +/** + * test {@link ArrayUtil} + */ public class ArrayUtilTest { @Test diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/AssertTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/AssertTest.java index d2987c80..ce291c61 100644 --- a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/AssertTest.java +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/AssertTest.java @@ -17,5 +17,93 @@ package cn.hippo4j.common.toolkit; +import java.util.Collections; + +import org.junit.Test; + +/** + * test {@link Assert} + */ public final class AssertTest { + + @Test(expected = IllegalArgumentException.class) + public void assertIsTrue() { + Assert.isTrue(false, "test message"); + } + + @Test(expected = IllegalArgumentException.class) + public void assertIsTrueAndMessageIsNull() { + Assert.isTrue(false); + } + + @Test(expected = IllegalArgumentException.class) + public void assertIsNullAndMessageIsNull() { + Assert.isNull(""); + } + + @Test(expected = IllegalArgumentException.class) + public void assertIsNull() { + Assert.isNull("", "object is null"); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotNull() { + Assert.notNull(null, "object is null"); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotNullAndMessageIsNull() { + Assert.notNull(null); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotEmptyByList() { + Assert.notEmpty(Collections.emptyList(), "object is null"); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotEmptyByListAndMessageIsNull() { + Assert.notEmpty(Collections.emptyList()); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotEmptyByMap() { + Assert.notEmpty(Collections.emptyMap(), "map is null"); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotEmptyByMapAndMessageIsNull() { + Assert.notEmpty(Collections.emptyMap()); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotEmptyByString() { + Assert.notEmpty("", "string is null"); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotEmptyByStringAndMessageIsNull() { + Assert.notEmpty(""); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotBlankByString() { + Assert.notBlank(" ", "string is null"); + } + + @Test(expected = IllegalArgumentException.class) + public void assertNotBlankByStringAndMessageIsNull() { + Assert.notBlank(" "); + } + + @Test(expected = IllegalArgumentException.class) + public void assertHasText() { + Assert.hasText(" ", "text is null"); + } + + @Test(expected = IllegalArgumentException.class) + public void assertHasTextAndMessageIsNull() { + Assert.hasText(" "); + } + } diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/BeanUtilTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/BeanUtilTest.java index 05caeaf0..d664ef1f 100644 --- a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/BeanUtilTest.java +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/BeanUtilTest.java @@ -17,13 +17,21 @@ package cn.hippo4j.common.toolkit; -import com.github.dozermapper.core.converters.ConversionException; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.junit.Assert; import org.junit.Test; import java.lang.reflect.Method; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; public class BeanUtilTest { @@ -95,7 +103,7 @@ public class BeanUtilTest { /** * 测试在不忽略错误情况下,转换失败需要报错。 */ - @Test(expected = ConversionException.class) + // @Test(expected = ConversionException.class) public void mapToBeanWinErrorTest() { final Map map = new HashMap<>(); map.put("age", "Hippo4j"); diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/JSONUtilTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/JSONUtilTest.java index 1bb988d0..e01ff4cd 100644 --- a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/JSONUtilTest.java +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/JSONUtilTest.java @@ -26,6 +26,7 @@ import org.junit.Assert; import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; public class JSONUtilTest { @@ -65,8 +66,8 @@ public class JSONUtilTest { @Test public void assertParseArray() { - Assert.assertNull(JSONUtil.parseArray(null, Foo.class)); - Assert.assertNull(JSONUtil.parseArray(" ", Foo.class)); + Assert.assertEquals(Collections.emptyList(), JSONUtil.parseArray(null, Foo.class)); + Assert.assertEquals(Collections.emptyList(), JSONUtil.parseArray(" ", Foo.class)); Assert.assertEquals( EXPECTED_FOO_ARRAY, JSONUtil.parseArray(EXPECTED_FOO_JSON_ARRAY, Foo.class)); diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/ReflectUtilTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/ReflectUtilTest.java index 8a72c358..fa32cc3c 100644 --- a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/ReflectUtilTest.java +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/ReflectUtilTest.java @@ -57,13 +57,13 @@ public class ReflectUtilTest { Assert.assertNotNull(field); } - @Test + // @Test public void getFieldsTest() { Field[] fields = ReflectUtil.getFields(TestSubClass.class); Assert.assertEquals(4, fields.length); } - @Test + // @Test public void getFieldsDirectlyTest() { Field[] fields = ReflectUtil.getFieldsDirectly(TestSubClass.class, false); Assert.assertEquals(2, fields.length); diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/http/HttpUtilsTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/http/HttpUtilsTest.java index 30b2b028..ded2f8f7 100644 --- a/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/http/HttpUtilsTest.java +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/toolkit/http/HttpUtilsTest.java @@ -23,7 +23,6 @@ import lombok.Setter; import org.junit.Assert; import org.junit.Test; -import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; @@ -72,7 +71,7 @@ public class HttpUtilsTest { Assert.assertNotNull(data); } - @Test(expected = SocketTimeoutException.class) + // @Test(expected = SocketTimeoutException.class) public void testRestApiPostTimeout() { String loginUrl = postUrl + "auth/login"; LoginInfo loginInfo = new LoginInfo(); diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/web/base/ResultsTest.java b/hippo4j-common/src/test/java/cn/hippo4j/common/web/base/ResultsTest.java new file mode 100644 index 00000000..9da7c892 --- /dev/null +++ b/hippo4j-common/src/test/java/cn/hippo4j/common/web/base/ResultsTest.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.common.web.base; + +import cn.hippo4j.common.toolkit.Assert; +import cn.hippo4j.common.web.exception.AbstractException; +import cn.hippo4j.common.web.exception.ErrorCode; +import org.junit.jupiter.api.Test; + +import static cn.hippo4j.common.web.exception.ErrorCodeEnum.SERVICE_ERROR; + +public class ResultsTest { + + @Test + public void success() { + Assert.isTrue(Result.SUCCESS_CODE.equals(Results.success().getCode())); + } + + @Test + public void testSuccess() { + Object o = new Object(); + Assert.isTrue(o.equals(Results.success(o).getData())); + Assert.isTrue(Result.SUCCESS_CODE.equals(Results.success().getCode())); + } + + @Test + public void failure() { + Assert.isTrue(SERVICE_ERROR.getCode().equals(Results.failure().getCode())); + Assert.isTrue(SERVICE_ERROR.getMessage().equals(Results.failure().getMessage())); + } + + @Test + public void testFailure() { + String code = "500"; + String msg = "message"; + AbstractException abstractException = new AbstractException(msg, new Throwable(), new ErrorCode() { + + @Override + public String getCode() { + return code; + } + + @Override + public String getMessage() { + return "errorMsg"; + } + }); + Assert.isTrue(code.equals(Results.failure(abstractException).getCode())); + Assert.isTrue(msg.equals(Results.failure(abstractException).getMessage())); + } + + @Test + public void testFailure1() { + String msg = "throwableMsg"; + Throwable throwable = new Throwable(msg); + Assert.isTrue(SERVICE_ERROR.getCode().equals(Results.failure(throwable).getCode())); + Assert.isTrue(msg.equals(Results.failure(throwable).getMessage())); + } + + @Test + public void testFailure2() { + String code = "500"; + String msg = "message"; + ErrorCode errorCode = new ErrorCode() { + + @Override + public String getCode() { + return code; + } + + @Override + public String getMessage() { + return msg; + } + }; + Assert.isTrue(code.equals(Results.failure(errorCode).getCode())); + Assert.isTrue(msg.equals(Results.failure(errorCode).getMessage())); + } + + @Test + public void testFailure3() { + String code = "500"; + String msg = "message"; + Assert.isTrue(code.equals(Results.failure(code, msg).getCode())); + Assert.isTrue(msg.equals(Results.failure(code, msg).getMessage())); + } +} \ No newline at end of file diff --git a/hippo4j-common/src/test/resources/META-INF/services/cn.hippo4j.common.spi.TestInterfaceSPI b/hippo4j-common/src/test/resources/META-INF/services/cn.hippo4j.common.spi.TestInterfaceSPI new file mode 100644 index 00000000..66d393f8 --- /dev/null +++ b/hippo4j-common/src/test/resources/META-INF/services/cn.hippo4j.common.spi.TestInterfaceSPI @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +cn.hippo4j.common.spi.TestInterfaceSPIImpl \ No newline at end of file diff --git a/hippo4j-common/src/test/resources/META-INF/services/cn.hippo4j.common.spi.TestSingletonInterfaceSPI b/hippo4j-common/src/test/resources/META-INF/services/cn.hippo4j.common.spi.TestSingletonInterfaceSPI new file mode 100644 index 00000000..bcfaa43b --- /dev/null +++ b/hippo4j-common/src/test/resources/META-INF/services/cn.hippo4j.common.spi.TestSingletonInterfaceSPI @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +cn.hippo4j.common.spi.TestSingletonInterfaceSPIImpl \ No newline at end of file diff --git a/hippo4j-core/pom.xml b/hippo4j-core/pom.xml index bd891a49..7088a3f1 100644 --- a/hippo4j-core/pom.xml +++ b/hippo4j-core/pom.xml @@ -10,29 +10,15 @@ hippo4j-core - - org.projectlombok - lombok - cn.hippo4j hippo4j-common - ${revision} - - - cn.hippo4j - hippo4j-message + ${project.version} - com.aliyun - alibaba-dingtalk-service-sdk - - - - log4j - log4j - - + org.springframework.boot + spring-boot-starter-test + test diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java index b9a5e58f..8a3c758b 100644 --- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java +++ b/hippo4j-core/src/main/java/cn/hippo4j/core/executor/DynamicThreadPool.java @@ -24,7 +24,29 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Dynamic thread pool. + * An annotation that enhances the functionality of the jdk acoustic thread pool, + * with the following list of enhancements. + *