diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..b2ce8a63cf --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# 所有文件默认使用 LF,提交时转换为 LF,检出时不转换 (避免不同系统间的换行符冲突) +* text=auto eol=lf + +# 二进制文件(如图片、可执行文件)不转换 +*.png binary +*.jpg binary +*.exe binary \ No newline at end of file diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 289a9ab313..e35be8c100 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -18,8 +18,6 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - # if your docs needs submodules, uncomment the following line - # submodules: true - name: Install pnpm uses: pnpm/action-setup@v2 @@ -30,7 +28,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20.19.3 cache: pnpm - name: Build Docs @@ -44,6 +42,5 @@ jobs: uses: JamesIves/github-pages-deploy-action@v4 if: github.event_name == 'push' && github.ref == 'refs/heads/master' with: - # This is the branch where the docs are deployed to branch: gh-pages folder: src/.vuepress/dist diff --git a/.gitignore b/.gitignore index 830f16d2aa..80f39b4610 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ src/.vuepress/dist/ .idea/ .DS_Store .npmrc + diff --git a/.prettierrc b/.prettierrc index 3ec781d387..432fc85304 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1 +1,5 @@ -{ "singleQuote": false, "semi": true, "trailingComma": "none" } +{ + "endOfLine": "auto", + "printWidth": 80 +} + diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000000..fad1c057f6 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,95 @@ +import pluginJs from "@eslint/js"; +import pluginTs from "@typescript-eslint/eslint-plugin"; +import pluginVue from "eslint-plugin-vue"; +import vueEslintParser from "vue-eslint-parser"; +import tsEslintParser from "@typescript-eslint/parser"; +import pluginPrettier from "eslint-plugin-prettier"; + +export default [ + // 1. 优先排除不需要检查的文件(放在最前面) + { + ignores: [ + "node_modules/**", + "src/.vuepress/dist/**", + "src/.vuepress/.temp/**", + "src/.vuepress/.cache/**", + ], + }, + + // 2. 基础推荐规则(对所有文件生效) + pluginJs.configs.recommended, + ...pluginVue.configs["flat/essential"], + + // 3. 全局语言选项配置(对所有文件生效) + { + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: { + window: "readonly", + document: "readonly", + process: "readonly", + fetch: "readonly", + console: "readonly", + requestAnimationFrame: "readonly", + location: "readonly", + frontmatter: "readonly", + cancelAnimationFrame: "readonly", + clearTimeout: "readonly", + getComputedStyle: "readonly", + }, + }, + }, + + // 4. TypeScript 和 Vue 特定配置(针对 .ts 和 .vue 文件) + { + files: ["**/*.{ts,vue}"], + languageOptions: { + parser: vueEslintParser, // 解析 .vue 文件 + parserOptions: { + parser: tsEslintParser, // 解析 - - + + + + + diff --git a/src/.vuepress/components/GlareHover.vue b/src/.vuepress/components/GlareHover.vue new file mode 100644 index 0000000000..a8a7b34222 --- /dev/null +++ b/src/.vuepress/components/GlareHover.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/.vuepress/components/HomePage.vue b/src/.vuepress/components/HomePage.vue index e6175b144f..cbaf5dcd0a 100644 --- a/src/.vuepress/components/HomePage.vue +++ b/src/.vuepress/components/HomePage.vue @@ -1,835 +1,976 @@ - - - - - -../composables/index.js + + + + + diff --git a/src/.vuepress/components/HonorComp.vue b/src/.vuepress/components/HonorComp.vue index 1bbe602035..af3622e34a 100644 --- a/src/.vuepress/components/HonorComp.vue +++ b/src/.vuepress/components/HonorComp.vue @@ -1,229 +1,230 @@ - - - - - + + + + + diff --git a/src/.vuepress/components/LogoAnimation.vue b/src/.vuepress/components/LogoAnimation.vue index 085ed003da..64da8e810d 100644 --- a/src/.vuepress/components/LogoAnimation.vue +++ b/src/.vuepress/components/LogoAnimation.vue @@ -1,1062 +1,1059 @@ - - - - - + + + + + diff --git a/src/.vuepress/components/PageFooter.vue b/src/.vuepress/components/PageFooter.vue index ee179bc296..6c3a2842c3 100644 --- a/src/.vuepress/components/PageFooter.vue +++ b/src/.vuepress/components/PageFooter.vue @@ -1,307 +1,387 @@ - - - - - + + + + + diff --git a/src/.vuepress/components/SiteSection.vue b/src/.vuepress/components/SiteSection.vue index 12d019d971..05a1829d83 100644 --- a/src/.vuepress/components/SiteSection.vue +++ b/src/.vuepress/components/SiteSection.vue @@ -1,381 +1,533 @@ - - - - - + + + + + diff --git a/src/.vuepress/components/banner/Galaxy.vue b/src/.vuepress/components/banner/Galaxy.vue new file mode 100644 index 0000000000..b4ad6df0ed --- /dev/null +++ b/src/.vuepress/components/banner/Galaxy.vue @@ -0,0 +1,372 @@ + + + + diff --git a/src/.vuepress/components/banner/LightRays.vue b/src/.vuepress/components/banner/LightRays.vue new file mode 100644 index 0000000000..9c6eb3f06d --- /dev/null +++ b/src/.vuepress/components/banner/LightRays.vue @@ -0,0 +1,561 @@ + + + + + diff --git a/src/.vuepress/components/hidden-text/TextEllipsisHover.vue b/src/.vuepress/components/hidden-text/TextEllipsisHover.vue new file mode 100644 index 0000000000..fd45c58293 --- /dev/null +++ b/src/.vuepress/components/hidden-text/TextEllipsisHover.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/src/.vuepress/components/hover-light/HoverLight.vue b/src/.vuepress/components/hover-light/HoverLight.vue new file mode 100644 index 0000000000..fb252f8f16 --- /dev/null +++ b/src/.vuepress/components/hover-light/HoverLight.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/src/.vuepress/composables/footer/en.ts b/src/.vuepress/composables/footer/en.ts index 141a752371..9d9535633b 100644 --- a/src/.vuepress/composables/footer/en.ts +++ b/src/.vuepress/composables/footer/en.ts @@ -1,13 +1,13 @@ -import { type FooterOption } from "./types.js"; - -export const enFooterOption: FooterOption = { - RESOURCES: "Resources", - INVOLVED: "Get Involved", - About: "About Us", - BLOG: "Blog", - DOCUMENT: "Document", - WECHAT: "Media Platform", - KNOWLEDGE_PLANET: "Knowledge Planet", - DONATE: "Donate dromara", - Email: "Email Feedback" -}; +import { type FooterOption } from "./types.js"; + +export const enFooterOption: FooterOption = { + RESOURCES: "Resources", + INVOLVED: "Get Involved", + About: "About Us", + BLOG: "Blog", + DOCUMENT: "Document", + WECHAT: "Media Platform", + KNOWLEDGE_PLANET: "Knowledge Planet", + DONATE: "Donate dromara", + Email: "Email Feedback", +}; diff --git a/src/.vuepress/composables/footer/index.ts b/src/.vuepress/composables/footer/index.ts index ef6796b021..f9cf9ed064 100644 --- a/src/.vuepress/composables/footer/index.ts +++ b/src/.vuepress/composables/footer/index.ts @@ -7,7 +7,7 @@ import { zhFooterOption } from "./zh.js"; export const useFooterLocale = () => useLocaleConfig({ "/": enFooterOption, - "/zh/": zhFooterOption + "/zh/": zhFooterOption, }); export * from "./types.js"; diff --git a/src/.vuepress/composables/footer/types.ts b/src/.vuepress/composables/footer/types.ts index 36f33e3671..71167ec91a 100644 --- a/src/.vuepress/composables/footer/types.ts +++ b/src/.vuepress/composables/footer/types.ts @@ -1,11 +1,11 @@ -export interface FooterOption { - RESOURCES: string - INVOLVED: string - About: string - BLOG: string - DOCUMENT: string - WECHAT: string - KNOWLEDGE_PLANET: string - DONATE: string - Email: string -} +export interface FooterOption { + RESOURCES: string; + INVOLVED: string; + About: string; + BLOG: string; + DOCUMENT: string; + WECHAT: string; + KNOWLEDGE_PLANET: string; + DONATE: string; + Email: string; +} diff --git a/src/.vuepress/composables/footer/zh.ts b/src/.vuepress/composables/footer/zh.ts index 1749b6ae82..31e3d06073 100644 --- a/src/.vuepress/composables/footer/zh.ts +++ b/src/.vuepress/composables/footer/zh.ts @@ -1,13 +1,13 @@ -import { type FooterOption } from "./types.js"; - -export const zhFooterOption: FooterOption = { - RESOURCES: "资源", - INVOLVED: "参与进来", - About: "关于我们", - BLOG: "博客", - DOCUMENT: "文档", - WECHAT: "微信公众号", - KNOWLEDGE_PLANET: "知识星球", - DONATE: "支持社区", - Email: "意见反馈" -}; +import { type FooterOption } from "./types.js"; + +export const zhFooterOption: FooterOption = { + RESOURCES: "资源", + INVOLVED: "参与进来", + About: "关于我们", + BLOG: "博客", + DOCUMENT: "文档", + WECHAT: "微信公众号", + KNOWLEDGE_PLANET: "知识星球", + DONATE: "支持社区", + Email: "意见反馈", +}; diff --git a/src/.vuepress/composables/home/en.ts b/src/.vuepress/composables/home/en.ts index 25e467a29f..19d09e4b59 100644 --- a/src/.vuepress/composables/home/en.ts +++ b/src/.vuepress/composables/home/en.ts @@ -1,158 +1,159 @@ -import { type HomeOption } from "./types.js"; - -export const enHomeOption: HomeOption = { - QUICK_START: "Quick Start", - DESCRIPTION: "A non-profit organization where open-source enthusiasts gather", - INCUBATOR: "Incubator", - FEATURES: [ - { - name: "open", - title: "Open", - desc: "The technology stack is fully open source construction, maintain the community neutrality, compatible with the community open source ecology, welcome to participate in various contributions at any time." - }, - { - name: "vision", - title: "Vision", - desc: "Let every open source enthusiast experience the happiness of open source." - }, - { - name: "slogan", - title: "Slogan", - desc: "One person may be able to go faster, but a group of people will go further." - } - ], - STARS_OVERALL: "Total stars exceed", - DATA_SOURCE: "——Data sourced from Gitee and Github", - OUR: "Our", - PROJECT: "Recommended Projects", - MORE_PROJECTS: "View All Projects", - VIEW_PROJECT: "View Project", - PROJECT_DETAILS: [ - { - name: "hmily", - description: "Flexible distributed transaction solution.", - url: "https://gitee.com/dromara/hmily" - }, - { - name: "hutool", - description: "A set of tools that keep Java sweet.", - url: "https://hutool.cn/" - }, - { - name: "sa-token", - description: "The most comprehensive Java permission framework.", - url: "http://sa-token.dev33.cn/" - }, - { - name: "Jpom", - description: "Simple & Low-intrusion project management platform.", - url: "https://jpom.io/" - }, - { - name: "TLog", - description: "Lightweight distributed log label tracking framework.", - url: "https://yomahub.com/tlog/" - }, - { - name: "cubic", - description: "Distributed monitoring system.", - url: "https://cubic.jiagoujishu.com/" - }, - { - name: "koalas-rpc", - description: "Highly available and extensible RPC framework.", - url: "https://github.com/dromara/koalas-rpc" - }, - { - name: "fast-request", - description: "IDEA's postman.", - url: "https://api-buddy.cn" - }, - { - name: "carbon", - description: "A simple, semantic and developer-friendly golang package for time.", - url: "https://github.com/dromara/carbon" - } - ], - COMMUNITY: "Community", - COMMUNITY_ITEM: [ - { - category: "Blog", - icon: "/assets/img/blog.png", - details: [ - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - } - ] - }, - { - category: "Activity", - icon: "/assets/img/activity.png", - details: [ - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - } - ] - }, - { - category: "News", - icon: "/assets/img/news.png", - details: [ - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - }, - { - title: "Soul Gateway Learning Apache Dubbo Plugin", - time: "2021-03-23" - } - ] - } - ] -}; +import { type HomeOption } from "./types.js"; + +export const enHomeOption: HomeOption = { + QUICK_START: "Quick Start", + DESCRIPTION: "A non-profit organization where open-source enthusiasts gather", + INCUBATOR: "Incubator", + FEATURES: [ + { + name: "open", + title: "Open", + desc: "The tech stack is fully open-source, community-co-built, neutral, ecosystem-compatible, and always welcomes contributions.", + }, + { + name: "vision", + title: "Vision", + desc: "Let every open source enthusiast experience the happiness of open source.", + }, + { + name: "slogan", + title: "Slogan", + desc: "One person may be able to go faster, but a group of people will go further.", + }, + ], + STARS_OVERALL: "Total stars exceed", + DATA_SOURCE: "Data sourced from Gitee and Github", + OUR: "Our", + PROJECT: "Recommended Projects", + MORE_PROJECTS: "View All Projects", + VIEW_PROJECT: "View Project", + PROJECT_DETAILS: [ + { + name: "hmily", + description: "Flexible distributed transaction solution.", + url: "https://gitee.com/dromara/hmily", + }, + { + name: "hutool", + description: "A set of tools that keep Java sweet.", + url: "https://hutool.cn/", + }, + { + name: "sa-token-home", + description: "The most comprehensive Java permission framework.", + url: "http://sa-token.dev33.cn/", + }, + { + name: "Jpom", + description: "Simple & Low-intrusion project management platform.", + url: "https://jpom.io/", + }, + { + name: "TLog-home", + description: "Lightweight distributed log label tracking framework.", + url: "https://tlog.yomahub.com/", + }, + { + name: "cubic-home", + description: "Distributed monitoring system.", + url: "https://www.jiagoujishu.cn/blogs/cubic/guide.html", + }, + { + name: "koalas-rpc", + description: "Highly available and extensible RPC framework.", + url: "https://github.com/dromara/koalas-rpc", + }, + { + name: "fast-request", + description: "IDEA's postman.", + url: "https://api-buddy.cn", + }, + { + name: "carbon", + description: + "A simple, semantic and developer-friendly golang package for time.", + url: "https://github.com/dromara/carbon", + }, + ], + COMMUNITY: "Community", + COMMUNITY_ITEM: [ + { + category: "Blog", + icon: "/assets/img/blog.png", + details: [ + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + ], + }, + { + category: "Activity", + icon: "/assets/img/activity.png", + details: [ + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + ], + }, + { + category: "News", + icon: "/assets/img/news.png", + details: [ + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + { + title: "Soul Gateway Learning Apache Dubbo Plugin", + time: "2021-03-23", + }, + ], + }, + ], +}; diff --git a/src/.vuepress/composables/home/index.ts b/src/.vuepress/composables/home/index.ts index d080578654..b6cc412762 100644 --- a/src/.vuepress/composables/home/index.ts +++ b/src/.vuepress/composables/home/index.ts @@ -6,7 +6,7 @@ import { zhHomeOption } from "./zh.js"; export const useHomeLocale = () => useLocaleConfig({ "/": enHomeOption, - "/zh/": zhHomeOption + "/zh/": zhHomeOption, }); export * from "./types.js"; diff --git a/src/.vuepress/composables/home/types.ts b/src/.vuepress/composables/home/types.ts index a52d8a111a..51e33777a2 100644 --- a/src/.vuepress/composables/home/types.ts +++ b/src/.vuepress/composables/home/types.ts @@ -1,56 +1,56 @@ -interface Feature { - name: string - title: string - desc: string -} - -interface ProjectDetail { - name: string - description: string - url: string -} - -interface CommunityDetail { - title: string - time: string -} - -interface CommunityItem { - category: string - icon: string - details: CommunityDetail[] -} - -export interface HomeOption { - QUICK_START: string - DESCRIPTION: string - INCUBATOR: string - FEATURES: Feature[] - STARS_OVERALL: string - DATA_SOURCE: string - OUR: string - PROJECT: string - MORE_PROJECTS: string - VIEW_PROJECT: string - PROJECT_DETAILS: ProjectDetail[] - COMMUNITY: string - COMMUNITY_ITEM: CommunityItem[] -} - -interface GroupedPost { - title: string - url: string - time: string -} - -export type GroupedPosts = Record; - -export interface CommunityLink { - category: string - icon: string - details: Array<{ - title: string - time: string - url: string - }> -} +interface Feature { + name: string; + title: string; + desc: string; +} + +interface ProjectDetail { + name: string; + description: string; + url: string; +} + +interface CommunityDetail { + title: string; + time: string; +} + +interface CommunityItem { + category: string; + icon: string; + details: CommunityDetail[]; +} + +export interface HomeOption { + QUICK_START: string; + DESCRIPTION: string; + INCUBATOR: string; + FEATURES: Feature[]; + STARS_OVERALL: string; + DATA_SOURCE: string; + OUR: string; + PROJECT: string; + MORE_PROJECTS: string; + VIEW_PROJECT: string; + PROJECT_DETAILS: ProjectDetail[]; + COMMUNITY: string; + COMMUNITY_ITEM: CommunityItem[]; +} + +interface GroupedPost { + title: string; + url: string; + time: string; +} + +export type GroupedPosts = Record; + +export interface CommunityLink { + category: string; + icon: string; + details: Array<{ + title: string; + time: string; + url: string; + }>; +} diff --git a/src/.vuepress/composables/home/zh.ts b/src/.vuepress/composables/home/zh.ts index 626fabd68d..78297b8443 100644 --- a/src/.vuepress/composables/home/zh.ts +++ b/src/.vuepress/composables/home/zh.ts @@ -1,158 +1,158 @@ -import { type HomeOption } from "./types.js"; - -export const zhHomeOption: HomeOption = { - QUICK_START: "快速开始", - DESCRIPTION: "开源爱好者齐聚的非盈利组织", - INCUBATOR: "孵化器", - FEATURES: [ - { - name: "open", - title: "开放", - desc: "技术栈全面开源共建、 保持社区中立、兼容社区兼容开源生态,随时欢迎参与各种贡献" - }, - { - name: "vision", - title: "愿景", - desc: "让每一位开源爱好者,体会到开源的快乐" - }, - { - name: "slogan", - title: "口号", - desc: "为往圣继绝学,一个人或许能走得更快,但一群人会走得更远" - } - ], - STARS_OVERALL: "全网star数超过", - DATA_SOURCE: "数据来源于Gitee、Github", - OUR: "我们的", - PROJECT: "项目推荐", - MORE_PROJECTS: "查看所有项目", - VIEW_PROJECT: "查看项目", - PROJECT_DETAILS: [ - { - name: "hmily", - description: "柔性分布式事务解决方案", - url: "https://gitee.com/dromara/hmily" - }, - { - name: "hutool", - description: "小而全的Java工具类库", - url: "https://hutool.cn/" - }, - { - name: "sa-token", - description: "史上功能最全的 Java 权限认证框架", - url: "http://sa-token.dev33.cn/" - }, - { - name: "Jpom", - description: "简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件", - url: "https://jpom.io/" - }, - { - name: "TLog", - description: "轻量级的分布式日志标记追踪神器", - url: "https://yomahub.com/tlog/" - }, - { - name: "cubic", - description: "无侵入分布式监控,致力于应用级监控的工具", - url: "https://cubic.jiagoujishu.com/" - }, - { - name: "koalas-rpc", - description: "高可用可拓展的RPC框架", - url: "https://github.com/dromara/koalas-rpc" - }, - { - name: "fast-request", - description: "IDEA版postman,为简化 API 调试而生", - url: "https://api-buddy.cn" - }, - { - name: "carbon", - description: "一个轻量级、语义化、对开发者友好的 golang 时间处理库", - url: "https://github.com/dromara/carbon" - } - ], - COMMUNITY: "社区动态", - COMMUNITY_ITEM: [ - { - category: "博客", - icon: "/assets/img/blog.png", - details: [ - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - } - ] - }, - { - category: "活动", - icon: "/assets/img/activity.png", - details: [ - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - } - ] - }, - { - category: "新闻", - icon: "/assets/img/news.png", - details: [ - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - }, - { - title: "Soul网关学习Apache Dubbo插件原理解析", - time: "2021-03-23" - } - ] - } - ] -}; +import { type HomeOption } from "./types.js"; + +export const zhHomeOption: HomeOption = { + QUICK_START: "快速开始", + DESCRIPTION: "开源爱好者齐聚的非盈利组织", + INCUBATOR: "孵化器", + FEATURES: [ + { + name: "open", + title: "开放", + desc: "技术栈全面开源共建、 保持社区中立、兼容社区兼容开源生态,随时欢迎参与各种贡献", + }, + { + name: "vision", + title: "愿景", + desc: "让每一位开源爱好者,体会到开源的快乐", + }, + { + name: "slogan", + title: "口号", + desc: "为往圣继绝学,一个人或许能走得更快,但一群人会走得更远", + }, + ], + STARS_OVERALL: "全网star数超过", + DATA_SOURCE: "数据来源于Gitee、Github", + OUR: "我们的", + PROJECT: "项目推荐", + MORE_PROJECTS: "查看所有项目", + VIEW_PROJECT: "查看项目", + PROJECT_DETAILS: [ + { + name: "hmily", + description: "柔性分布式事务解决方案", + url: "https://gitee.com/dromara/hmily", + }, + { + name: "hutool", + description: "小而全的Java工具类库", + url: "https://hutool.cn/", + }, + { + name: "sa-token-home", + description: "史上功能最全的 Java 权限认证框架", + url: "http://sa-token.dev33.cn/", + }, + { + name: "Jpom", + description: "简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件", + url: "https://jpom.io/", + }, + { + name: "TLog-home", + description: "轻量级的分布式日志标记追踪神器", + url: "https://tlog.yomahub.com/", + }, + { + name: "cubic-home", + description: "无侵入分布式监控,致力于应用级监控的工具", + url: "https://www.jiagoujishu.cn/blogs/cubic/guide.html", + }, + { + name: "koalas-rpc", + description: "高可用可拓展的RPC框架", + url: "https://github.com/dromara/koalas-rpc", + }, + { + name: "fast-request", + description: "IDEA版postman,为简化 API 调试而生", + url: "https://api-buddy.cn", + }, + { + name: "carbon", + description: "一个轻量级、语义化、对开发者友好的 golang 时间处理库", + url: "https://github.com/dromara/carbon", + }, + ], + COMMUNITY: "社区动态", + COMMUNITY_ITEM: [ + { + category: "博客", + icon: "/assets/img/blog.png", + details: [ + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + ], + }, + { + category: "活动", + icon: "/assets/img/activity.png", + details: [ + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + ], + }, + { + category: "新闻", + icon: "/assets/img/news.png", + details: [ + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + { + title: "Soul网关学习Apache Dubbo插件原理解析", + time: "2021-03-23", + }, + ], + }, + ], +}; diff --git a/src/.vuepress/composables/honor/en.ts b/src/.vuepress/composables/honor/en.ts index 50f88388e9..0435cad727 100644 --- a/src/.vuepress/composables/honor/en.ts +++ b/src/.vuepress/composables/honor/en.ts @@ -1,89 +1,89 @@ -import { type HonorOption } from "./types.js"; -export const enHonorOption: HonorOption = { - SLIDES: [ - { - desc: "2022 Open Source Community - China Open Source Annual Report awarded Most Popular Open Source Organization", - img: "/assets/img/about/2022_OSC_Top_Org.png" - }, - { - desc: "2022 OSChina China Open Source Project Selection, awarded Healthy Case of China Open Source Community", - img: "/assets/img/about/OSC_China_Health_Case.png" - }, - { - desc: "2022 Community projects Forest, Hutool, Sa-Token won OSC '2022 Hottest China Open Source Project Community'", - img: "/assets/img/about/OSC_China_Hottest.png" - }, - { - desc: "2022 OSChina awarded Excellent Open Source Technology Team", - img: "/assets/img/about/OSC_Awards_Tech_Team.jpg" - }, - { - desc: "2022 Excellent Award in China Open Source Innovation Competition", - img: "/assets/img/about/Innovation_Excellence_Award.jpg" - }, - { - desc: "2022 Annual Popularity Technology Team", - img: "/assets/img/about/Popular_Technical_Team.jpg" - }, - { - desc: "2023 The most active open source organization", - img: "/assets/img/about/2023_Most_Active_Org.jpg" - }, - { - desc: "2023 The most popular and beloved open source organization", - img: "/assets/img/about/Concerned_favorite_Org.jpg" - }, - { - desc: "2024 China Internet Development Innovation and Investment Competition (Open Source)", - img: "/assets/img/about/2024-China-Internet-Development-Innovation.jpg" - } - ], - TIME_LINE: [ - { - year: "2017", - items: [ - "Entered the open-source stage", - "Established two top-level projects: Hmily and Myth" - ] - }, - { - year: "2021", - items: [ - "Joined 14 top-level projects", - "Including hutool, Sa-Token, Jpom, etc.", - "Community instantly became colorful" - ] - }, - { - year: "2022", - items: [ - "Donated incubation for 13 projects", - "Total community projects reached 33", - "Added 6 GVP (Gitee Most Valuable Open Source) projects", - "Gitee received over 84.2K stars", - "Honored with the 2022 Excellent Open Source Technology Team award" - ] - }, - { - year: "2023", - items: [ - "Added 4 more top-level projects: RuoYi-Vue-Plus, sms4j, etc.", - "Brimming with energy, joined incubation for 9 projects", - "Won the 2022 China Open Source Community Health Case Award", - "Won the title of 2022 Popular Technology Team of the Year", - "Won the Excellence Award at the 2022 China Open Source Innovation Competition" - ] - }, - { - year: "2024", - items: [ - "The number of stars in the whole network exceeds 305.5k", - "21 GVP(Gitee Most Valuable Open Source) projects", - "The current total number of Gitee project repositories has reached 76", - "Awarded the most watched and loved open source organization of 2023", - "Most Active Open Source Organization in 2023", - "The Second Prize of the 2024 China Internet Development Innovation and Investment Competition (Open Source)" - ] - } - ] -}; +import { type HonorOption } from "./types.js"; +export const enHonorOption: HonorOption = { + SLIDES: [ + { + desc: "2022 Open Source Community - China Open Source Annual Report awarded Most Popular Open Source Organization", + img: "/assets/img/about/2022_OSC_Top_Org.png", + }, + { + desc: "2022 OSChina China Open Source Project Selection, awarded Healthy Case of China Open Source Community", + img: "/assets/img/about/OSC_China_Health_Case.png", + }, + { + desc: "2022 Community projects Forest, Hutool, Sa-Token won OSC '2022 Hottest China Open Source Project Community'", + img: "/assets/img/about/OSC_China_Hottest.png", + }, + { + desc: "2022 OSChina awarded Excellent Open Source Technology Team", + img: "/assets/img/about/OSC_Awards_Tech_Team.jpg", + }, + { + desc: "2022 Excellent Award in China Open Source Innovation Competition", + img: "/assets/img/about/Innovation_Excellence_Award.jpg", + }, + { + desc: "2022 Annual Popularity Technology Team", + img: "/assets/img/about/Popular_Technical_Team.jpg", + }, + { + desc: "2023 The most active open source organization", + img: "/assets/img/about/2023_Most_Active_Org.jpg", + }, + { + desc: "2023 The most popular and beloved open source organization", + img: "/assets/img/about/Concerned_favorite_Org.jpg", + }, + { + desc: "2024 China Internet Development Innovation and Investment Competition (Open Source)", + img: "/assets/img/about/2024-China-Internet-Development-Innovation.jpg", + }, + ], + TIME_LINE: [ + { + year: "2017", + items: [ + "Entered the open-source stage", + "Established two top-level projects: Hmily and Myth", + ], + }, + { + year: "2021", + items: [ + "Joined 14 top-level projects", + "Including hutool, Sa-Token, Jpom, etc.", + "Community instantly became colorful", + ], + }, + { + year: "2022", + items: [ + "Donated incubation for 13 projects", + "Total community projects reached 33", + "Added 6 GVP (Gitee Most Valuable Open Source) projects", + "Gitee received over 84.2K stars", + "Honored with the 2022 Excellent Open Source Technology Team award", + ], + }, + { + year: "2023", + items: [ + "Added 4 more top-level projects: RuoYi-Vue-Plus, sms4j, etc.", + "Brimming with energy, joined incubation for 9 projects", + "Won the 2022 China Open Source Community Health Case Award", + "Won the title of 2022 Popular Technology Team of the Year", + "Won the Excellence Award at the 2022 China Open Source Innovation Competition", + ], + }, + { + year: "2024", + items: [ + "The number of stars in the whole network exceeds 305.5k", + "21 GVP(Gitee Most Valuable Open Source) projects", + "The current total number of Gitee project repositories has reached 76", + "Awarded the most watched and loved open source organization of 2023", + "Most Active Open Source Organization in 2023", + "The Second Prize of the 2024 China Internet Development Innovation and Investment Competition (Open Source)", + ], + }, + ], +}; diff --git a/src/.vuepress/composables/honor/index.ts b/src/.vuepress/composables/honor/index.ts index f48b6da747..4ffbdfbd80 100644 --- a/src/.vuepress/composables/honor/index.ts +++ b/src/.vuepress/composables/honor/index.ts @@ -1,12 +1,12 @@ -import { useLocaleConfig } from "@vuepress/helper/client"; - -import { enHonorOption } from "./en.js"; -import { zhHonorOption } from "./zh.js"; - -export const useHonorLocale = () => - useLocaleConfig({ - "/": enHonorOption, - "/zh/": zhHonorOption - }); - -export * from "./types.js"; +import { useLocaleConfig } from "@vuepress/helper/client"; + +import { enHonorOption } from "./en.js"; +import { zhHonorOption } from "./zh.js"; + +export const useHonorLocale = () => + useLocaleConfig({ + "/": enHonorOption, + "/zh/": zhHonorOption, + }); + +export * from "./types.js"; diff --git a/src/.vuepress/composables/honor/types.ts b/src/.vuepress/composables/honor/types.ts index 31660305c4..45739e517a 100644 --- a/src/.vuepress/composables/honor/types.ts +++ b/src/.vuepress/composables/honor/types.ts @@ -1,14 +1,14 @@ -export interface Slide { - desc: string - img: string -} - -export interface TimeLineItem { - year: string - items: string[] -} - -export interface HonorOption { - SLIDES: Slide[] - TIME_LINE: TimeLineItem[] -} +export interface Slide { + desc: string; + img: string; +} + +export interface TimeLineItem { + year: string; + items: string[]; +} + +export interface HonorOption { + SLIDES: Slide[]; + TIME_LINE: TimeLineItem[]; +} diff --git a/src/.vuepress/composables/honor/zh.ts b/src/.vuepress/composables/honor/zh.ts index 2e10ed1af6..a0080c812c 100644 --- a/src/.vuepress/composables/honor/zh.ts +++ b/src/.vuepress/composables/honor/zh.ts @@ -1,87 +1,87 @@ -import { type HonorOption } from "./types.js"; - -export const zhHonorOption: HonorOption = { - SLIDES: [ - { - desc: "2022年开源社•中国开源年度报告荣获最受欢迎开源组织", - img: "/assets/img/about/2022_OSC_Top_Org.png" - }, - { - desc: "2022年OSChina中国开源项目评选,荣获中国开源社区健康案例", - img: "/assets/img/about/OSC_China_Health_Case.png" - }, - { - desc: "2022年社区下项目 Forest, Hutool, Sa-Token 斩获 OSC “2022年度最火热中国开源项目社区”", - img: "/assets/img/about/OSC_China_Hottest.png" - }, - { - desc: "2022年OSChina授予优秀开源技术团队", - img: "/assets/img/about/OSC_Awards_Tech_Team.jpg" - }, - { - desc: "2022年中国开源创新大赛优秀奖", - img: "/assets/img/about/Innovation_Excellence_Award.jpg" - }, - { - desc: "2022年年度人气技术团队", - img: "/assets/img/about/Popular_Technical_Team.jpg" - }, - { - desc: "2023年最活跃的开源组织", - img: "/assets/img/about/2023_Most_Active_Org.jpg" - }, - { - desc: "2023年最受关注和喜爱的开源组织", - img: "/assets/img/about/Concerned_favorite_Org.jpg" - }, - { - desc: "2024中国互联网发展创新与投资大赛(开源)", - img: "/assets/img/about/2024-China-Internet-Development-Innovation.jpg" - } - ], - TIME_LINE: [ - { - year: "2017", - items: ["进入开源舞台", "创立了两个顶级项目:Hmily 和 Myth"] - }, - { - year: "2021", - items: [ - "加入14个顶级项目", - "包括 hutool、Sa-Token、Jpom 等", - "社区瞬间多彩斑斓" - ] - }, - { - year: "2022", - items: [ - "捐赠孵化了13个项目", - "社区项目总数达33个", - "新增 GVP(Gitee最具价值开源)项目 6 个", - "Gitee收获超过84.2K颗star", - "荣获2022年度优秀开源技术团队荣誉" - ] - }, - { - year: "2023", - items: [ - "再添4个顶级项目:RuoYi-Vue-Plus、sms4j等", - "涌动活力,加入孵化项目9个", - "荣获2022年中国开源社区健康案例", - "荣获2022年中国开源创新大赛优秀奖", - "荣获2022年年度人气技术团队" - ] - }, - { - year: "2024", - items: [ - "全网star数超过305.5k", - "GVP(Gitee最具价值开源)项目达到21个", - "Gitee项目仓库目前总数已达76个", - "荣获2023年最受关注和喜爱的开源组织", - "荣获2023年最活跃的开源组织", - "2024中国互联网发展创新与投资大赛(开源)-二等奖" - ] - } - ] -}; +import { type HonorOption } from "./types.js"; + +export const zhHonorOption: HonorOption = { + SLIDES: [ + { + desc: "2022年开源社•中国开源年度报告荣获最受欢迎开源组织", + img: "/assets/img/about/2022_OSC_Top_Org.png", + }, + { + desc: "2022年OSChina中国开源项目评选,荣获中国开源社区健康案例", + img: "/assets/img/about/OSC_China_Health_Case.png", + }, + { + desc: "2022年社区下项目 Forest, Hutool, Sa-Token 斩获 OSC “2022年度最火热中国开源项目社区”", + img: "/assets/img/about/OSC_China_Hottest.png", + }, + { + desc: "2022年OSChina授予优秀开源技术团队", + img: "/assets/img/about/OSC_Awards_Tech_Team.jpg", + }, + { + desc: "2022年中国开源创新大赛优秀奖", + img: "/assets/img/about/Innovation_Excellence_Award.jpg", + }, + { + desc: "2022年年度人气技术团队", + img: "/assets/img/about/Popular_Technical_Team.jpg", + }, + { + desc: "2023年最活跃的开源组织", + img: "/assets/img/about/2023_Most_Active_Org.jpg", + }, + { + desc: "2023年最受关注和喜爱的开源组织", + img: "/assets/img/about/Concerned_favorite_Org.jpg", + }, + { + desc: "2024中国互联网发展创新与投资大赛(开源)", + img: "/assets/img/about/2024-China-Internet-Development-Innovation.jpg", + }, + ], + TIME_LINE: [ + { + year: "2017", + items: ["进入开源舞台", "创立了两个顶级项目:Hmily 和 Myth"], + }, + { + year: "2021", + items: [ + "加入14个顶级项目", + "包括 hutool、Sa-Token、Jpom 等", + "社区瞬间多彩斑斓", + ], + }, + { + year: "2022", + items: [ + "捐赠孵化了13个项目", + "社区项目总数达33个", + "新增 GVP(Gitee最具价值开源)项目 6 个", + "Gitee收获超过84.2K颗star", + "荣获2022年度优秀开源技术团队荣誉", + ], + }, + { + year: "2023", + items: [ + "再添4个顶级项目:RuoYi-Vue-Plus、sms4j等", + "涌动活力,加入孵化项目9个", + "荣获2022年中国开源社区健康案例", + "荣获2022年中国开源创新大赛优秀奖", + "荣获2022年年度人气技术团队", + ], + }, + { + year: "2024", + items: [ + "全网star数超过305.5k", + "GVP(Gitee最具价值开源)项目达到21个", + "Gitee项目仓库目前总数已达76个", + "荣获2023年最受关注和喜爱的开源组织", + "荣获2023年最活跃的开源组织", + "2024中国互联网发展创新与投资大赛(开源)-二等奖", + ], + }, + ], +}; diff --git a/src/.vuepress/composables/members/en.ts b/src/.vuepress/composables/members/en.ts index 5ce0768e71..4ee0b8273d 100644 --- a/src/.vuepress/composables/members/en.ts +++ b/src/.vuepress/composables/members/en.ts @@ -1,548 +1,549 @@ -import { type MembersOption } from "./types.js"; - -export const enMembersOption: MembersOption = { - MEMBERS: "Members", - DESCRIPTION: - "Dromara community members, from diverse backgrounds, include authors, experts, and developers. They work together to boost open source and invigorate the community's future.", - FUNDER_TITLE: "Funder", - COMMITTEE_TITLE: "Committee Members", - COMMITTER_TITLE: "Organization Members", - FOUNDER: { - role: "Founder", - name: "Xiao Yu", - photo: "/assets/img/members/xiaoyu.webp", - desc: "Founder/VP of Apache ShenYu, Apache Member, author of Hmliy, Raincat, Myth and other distributed transaction frameworks, TVP of Tencent Cloud. Author of \"Deep Understanding of Distributed Transactions: Principles and Practices\". He has been honored as one of the 33 Open Source Pioneers, OSCAR Peak Open Source Person of ICT Academy and other awards." - }, - MEMBERS_ITEM: [ - { - header: "Secretariat", - members: [ - { - role: "Secretary General", - name: "Gong Chao", - photo: "/assets/img/members/tom.webp", - desc: "An open source enthusiast and practitioner, hoping to work on open source while eating bread and singing songs." - }, - { - role: "Deputy Secretary General", - name: "A Chao", - photo: "/assets/img/members/achao.webp", - desc: "00s full-stack developer, Cover Person of Gitee 33rd, author of Stream-Query, Apache StreamPark Committer, Apache ShenYu Committer, Hutool Committer, Mybatis-Plus Committer" - }, - { - role: "Deputy Secretary General", - name: "moremind", - photo: "/assets/img/members/hefengen.webp", - desc: "Member of the Apache ShenYu Project Management Committee (PMC), an enthusiast of open source and technology, embracing open source and daring to practice." - }, - { - role: "Deputy Secretary General", - name: "Don Tang", - photo: "/assets/img/members/tangzhenchao.webp", - desc: "Coordinate spring city Jinan, open source enthusiasts. Author of mybatis-plus-ext and AutoTable" - }, - { - role: "Assistant Secretary-General", - name: "Li Nan", - photo: "/assets/img/members/linan.webp", - desc: "Member of the Open Source Society, Assistant Secretary-General of the dromara open source community, miscellaneous tasks in the open-source community." - }, - ] - }, - { - header: "TOC members", - members: [ - { - role: "TOC Member", - name: "Chen Bin", - photo: "/assets/img/members/chenbin.webp", - desc: "An ordinary bricklayer" - }, - { - role: "TOC Member", - name: "Zhang Yonglun", - photo: "/assets/img/members/zhangyonglun.webp", - desc: "" - }, - { - role: "TOC Member", - name: "Qiu Er", - photo: "/assets/img/members/qiuer.webp", - desc: "Author of Jpom" - }, - { - role: "TOC Member", - name: "Bosai Dong", - photo: "/assets/img/members/bryan31.webp", - desc: "Author of open source project LiteFlow, author of open source project TLog, 2 GVP honors, owner of [Element Tribe] WeChat Official Account. Basic middleware architect, dedicated to architecture-related work for more than ten years. A developer who is always brave and firm in pursuit of light and will soon be confused. Aspire to bring LiteFlow into the ranks of top open source software in China." - }, - { - role: "TOC Member", - name: "Gongzi Jun", - photo: "/assets/img/members/gongzijun.webp", - desc: "Author of Forest" - }, - { - role: "TOC Member", - name: "Lu Xiaolei", - photo: "/assets/img/members/looly.webp", - desc: "Author of Hutool" - }, - { - role: "TOC Member", - name: "Liu Xiao", - photo: "/assets/img/members/liuxiao.webp", - desc: "Author of Sa-Token" - }, - { - role: "TOC Member", - name: "Nuoyan", - photo: "/assets/img/members/nuoyan.webp", - desc: "Member of Apache Shenyu PMC, technical leader of a parking industry company, passionate about open source!" - }, - { - role: "TOC Member", - name: "Qiang Lu", - photo: "/assets/img/members/qianglu.webp", - desc: "Author of Cubic" - }, - { - role: "TOC Member", - name: "mouday", - photo: "/assets/img/members/mouday.webp", - desc: "Author of Domain Admin" - } - ] - }, - { - header: "Organization Members", - members: [ - { - role: "Member", - name: "Acbox Liu (Acbox Sky)", - photo: "/assets/img/members/acbox-liu.webp", - desc: "The author of Newcar, and the creator of BugDuck Open Soruce Team." - }, - { - role: "Member", - name: "Binghe", - photo: "/assets/img/members/binghe.webp", - desc: "Internet technology expert, TVP Tencent Cloud Most Valuable Expert, and technical consultant for multiple companies. Specialized in distributed systems, microservices, and big data with extensive R&D experience. Author of key books and creator of 'Binghe Technology' WeChat account." - }, - { - role: "Member", - name: "Crazy Lion Li", - photo: "/assets/img/members/lion.webp", - desc: "Author of RuoYi-Vue-Plus/RuoYi-Cloud-Plus" - }, - { - role: "Member", - name: "Yuyun Shize / Aoshi Guchen", - photo: "/assets/img/members/aoshiguchen.webp", - desc: "Author of Neutrino-Proxy" - }, - { - role: "Member", - name: "Wind Ruge", - photo: "/assets/img/members/fengruge.webp", - desc: "Author of Sms4j" - }, - { - role: "Member", - name: "Goddess of Failure", - photo: "/assets/img/members/godness.webp", - desc: "Author of Testhub" - }, - { - role: "Member", - name: "Lao Han", - photo: "/assets/img/members/laohan.webp", - desc: "Author of Easy-Es" - }, - { - role: "Member", - name: "Jiang Wei", - photo: "/assets/img/members/vakinge.webp", - desc: "Author of Mendmix, 15 years of experience in architecture within large-scale internet enterprises. Awarded as one of the top 10 practitioners at the 2021 Distributed Database Conference. Led the successful implementation of technology middle platforms for multiple large publicly listed companies, enabling their migration to the cloud." - }, - { - role: "Member", - name: "Running Noodles", - photo: "/assets/img/members/miantiao.webp", - desc: "Author of Goview" - }, - { - role: "Member", - name: "Wang Lei", - photo: "/assets/img/members/wanglei.webp", - desc: "Co-founder of Shaanxi Buddies Network Technology Co., Ltd., Author of Easy-Trans and FHS-Framework, Mybatis Plus Committer" - }, - { - role: "Member", - name: "Gao Shuaixing", - photo: "/assets/img/members/gaoshuaixing.webp", - desc: "Author of Electron-Egg" - }, - { - role: "Member", - name: "Zeming", - photo: "/assets/img/members/zeming.webp", - desc: "Author of open-source project Gobrs-Async, Middleware Architect specializing in concurrent programming and microservices component development, deep expertise in architectural design. If a system is suffering from poor performance, I wave my technical wand like a magician to make it faster and more powerful!" - }, - { - role: "Member", - name: "Zhou Zhou", - photo: "/assets/img/members/zhouzhou.webp", - desc: "Co-founder of j2eefast, Java Backend Architect with over 10 years of Java development experience, working in domains including banking, healthcare, and IoT." - }, - { - role: "Member", - name: "Tang Yuan", - photo: "/assets/img/members/tangyuan.webp", - desc: "Author of Hodor, passionate about research and development related to foundational components." - }, - { - role: "Member", - name: "Xu YanWu", - photo: "/assets/img/members/xuyanwu.webp", - desc: "Author of X File Storage" - }, - { - role: "Member", - name: "Long Zhengyu", - photo: "/assets/img/members/longzhengyu.webp", - desc: "The author of the open-source project WeMQ, the author of the open-source communication middleware Nmqs, engaged in fields such as the Internet of Things, and passionate about open-source." - }, - { - role: "Member", - name: "Kevin", - photo: "/assets/img/members/Kevin.webp", - desc: "Author of Northstar" - }, - { - role: "Member", - name: "yanhom", - photo: "/assets/img/members/yanhom.webp", - desc: "Author of Dynamic-Tp" - }, - { - role: "TOC成员", - name: "Shi Ming", - photo: "/assets/img/members/shiming.webp", - desc: "Author of MaxKey" - }, - { - role: "Member", - name: "zyplayer", - photo: "/assets/img/members/zyplayer.webp", - desc: "Author of zyplayer-doc" - }, - { - role: "Member", - name: "Xiao Hua", - photo: "/assets/img/members/xiaohua.webp", - desc: "Author of Warm-Flow" - }, - { - role: "Member", - name: "xsx", - photo: "/assets/img/members/xsx.webp", - desc: "Author of x-easypdf" - }, - { - role: "Member", - name: "xgc", - photo: "/assets/img/members/xgc.webp", - desc: "Open Source Enthusiast, Java Backend Architect, Author of MilvusPlus, Author of JavaVision, Author of easy-flv " - } - ] - }, - { - header: "Committee Members", - members: [ - { - role: "Committee Member", - name: "Handy", - photo: "/assets/img/members/handy.webp", - desc: "Hutool, Sms4j Committer" - }, - { - role: "Committee Member", - name: "Zhang Zicheng", - photo: "/assets/img/members/zhangzicheng.webp", - desc: "Dynamic-Tp Committer" - }, - { - role: "Committee Member", - name: "Createsequence", - photo: "/assets/img/members/Createsequence.webp", - desc: "Hutool Committer" - }, - { - role: "Committee Member", - name: "Emptypoint", - photo: "/assets/img/members/emptypoint.webp", - desc: "Hutool Committer" - }, - { - role: "Committee Member", - name: "Timothy Lau", - photo: "/assets/img/members/xiaoyi.webp", - desc: "Liteflow Committer, author of LiteFlowX (IDEA Plugin)" - }, - { - role: "Committee Member", - name: "Liang Yun", - photo: "/assets/img/members/liangyun.webp", - desc: "Sa-Token Committer" - }, - { - role: "Committee Member", - name: "AppleOfGray", - photo: "/assets/img/members/AppleOfGray.webp", - desc: "Sa-Token Committer" - }, - { - role: "Committee Member", - name: "Michelle Chung", - photo: "/assets/img/members/MichelleChung.webp", - desc: "RuoYi-Vue-Plus/RuoYi-Cloud-Plus Committer" - }, - { - role: "Committee Member", - name: "Hua Luo Mo", - photo: "/assets/img/members/hualuomo.webp", - desc: "Easy-Es Committer" - }, - { - role: "Committee Member", - name: "Fabian", - photo: "/assets/img/members/fabian.webp", - desc: "Dynamic-Tp Committer" - }, - { - role: "Committee Member", - name: "Zang Zang", - photo: "/assets/img/members/zangzang.webp", - desc: "Stream-Query Committer" - }, - { - role: "Committee Member", - name: "Cason", - photo: "/assets/img/members/Cason.webp", - desc: "Stream-Query Committer" - }, - { - role: "Committee Member", - name: "Charles7c", - photo: "/assets/img/members/Charles7c.webp", - desc: "Sms4j Committer" - }, - { - role: "Committee Member", - name: "Rain", - photo: "/assets/img/members/Rain.webp", - desc: "LiteFlow Committer" - }, - { - role: "Committee Member", - name: "Ricahrd", - photo: "/assets/img/members/Ricahrd.webp", - desc: "Sms4j Committer" - }, - { - role: "Committee Member", - name: "Hua Cheng", - photo: "/assets/img/members/huacheng.webp", - desc: "HertzBeat Committer, major contributions include Zookeeper protocol compatibility, JVM protocol compatibility, tomcat server monitoring, MQ monitoring, ElasticSearch middleware monitoring adaptation, etc." - }, - { - role: "Committee Member", - name: "Xiao Yan", - photo: "/assets/img/members/xiaoyan.webp", - desc: "Sms4j Committer" - }, - { - role: "Committee Member", - name: "Xiao Ma", - photo: "/assets/img/members/xiaoma.webp", - desc: "Liteflow Committer" - }, - { - role: "Committee Member", - name: "Redick01", - photo: "/assets/img/members/Redick01.webp", - desc: "Dynamic-Tp Committer" - }, - { - role: "Committee Member", - name: "Cheng Kaitang", - photo: "/assets/img/members/chengkaitang.webp", - desc: "Liteflow Committer, major contributions include development of liteflow-sql plugin" - }, - { - role: "Committee Member", - name: "Dale Lee", - photo: "/assets/img/members/DaleLee.webp", - desc: "Liteflow Committer, major contributions include timeout control for any component" - }, - { - role: "Committee Member", - name: "Kam To Hung", - photo: "/assets/img/members/KamToHung.webp", - desc: "Dynamic-Tp/Stream-Query Committer" - }, - { - role: "Committee Member", - name: "Xiao Zhang", - photo: "/assets/img/members/xiaozhang.webp", - desc: "Liteflow Committer, major contributions include circular asynchronous mode" - }, - { - role: "Committee Member", - name: "Rainie", - photo: "/assets/img/members/Rainie.webp", - desc: "Liteflow Committer" - }, - { - role: "Committee Member", - name: "Hotstrip", - photo: "/assets/img/members/Hotstrip.webp", - desc: "Jpom Committer" - }, - { - role: "Committee Member", - name: "Xu Chang An", - photo: "/assets/img/members/xuchangan.webp", - desc: "Jpom Committer" - }, - { - role: "Committee Member", - name: "Tiejia Xiao Bao", - photo: "/assets/img/members/tiejiaxiaobao.webp", - desc: "HertzBeat/Sms4j Committer" - }, - { - role: "Committee Member", - name: "Bleachtred", - photo: "/assets/img/members/bleachtred.webp", - desc: "Sms4j Committer" - }, - { - role: "Committee Member", - name: "Ceilzcx", - photo: "/assets/img/members/Ceilzcx.webp", - desc: "Hertzbeat Committer" - }, - { - role: "Committee Member", - name: "Zend Wang", - photo: "/assets/img/members/zendwang.webp", - desc: "Liteflow Committer" - }, - { - role: "Committee Member", - name: "Sh1yu", - photo: "/assets/img/members/Sh1yu.webp", - desc: "Zyplayer-Doc Committer" - }, - { - role: "Committee Member", - name: "Frog Catcher", - photo: "/assets/img/members/zhuawashi.webp", - desc: "RuoYi-Vue-Plus Committer" - }, - { - role: "Committee Member", - name: "Zhao Qingran", - photo: "/assets/img/members/zhaoqingran.webp", - desc: "Hertzbeat Committer" - }, - { - role: "Committee Member", - name: "CHMing", - photo: "/assets/img/members/CHMing.webp", - desc: "Forest Committer" - }, - { - role: "Committee Member", - name: "Yuyuan", - photo: "/assets/img/members/yuyuan.webp", - desc: "Hodor Committer" - }, - { - role: "Committee Member", - name: "Superlit", - photo: "/assets/img/members/Superlit.webp", - desc: "Hodor Committer" - }, - { - role: "Committee Member", - name: "Dazer007", - photo: "/assets/img/members/Dazer007.webp", - desc: "Hutool Committer" - }, - { - role: "Committee Member", - name: "LuoYi", - photo: "/assets/img/members/luoyi.webp", - desc: "LiteFlow Committer" - }, - { - role: "Committee Member", - name: "Ge Zu Ao", - photo: "/assets/img/members/gezuao.webp", - desc: "LiteFlow Committer" - }, - { - role: "Committee Member", - name: "Yao Kai", - photo: "/assets/img/members/yaokai.webp", - desc: "Dynamic-Tp Committer" - }, - { - role: "Committee Member", - name: "Dong Feng", - photo: "/assets/img/members/dongfeng.webp", - desc: "Sms4j Committer" - }, - { - role: "Committee Member", - name: "Son Xiao", - photo: "/assets/img/members/sonxiao.webp", - desc: "Hertzbeat Committer" - }, - { - role: "Committee Member", - name: "Xue jia ming", - photo: "/assets/img/members/xuejiaming.webp", - desc: "Easy-Query Committer" - }, - { - role: "Committee Member", - name: "Tirion Fordring", - photo: "/assets/img/members/TirionFordring.webp", - desc: "Warm-Flow Committer" - }, - { - role: "Committee Member", - name: "liuyq", - photo: "/assets/img/members/xiaohua.webp", - desc: "Warm-Flow Committer" - }, - { - role: "Committee Member", - name: "Vanlin", - photo: "/assets/img/members/vanlin.webp", - desc: "Warm-Flow Committer" - }, - { - role: "Committee Member", - name: "Zhen", - photo: "/assets/img/members/zhen.webp", - desc: "Warm-Flow Committer" - } - ] - } - ] -}; +import { type MembersOption } from "./types.js"; + +export const enMembersOption: MembersOption = { + SLOGAN: "Dromara : Gathered for passion, advancing together for technology.", + DESCRIPTION: + "Dromara community members, from diverse backgrounds, include authors, experts, and developers. They work together to boost open source and invigorate the community's future.", + FUNDER_TITLE: "Funder", + COMMITTEE_TITLE: "Committee Members", + COMMITTER_TITLE: "Organization Members", + BUTTON: "Join us", + FOUNDER: { + role: "Founder", + name: "Xiao Yu", + photo: "/assets/img/members/xiaoyu.webp", + desc: 'Founder/VP of Apache ShenYu, Apache Member, author of Hmliy, Raincat, Myth and other distributed transaction frameworks, TVP of Tencent Cloud. Author of "Deep Understanding of Distributed Transactions: Principles and Practices". He has been honored as one of the 33 Open Source Pioneers, OSCAR Peak Open Source Person of ICT Academy and other awards.', + }, + MEMBERS_ITEM: [ + { + header: "Secretariat", + members: [ + { + role: "Secretary General", + name: "Gong Chao", + photo: "/assets/img/members/tom.webp", + desc: "An open source enthusiast and practitioner, hoping to work on open source while eating bread and singing songs.", + }, + { + role: "Deputy Secretary General", + name: "A Chao", + photo: "/assets/img/members/achao.webp", + desc: "00s full-stack developer, Cover Person of Gitee 33rd, author of Stream-Query, Apache StreamPark Committer, Apache ShenYu Committer, Hutool Committer, Mybatis-Plus Committer", + }, + { + role: "Deputy Secretary General", + name: "moremind", + photo: "/assets/img/members/hefengen.webp", + desc: "Member of the Apache ShenYu Project Management Committee (PMC), an enthusiast of open source and technology, embracing open source and daring to practice.", + }, + { + role: "Deputy Secretary General", + name: "Don Tang", + photo: "/assets/img/members/tangzhenchao.webp", + desc: "Coordinate spring city Jinan, open source enthusiasts. Author of mybatis-plus-ext and AutoTable", + }, + { + role: "Assistant Secretary-General", + name: "Li Nan", + photo: "/assets/img/members/linan.webp", + desc: "Member of the Open Source Society, Assistant Secretary-General of the dromara open source community, miscellaneous tasks in the open-source community.", + }, + ], + }, + { + header: "TOC members", + members: [ + { + role: "TOC Member", + name: "Chen Bin", + photo: "/assets/img/members/chenbin.webp", + desc: "An ordinary bricklayer", + }, + { + role: "TOC Member", + name: "Zhang Yonglun", + photo: "/assets/img/members/zhangyonglun.webp", + desc: "", + }, + { + role: "TOC Member", + name: "Qiu Er", + photo: "/assets/img/members/qiuer.webp", + desc: "Author of Jpom", + }, + { + role: "TOC Member", + name: "Bosai Dong", + photo: "/assets/img/members/bryan31.webp", + desc: "Author of open source project LiteFlow, author of open source project TLog, 2 GVP honors, owner of [Element Tribe] WeChat Official Account. Basic middleware architect, dedicated to architecture-related work for more than ten years. A developer who is always brave and firm in pursuit of light and will soon be confused. Aspire to bring LiteFlow into the ranks of top open source software in China.", + }, + { + role: "TOC Member", + name: "Gongzi Jun", + photo: "/assets/img/members/gongzijun.webp", + desc: "Author of Forest", + }, + { + role: "TOC Member", + name: "Lu Xiaolei", + photo: "/assets/img/members/looly.webp", + desc: "Author of Hutool", + }, + { + role: "TOC Member", + name: "Liu Xiao", + photo: "/assets/img/members/liuxiao.webp", + desc: "Author of Sa-Token", + }, + { + role: "TOC Member", + name: "Nuoyan", + photo: "/assets/img/members/nuoyan.webp", + desc: "Member of Apache Shenyu PMC, technical leader of a parking industry company, passionate about open source!", + }, + { + role: "TOC Member", + name: "Qiang Lu", + photo: "/assets/img/members/qianglu.webp", + desc: "Author of Cubic", + }, + { + role: "TOC Member", + name: "mouday", + photo: "/assets/img/members/mouday.webp", + desc: "Author of Domain Admin", + }, + ], + }, + { + header: "Organization Members", + members: [ + { + role: "Member", + name: "Acbox Liu (Acbox Sky)", + photo: "/assets/img/members/acbox-liu.webp", + desc: "The author of Newcar, and the creator of BugDuck Open Soruce Team.", + }, + { + role: "Member", + name: "Binghe", + photo: "/assets/img/members/binghe.webp", + desc: "Internet technology expert, TVP Tencent Cloud Most Valuable Expert, and technical consultant for multiple companies. Specialized in distributed systems, microservices, and big data with extensive R&D experience. Author of key books and creator of 'Binghe Technology' WeChat account.", + }, + { + role: "Member", + name: "Crazy Lion Li", + photo: "/assets/img/members/lion.webp", + desc: "Author of RuoYi-Vue-Plus/RuoYi-Cloud-Plus", + }, + { + role: "Member", + name: "Yuyun Shize / Aoshi Guchen", + photo: "/assets/img/members/aoshiguchen.webp", + desc: "Author of Neutrino-Proxy", + }, + { + role: "Member", + name: "Wind Ruge", + photo: "/assets/img/members/fengruge.webp", + desc: "Author of Sms4j", + }, + { + role: "Member", + name: "Goddess of Failure", + photo: "/assets/img/members/godness.webp", + desc: "Author of Testhub", + }, + { + role: "Member", + name: "Lao Han", + photo: "/assets/img/members/laohan.webp", + desc: "Author of Easy-Es", + }, + { + role: "Member", + name: "Jiang Wei", + photo: "/assets/img/members/vakinge.webp", + desc: "Author of Mendmix, 15 years of experience in architecture within large-scale internet enterprises. Awarded as one of the top 10 practitioners at the 2021 Distributed Database Conference. Led the successful implementation of technology middle platforms for multiple large publicly listed companies, enabling their migration to the cloud.", + }, + { + role: "Member", + name: "Running Noodles", + photo: "/assets/img/members/miantiao.webp", + desc: "Author of Goview", + }, + { + role: "Member", + name: "Wang Lei", + photo: "/assets/img/members/wanglei.webp", + desc: "Co-founder of Shaanxi Buddies Network Technology Co., Ltd., Author of Easy-Trans and FHS-Framework, Mybatis Plus Committer", + }, + { + role: "Member", + name: "Gao Shuaixing", + photo: "/assets/img/members/gaoshuaixing.webp", + desc: "Author of Electron-Egg", + }, + { + role: "Member", + name: "Zeming", + photo: "/assets/img/members/zeming.webp", + desc: "Author of open-source project Gobrs-Async, Middleware Architect specializing in concurrent programming and microservices component development, deep expertise in architectural design. If a system is suffering from poor performance, I wave my technical wand like a magician to make it faster and more powerful!", + }, + { + role: "Member", + name: "Zhou Zhou", + photo: "/assets/img/members/zhouzhou.webp", + desc: "Co-founder of j2eefast, Java Backend Architect with over 10 years of Java development experience, working in domains including banking, healthcare, and IoT.", + }, + { + role: "Member", + name: "Tang Yuan", + photo: "/assets/img/members/tangyuan.webp", + desc: "Author of Hodor, passionate about research and development related to foundational components.", + }, + { + role: "Member", + name: "Xu YanWu", + photo: "/assets/img/members/xuyanwu.webp", + desc: "Author of X File Storage", + }, + { + role: "Member", + name: "Long Zhengyu", + photo: "/assets/img/members/longzhengyu.webp", + desc: "The author of the open-source project WeMQ, the author of the open-source communication middleware Nmqs, engaged in fields such as the Internet of Things, and passionate about open-source.", + }, + { + role: "Member", + name: "Kevin", + photo: "/assets/img/members/Kevin.webp", + desc: "Author of Northstar", + }, + { + role: "Member", + name: "yanhom", + photo: "/assets/img/members/yanhom.webp", + desc: "Author of Dynamic-Tp", + }, + { + role: "TOC成员", + name: "Shi Ming", + photo: "/assets/img/members/shiming.webp", + desc: "Author of MaxKey", + }, + { + role: "Member", + name: "zyplayer", + photo: "/assets/img/members/zyplayer.webp", + desc: "Author of zyplayer-doc", + }, + { + role: "Member", + name: "Xiao Hua", + photo: "/assets/img/members/xiaohua.webp", + desc: "Author of Warm-Flow", + }, + { + role: "Member", + name: "xsx", + photo: "/assets/img/members/xsx.webp", + desc: "Author of x-easypdf", + }, + { + role: "Member", + name: "xgc", + photo: "/assets/img/members/xgc.webp", + desc: "Open Source Enthusiast, Java Backend Architect, Author of MilvusPlus, Author of JavaVision, Author of easy-flv ", + }, + ], + }, + { + header: "Committee Members", + members: [ + { + role: "Committee Member", + name: "Handy", + photo: "/assets/img/members/handy.webp", + desc: "Hutool, Sms4j Committer", + }, + { + role: "Committee Member", + name: "Zhang Zicheng", + photo: "/assets/img/members/zhangzicheng.webp", + desc: "Dynamic-Tp Committer", + }, + { + role: "Committee Member", + name: "Createsequence", + photo: "/assets/img/members/Createsequence.webp", + desc: "Hutool Committer", + }, + { + role: "Committee Member", + name: "Emptypoint", + photo: "/assets/img/members/emptypoint.webp", + desc: "Hutool Committer", + }, + { + role: "Committee Member", + name: "Timothy Lau", + photo: "/assets/img/members/xiaoyi.webp", + desc: "Liteflow Committer, author of LiteFlowX (IDEA Plugin)", + }, + { + role: "Committee Member", + name: "Liang Yun", + photo: "/assets/img/members/liangyun.webp", + desc: "Sa-Token Committer", + }, + { + role: "Committee Member", + name: "AppleOfGray", + photo: "/assets/img/members/AppleOfGray.webp", + desc: "Sa-Token Committer", + }, + { + role: "Committee Member", + name: "Michelle Chung", + photo: "/assets/img/members/MichelleChung.webp", + desc: "RuoYi-Vue-Plus/RuoYi-Cloud-Plus Committer", + }, + { + role: "Committee Member", + name: "Hua Luo Mo", + photo: "/assets/img/members/hualuomo.webp", + desc: "Easy-Es Committer", + }, + { + role: "Committee Member", + name: "Fabian", + photo: "/assets/img/members/fabian.webp", + desc: "Dynamic-Tp Committer", + }, + { + role: "Committee Member", + name: "Zang Zang", + photo: "/assets/img/members/zangzang.webp", + desc: "Stream-Query Committer", + }, + { + role: "Committee Member", + name: "Cason", + photo: "/assets/img/members/Cason.webp", + desc: "Stream-Query Committer", + }, + { + role: "Committee Member", + name: "Charles7c", + photo: "/assets/img/members/Charles7c.webp", + desc: "Sms4j Committer", + }, + { + role: "Committee Member", + name: "Rain", + photo: "/assets/img/members/Rain.webp", + desc: "LiteFlow Committer", + }, + { + role: "Committee Member", + name: "Ricahrd", + photo: "/assets/img/members/Ricahrd.webp", + desc: "Sms4j Committer", + }, + { + role: "Committee Member", + name: "Hua Cheng", + photo: "/assets/img/members/huacheng.webp", + desc: "HertzBeat Committer, major contributions include Zookeeper protocol compatibility, JVM protocol compatibility, tomcat server monitoring, MQ monitoring, ElasticSearch middleware monitoring adaptation, etc.", + }, + { + role: "Committee Member", + name: "Xiao Yan", + photo: "/assets/img/members/xiaoyan.webp", + desc: "Sms4j Committer", + }, + { + role: "Committee Member", + name: "Xiao Ma", + photo: "/assets/img/members/xiaoma.webp", + desc: "Liteflow Committer", + }, + { + role: "Committee Member", + name: "Redick01", + photo: "/assets/img/members/Redick01.webp", + desc: "Dynamic-Tp Committer", + }, + { + role: "Committee Member", + name: "Cheng Kaitang", + photo: "/assets/img/members/chengkaitang.webp", + desc: "Liteflow Committer, major contributions include development of liteflow-sql plugin", + }, + { + role: "Committee Member", + name: "Dale Lee", + photo: "/assets/img/members/DaleLee.webp", + desc: "Liteflow Committer, major contributions include timeout control for any component", + }, + { + role: "Committee Member", + name: "Kam To Hung", + photo: "/assets/img/members/KamToHung.webp", + desc: "Dynamic-Tp/Stream-Query Committer", + }, + { + role: "Committee Member", + name: "Xiao Zhang", + photo: "/assets/img/members/xiaozhang.webp", + desc: "Liteflow Committer, major contributions include circular asynchronous mode", + }, + { + role: "Committee Member", + name: "Rainie", + photo: "/assets/img/members/Rainie.webp", + desc: "Liteflow Committer", + }, + { + role: "Committee Member", + name: "Hotstrip", + photo: "/assets/img/members/Hotstrip.webp", + desc: "Jpom Committer", + }, + { + role: "Committee Member", + name: "Xu Chang An", + photo: "/assets/img/members/xuchangan.webp", + desc: "Jpom Committer", + }, + { + role: "Committee Member", + name: "Tiejia Xiao Bao", + photo: "/assets/img/members/tiejiaxiaobao.webp", + desc: "HertzBeat/Sms4j Committer", + }, + { + role: "Committee Member", + name: "Bleachtred", + photo: "/assets/img/members/bleachtred.webp", + desc: "Sms4j Committer", + }, + { + role: "Committee Member", + name: "Ceilzcx", + photo: "/assets/img/members/Ceilzcx.webp", + desc: "Hertzbeat Committer", + }, + { + role: "Committee Member", + name: "Zend Wang", + photo: "/assets/img/members/zendwang.webp", + desc: "Liteflow Committer", + }, + { + role: "Committee Member", + name: "Sh1yu", + photo: "/assets/img/members/Sh1yu.webp", + desc: "Zyplayer-Doc Committer", + }, + { + role: "Committee Member", + name: "Frog Catcher", + photo: "/assets/img/members/zhuawashi.webp", + desc: "RuoYi-Vue-Plus Committer", + }, + { + role: "Committee Member", + name: "Zhao Qingran", + photo: "/assets/img/members/zhaoqingran.webp", + desc: "Hertzbeat Committer", + }, + { + role: "Committee Member", + name: "CHMing", + photo: "/assets/img/members/CHMing.webp", + desc: "Forest Committer", + }, + { + role: "Committee Member", + name: "Yuyuan", + photo: "/assets/img/members/yuyuan.webp", + desc: "Hodor Committer", + }, + { + role: "Committee Member", + name: "Superlit", + photo: "/assets/img/members/Superlit.webp", + desc: "Hodor Committer", + }, + { + role: "Committee Member", + name: "Dazer007", + photo: "/assets/img/members/Dazer007.webp", + desc: "Hutool Committer", + }, + { + role: "Committee Member", + name: "LuoYi", + photo: "/assets/img/members/luoyi.webp", + desc: "LiteFlow Committer", + }, + { + role: "Committee Member", + name: "Ge Zu Ao", + photo: "/assets/img/members/gezuao.webp", + desc: "LiteFlow Committer", + }, + { + role: "Committee Member", + name: "Yao Kai", + photo: "/assets/img/members/yaokai.webp", + desc: "Dynamic-Tp Committer", + }, + { + role: "Committee Member", + name: "Dong Feng", + photo: "/assets/img/members/dongfeng.webp", + desc: "Sms4j Committer", + }, + { + role: "Committee Member", + name: "Son Xiao", + photo: "/assets/img/members/sonxiao.webp", + desc: "Hertzbeat Committer", + }, + { + role: "Committee Member", + name: "Xue jia ming", + photo: "/assets/img/members/xuejiaming.webp", + desc: "Easy-Query Committer", + }, + { + role: "Committee Member", + name: "Tirion Fordring", + photo: "/assets/img/members/TirionFordring.webp", + desc: "Warm-Flow Committer", + }, + { + role: "Committee Member", + name: "liuyq", + photo: "/assets/img/members/xiaohua.webp", + desc: "Warm-Flow Committer", + }, + { + role: "Committee Member", + name: "Vanlin", + photo: "/assets/img/members/vanlin.webp", + desc: "Warm-Flow Committer", + }, + { + role: "Committee Member", + name: "Zhen", + photo: "/assets/img/members/zhen.webp", + desc: "Warm-Flow Committer", + }, + ], + }, + ], +}; diff --git a/src/.vuepress/composables/members/index.ts b/src/.vuepress/composables/members/index.ts index ce5a1b5e71..2d9ae726e6 100644 --- a/src/.vuepress/composables/members/index.ts +++ b/src/.vuepress/composables/members/index.ts @@ -6,7 +6,7 @@ import { zhMembersOption } from "./zh.js"; export const useMembersLocale = () => useLocaleConfig({ "/": enMembersOption, - "/zh/": zhMembersOption + "/zh/": zhMembersOption, }); export * from "./types.js"; diff --git a/src/.vuepress/composables/members/types.ts b/src/.vuepress/composables/members/types.ts index 213d2e7454..7695af5e4d 100644 --- a/src/.vuepress/composables/members/types.ts +++ b/src/.vuepress/composables/members/types.ts @@ -1,21 +1,22 @@ -export interface Member { - role: string - photo: string - name: string - desc: string -} - -interface MembersGroup { - header: string - members: Member[] -} - -export interface MembersOption { - MEMBERS: string - DESCRIPTION: string - FOUNDER: Member - Funder_TITLE: string - COMMITTEE_TITLE: string - COMMITTER_TITLE: string - MEMBERS_ITEM: MembersGroup[] -} +export interface Member { + role: string; + photo: string; + name: string; + desc: string; +} + +interface MembersGroup { + header: string; + members: Member[]; +} + +export interface MembersOption { + SLOGAN: string; + DESCRIPTION: string; + FOUNDER: Member; + FUNDER_TITLE: string; + COMMITTEE_TITLE: string; + COMMITTER_TITLE: string; + MEMBERS_ITEM: MembersGroup[]; + BUTTON: string; +} diff --git a/src/.vuepress/composables/members/zh.ts b/src/.vuepress/composables/members/zh.ts index 314ede3061..daef865b96 100644 --- a/src/.vuepress/composables/members/zh.ts +++ b/src/.vuepress/composables/members/zh.ts @@ -1,554 +1,555 @@ -import { type MembersOption } from "./types.js"; - -export const zhMembersOption: MembersOption = { - MEMBERS: "成员", - DESCRIPTION: - "Dromara开源社区汇集了开源项目创作者、技术专家、开发者和热心于开源与技术的伙伴们。成员们互相支持和协作,共同推动开源技术前行,为社区注入活力。", - FUNDER_TITLE: "创始人", - COMMITTEE_TITLE: "委员会成员", - COMMITTER_TITLE: "组织成员", - FOUNDER: { - role: "创始人", - name: "肖宇", - photo: "/assets/img/members/xiaoyu.webp", - desc: "Apache ShenYu 创始人/VP,Apache Member,Hmliy,Raincat,Myth 等分布式事务框架作者, 腾讯云TVP. 著有《深入理解分布式事务:原理与实战》一书。曾获评 开源先锋 33 之一, 信通院 OSCAR 尖峰开源人物等奖项。" - }, - MEMBERS_ITEM: [ - { - header: "秘书处", - members: [ - { - role: "秘书长", - name: "巩超", - photo: "/assets/img/members/tom.webp", - desc: "开源爱好与实践者,希望边做着开源边吃着面包边唱着歌。" - }, - { - role: "副秘书长", - name: "阿超", - photo: "/assets/img/members/achao.webp", - desc: "00后全栈开发,Gitee第33期封面人物,Stream-Query 作者,Apache StreamPark Committer、Apache ShenYu Committer、Hutool Committer、Mybatis-Plus Committer" - }, - { - role: "副秘书长", - name: "何凤恩", - photo: "/assets/img/members/hefengen.webp", - desc: "Apache ShenYu PMC member, 开源与技术爱好者,拥抱开源,敢于实践。" - }, - { - role: "副秘书长", - name: "唐振超", - photo: "/assets/img/members/tangzhenchao.webp", - desc: "坐标泉城济南,开源爱好者。mybatis-plus-ext 作者,AutoTable 作者" - }, - { - role: "秘书长助理", - name: "李楠", - photo: "/assets/img/members/linan.webp", - desc: "开源社成员、dromara开源社区秘书长助理,开源社区打杂" - }, - ] - }, - { - header: "技术委员会成员", - members: [ - { - role: "TOC成员", - name: "陈斌", - photo: "/assets/img/members/chenbin.webp", - desc: "一个普通的搬砖人" - }, - { - role: "TOC成员", - name: "张永伦", - photo: "/assets/img/members/zhangyonglun.webp", - desc: "" - }, - { - role: "TOC成员", - name: "秋尔", - photo: "/assets/img/members/qiuer.webp", - desc: "Jpom 作者" - }, - { - role: "TOC成员", - name: "铂赛东", - photo: "/assets/img/members/bryan31.webp", - desc: "开源项目LiteFlow作者,开源项目TLog作者,拥有2个GVP荣誉,[元人部落]公众号博主。基础中间件架构师,深耕架构相关工作十多年。一个永远勇敢坚定追逐光的即将不惑之年的开发者。立志把LiteFlow带入中国顶尖开源软件的行列。" - }, - { - role: "TOC成员", - name: "公子骏", - photo: "/assets/img/members/gongzijun.webp", - desc: "开源项目 Forest 作者,Java 后台架构师。有十多年 Java 开发经验,主要专注于架构、微服务、分布式等领域,也会研究机器学习、算法等业界新技术。" - }, - { - role: "TOC成员", - name: "路小磊", - photo: "/assets/img/members/looly.webp", - desc: "Hutool 作者" - }, - { - role: "TOC成员", - name: "刘潇", - photo: "/assets/img/members/liuxiao.webp", - desc: "Sa-Token 作者" - }, - { - role: "TOC成员", - name: "诺言", - photo: "/assets/img/members/nuoyan.webp", - desc: "Apache Shenyu PMC 成员,停车行业某公司技术负责人,热爱开源!" - }, - { - role: "TOC成员", - name: "石鸣", - photo: "/assets/img/members/shiming.webp", - desc: "MaxKey 作者" - }, - { - role: "TOC成员", - name: "鲁强", - photo: "/assets/img/members/qianglu.webp", - desc: "Cubic 作者" - } - ] - }, - { - header: "委员会成员", - members: [ - { - role: "委员会成员", - name: "刘陈阳", - photo: "/assets/img/members/acbox-liu.webp", - desc: "Newcar作者,BugDuck开源团队创始人" - }, - { - role: "委员会成员", - name: "冰河", - photo: "/assets/img/members/binghe.webp", - desc: "互联网技术专家、TVP腾讯云最具价值专家、多家互联网公司技术顾问。致力于分布式系统、微服务、数据库和大数据技术研究,拥有丰富的架构和研发经验。著有《深入理解高并发编程:核心原理与案例实战》、《深入理解高并发编程:JDK核心技术》、《深入理解分布式事务:原理与实战》等多本畅享书;“冰河技术”公众号作者。" - }, - { - role: "委员会成员", - name: "疯狂的狮子Li", - photo: "/assets/img/members/lion.webp", - desc: "RuoYi-Vue-Plus/RuoYi-Cloud-Plus 作者" - }, - { - role: "委员会成员", - name: "雨韵诗泽 / 傲世孤尘", - photo: "/assets/img/members/aoshiguchen.webp", - desc: "Neutrino-Proxy 作者" - }, - { - role: "委员会成员", - name: "风如歌", - photo: "/assets/img/members/fengruge.webp", - desc: "Sms4j 作者" - }, - { - role: "委员会成员", - name: "失败女神", - photo: "/assets/img/members/godness.webp", - desc: "Testhub 作者" - }, - { - role: "委员会成员", - name: "老汉", - photo: "/assets/img/members/laohan.webp", - desc: "Easy-Es 作者" - }, - { - role: "委员会成员", - name: "姜维", - photo: "/assets/img/members/vakinge.webp", - desc: "Mendmix 作者,15互联网大型企业架构经验。荣获2021年分布式数据库大会十大实践人物。主导完成多家大型上市公司技术中台建设,服务上云。" - }, - { - role: "委员会成员", - name: "奔跑的面条", - photo: "/assets/img/members/miantiao.webp", - desc: "Goview 作者" - }, - { - role: "委员会成员", - name: "王磊", - photo: "/assets/img/members/wanglei.webp", - desc: "陕西小伙伴网络科技有限公司联合创始人,Easy-Trans,FHS-Framework作者,Mybatis Plus Committer" - }, - { - role: "委员会成员", - name: "高帅星", - photo: "/assets/img/members/gaoshuaixing.webp", - desc: "Electron-Egg 作者" - }, - { - role: "委员会成员", - name: "泽铭", - photo: "/assets/img/members/zeming.webp", - desc: "开源项目Gobrs-Async 作者, 中间件架构师、专注于并发编程、微服务组件研发领域、深耕架构方向多年。如果系统出现了糟糕的性能,我会像个魔术师一样挥舞着我的技术魔杖,让它们变得更快、更强大!" - }, - { - role: "委员会成员", - name: "周周", - photo: "/assets/img/members/zhouzhou.webp", - desc: "j2eefast联合创始人、Java后端架构师、有10年以上 Java 开发经验、从事银行金融、医疗行业、物联网等领域。" - }, - { - role: "委员会成员", - name: "汤圆", - photo: "/assets/img/members/tangyuan.webp", - desc: "Hodor作者,热爱基础组件相关的研究、开发" - }, - { - role: "委员会成员", - name: "许言武", - photo: "/assets/img/members/xuyanwu.webp", - desc: "X File Storage 作者" - }, - { - role: "委员会成员", - name: "龙政宇", - photo: "/assets/img/members/longzhengyu.webp", - desc: "开源项目 WeMQ 作者,开源通信层中间件 Nmqs 作者,从事物联网等领域,热爱开源。" - }, - { - role: "委员会成员", - name: "黄伟亮", - photo: "/assets/img/members/Kevin.webp", - desc: "Northstar 作者" - }, - { - role: "委员会成员", - name: "yanhom", - photo: "/assets/img/members/yanhom.webp", - desc: "Dynamic-Tp 作者" - }, - { - role: "委员会成员", - name: "暮光:城中城", - photo: "/assets/img/members/zyplayer.webp", - desc: "zyplayer-doc 作者" - }, - { - role: "委员会成员", - name: "mouday", - photo: "/assets/img/members/mouday.webp", - desc: "Domain Admin作者" - }, - { - role: "委员会成员", - name: "晓华", - photo: "/assets/img/members/xiaohua.webp", - desc: "Warm-Flow 作者" - }, - { - role: "委员会成员", - name: "xsx", - photo: "/assets/img/members/xsx.webp", - desc: "x-esypdf作者" - }, - { - role: "委员会成员", - name: "勾国印", - photo: "/assets/img/members/gouguoyin.webp", - desc: "坐标北京,开源爱好者,carbon 作者;性别男,爱好女;非著名北漂挨踢男、程序猿,伪文艺2B青年,资深屌丝;据说爱美女,爱网络,爱旅游,各种控,各种宅,不纠结会死星人,不折腾会死星人。此人纯属虚构,如有雷同,纯属被抄袭…… " - }, - { - role: "委员会成员", - name: "熊国超", - photo: "/assets/img/members/xgc.webp", - desc: "开源爱好者,Java后端架构师,MilvusPlus 作者,JavaVision作者,easy-flv作者 " - } - ] - }, - { - header: "组织成员", - members: [ - { - role: "成员", - name: "Handy", - photo: "/assets/img/members/handy.webp", - desc: "Hutool、Sms4j Committer" - }, - { - role: "成员", - name: "张子成", - photo: "/assets/img/members/zhangzicheng.webp", - desc: "Dynamic-Tp Committer" - }, - { - role: "成员", - name: "Createsequence", - photo: "/assets/img/members/Createsequence.webp", - desc: "Hutool Committer" - }, - { - role: "成员", - name: "emptypoint", - photo: "/assets/img/members/emptypoint.webp", - desc: "Hutool Committer" - }, - { - role: "成员", - name: "刘沛强", - photo: "/assets/img/members/xiaoyi.webp", - desc: "Liteflow Committer,LiteFlowX(IDEA Plugin)作者" - }, - { - role: "成员", - name: "凉云", - photo: "/assets/img/members/liangyun.webp", - desc: "Sa-Token Committer" - }, - { - role: "成员", - name: "AppleOfGray", - photo: "/assets/img/members/AppleOfGray.webp", - desc: "Sa-Token Committer" - }, - { - role: "成员", - name: "MichelleChung", - photo: "/assets/img/members/MichelleChung.webp", - desc: "RuoYi-Vue-Plus/RuoYi-Cloud-Plus Committer" - }, - { - role: "成员", - name: "花落陌", - photo: "/assets/img/members/hualuomo.webp", - desc: "Easy-Es Committer" - }, - { - role: "成员", - name: "fabian", - photo: "/assets/img/members/fabian.webp", - desc: "Dynamic-Tp Committer" - }, - { - role: "成员", - name: "臧臧", - photo: "/assets/img/members/zangzang.webp", - desc: "Stream-Query Committer" - }, - { - role: "成员", - name: "Cason", - photo: "/assets/img/members/Cason.webp", - desc: "Stream-Query Committer" - }, - { - role: "成员", - name: "Charles7c", - photo: "/assets/img/members/Charles7c.webp", - desc: "Sms4j Committer" - }, - { - role: "成员", - name: "Rain", - photo: "/assets/img/members/Rain.webp", - desc: "LiteFlow Committer" - }, - { - role: "成员", - name: "Ricahrd", - photo: "/assets/img/members/Ricahrd.webp", - desc: "Sms4j Committer" - }, - { - role: "成员", - name: "花城", - photo: "/assets/img/members/huacheng.webp", - desc: "HertzBeat Committer,主要贡献有 Zookeeper协议兼容、JVM协议兼容、tomcat服务器监控、MQ监控、适配ElasticSearch中间件监控等" - }, - { - role: "成员", - name: "小眼", - photo: "/assets/img/members/xiaoyan.webp", - desc: "Sms4j Committer" - }, - { - role: "成员", - name: "小马", - photo: "/assets/img/members/xiaoma.webp", - desc: "Liteflow Committer" - }, - { - role: "成员", - name: "Redick01", - photo: "/assets/img/members/Redick01.webp", - desc: "Dynamic-Tp Committer" - }, - { - role: "成员", - name: "城铠唐", - photo: "/assets/img/members/chengkaitang.webp", - desc: "Liteflow Committer,主要贡献有liteflow-sql插件开发等" - }, - { - role: "成员", - name: "DaleLee", - photo: "/assets/img/members/DaleLee.webp", - desc: "Liteflow Committer,主要贡献有对任意组件进行超时控制等" - }, - { - role: "成员", - name: "KamToHung", - photo: "/assets/img/members/KamToHung.webp", - desc: "Dynamic-Tp/Stream-Query Committer" - }, - { - role: "成员", - name: "小张", - photo: "/assets/img/members/xiaozhang.webp", - desc: "Liteflow Committer,主要贡献有循环异步模式等" - }, - { - role: "成员", - name: "Rainie", - photo: "/assets/img/members/Rainie.webp", - desc: "Liteflow Committer" - }, - { - role: "成员", - name: "Hotstrip", - photo: "/assets/img/members/Hotstrip.webp", - desc: "Jpom Committer" - }, - { - role: "成员", - name: "许长安", - photo: "/assets/img/members/xuchangan.webp", - desc: "Jpom Committer" - }, - { - role: "成员", - name: "铁甲小宝", - photo: "/assets/img/members/tiejiaxiaobao.webp", - desc: "HertzBeat/Sms4j Committer" - }, - { - role: "成员", - name: "bleachtred", - photo: "/assets/img/members/bleachtred.webp", - desc: "Sms4j Committer" - }, - { - role: "成员", - name: "Ceilzcx", - photo: "/assets/img/members/Ceilzcx.webp", - desc: "Hertzbeat Committer" - }, - { - role: "成员", - name: "zendwang", - photo: "/assets/img/members/zendwang.webp", - desc: "Liteflow Committer" - }, - { - role: "成员", - name: "Sh1yu", - photo: "/assets/img/members/Sh1yu.webp", - desc: "Zyplayer-Doc Committer" - }, - { - role: "成员", - name: "抓蛙师", - photo: "/assets/img/members/zhuawashi.webp", - desc: "RuoYi-Vue-Plus Committer" - }, - { - role: "成员", - name: "赵青然", - photo: "/assets/img/members/zhaoqingran.webp", - desc: "Hertzbeat Committer" - }, - { - role: "成员", - name: "CHMing", - photo: "/assets/img/members/CHMing.webp", - desc: "Forest Committer" - }, - { - role: "成员", - name: "芋圆", - photo: "/assets/img/members/yuyuan.webp", - desc: "Hodor Committer" - }, - { - role: "成员", - name: "Superlit", - photo: "/assets/img/members/Superlit.webp", - desc: "Hodor Committer" - }, - { - role: "成员", - name: "Dazer007", - photo: "/assets/img/members/Dazer007.webp", - desc: "Hutool Committer" - }, - { - role: "成员", - name: "落一", - photo: "/assets/img/members/luoyi.webp", - desc: "LiteFlow Committer" - }, - { - role: "成员", - name: "葛足澳", - photo: "/assets/img/members/gezuao.webp", - desc: "LiteFlow Committer" - }, - { - role: "成员", - name: "姚凯", - photo: "/assets/img/members/yaokai.webp", - desc: "Dynamic-Tp Committer" - }, - { - role: "成员", - name: "东风", - photo: "/assets/img/members/dongfeng.webp", - desc: "Sms4j Committer" - }, - { - role: "成员", - name: "淞筱", - photo: "/assets/img/members/sonxiao.webp", - desc: "Hertzbeat Committer" - }, - { - role: "成员", - name: "薛家明", - photo: "/assets/img/members/xuejiaming.webp", - desc: "Easy-Query Committer" - }, - { - role: "成员", - name: "夏日刚", - photo: "/assets/img/members/TirionFordring.webp", - desc: "Warm-Flow Committer" - }, - { - role: "成员", - name: "梁小梁", - photo: "/assets/img/members/xiaohua.webp", - desc: "Warm-Flow Committer" - }, - { - role: "成员", - name: "Vanlin", - photo: "/assets/img/members/vanlin.webp", - desc: "Warm-Flow Committer" - }, - { - role: "成员", - name: "Zhen", - photo: "/assets/img/members/zhen.webp", - desc: "Warm-Flow Committer" - } - ] - } - ] -}; +import { type MembersOption } from "./types.js"; + +export const zhMembersOption: MembersOption = { + SLOGAN: "Dromara 开源社区: 因热爱相聚,为技术共进", + DESCRIPTION: + "Dromara开源社区汇集了开源项目创作者、技术专家、开发者和热心于开源与技术的伙伴们。成员们互相支持和协作,共同推动开源技术前行,为社区注入活力。", + FUNDER_TITLE: "创始人", + COMMITTEE_TITLE: "委员会成员", + COMMITTER_TITLE: "组织成员", + BUTTON: "加入我们", + FOUNDER: { + role: "创始人", + name: "肖宇", + photo: "/assets/img/members/xiaoyu.webp", + desc: "Apache ShenYu 创始人/VP,Apache Member,Hmliy,Raincat,Myth 等分布式事务框架作者, 腾讯云TVP. 著有《深入理解分布式事务:原理与实战》一书。曾获评 开源先锋 33 之一, 信通院 OSCAR 尖峰开源人物等奖项。", + }, + MEMBERS_ITEM: [ + { + header: "秘书处", + members: [ + { + role: "秘书长", + name: "巩超", + photo: "/assets/img/members/tom.webp", + desc: "开源爱好与实践者,希望边做着开源边吃着面包边唱着歌。", + }, + { + role: "副秘书长", + name: "阿超", + photo: "/assets/img/members/achao.webp", + desc: "00后全栈开发,Gitee第33期封面人物,Stream-Query 作者,Apache StreamPark Committer、Apache ShenYu Committer、Hutool Committer、Mybatis-Plus Committer", + }, + { + role: "副秘书长", + name: "何凤恩", + photo: "/assets/img/members/hefengen.webp", + desc: "Apache ShenYu PMC member, 开源与技术爱好者,拥抱开源,敢于实践。", + }, + { + role: "副秘书长", + name: "唐振超", + photo: "/assets/img/members/tangzhenchao.webp", + desc: "坐标泉城济南,开源爱好者。mybatis-plus-ext 作者,AutoTable 作者", + }, + { + role: "秘书长助理", + name: "李楠", + photo: "/assets/img/members/linan.webp", + desc: "开源社成员、dromara开源社区秘书长助理,开源社区打杂", + }, + ], + }, + { + header: "技术委员会成员", + members: [ + { + role: "TOC成员", + name: "陈斌", + photo: "/assets/img/members/chenbin.webp", + desc: "一个普通的搬砖人", + }, + { + role: "TOC成员", + name: "张永伦", + photo: "/assets/img/members/zhangyonglun.webp", + desc: "", + }, + { + role: "TOC成员", + name: "秋尔", + photo: "/assets/img/members/qiuer.webp", + desc: "Jpom 作者", + }, + { + role: "TOC成员", + name: "铂赛东", + photo: "/assets/img/members/bryan31.webp", + desc: "开源项目LiteFlow作者,开源项目TLog作者,拥有2个GVP荣誉,[元人部落]公众号博主。基础中间件架构师,深耕架构相关工作十多年。一个永远勇敢坚定追逐光的即将不惑之年的开发者。立志把LiteFlow带入中国顶尖开源软件的行列。", + }, + { + role: "TOC成员", + name: "公子骏", + photo: "/assets/img/members/gongzijun.webp", + desc: "开源项目 Forest 作者,Java 后台架构师。有十多年 Java 开发经验,主要专注于架构、微服务、分布式等领域,也会研究机器学习、算法等业界新技术。", + }, + { + role: "TOC成员", + name: "路小磊", + photo: "/assets/img/members/looly.webp", + desc: "Hutool 作者", + }, + { + role: "TOC成员", + name: "刘潇", + photo: "/assets/img/members/liuxiao.webp", + desc: "Sa-Token 作者", + }, + { + role: "TOC成员", + name: "诺言", + photo: "/assets/img/members/nuoyan.webp", + desc: "Apache Shenyu PMC 成员,停车行业某公司技术负责人,热爱开源!", + }, + { + role: "TOC成员", + name: "石鸣", + photo: "/assets/img/members/shiming.webp", + desc: "MaxKey 作者", + }, + { + role: "TOC成员", + name: "鲁强", + photo: "/assets/img/members/qianglu.webp", + desc: "Cubic 作者", + }, + ], + }, + { + header: "委员会成员", + members: [ + { + role: "委员会成员", + name: "刘陈阳", + photo: "/assets/img/members/acbox-liu.webp", + desc: "Newcar作者,BugDuck开源团队创始人", + }, + { + role: "委员会成员", + name: "冰河", + photo: "/assets/img/members/binghe.webp", + desc: "互联网技术专家、TVP腾讯云最具价值专家、多家互联网公司技术顾问。致力于分布式系统、微服务、数据库和大数据技术研究,拥有丰富的架构和研发经验。著有《深入理解高并发编程:核心原理与案例实战》、《深入理解高并发编程:JDK核心技术》、《深入理解分布式事务:原理与实战》等多本畅享书;“冰河技术”公众号作者。", + }, + { + role: "委员会成员", + name: "疯狂的狮子Li", + photo: "/assets/img/members/lion.webp", + desc: "RuoYi-Vue-Plus/RuoYi-Cloud-Plus 作者", + }, + { + role: "委员会成员", + name: "雨韵诗泽 / 傲世孤尘", + photo: "/assets/img/members/aoshiguchen.webp", + desc: "Neutrino-Proxy 作者", + }, + { + role: "委员会成员", + name: "风如歌", + photo: "/assets/img/members/fengruge.webp", + desc: "Sms4j 作者", + }, + { + role: "委员会成员", + name: "失败女神", + photo: "/assets/img/members/godness.webp", + desc: "Testhub 作者", + }, + { + role: "委员会成员", + name: "老汉", + photo: "/assets/img/members/laohan.webp", + desc: "Easy-Es 作者", + }, + { + role: "委员会成员", + name: "姜维", + photo: "/assets/img/members/vakinge.webp", + desc: "Mendmix 作者,15互联网大型企业架构经验。荣获2021年分布式数据库大会十大实践人物。主导完成多家大型上市公司技术中台建设,服务上云。", + }, + { + role: "委员会成员", + name: "奔跑的面条", + photo: "/assets/img/members/miantiao.webp", + desc: "Goview 作者", + }, + { + role: "委员会成员", + name: "王磊", + photo: "/assets/img/members/wanglei.webp", + desc: "陕西小伙伴网络科技有限公司联合创始人,Easy-Trans,FHS-Framework作者,Mybatis Plus Committer", + }, + { + role: "委员会成员", + name: "高帅星", + photo: "/assets/img/members/gaoshuaixing.webp", + desc: "Electron-Egg 作者", + }, + { + role: "委员会成员", + name: "泽铭", + photo: "/assets/img/members/zeming.webp", + desc: "开源项目Gobrs-Async 作者, 中间件架构师、专注于并发编程、微服务组件研发领域、深耕架构方向多年。如果系统出现了糟糕的性能,我会像个魔术师一样挥舞着我的技术魔杖,让它们变得更快、更强大!", + }, + { + role: "委员会成员", + name: "周周", + photo: "/assets/img/members/zhouzhou.webp", + desc: "j2eefast联合创始人、Java后端架构师、有10年以上 Java 开发经验、从事银行金融、医疗行业、物联网等领域。", + }, + { + role: "委员会成员", + name: "汤圆", + photo: "/assets/img/members/tangyuan.webp", + desc: "Hodor作者,热爱基础组件相关的研究、开发", + }, + { + role: "委员会成员", + name: "许言武", + photo: "/assets/img/members/xuyanwu.webp", + desc: "X File Storage 作者", + }, + { + role: "委员会成员", + name: "龙政宇", + photo: "/assets/img/members/longzhengyu.webp", + desc: "开源项目 WeMQ 作者,开源通信层中间件 Nmqs 作者,从事物联网等领域,热爱开源。", + }, + { + role: "委员会成员", + name: "黄伟亮", + photo: "/assets/img/members/Kevin.webp", + desc: "Northstar 作者", + }, + { + role: "委员会成员", + name: "yanhom", + photo: "/assets/img/members/yanhom.webp", + desc: "Dynamic-Tp 作者", + }, + { + role: "委员会成员", + name: "暮光:城中城", + photo: "/assets/img/members/zyplayer.webp", + desc: "zyplayer-doc 作者", + }, + { + role: "委员会成员", + name: "mouday", + photo: "/assets/img/members/mouday.webp", + desc: "Domain Admin作者", + }, + { + role: "委员会成员", + name: "晓华", + photo: "/assets/img/members/xiaohua.webp", + desc: "Warm-Flow 作者", + }, + { + role: "委员会成员", + name: "xsx", + photo: "/assets/img/members/xsx.webp", + desc: "x-esypdf作者", + }, + { + role: "委员会成员", + name: "勾国印", + photo: "/assets/img/members/gouguoyin.webp", + desc: "坐标北京,开源爱好者,carbon 作者;性别男,爱好女;非著名北漂挨踢男、程序猿,伪文艺2B青年,资深屌丝;据说爱美女,爱网络,爱旅游,各种控,各种宅,不纠结会死星人,不折腾会死星人。此人纯属虚构,如有雷同,纯属被抄袭…… ", + }, + { + role: "委员会成员", + name: "熊国超", + photo: "/assets/img/members/xgc.webp", + desc: "开源爱好者,Java后端架构师,MilvusPlus 作者,JavaVision作者,easy-flv作者 ", + }, + ], + }, + { + header: "组织成员", + members: [ + { + role: "成员", + name: "Handy", + photo: "/assets/img/members/handy.webp", + desc: "Hutool、Sms4j Committer", + }, + { + role: "成员", + name: "张子成", + photo: "/assets/img/members/zhangzicheng.webp", + desc: "Dynamic-Tp Committer", + }, + { + role: "成员", + name: "Createsequence", + photo: "/assets/img/members/Createsequence.webp", + desc: "Hutool Committer", + }, + { + role: "成员", + name: "emptypoint", + photo: "/assets/img/members/emptypoint.webp", + desc: "Hutool Committer", + }, + { + role: "成员", + name: "刘沛强", + photo: "/assets/img/members/xiaoyi.webp", + desc: "Liteflow Committer,LiteFlowX(IDEA Plugin)作者", + }, + { + role: "成员", + name: "凉云", + photo: "/assets/img/members/liangyun.webp", + desc: "Sa-Token Committer", + }, + { + role: "成员", + name: "AppleOfGray", + photo: "/assets/img/members/AppleOfGray.webp", + desc: "Sa-Token Committer", + }, + { + role: "成员", + name: "MichelleChung", + photo: "/assets/img/members/MichelleChung.webp", + desc: "RuoYi-Vue-Plus/RuoYi-Cloud-Plus Committer", + }, + { + role: "成员", + name: "花落陌", + photo: "/assets/img/members/hualuomo.webp", + desc: "Easy-Es Committer", + }, + { + role: "成员", + name: "fabian", + photo: "/assets/img/members/fabian.webp", + desc: "Dynamic-Tp Committer", + }, + { + role: "成员", + name: "臧臧", + photo: "/assets/img/members/zangzang.webp", + desc: "Stream-Query Committer", + }, + { + role: "成员", + name: "Cason", + photo: "/assets/img/members/Cason.webp", + desc: "Stream-Query Committer", + }, + { + role: "成员", + name: "Charles7c", + photo: "/assets/img/members/Charles7c.webp", + desc: "Sms4j Committer", + }, + { + role: "成员", + name: "Rain", + photo: "/assets/img/members/Rain.webp", + desc: "LiteFlow Committer", + }, + { + role: "成员", + name: "Ricahrd", + photo: "/assets/img/members/Ricahrd.webp", + desc: "Sms4j Committer", + }, + { + role: "成员", + name: "花城", + photo: "/assets/img/members/huacheng.webp", + desc: "HertzBeat Committer,主要贡献有 Zookeeper协议兼容、JVM协议兼容、tomcat服务器监控、MQ监控、适配ElasticSearch中间件监控等", + }, + { + role: "成员", + name: "小眼", + photo: "/assets/img/members/xiaoyan.webp", + desc: "Sms4j Committer", + }, + { + role: "成员", + name: "小马", + photo: "/assets/img/members/xiaoma.webp", + desc: "Liteflow Committer", + }, + { + role: "成员", + name: "Redick01", + photo: "/assets/img/members/Redick01.webp", + desc: "Dynamic-Tp Committer", + }, + { + role: "成员", + name: "城铠唐", + photo: "/assets/img/members/chengkaitang.webp", + desc: "Liteflow Committer,主要贡献有liteflow-sql插件开发等", + }, + { + role: "成员", + name: "DaleLee", + photo: "/assets/img/members/DaleLee.webp", + desc: "Liteflow Committer,主要贡献有对任意组件进行超时控制等", + }, + { + role: "成员", + name: "KamToHung", + photo: "/assets/img/members/KamToHung.webp", + desc: "Dynamic-Tp/Stream-Query Committer", + }, + { + role: "成员", + name: "小张", + photo: "/assets/img/members/xiaozhang.webp", + desc: "Liteflow Committer,主要贡献有循环异步模式等", + }, + { + role: "成员", + name: "Rainie", + photo: "/assets/img/members/Rainie.webp", + desc: "Liteflow Committer", + }, + { + role: "成员", + name: "Hotstrip", + photo: "/assets/img/members/Hotstrip.webp", + desc: "Jpom Committer", + }, + { + role: "成员", + name: "许长安", + photo: "/assets/img/members/xuchangan.webp", + desc: "Jpom Committer", + }, + { + role: "成员", + name: "铁甲小宝", + photo: "/assets/img/members/tiejiaxiaobao.webp", + desc: "HertzBeat/Sms4j Committer", + }, + { + role: "成员", + name: "bleachtred", + photo: "/assets/img/members/bleachtred.webp", + desc: "Sms4j Committer", + }, + { + role: "成员", + name: "Ceilzcx", + photo: "/assets/img/members/Ceilzcx.webp", + desc: "Hertzbeat Committer", + }, + { + role: "成员", + name: "zendwang", + photo: "/assets/img/members/zendwang.webp", + desc: "Liteflow Committer", + }, + { + role: "成员", + name: "Sh1yu", + photo: "/assets/img/members/Sh1yu.webp", + desc: "Zyplayer-Doc Committer", + }, + { + role: "成员", + name: "抓蛙师", + photo: "/assets/img/members/zhuawashi.webp", + desc: "RuoYi-Vue-Plus Committer", + }, + { + role: "成员", + name: "赵青然", + photo: "/assets/img/members/zhaoqingran.webp", + desc: "Hertzbeat Committer", + }, + { + role: "成员", + name: "CHMing", + photo: "/assets/img/members/CHMing.webp", + desc: "Forest Committer", + }, + { + role: "成员", + name: "芋圆", + photo: "/assets/img/members/yuyuan.webp", + desc: "Hodor Committer", + }, + { + role: "成员", + name: "Superlit", + photo: "/assets/img/members/Superlit.webp", + desc: "Hodor Committer", + }, + { + role: "成员", + name: "Dazer007", + photo: "/assets/img/members/Dazer007.webp", + desc: "Hutool Committer", + }, + { + role: "成员", + name: "落一", + photo: "/assets/img/members/luoyi.webp", + desc: "LiteFlow Committer", + }, + { + role: "成员", + name: "葛足澳", + photo: "/assets/img/members/gezuao.webp", + desc: "LiteFlow Committer", + }, + { + role: "成员", + name: "姚凯", + photo: "/assets/img/members/yaokai.webp", + desc: "Dynamic-Tp Committer", + }, + { + role: "成员", + name: "东风", + photo: "/assets/img/members/dongfeng.webp", + desc: "Sms4j Committer", + }, + { + role: "成员", + name: "淞筱", + photo: "/assets/img/members/sonxiao.webp", + desc: "Hertzbeat Committer", + }, + { + role: "成员", + name: "薛家明", + photo: "/assets/img/members/xuejiaming.webp", + desc: "Easy-Query Committer", + }, + { + role: "成员", + name: "夏日刚", + photo: "/assets/img/members/TirionFordring.webp", + desc: "Warm-Flow Committer", + }, + { + role: "成员", + name: "梁小梁", + photo: "/assets/img/members/xiaohua.webp", + desc: "Warm-Flow Committer", + }, + { + role: "成员", + name: "Vanlin", + photo: "/assets/img/members/vanlin.webp", + desc: "Warm-Flow Committer", + }, + { + role: "成员", + name: "Zhen", + photo: "/assets/img/members/zhen.webp", + desc: "Warm-Flow Committer", + }, + ], + }, + ], +}; diff --git a/src/.vuepress/composables/project/data.ts b/src/.vuepress/composables/project/data.ts index 84be1cd075..7d8976a4a5 100644 --- a/src/.vuepress/composables/project/data.ts +++ b/src/.vuepress/composables/project/data.ts @@ -8,7 +8,7 @@ export const noGithubProjects = [ "zyplayer-doc", "easy-trans", "x-easypdf", - "image-combiner" + "image-combiner", ]; export const noImageProject = [ "jinx", @@ -24,7 +24,7 @@ export const noImageProject = [ "mayfly-go", "skyeye-oa", "carbon", - "warm-flow" + "warm-flow", ]; export const useProjectsData = () => { @@ -34,7 +34,7 @@ export const useProjectsData = () => { const projectOrder = computed(() => [ { groupName: projectLocale.value.ENTERPRISE_CERTIFICATION, - projects: ["sa-token", "MaxKey", "sureness"] + projects: ["sa-token", "MaxKey", "sureness"], }, { groupName: projectLocale.value.POPULAR_TOOLS, @@ -81,8 +81,8 @@ export const useProjectsData = () => { "open-giteye-api", "newcar", "jinx", - "carbon" - ] + "carbon", + ], }, { groupName: projectLocale.value.MICROSERVICE, @@ -93,30 +93,30 @@ export const useProjectsData = () => { "mendmix", "koalas-rpc", "dante-cloud", - "J2EEFAST" - ] + "J2EEFAST", + ], }, { groupName: projectLocale.value.OPERATIONS_AND_MAINTENANCE_CONTROL, - projects: ["Jpom", "cubic", "TestHub", "athena", "domain-admin"] + projects: ["Jpom", "cubic", "TestHub", "athena", "domain-admin"], }, { groupName: projectLocale.value.DISTRIBUTED_LOG, - projects: ["TLog"] + projects: ["TLog"], }, { groupName: projectLocale.value.DISTRIBUTED_TRANSACTION, - projects: ["hmily", "myth", "Raincat"] + projects: ["hmily", "myth", "Raincat"], }, { groupName: projectLocale.value.BIG_DATA, - projects: ["data-compare", "CloudEon"] + projects: ["data-compare", "CloudEon"], }, { groupName: projectLocale.value.DISTRIBUTED_SCHEDULING, - projects: ["Disjob", "hodor"] - } + projects: ["Disjob", "hodor"], + }, ]); const projectItemsOrigin = computed(() => [ @@ -152,7 +152,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "Raincat", @@ -164,7 +164,7 @@ export const useProjectsData = () => { Total lines License Maven Central -QQ群` +QQ群`, }, { name: "myth", @@ -175,9 +175,9 @@ export const useProjectsData = () => { link: `Total lines License Maven Central -QQ群

` - } - ] +QQ群

`, + }, + ], }, { groupName: projectLocale.value.POPULAR_TOOLS, @@ -211,7 +211,7 @@ export const useProjectsData = () => { -` +`, }, { name: "forest", @@ -233,7 +233,7 @@ export const useProjectsData = () => { Author - ` + `, }, { name: "liteFlow", @@ -247,7 +247,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "fast-request", @@ -264,7 +264,7 @@ export const useProjectsData = () => { Version Downloads Slack - OSCS Status` + OSCS Status`, }, { name: "dynamic-tp", @@ -279,7 +279,7 @@ export const useProjectsData = () => { 备注加群 - ` + `, }, { name: "easy-es", @@ -295,7 +295,7 @@ export const useProjectsData = () => { code style - ` + `, }, { name: "go-view", @@ -309,7 +309,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "image-combiner", @@ -320,7 +320,7 @@ export const useProjectsData = () => { link: ` - ` + `, }, { name: "jinx", @@ -332,7 +332,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "WeMQ", @@ -346,7 +346,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "X-File-Storage", @@ -373,7 +373,7 @@ export const useProjectsData = () => { -` +`, }, { name: "electron-egg", @@ -387,7 +387,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "northstar", @@ -401,7 +401,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "easy_trans", @@ -415,7 +415,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "redisfront", @@ -425,7 +425,7 @@ export const useProjectsData = () => { date: "2022.12", link: `JDK Apache 2.0 - Release` + Release`, }, { name: "x-easypdf", @@ -436,7 +436,7 @@ export const useProjectsData = () => { link: ` - ` + `, }, { name: "gobrs-async", @@ -450,7 +450,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "open-giteye-api", @@ -464,7 +464,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "Binlog4j", @@ -488,7 +488,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "sms4j", @@ -500,7 +500,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "stream-query", @@ -521,7 +521,7 @@ export const useProjectsData = () => { github star - ` + `, }, { name: "payment-spring-boot", @@ -551,7 +551,7 @@ export const useProjectsData = () => { 点击加入QQ交流②群 - ` + `, }, { name: "Neutrino-Proxy", @@ -568,7 +568,7 @@ export const useProjectsData = () => { license MIT - ` + `, }, { name: "TestHub", @@ -580,7 +580,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "yft-design", @@ -594,7 +594,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "zyplayer-doc", @@ -605,7 +605,7 @@ export const useProjectsData = () => { link: ` - ` + `, }, { name: "orion-visor", @@ -640,7 +640,7 @@ export const useProjectsData = () => { star - ` + `, }, { name: "DyJava", @@ -650,7 +650,7 @@ export const useProjectsData = () => { date: "2024.04", link: ` Gitee Stars - ` + `, }, { name: "warm-flow", @@ -660,7 +660,7 @@ export const useProjectsData = () => { date: "2023.12", link: ` Gitee Stars - ` + `, }, { name: "dbswitch", @@ -670,7 +670,7 @@ export const useProjectsData = () => { date: "2020.12", link: ` Gitee Stars - ` + `, }, { name: "skyeye-oa", @@ -680,7 +680,7 @@ export const useProjectsData = () => { date: "2022.08", link: ` Gitee Stars - ` + `, }, { name: "easyAi", @@ -690,7 +690,7 @@ export const useProjectsData = () => { date: "2024.05", link: ` Gitee Stars - ` + `, }, { name: "mybatis-plus-ext", @@ -700,7 +700,7 @@ export const useProjectsData = () => { date: "2021.08", link: ` Gitee Stars - ` + `, }, { name: "easy-query", @@ -717,7 +717,7 @@ export const useProjectsData = () => { Apache 2 jdk-8 jdk-11 - jdk-17` + jdk-17`, }, { name: "tianai-captcha", @@ -728,7 +728,7 @@ export const useProjectsData = () => { link: ` Gitee Stars - ` + `, }, { name: "dax-pay", @@ -743,7 +743,7 @@ export const useProjectsData = () => { Build Status Downloads - ` + `, }, { name: "mayfly-go", @@ -768,7 +768,7 @@ export const useProjectsData = () => { vue - ` + `, }, { name: "MilvusPlus", @@ -779,7 +779,7 @@ export const useProjectsData = () => { link: ` Gitee Stars - ` + `, }, { name: "newcar", @@ -794,7 +794,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "ujcms", @@ -805,7 +805,7 @@ export const useProjectsData = () => { link: ` Gitee Stars - ` + `, }, { name: "sayOrder", @@ -816,7 +816,7 @@ export const useProjectsData = () => { link: ` Gitee Stars - ` + `, }, { name: "Akali", @@ -827,7 +827,7 @@ export const useProjectsData = () => { link: ` Gitee Stars - ` + `, }, { name: "carbon", @@ -844,9 +844,9 @@ export const useProjectsData = () => { -` - } - ] +`, + }, + ], }, { groupName: projectLocale.value.ENTERPRISE_CERTIFICATION, @@ -865,7 +865,7 @@ export const useProjectsData = () => { -` +`, }, { name: "MaxKey", @@ -886,7 +886,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "sureness", @@ -900,9 +900,9 @@ export const useProjectsData = () => { Gitter GitHub Release Date star - star` - } - ] + star`, + }, + ], }, { groupName: projectLocale.value.OPERATIONS_AND_MAINTENANCE_CONTROL, @@ -938,7 +938,7 @@ export const useProjectsData = () => { docker pull - ` + `, }, { name: "cubic", @@ -951,7 +951,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "athena", @@ -963,7 +963,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "TestHub", @@ -975,7 +975,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "domain-admin", @@ -985,9 +985,9 @@ export const useProjectsData = () => { date: "2024.08", link: ` - ` - } - ] + `, + }, + ], }, { groupName: projectLocale.value.DISTRIBUTED_LOG, @@ -1004,9 +1004,9 @@ export const useProjectsData = () => { - ` - } - ] + `, + }, + ], }, { groupName: projectLocale.value.BIG_DATA, @@ -1021,7 +1021,7 @@ export const useProjectsData = () => { Commits pre-commit All Contributors - GitHub license` + GitHub license`, }, { name: "data-compare", @@ -1037,9 +1037,9 @@ export const useProjectsData = () => { - ` - } - ] + `, + }, + ], }, { groupName: projectLocale.value.MICROSERVICE, @@ -1057,7 +1057,7 @@ export const useProjectsData = () => { 996icu - ` + `, }, { name: "mendmix", @@ -1071,7 +1071,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "lamp-cloud", @@ -1088,7 +1088,7 @@ export const useProjectsData = () => { Fork Star Fork - ` + `, }, { name: "dante-cloud", @@ -1107,7 +1107,7 @@ export const useProjectsData = () => { Github fork Gitee star Gitee fork - ` + `, }, { name: "open-capacity-platform", @@ -1125,7 +1125,7 @@ export const useProjectsData = () => { star Fork me on Gitee fork - ` + `, }, { name: "RuoYi-Vue-Plus", @@ -1142,7 +1142,7 @@ export const useProjectsData = () => { RuoYi-Vue-Plus Spring Boot JDK-8+ - JDK-11` + JDK-11`, }, { name: "J2EEFAST", @@ -1154,9 +1154,9 @@ export const useProjectsData = () => { - ` - } - ] + `, + }, + ], }, { groupName: projectLocale.value.DISTRIBUTED_SCHEDULING, @@ -1173,7 +1173,7 @@ export const useProjectsData = () => { - ` + `, }, { name: "Disjob", @@ -1188,14 +1188,14 @@ export const useProjectsData = () => { -` - } - ] - } +`, + }, + ], + }, ]); const projectItems = computed(() => - orderProjects(projectItemsOrigin.value, projectOrder.value) + orderProjects(projectItemsOrigin.value, projectOrder.value), ); return { projectItems, projectLocale }; diff --git a/src/.vuepress/composables/project/en.ts b/src/.vuepress/composables/project/en.ts index c20142814e..3042b4967c 100644 --- a/src/.vuepress/composables/project/en.ts +++ b/src/.vuepress/composables/project/en.ts @@ -1,128 +1,130 @@ -import { type ProjectsOption } from "./types.js"; - -export const enProjectsOption: ProjectsOption = { - PROJECTS: "Projects", - DESCRIPTION: - "Dromara Community unites top open-source authors, offering products, solutions, and training across domains like transactions, tools, microservices, and big data. Join for the joy of open source.", - PROJECT_SPONSOR: "Project sponsor", - JOINING_DATE: "Joining date", - START_UP: "Start up", - - DISTRIBUTED_TRANSACTION: "Distributed Transaction", - HMILY_DESC: "Flexible distributed transaction solution", - RAINCAT_DESC: "Strongly consistent distributed transaction solution", - MYTH_DESC: "Reliable messages distributed transaction solution", - - POPULAR_TOOLS: "Popular Tools", - HUTOOL_DESC: "A set of tools that keep Java sweet", - FOREST_DESC: "A high-level and lightweight HTTP client framework for Java", - LITEFLOW_DESC: "a lightweight and practical micro-process framework", - DYNAMIC_TP_DESC: - "Lightweight dynamic threadpool with monitoring and alarming", - EASY_ES_DESC: - "An easier-to-use ES search engine framework, born to simplify development", - GO_VIEW_DESC: "A efficient low-code data visualization development platform", - IMAGE_COMBINER_DESC: "Image combine tool for java server side", - JINX_DESC: "Netty is used as httpServer in Spring-boot", - WEMQ_DESC: - "WeMQ is an open-source IoT debugging system for device operators, providing device, MQTT server, and client management features, along with a secure Nmqs communication layer for encrypted connections.", - ELECTRON_EGG_DESC: - "A simple, cross-platform, enterprise-level desktop software development framework.", - NORTHSTAR_DESC: "Quantitative Trading Platform", - EASY_TRANS_DESC: "Data translation with an annotation.Write 30% less code", - FAST_REQUEST_DESC: "Born to simplify debugging APIs", - REDISFRONT_DESC: "An open-source cross-platform Redis GUI.", - X_EASYPDF_DESC: - "A framework for constructing PDFs in a building-block manner (based on PDFBox).", - GOBRS_ASYNC_DESC: - "A powerful, flexible-configured asynchronous orchestration framework with full link exception callback, memory optimization, and exception state management.", - DYJAVA: - "DyJava is the 1 powerful tremolo Java development kit (SDK), which supports rapid calling of tremolo application OpenAPI, including but not limited to mobile/website applications, tremolo open platform, tremolo shop and tremolo applet, etc.", - ORION_VISOR: - "1 high-value, modern intelligent operation and maintenance & lightweight bastion machine platform.", - WARM_FLOW: - "Domestic workflow engine, its characteristics are simple and lightweight but not simple, complete, independent components, scalable, can meet the components of small and medium-sized projects. Address the pain points of complex flowable and activities, high learning costs and difficult integration.", - DBSWITCH: "Heterogeneous database migration synchronization (move) tool", - SKYEYE_OA: - "Intelligent manufacturing integration, using winUI-Springboot low-code platform development model", - EASYAI: - "The most popular Java artificial intelligence algorithm framework in China (Java version pytorch). It can be used out of the box without any additional environment configuration and dependencies, and can be used out of the box.", - MYBATIS_PLUS_EXT: - "The expansion package of the mybatis-plus framework is further lightly encapsulated on the basis of the original framework", - EASY_QUERY: - "Under the 1 java, strong type, lightweight, high-performance ORM is supported.", - TIANAI_CAPTCHA: - "It may be the best open source behavior verification code in java, [slider verification code, click verification code, behavior verification code, rotation verification code, sliding verification code]", - DAX_PAY: - "DaxPay is the 1 free and open source payment system, which supports channels such as Alipay, WeChat and Cloud Flash Payment.", - MYFLY_GO: - "Web version linux (terminal [terminal playback] file script process planning task), database (mysql postgres oracle sqlserver Dameng Gauss sqlite), redis (single-machine sentry cluster), mongo and other unified management and operation platforms integrating work order process approval", - MILVUSPLUS: - "Use the MyBatisPlus way, elegant operation vector database Milvus, support spring and solon at the same time", - NEWCAR: "Modernized CanvasKit-WASM-based animation engine", - UJCMS: - "Supports headless CMS, custom fields and visual field design. Free for commercial websites", - SAYORDER: - "The project is based on the easyAi engine JAVA high-performance, low-cost, lightweight intelligent customer service", - AKALI: - "Akali (Akali), lightweight localization hotspot detection/degradation framework, can be accessed in 10 seconds! Artifact under large flow", - OPEN_GITEYE_API_DESC: - "A data chart service tool designed specifically for open source authors, providing services including Star trend charts, contributor lists, and Gitee indexes.", - BINLOG4J_DESC: "A lightweight Mysql Binlog client based on Java.", - SMS4J_DESC: - "SMS4J is a text messaging aggregation framework that makes it easy to integrate services from multiple providers.", - STREAM_QUERY_DESC: "Experience mybatis-plus without the need for Mappers!", - PAYMENT_SPRING_BOOT_DESC: - "The most complete and easy-to-use WeChat Payment V3 component for Spring Boot.", - EASYTRANS_DESC: - "Data translation with just one annotation, reducing SQL code by 30%.", - NEUTRINO_PROXY_DESC: - "An open-source Java intranet penetration project based on netty.", - TESTHUB_DESC: - "TestHub is an automation testing tool based on workflow orchestration.", - DOMAIN_ADMIN_DESC: "Domain and SSL Cert monitor System.", - YFT_DESIGN_DESC: "An open-source version of 'Creator Post' based on Canvas.", - ZYPLAYER_DOC_DESC: - "A knowledge base, note, and WIKI documentation management tool suitable for team and individual private deployment.", - X_FILE_STORAGE_DESC: - "One-stop file storage, integrating with all mainstream storage platforms.", - - ENTERPRISE_CERTIFICATION: "Enterprise Certification", - SA_TOKEN_DESC: "The most comprehensive Java permission framework", - MAXKEY_DESC: "MaxKey Leading Edge Enterprise Class open source IAM product", - SURENESS_DESC: "Focusing on Protection of REST API", - - OPERATIONS_AND_MAINTENANCE_CONTROL: "Operations and Maintenance Control", - JPOM_DESC: "Simple & Low-intrusion project management platform", - CUBIC_DESC: "Distributed monitoring system", - ATHENA_DESC: "Metrics bytecode tool", - - DISTRIBUTED_LOG: "Distributed Log", - TLOG_DESC: "Lightweight distributed log label tracking framwork", - - BIG_DATA: "Big Data", - CLOUDEON_DESC: - "A cloud-native big data platform based on Kubernetes, dedicated to simplifying the operation and maintenance of big data clusters on k8s.", - DATA_COMPARE_DESC: - "Database comparison tool: comparison of hive table data, and comparison of mysql, Doris data.", - - MICROSERVICE: "Microservice", - KOALAS_RPC_DESC: "Highly available and extensible RPC framework", - MENDMIX_DESC: - "A technology base for distributed architecture and cloud native architecture", - LAMP_CLOUD_DESC: - "A rapid development platform for the middle and back office of microservices focusing on multi tenant solutions", - DANTE_CLOUD_DESC: - "Enterprise level microservice architecture based on spring authorization server and OAuth 2.1 protocol", - OPEN_CAPACITY_PLATFORM_DESC: - "Spring Cloud-based enterprise microservices framework", - RUOYI_VUE_PLUS_DESC: - "A backend management system that rewrites all features of RuoYi-Vue, integrating Sa-Token, Mybatis-Plus, Jackson, Xxl-Job, SpringDoc, Hutool, and OSS.", - J2EEFAST_DESC: - "J2eeFAST is a Java EE enterprise-level fast development platform. ", - - DISTRIBUTED_SCHEDULING: "Distributed Scheduling", - HODOR_DESC: "Distributed scheduling framework", - DISJOB_DESC: "A distributed task scheduling framework.", - CARBON_DESC: "A simple, semantic and developer-friendly golang package for time." -}; +import { type ProjectsOption } from "./types.js"; + +export const enProjectsOption: ProjectsOption = { + PROJECTS: + "Dromara : Gathering top-notch projects, exploring the joys of open source together.", + DESCRIPTION: + "Dromara Community unites top open-source authors, offering products, solutions, and training across domains like transactions, tools, microservices, and big data. Join for the joy of open source.", + PROJECT_SPONSOR: "Project sponsor", + JOINING_DATE: "Joining date", + START_UP: "Start up", + + DISTRIBUTED_TRANSACTION: "Distributed Transaction", + HMILY_DESC: "Flexible distributed transaction solution", + RAINCAT_DESC: "Strongly consistent distributed transaction solution", + MYTH_DESC: "Reliable messages distributed transaction solution", + + POPULAR_TOOLS: "Popular Tools", + HUTOOL_DESC: "A set of tools that keep Java sweet", + FOREST_DESC: "A high-level and lightweight HTTP client framework for Java", + LITEFLOW_DESC: "a lightweight and practical micro-process framework", + DYNAMIC_TP_DESC: + "Lightweight dynamic threadpool with monitoring and alarming", + EASY_ES_DESC: + "An easier-to-use ES search engine framework, born to simplify development", + GO_VIEW_DESC: "A efficient low-code data visualization development platform", + IMAGE_COMBINER_DESC: "Image combine tool for java server side", + JINX_DESC: "Netty is used as httpServer in Spring-boot", + WEMQ_DESC: + "WeMQ is an open-source IoT debugging system for device operators, providing device, MQTT server, and client management features, along with a secure Nmqs communication layer for encrypted connections.", + ELECTRON_EGG_DESC: + "A simple, cross-platform, enterprise-level desktop software development framework.", + NORTHSTAR_DESC: "Quantitative Trading Platform", + EASY_TRANS_DESC: "Data translation with an annotation.Write 30% less code", + FAST_REQUEST_DESC: "Born to simplify debugging APIs", + REDISFRONT_DESC: "An open-source cross-platform Redis GUI.", + X_EASYPDF_DESC: + "A framework for constructing PDFs in a building-block manner (based on PDFBox).", + GOBRS_ASYNC_DESC: + "A powerful, flexible-configured asynchronous orchestration framework with full link exception callback, memory optimization, and exception state management.", + DYJAVA: + "DyJava is the 1 powerful tremolo Java development kit (SDK), which supports rapid calling of tremolo application OpenAPI, including but not limited to mobile/website applications, tremolo open platform, tremolo shop and tremolo applet, etc.", + ORION_VISOR: + "1 high-value, modern intelligent operation and maintenance & lightweight bastion machine platform.", + WARM_FLOW: + "Domestic workflow engine, its characteristics are simple and lightweight but not simple, complete, independent components, scalable, can meet the components of small and medium-sized projects. Address the pain points of complex flowable and activities, high learning costs and difficult integration.", + DBSWITCH: "Heterogeneous database migration synchronization (move) tool", + SKYEYE_OA: + "Intelligent manufacturing integration, using winUI-Springboot low-code platform development model", + EASYAI: + "The most popular Java artificial intelligence algorithm framework in China (Java version pytorch). It can be used out of the box without any additional environment configuration and dependencies, and can be used out of the box.", + MYBATIS_PLUS_EXT: + "The expansion package of the mybatis-plus framework is further lightly encapsulated on the basis of the original framework", + EASY_QUERY: + "Under the 1 java, strong type, lightweight, high-performance ORM is supported.", + TIANAI_CAPTCHA: + "It may be the best open source behavior verification code in java, [slider verification code, click verification code, behavior verification code, rotation verification code, sliding verification code]", + DAX_PAY: + "DaxPay is the 1 free and open source payment system, which supports channels such as Alipay, WeChat and Cloud Flash Payment.", + MYFLY_GO: + "Web version linux (terminal [terminal playback] file script process planning task), database (mysql postgres oracle sqlserver Dameng Gauss sqlite), redis (single-machine sentry cluster), mongo and other unified management and operation platforms integrating work order process approval", + MILVUSPLUS: + "Use the MyBatisPlus way, elegant operation vector database Milvus, support spring and solon at the same time", + NEWCAR: "Modernized CanvasKit-WASM-based animation engine", + UJCMS: + "Supports headless CMS, custom fields and visual field design. Free for commercial websites", + SAYORDER: + "The project is based on the easyAi engine JAVA high-performance, low-cost, lightweight intelligent customer service", + AKALI: + "Akali (Akali), lightweight localization hotspot detection/degradation framework, can be accessed in 10 seconds! Artifact under large flow", + OPEN_GITEYE_API_DESC: + "A data chart service tool designed specifically for open source authors, providing services including Star trend charts, contributor lists, and Gitee indexes.", + BINLOG4J_DESC: "A lightweight Mysql Binlog client based on Java.", + SMS4J_DESC: + "SMS4J is a text messaging aggregation framework that makes it easy to integrate services from multiple providers.", + STREAM_QUERY_DESC: "Experience mybatis-plus without the need for Mappers!", + PAYMENT_SPRING_BOOT_DESC: + "The most complete and easy-to-use WeChat Payment V3 component for Spring Boot.", + EASYTRANS_DESC: + "Data translation with just one annotation, reducing SQL code by 30%.", + NEUTRINO_PROXY_DESC: + "An open-source Java intranet penetration project based on netty.", + TESTHUB_DESC: + "TestHub is an automation testing tool based on workflow orchestration.", + DOMAIN_ADMIN_DESC: "Domain and SSL Cert monitor System.", + YFT_DESIGN_DESC: "An open-source version of 'Creator Post' based on Canvas.", + ZYPLAYER_DOC_DESC: + "A knowledge base, note, and WIKI documentation management tool suitable for team and individual private deployment.", + X_FILE_STORAGE_DESC: + "One-stop file storage, integrating with all mainstream storage platforms.", + + ENTERPRISE_CERTIFICATION: "Enterprise Certification", + SA_TOKEN_DESC: "The most comprehensive Java permission framework", + MAXKEY_DESC: "MaxKey Leading Edge Enterprise Class open source IAM product", + SURENESS_DESC: "Focusing on Protection of REST API", + + OPERATIONS_AND_MAINTENANCE_CONTROL: "Operations and Maintenance Control", + JPOM_DESC: "Simple & Low-intrusion project management platform", + CUBIC_DESC: "Distributed monitoring system", + ATHENA_DESC: "Metrics bytecode tool", + + DISTRIBUTED_LOG: "Distributed Log", + TLOG_DESC: "Lightweight distributed log label tracking framwork", + + BIG_DATA: "Big Data", + CLOUDEON_DESC: + "A cloud-native big data platform based on Kubernetes, dedicated to simplifying the operation and maintenance of big data clusters on k8s.", + DATA_COMPARE_DESC: + "Database comparison tool: comparison of hive table data, and comparison of mysql, Doris data.", + + MICROSERVICE: "Microservice", + KOALAS_RPC_DESC: "Highly available and extensible RPC framework", + MENDMIX_DESC: + "A technology base for distributed architecture and cloud native architecture", + LAMP_CLOUD_DESC: + "A rapid development platform for the middle and back office of microservices focusing on multi tenant solutions", + DANTE_CLOUD_DESC: + "Enterprise level microservice architecture based on spring authorization server and OAuth 2.1 protocol", + OPEN_CAPACITY_PLATFORM_DESC: + "Spring Cloud-based enterprise microservices framework", + RUOYI_VUE_PLUS_DESC: + "A backend management system that rewrites all features of RuoYi-Vue, integrating Sa-Token, Mybatis-Plus, Jackson, Xxl-Job, SpringDoc, Hutool, and OSS.", + J2EEFAST_DESC: + "J2eeFAST is a Java EE enterprise-level fast development platform. ", + + DISTRIBUTED_SCHEDULING: "Distributed Scheduling", + HODOR_DESC: "Distributed scheduling framework", + DISJOB_DESC: "A distributed task scheduling framework.", + CARBON_DESC: + "A simple, semantic and developer-friendly golang package for time.", +}; diff --git a/src/.vuepress/composables/project/locales.ts b/src/.vuepress/composables/project/locales.ts index cf02cf7bb3..f11336fbe0 100644 --- a/src/.vuepress/composables/project/locales.ts +++ b/src/.vuepress/composables/project/locales.ts @@ -6,5 +6,5 @@ import { zhProjectsOption } from "./zh.js"; export const useProjectLocale = () => useLocaleConfig({ "/": enProjectsOption, - "/zh/": zhProjectsOption + "/zh/": zhProjectsOption, }); diff --git a/src/.vuepress/composables/project/types.ts b/src/.vuepress/composables/project/types.ts index 35c1b29dda..b967bd1889 100644 --- a/src/.vuepress/composables/project/types.ts +++ b/src/.vuepress/composables/project/types.ts @@ -1,108 +1,108 @@ -export interface ProjectsOption { - PROJECTS: string - DESCRIPTION: string - PROJECT_SPONSOR: string - JOINING_DATE: string - START_UP: string - - DISTRIBUTED_TRANSACTION: string - HMILY_DESC: string - RAINCAT_DESC: string - MYTH_DESC: string - - POPULAR_TOOLS: string - HUTOOL_DESC: string - FOREST_DESC: string - LITEFLOW_DESC: string - DYNAMIC_TP_DESC: string - EASY_ES_DESC: string - GO_VIEW_DESC: string - IMAGE_COMBINER_DESC: string - JINX_DESC: string - WEMQ_DESC: string - ELECTRON_EGG_DESC: string - NORTHSTAR_DESC: string - EASY_TRANS_DESC: string - FAST_REQUEST_DESC: string - REDISFRONT_DESC: string - X_EASYPDF_DESC: string - GOBRS_ASYNC_DESC: string - OPEN_GITEYE_API_DESC: string - DYJAVA: string - ORION_VISOR: string - WARM_FLOW: string - DBSWITCH: string - SKYEYE_OA: string - EASYAI: string - MYBATIS_PLUS_EXT: string - EASY_QUERY: string - TIANAI_CAPTCHA: string - DAX_PAY: string - MYFLY_GO: string - MILVUSPLUS: string - NEWCAR: string - UJCMS: string - SAYORDER: string - AKALI: string - BINLOG4J_DESC: string - SMS4J_DESC: string - STREAM_QUERY_DESC: string - PAYMENT_SPRING_BOOT_DESC: string - EASYTRANS_DESC: string - NEUTRINO_PROXY_DESC: string - TESTHUB_DESC: string - DOMAIN_ADMIN_DESC: string - YFT_DESIGN_DESC: string - ZYPLAYER_DOC_DESC: string - X_FILE_STORAGE_DESC: string - - ENTERPRISE_CERTIFICATION: string - SA_TOKEN_DESC: string - MAXKEY_DESC: string - SURENESS_DESC: string - - OPERATIONS_AND_MAINTENANCE_CONTROL: string - JPOM_DESC: string - CUBIC_DESC: string - ATHENA_DESC: string - - DISTRIBUTED_LOG: string - TLOG_DESC: string - - BIG_DATA: string - CLOUDEON_DESC: string - DATA_COMPARE_DESC: string - - MICROSERVICE: string - KOALAS_RPC_DESC: string - MENDMIX_DESC: string - LAMP_CLOUD_DESC: string - DANTE_CLOUD_DESC: string - OPEN_CAPACITY_PLATFORM_DESC: string - RUOYI_VUE_PLUS_DESC: string - J2EEFAST_DESC: string - - DISTRIBUTED_SCHEDULING: string - HODOR_DESC: string - DISJOB_DESC: string - CARBON_DESC: string -} - -export interface ProjectItem { - name: string - website: string - description: string - sponsor: string - date: string - link: string -} - -export interface GroupDetail { - groupName: string - projects: ProjectItem[] -} - -export interface GroupOrder { - groupName: string - projects: string[] -} +export interface ProjectsOption { + PROJECTS: string; + DESCRIPTION: string; + PROJECT_SPONSOR: string; + JOINING_DATE: string; + START_UP: string; + + DISTRIBUTED_TRANSACTION: string; + HMILY_DESC: string; + RAINCAT_DESC: string; + MYTH_DESC: string; + + POPULAR_TOOLS: string; + HUTOOL_DESC: string; + FOREST_DESC: string; + LITEFLOW_DESC: string; + DYNAMIC_TP_DESC: string; + EASY_ES_DESC: string; + GO_VIEW_DESC: string; + IMAGE_COMBINER_DESC: string; + JINX_DESC: string; + WEMQ_DESC: string; + ELECTRON_EGG_DESC: string; + NORTHSTAR_DESC: string; + EASY_TRANS_DESC: string; + FAST_REQUEST_DESC: string; + REDISFRONT_DESC: string; + X_EASYPDF_DESC: string; + GOBRS_ASYNC_DESC: string; + OPEN_GITEYE_API_DESC: string; + DYJAVA: string; + ORION_VISOR: string; + WARM_FLOW: string; + DBSWITCH: string; + SKYEYE_OA: string; + EASYAI: string; + MYBATIS_PLUS_EXT: string; + EASY_QUERY: string; + TIANAI_CAPTCHA: string; + DAX_PAY: string; + MYFLY_GO: string; + MILVUSPLUS: string; + NEWCAR: string; + UJCMS: string; + SAYORDER: string; + AKALI: string; + BINLOG4J_DESC: string; + SMS4J_DESC: string; + STREAM_QUERY_DESC: string; + PAYMENT_SPRING_BOOT_DESC: string; + EASYTRANS_DESC: string; + NEUTRINO_PROXY_DESC: string; + TESTHUB_DESC: string; + DOMAIN_ADMIN_DESC: string; + YFT_DESIGN_DESC: string; + ZYPLAYER_DOC_DESC: string; + X_FILE_STORAGE_DESC: string; + + ENTERPRISE_CERTIFICATION: string; + SA_TOKEN_DESC: string; + MAXKEY_DESC: string; + SURENESS_DESC: string; + + OPERATIONS_AND_MAINTENANCE_CONTROL: string; + JPOM_DESC: string; + CUBIC_DESC: string; + ATHENA_DESC: string; + + DISTRIBUTED_LOG: string; + TLOG_DESC: string; + + BIG_DATA: string; + CLOUDEON_DESC: string; + DATA_COMPARE_DESC: string; + + MICROSERVICE: string; + KOALAS_RPC_DESC: string; + MENDMIX_DESC: string; + LAMP_CLOUD_DESC: string; + DANTE_CLOUD_DESC: string; + OPEN_CAPACITY_PLATFORM_DESC: string; + RUOYI_VUE_PLUS_DESC: string; + J2EEFAST_DESC: string; + + DISTRIBUTED_SCHEDULING: string; + HODOR_DESC: string; + DISJOB_DESC: string; + CARBON_DESC: string; +} + +export interface ProjectItem { + name: string; + website: string; + description: string; + sponsor: string; + date: string; + link: string; +} + +export interface GroupDetail { + groupName: string; + projects: ProjectItem[]; +} + +export interface GroupOrder { + groupName: string; + projects: string[]; +} diff --git a/src/.vuepress/composables/project/utils.ts b/src/.vuepress/composables/project/utils.ts index e81ea31345..b508cc85d7 100644 --- a/src/.vuepress/composables/project/utils.ts +++ b/src/.vuepress/composables/project/utils.ts @@ -2,24 +2,26 @@ import type { GroupDetail, GroupOrder, ProjectItem } from "./types.js"; export const orderProjects = ( detailsArray: GroupDetail[], - orderArray: GroupOrder[] + orderArray: GroupOrder[], ): GroupDetail[] => orderArray .map((orderGroup) => { const detailGroup = detailsArray.find( - (detail) => detail.groupName === orderGroup.groupName + (detail) => detail.groupName === orderGroup.groupName, ); if (detailGroup != null) { const orderedProjects = orderGroup.projects .map((projectName) => - detailGroup.projects.find((project) => project.name === projectName) + detailGroup.projects.find( + (project) => project.name === projectName, + ), ) .filter(Boolean) as ProjectItem[]; return { groupName: orderGroup.groupName, - projects: orderedProjects + projects: orderedProjects, }; } diff --git a/src/.vuepress/composables/project/zh.ts b/src/.vuepress/composables/project/zh.ts index ba30588f54..559a67171e 100644 --- a/src/.vuepress/composables/project/zh.ts +++ b/src/.vuepress/composables/project/zh.ts @@ -1,101 +1,109 @@ -import { type ProjectsOption } from "./types.js"; - -export const zhProjectsOption: ProjectsOption = { - PROJECTS: "项目", - DESCRIPTION: - "Dromara开源社区,汇集一流开源项目作者,提供开源产品、解决方案、咨询、支持和培训。涵盖分布式事务、工具、认证、微服务RPC、监控、日志、大数据等领域。加入我们,体验开源的乐趣。", - PROJECT_SPONSOR: "项目sponsor", - JOINING_DATE: "加入时间", - START_UP: "开始", - - DISTRIBUTED_TRANSACTION: "分布式事务", - HMILY_DESC: "柔性分布式事务解决方案", - RAINCAT_DESC: "强一致性分布式事务解决方案", - MYTH_DESC: "可靠消息分布式事务解决方案", - - POPULAR_TOOLS: "流行工具", - HUTOOL_DESC: "小而全的Java工具类库", - FOREST_DESC: "更轻量,更简单实用的HTTP客户端框架", - LITEFLOW_DESC: "轻量,快速,稳定,可编排的组件式流程引擎", - DYNAMIC_TP_DESC: "基于配置中心的轻量级动态线程池,内置监控告警功能", - EASY_ES_DESC: "更易用的ES搜索引擎框架", - GO_VIEW_DESC: "高效的低代码「数据可视化」开发平台", - IMAGE_COMBINER_DESC: "Java服务端图片合成工具", - JINX_DESC: "Spring-boot http服务:netty", - WEMQ_DESC: - "WeMQ是一款面向物联网设备运营商的开源物联网设备调试系统,提供集成设备管理、MQTT服务器管理、客户管理等功能,自研Nmqs通信层组件,实现了连接信息的加密,保证了数据的安全性。", - ELECTRON_EGG_DESC: "一个入门简单、跨平台、企业级桌面软件开发框架", - NORTHSTAR_DESC: "量化交易平台", - EASY_TRANS_DESC: "一个注解搞定数据翻译,减少30%SQL代码量", - FAST_REQUEST_DESC: "为简化 API 调试而生", - REDISFRONT_DESC: "一款开源跨平台Redis 桌面客户端工具", - X_EASYPDF_DESC: "一个用搭积木的方式构建pdf的框架(基于pdfbox)", - GOBRS_ASYNC_DESC: - "一款功能强大、配置灵活、带有全链路异常回调、内存优化、异常状态管理于一身的高性能异步编排框架", - DYJAVA: "DyJava是一款功能强大的抖音Java开发工具包(SDK),支持抖音各个应用OpenAPI快速调用,包括但不限于移动/网站应用、抖音开放平台、抖店和抖音小程序等", - ORION_VISOR: "一款高颜值、现代化的智能运维&轻量堡垒机平台。", - WARM_FLOW: "国产工作流引擎,其特点简洁轻量但又不简单,五脏俱全,组件独立,可扩展,可满足中小项目的组件.解决flowable和activities复杂、学习成本高和集成难等痛点。", - DBSWITCH: "异构数据库迁移同步(搬家)工具", - SKYEYE_OA: "智能制造一体化,采用Springboot + winUI的低代码平台开发模式", - EASYAI: "国内人气最高的Java人工智能算法框架(java版pytorch)。它可以Maven一键丝滑引入我们的Java项目,无需任何额外的环境配置与依赖,做到开箱即用", - MYBATIS_PLUS_EXT: "mybatis-plus框架的拓展包,在框架原有基础上做了进一步的轻度封装", - EASY_QUERY: "一款java下面支持强类型、轻量级、高性能的ORM", - TIANAI_CAPTCHA: "可能是java界最好的开源行为验证码, [滑块验证码、点选验证码、行为验证码、旋转验证码,滑动验证码]", - DAX_PAY: "DaxPay是一款免费开源的支付系统,支持支付宝、微信、云闪付等通道", - MYFLY_GO: "web 版 linux(终端[终端回放] 文件 脚本 进程 计划任务)、数据库(mysql postgres oracle sqlserver 达梦 高斯 sqlite)、redis(单机 哨兵 集群)、mongo 等集工单流程审批于一体的统一管理操作平台", - MILVUSPLUS: "使用MyBatisPlus的方式,优雅的操作向量数据库 Milvus,同时支持spring和solon", - NEWCAR: "基于CanvasKit-WASM的现代化动画引擎", - UJCMS: "支持无头CMS,支持自定义字段及字段可视化设计。可免费用于商业网站", - SAYORDER: "该项目是基于easyAi引擎的JAVA高性能,低成本,轻量级智能客服", - AKALI: "Akali(阿卡丽),轻量级本地化热点检测/降级框架,10秒钟即可接入使用!大流量下的神器", - OPEN_GITEYE_API_DESC: - "专为开源作者设计的数据图表服务工具类站点,提供了包括Star趋势图、贡献者列表、Gitee指数等数据图表服务。", - BINLOG4J_DESC: "基于 Java 轻量级的 Mysql Binlog 客户端。", - SMS4J_DESC: "SMS4J为短信聚合框架,帮您轻松集成多家短信服务。", - STREAM_QUERY_DESC: "允许完全摆脱Mapper的mybatis-plus体验!", - PAYMENT_SPRING_BOOT_DESC: "最全最好用的微信支付V3 Spring Boot 组件。", - EASYTRANS_DESC: "一个注解搞定数据翻译,减少30%SQL代码量。", - NEUTRINO_PROXY_DESC: "一个基于netty的、开源的java内网穿透项目。", - TESTHUB_DESC: "TestHub 是一款基于流程编排的自动化测试工具。", - DOMAIN_ADMIN_DESC: "域名SSL证书监测平台、SSL证书申请自动续签。", - YFT_DESIGN_DESC: "基于Canvas的开源版“创客贴”。", - ZYPLAYER_DOC_DESC: - "一款适合团队和个人私有化部署使用的知识库、笔记、WIKI文档管理工具。", - X_FILE_STORAGE_DESC: "一站式文件存储,聚合对接所有主流存储平台。", - - ENTERPRISE_CERTIFICATION: "企业级认证", - SA_TOKEN_DESC: "史上功能最全的 Java 权限认证框架", - MAXKEY_DESC: "业界领先的企业级开源IAM身份管理和身份认证产品", - SURENESS_DESC: "面向REST API的高性能认证鉴权框架", - - OPERATIONS_AND_MAINTENANCE_CONTROL: "运维管控", - JPOM_DESC: "简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件", - CUBIC_DESC: "无侵入分布式监控,致力于应用级监控的工具", - ATHENA_DESC: "metrics 字节码工具", - - DISTRIBUTED_LOG: "分布式日志", - TLOG_DESC: "轻量级的分布式日志标记追踪神器", - - BIG_DATA: "大数据", - CLOUDEON_DESC: - "一款基于kubernetes的云原生大数据平台,致力于简化k8s上大数据集群的运维管理。", - DATA_COMPARE_DESC: "数据库比对工具: hive 表数据比对,mysql、Doris 数据比对。", - - MICROSERVICE: "微服务", - KOALAS_RPC_DESC: "高可用可拓展的RPC框架", - MENDMIX_DESC: "一站式分布式云原生架构技术底座", - LAMP_CLOUD_DESC: "专注于多租户(SaaS架构)解决方案的微服务中后台快速开发平台", - DANTE_CLOUD_DESC: - "基于 Spring Authorization Server 全新适配 OAuth 2.1 协议的企业级微服务架构", - OPEN_CAPACITY_PLATFORM_DESC: "基于Spring Cloud的企业级微服务框架", - RUOYI_VUE_PLUS_DESC: - "后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token+Mybatis-Plus+Jackson+Xxl-Job+SpringDoc+Hutool+OSS", - J2EEFAST_DESC: "J2eeFAST 是一个 Java EE 企业级快速开发平台。", - - DISTRIBUTED_SCHEDULING: "分布式调度", - HODOR_DESC: "分布式调度框架", - DISJOB_DESC: "一个分布式的任务调度框架。", - CARBON_DESC: "一个轻量级、语义化、对开发者友好的 golang 时间处理库" -}; - -export default zhProjectsOption; +import { type ProjectsOption } from "./types.js"; + +export const zhProjectsOption: ProjectsOption = { + PROJECTS: "Dromara 开源社区:汇聚顶尖项目,共探开源乐趣", + DESCRIPTION: + "Dromara开源社区,汇集一流开源项目作者,提供开源产品、解决方案、咨询、支持和培训。涵盖分布式事务、工具、认证、微服务RPC、监控、日志、大数据等领域。加入我们,体验开源的乐趣。", + PROJECT_SPONSOR: "项目sponsor", + JOINING_DATE: "加入时间", + START_UP: "开始", + + DISTRIBUTED_TRANSACTION: "分布式事务", + HMILY_DESC: "柔性分布式事务解决方案", + RAINCAT_DESC: "强一致性分布式事务解决方案", + MYTH_DESC: "可靠消息分布式事务解决方案", + + POPULAR_TOOLS: "流行工具", + HUTOOL_DESC: "小而全的Java工具类库", + FOREST_DESC: "更轻量,更简单实用的HTTP客户端框架", + LITEFLOW_DESC: "轻量,快速,稳定,可编排的组件式流程引擎", + DYNAMIC_TP_DESC: "基于配置中心的轻量级动态线程池,内置监控告警功能", + EASY_ES_DESC: "更易用的ES搜索引擎框架", + GO_VIEW_DESC: "高效的低代码「数据可视化」开发平台", + IMAGE_COMBINER_DESC: "Java服务端图片合成工具", + JINX_DESC: "Spring-boot http服务:netty", + WEMQ_DESC: + "WeMQ是一款面向物联网设备运营商的开源物联网设备调试系统,提供集成设备管理、MQTT服务器管理、客户管理等功能,自研Nmqs通信层组件,实现了连接信息的加密,保证了数据的安全性。", + ELECTRON_EGG_DESC: "一个入门简单、跨平台、企业级桌面软件开发框架", + NORTHSTAR_DESC: "量化交易平台", + EASY_TRANS_DESC: "一个注解搞定数据翻译,减少30%SQL代码量", + FAST_REQUEST_DESC: "为简化 API 调试而生", + REDISFRONT_DESC: "一款开源跨平台Redis 桌面客户端工具", + X_EASYPDF_DESC: "一个用搭积木的方式构建pdf的框架(基于pdfbox)", + GOBRS_ASYNC_DESC: + "一款功能强大、配置灵活、带有全链路异常回调、内存优化、异常状态管理于一身的高性能异步编排框架", + DYJAVA: + "DyJava是一款功能强大的抖音Java开发工具包(SDK),支持抖音各个应用OpenAPI快速调用,包括但不限于移动/网站应用、抖音开放平台、抖店和抖音小程序等", + ORION_VISOR: "一款高颜值、现代化的智能运维&轻量堡垒机平台。", + WARM_FLOW: + "国产工作流引擎,其特点简洁轻量但又不简单,五脏俱全,组件独立,可扩展,可满足中小项目的组件.解决flowable和activities复杂、学习成本高和集成难等痛点。", + DBSWITCH: "异构数据库迁移同步(搬家)工具", + SKYEYE_OA: "智能制造一体化,采用Springboot + winUI的低代码平台开发模式", + EASYAI: + "国内人气最高的Java人工智能算法框架(java版pytorch)。它可以Maven一键丝滑引入我们的Java项目,无需任何额外的环境配置与依赖,做到开箱即用", + MYBATIS_PLUS_EXT: + "mybatis-plus框架的拓展包,在框架原有基础上做了进一步的轻度封装", + EASY_QUERY: "一款java下面支持强类型、轻量级、高性能的ORM", + TIANAI_CAPTCHA: + "可能是java界最好的开源行为验证码, [滑块验证码、点选验证码、行为验证码、旋转验证码,滑动验证码]", + DAX_PAY: "DaxPay是一款免费开源的支付系统,支持支付宝、微信、云闪付等通道", + MYFLY_GO: + "web 版 linux(终端[终端回放] 文件 脚本 进程 计划任务)、数据库(mysql postgres oracle sqlserver 达梦 高斯 sqlite)、redis(单机 哨兵 集群)、mongo 等集工单流程审批于一体的统一管理操作平台", + MILVUSPLUS: + "使用MyBatisPlus的方式,优雅的操作向量数据库 Milvus,同时支持spring和solon", + NEWCAR: "基于CanvasKit-WASM的现代化动画引擎", + UJCMS: "支持无头CMS,支持自定义字段及字段可视化设计。可免费用于商业网站", + SAYORDER: "该项目是基于easyAi引擎的JAVA高性能,低成本,轻量级智能客服", + AKALI: + "Akali(阿卡丽),轻量级本地化热点检测/降级框架,10秒钟即可接入使用!大流量下的神器", + OPEN_GITEYE_API_DESC: + "专为开源作者设计的数据图表服务工具类站点,提供了包括Star趋势图、贡献者列表、Gitee指数等数据图表服务。", + BINLOG4J_DESC: "基于 Java 轻量级的 Mysql Binlog 客户端。", + SMS4J_DESC: "SMS4J为短信聚合框架,帮您轻松集成多家短信服务。", + STREAM_QUERY_DESC: "允许完全摆脱Mapper的mybatis-plus体验!", + PAYMENT_SPRING_BOOT_DESC: "最全最好用的微信支付V3 Spring Boot 组件。", + EASYTRANS_DESC: "一个注解搞定数据翻译,减少30%SQL代码量。", + NEUTRINO_PROXY_DESC: "一个基于netty的、开源的java内网穿透项目。", + TESTHUB_DESC: "TestHub 是一款基于流程编排的自动化测试工具。", + DOMAIN_ADMIN_DESC: "域名SSL证书监测平台、SSL证书申请自动续签。", + YFT_DESIGN_DESC: "基于Canvas的开源版“创客贴”。", + ZYPLAYER_DOC_DESC: + "一款适合团队和个人私有化部署使用的知识库、笔记、WIKI文档管理工具。", + X_FILE_STORAGE_DESC: "一站式文件存储,聚合对接所有主流存储平台。", + + ENTERPRISE_CERTIFICATION: "企业级认证", + SA_TOKEN_DESC: "史上功能最全的 Java 权限认证框架", + MAXKEY_DESC: "业界领先的企业级开源IAM身份管理和身份认证产品", + SURENESS_DESC: "面向REST API的高性能认证鉴权框架", + + OPERATIONS_AND_MAINTENANCE_CONTROL: "运维管控", + JPOM_DESC: "简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件", + CUBIC_DESC: "无侵入分布式监控,致力于应用级监控的工具", + ATHENA_DESC: "metrics 字节码工具", + + DISTRIBUTED_LOG: "分布式日志", + TLOG_DESC: "轻量级的分布式日志标记追踪神器", + + BIG_DATA: "大数据", + CLOUDEON_DESC: + "一款基于kubernetes的云原生大数据平台,致力于简化k8s上大数据集群的运维管理。", + DATA_COMPARE_DESC: "数据库比对工具: hive 表数据比对,mysql、Doris 数据比对。", + + MICROSERVICE: "微服务", + KOALAS_RPC_DESC: "高可用可拓展的RPC框架", + MENDMIX_DESC: "一站式分布式云原生架构技术底座", + LAMP_CLOUD_DESC: "专注于多租户(SaaS架构)解决方案的微服务中后台快速开发平台", + DANTE_CLOUD_DESC: + "基于 Spring Authorization Server 全新适配 OAuth 2.1 协议的企业级微服务架构", + OPEN_CAPACITY_PLATFORM_DESC: "基于Spring Cloud的企业级微服务框架", + RUOYI_VUE_PLUS_DESC: + "后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token+Mybatis-Plus+Jackson+Xxl-Job+SpringDoc+Hutool+OSS", + J2EEFAST_DESC: "J2eeFAST 是一个 Java EE 企业级快速开发平台。", + + DISTRIBUTED_SCHEDULING: "分布式调度", + HODOR_DESC: "分布式调度框架", + DISJOB_DESC: "一个分布式的任务调度框架。", + CARBON_DESC: "一个轻量级、语义化、对开发者友好的 golang 时间处理库", +}; + +export default zhProjectsOption; diff --git a/src/.vuepress/composables/section/enActivity.ts b/src/.vuepress/composables/section/enActivity.ts index db5c1beaec..7991d53f62 100644 --- a/src/.vuepress/composables/section/enActivity.ts +++ b/src/.vuepress/composables/section/enActivity.ts @@ -1,3 +1,3 @@ -export const enActivityOption = { - DESC: "Join Dromara's global activities, from meetups to conferences. We value collaboration and look forward to connecting and sharing expertise with you." -}; +export const enActivityOption = { + DESC: "Join Dromara's global activities, from meetups to conferences. We value collaboration and look forward to connecting and sharing expertise with you.", +}; diff --git a/src/.vuepress/composables/section/enBlog.ts b/src/.vuepress/composables/section/enBlog.ts index 0f8d4508db..735f1d28e8 100644 --- a/src/.vuepress/composables/section/enBlog.ts +++ b/src/.vuepress/composables/section/enBlog.ts @@ -1,3 +1,3 @@ -export const enBlogOption = { - DESC: "Dromara's blog shares open-source insights on projects, trends, and practices, focusing on distributed systems and microservices. Dive into the open-source world with us." -}; +export const enBlogOption = { + DESC: "Dromara's blog shares open-source insights on projects, trends, and practices, focusing on distributed systems and microservices. Dive into the open-source world with us.", +}; diff --git a/src/.vuepress/composables/section/enNews.ts b/src/.vuepress/composables/section/enNews.ts index 3e90aaa78e..3ea0a63443 100644 --- a/src/.vuepress/composables/section/enNews.ts +++ b/src/.vuepress/composables/section/enNews.ts @@ -1,3 +1,3 @@ -export const enNewsOption = { - DESC: "Discover the latest from Dromara Open Source Community. We bring you updates, innovations, and collaborations. Stay connected with our development through our news section." -}; +export const enNewsOption = { + DESC: "Discover the latest from Dromara Open Source Community. We bring you updates, innovations, and collaborations. Stay connected with our development through our news section.", +}; diff --git a/src/.vuepress/composables/section/types.ts b/src/.vuepress/composables/section/types.ts index dbac6ba7aa..882baf0dd1 100644 --- a/src/.vuepress/composables/section/types.ts +++ b/src/.vuepress/composables/section/types.ts @@ -1,14 +1,14 @@ -export interface ActivityOption { - DESC: string -} - -export interface GroupedSectionPage { - cover: string - tag?: string[] - title: string - url: string - author: string - date: string -} - -export type GroupedSectionPages = Record; +export interface ActivityOption { + DESC: string; +} + +export interface GroupedSectionPage { + cover: string; + tag?: string[]; + title: string; + url: string; + author: string; + date: string; +} + +export type GroupedSectionPages = Record; diff --git a/src/.vuepress/composables/section/zhActivity.ts b/src/.vuepress/composables/section/zhActivity.ts index f320e2dd07..d615131f6a 100644 --- a/src/.vuepress/composables/section/zhActivity.ts +++ b/src/.vuepress/composables/section/zhActivity.ts @@ -1,3 +1,3 @@ -export const zhActivityOption = { - DESC: "加入我们的全球开源社区活动,从本地聚会到行业会议。Dromara重视协作和交流,热切期待在我们的聚会中与您互相连接,分享专业知识,丰富我们的社区。" -}; +export const zhActivityOption = { + DESC: "加入我们的全球开源社区活动,从本地聚会到行业会议。Dromara重视协作和交流,热切期待在我们的聚会中与您互相连接,分享专业知识,丰富我们的社区。", +}; diff --git a/src/.vuepress/composables/section/zhBlog.ts b/src/.vuepress/composables/section/zhBlog.ts index f056ee4c2e..b41e554254 100644 --- a/src/.vuepress/composables/section/zhBlog.ts +++ b/src/.vuepress/composables/section/zhBlog.ts @@ -1,3 +1,3 @@ -export const zhBlogOption = { - DESC: "Dromara的合作博客提供开源洞见,涵盖项目、趋势和最佳实践。探索分布式系统、微服务和运维监控的更新。我们的博客与您共同探索开源领域。" -}; +export const zhBlogOption = { + DESC: "Dromara的合作博客提供开源洞见,涵盖项目、趋势和最佳实践。探索分布式系统、微服务和运维监控的更新。我们的博客与您共同探索开源领域。", +}; diff --git a/src/.vuepress/composables/section/zhNews.ts b/src/.vuepress/composables/section/zhNews.ts index 6c72af2930..7eecbf01ff 100644 --- a/src/.vuepress/composables/section/zhNews.ts +++ b/src/.vuepress/composables/section/zhNews.ts @@ -1,3 +1,3 @@ -export const zhNewsOption = { - DESC: "探寻Dromara开源社区的最新动向。我们致力于为您呈现Dromara最新的进展、创新和合作信息。新闻栏目为您提供了解Dromara的综合资源,与我们的持续发展保持联系。" -}; +export const zhNewsOption = { + DESC: "探寻Dromara开源社区的最新动向。我们致力于为您呈现Dromara最新的进展、创新和合作信息。新闻栏目为您提供了解Dromara的综合资源,与我们的持续发展保持联系。", +}; diff --git a/src/.vuepress/config.ts b/src/.vuepress/config.ts index ebf02c6030..0b523f1309 100644 --- a/src/.vuepress/config.ts +++ b/src/.vuepress/config.ts @@ -1,19 +1,19 @@ import { getDirname, path } from "vuepress/utils"; import { defineUserConfig } from "vuepress"; import { hopeTheme } from "vuepress-theme-hope"; -import { searchProPlugin } from "vuepress-plugin-search-pro"; + +// import { registerComponentsPlugin } from "@vuepress/plugin-register-components"; import { viteBundler } from "@vuepress/bundler-vite"; import { enNavbar, zhNavbar } from "./navbar/index.js"; import { getAllFrontmatter } from "./getAllFrontmatter.js"; -// eslint-disable-next-line @typescript-eslint/naming-convention const __dirname = getDirname(import.meta.url); export default defineUserConfig({ bundler: viteBundler({ viteOptions: {}, - vuePluginOptions: {} + vuePluginOptions: {}, }), base: "/", head: [ @@ -21,104 +21,98 @@ export default defineUserConfig({ "meta", { "http-equiv": "Cache-Control", - content: "max-age=3600, must-revalidate" - } + content: "max-age=3600, must-revalidate", + }, ], [ "meta", { name: "description", - content: "Non-profit organization where open-source enthusiasts gather." - } + content: + "Non-profit organization where open-source enthusiasts gather.", + }, ], - ["script", { src: "https://cdn.wwads.cn/js/makemoney.js", async: true }] + ["script", { src: "https://cdn.wwads.cn/js/makemoney.js", async: true }], ], locales: { "/": { lang: "en-US", - description: "A official website for dromara" + description: "A official website for dromara", }, "/zh/": { lang: "zh-CN", - description: "dromara的官网文档" - } + description: "dromara的官网文档", + }, }, theme: hopeTheme( { hostname: "https://vuepress-theme-hope-docs-demo.netlify.app", - logo: "/logo.svg", + logo: "/Dlogo.svg", repo: "dromara", docsDir: "src", breadcrumb: false, - darkmode: "disable", + darkmode: "enable", editLink: false, - iconAssets: "fontawesome-with-brands", + // 修复:将 icon 改为 plugins.icon.assets + plugins: { + slimsearch: true, + icon: { + assets: "fontawesome-with-brands", // 原 icon 配置迁移到这里 + }, + // 修复:移除 mdEnhance.figure,迁移到顶层 markdown.figure + // mdEnhance: { + // // 保留其他可能的 mdEnhance 配置(如果有) + // }, + // photoSwipe: false, + }, + + // 新增:将 figure 配置迁移到顶层 markdown + markdown: { + figure: true, // 原 mdEnhance.figure 迁移到这里 + }, navbarLayout: { start: ["Brand"], center: ["Links"], - end: ["Language", "GiteeRepo", "Repo", "Outlook", "Search"] + end: ["Language", "GiteeRepo", "Repo", "Outlook", "Search"], }, sidebarSorter: ["date-desc"], locales: { "/": { - // navbar navbar: enNavbar, - // sidebar - sidebar: false + // sidebar: false, }, - - /** - * Chinese locale config - */ "/zh/": { - // navbar navbar: zhNavbar, - // sidebar - sidebar: false - } - }, - - plugins: { - // All features are enabled for demo, only preserve features you need here - mdEnhance: { - figure: true + // sidebar: false, }, - photoSwipe: false - } + }, }, - { custom: true } + { custom: true }, ), - plugins: [ - // Search - searchProPlugin({ - // index all content - indexContent: true - }), - getAllFrontmatter - ], + plugins: [getAllFrontmatter], alias: { - // 你可以在这里将别名定向到自己的组件 - "@theme-hope/components/HomePage": path.resolve( + // 新版本存储地址修改 多了一个/home + "@theme-hope/components/home/HomePage": path.resolve( __dirname, - "./components/HomePage.vue" + "./components/HomePage.vue", ), - "@theme-hope/components/PageFooter": path.resolve( + "@theme-hope/components/base/PageFooter": path.resolve( __dirname, - "./components/PageFooter.vue" + "./components/PageFooter.vue", ), - "@MembersPage": path.resolve(__dirname, "./components/MembersPage.vue"), + // "@MembersPage": path.resolve(__dirname, "./components/MembersPage.vue"), "@ProjectsPage": path.resolve(__dirname, "./components/ProjectsPage.vue"), "@HonorComp": path.resolve(__dirname, "./components/HonorComp.vue"), - "@SiteSection": path.resolve(__dirname, "./components/SiteSection.vue") + "@SiteSection": path.resolve(__dirname, "./components/SiteSection.vue"), }, - shouldPrefetch: false + shouldPrefetch: false, }); diff --git a/src/.vuepress/getAllFrontmatter.ts b/src/.vuepress/getAllFrontmatter.ts index c77efaeb92..ad399ff6cf 100644 --- a/src/.vuepress/getAllFrontmatter.ts +++ b/src/.vuepress/getAllFrontmatter.ts @@ -4,7 +4,14 @@ import { type PluginObject } from "vuepress"; export const getAllFrontmatter: PluginObject = { name: "get-all-frontmatter", - extendsPage: (page, app) => { - (app.siteData.frontmatter ??= []).push(page.data.frontmatter); - } + onInitialized: (app) => { + // 在 app.siteData 上添加 frontmatter 数组 + app.siteData.frontmatter = app.pages.map((page) => page.frontmatter); + }, + + // 可选:在页面更新时同步更新 frontmatter 数据 + onPrepared: (app) => { + // 重新收集所有页面的 frontmatter + app.siteData.frontmatter = app.pages.map((page) => page.frontmatter); + }, }; diff --git a/src/.vuepress/layouts/members.vue b/src/.vuepress/layouts/members.vue new file mode 100644 index 0000000000..ea9e373dce --- /dev/null +++ b/src/.vuepress/layouts/members.vue @@ -0,0 +1,375 @@ + + + + + diff --git a/src/.vuepress/layouts/projects.vue b/src/.vuepress/layouts/projects.vue new file mode 100644 index 0000000000..44ec0a9032 --- /dev/null +++ b/src/.vuepress/layouts/projects.vue @@ -0,0 +1,534 @@ + + + + + diff --git a/src/.vuepress/layouts/siteLayout.vue b/src/.vuepress/layouts/siteLayout.vue new file mode 100644 index 0000000000..20bd3e1afb --- /dev/null +++ b/src/.vuepress/layouts/siteLayout.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/src/.vuepress/navbar/en.ts b/src/.vuepress/navbar/en.ts index 4e54e5f877..d3ae47a5bc 100644 --- a/src/.vuepress/navbar/en.ts +++ b/src/.vuepress/navbar/en.ts @@ -7,5 +7,5 @@ export const enNavbar = navbar([ "/blog/", "/activity/", "/donation/", - "/about/" + "/about/", ]); diff --git a/src/.vuepress/navbar/zh.ts b/src/.vuepress/navbar/zh.ts index 84393322b5..7dd8cb3fce 100644 --- a/src/.vuepress/navbar/zh.ts +++ b/src/.vuepress/navbar/zh.ts @@ -7,5 +7,5 @@ export const zhNavbar = navbar([ "/zh/blog/", "/zh/activity/", "/zh/donation/", - "/zh/about/" + "/zh/about/", ]); diff --git a/src/.vuepress/public/Dlogo.svg b/src/.vuepress/public/Dlogo.svg new file mode 100644 index 0000000000..bd89da40c8 --- /dev/null +++ b/src/.vuepress/public/Dlogo.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/.vuepress/public/assets/img/Banner.gif b/src/.vuepress/public/assets/img/Banner.gif new file mode 100644 index 0000000000..be08b24bde Binary files /dev/null and b/src/.vuepress/public/assets/img/Banner.gif differ diff --git a/src/.vuepress/public/assets/img/activity.png b/src/.vuepress/public/assets/img/activity.png index 38c2cd5cc1..bda72f8d12 100644 Binary files a/src/.vuepress/public/assets/img/activity.png and b/src/.vuepress/public/assets/img/activity.png differ diff --git a/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-0.png b/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-0.png new file mode 100644 index 0000000000..476a402819 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-0.png differ diff --git a/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-1.png b/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-1.png new file mode 100644 index 0000000000..4d52fb8e50 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-1.png differ diff --git a/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-2.png b/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-2.png new file mode 100644 index 0000000000..ed914bbc5d Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/Dromara-OSPP-2025-2.png differ diff --git a/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-10.jpg b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-10.jpg new file mode 100644 index 0000000000..1deaa9f060 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-10.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-12.png b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-12.png new file mode 100644 index 0000000000..7799e9255f Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-12.png differ diff --git a/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-14.png b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-14.png new file mode 100644 index 0000000000..205851e2ab Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-14.png differ diff --git a/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-2.gif b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-2.gif new file mode 100644 index 0000000000..ffb10880af Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-2.gif differ diff --git a/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-3.jpg b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-3.jpg new file mode 100644 index 0000000000..e7da802d94 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-3.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-9.jpg b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-9.jpg new file mode 100644 index 0000000000..a3d7da61b8 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/PowerData-AI-shanghai-4.19-9.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/apache-iceberg-meetup2025-0.png b/src/.vuepress/public/assets/img/activity/apache-iceberg-meetup2025-0.png new file mode 100644 index 0000000000..0abd24af17 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/apache-iceberg-meetup2025-0.png differ diff --git a/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-0.webp b/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-0.webp new file mode 100644 index 0000000000..c234b4826c Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-0.webp differ diff --git a/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-1.webp b/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-1.webp new file mode 100644 index 0000000000..40d4745158 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-1.webp differ diff --git a/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-2.png b/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-2.png new file mode 100644 index 0000000000..ccbf797989 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-2.png differ diff --git a/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-9.webp b/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-9.webp new file mode 100644 index 0000000000..8dc609a2ce Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/coscon24-forum-intro-9.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-0.gif b/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-0.gif new file mode 100644 index 0000000000..acdc7ccde3 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-0.gif differ diff --git a/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-1.webp b/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-1.webp new file mode 100644 index 0000000000..413a7849ba Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-1.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-2.webp b/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-2.webp new file mode 100644 index 0000000000..11b3a7f0bb Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dev-ai-frontier-meet-2.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-0.png b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-0.png new file mode 100644 index 0000000000..474fcc2913 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-0.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-1.png b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-1.png new file mode 100644 index 0000000000..f146aa6074 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-1.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-10.png b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-10.png new file mode 100644 index 0000000000..dfe94707bc Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-10.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-3.png b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-3.png new file mode 100644 index 0000000000..d7f17012e9 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-3.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-4.png b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-4.png new file mode 100644 index 0000000000..bf3efe565c Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-4.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-6.png b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-6.png new file mode 100644 index 0000000000..f146aa6074 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-6.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-8.png b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-8.png new file mode 100644 index 0000000000..d7f17012e9 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-8.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-9.png b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-9.png new file mode 100644 index 0000000000..98da180eb4 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-COSCon-letter-9.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-0.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-0.webp new file mode 100644 index 0000000000..fb3c76d83b Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-0.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-10.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-10.webp new file mode 100644 index 0000000000..74eb0ed011 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-10.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-11.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-11.png new file mode 100644 index 0000000000..bb0b790666 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-11.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-12.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-12.png new file mode 100644 index 0000000000..a06ba36f4f Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-12.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-13.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-13.png new file mode 100644 index 0000000000..d6c9e22e79 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-13.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-14.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-14.png new file mode 100644 index 0000000000..2f5c95bf67 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-14.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-15.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-15.png new file mode 100644 index 0000000000..3740b434e4 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-15.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-16.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-16.webp new file mode 100644 index 0000000000..a79aed681d Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-16.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-17.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-17.png new file mode 100644 index 0000000000..ef3780eb06 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-17.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-18.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-18.png new file mode 100644 index 0000000000..567e1805fd Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-18.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-19.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-19.png new file mode 100644 index 0000000000..121b4bd3f0 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-19.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-20.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-20.png new file mode 100644 index 0000000000..ccb06ee319 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-20.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-21.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-21.png new file mode 100644 index 0000000000..225c04d6d8 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-21.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-22.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-22.png new file mode 100644 index 0000000000..a92064945b Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-22.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-23.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-23.png new file mode 100644 index 0000000000..0661f95520 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-23.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-24.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-24.webp new file mode 100644 index 0000000000..149c0581a6 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-24.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-25.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-25.png new file mode 100644 index 0000000000..3cfcccf8eb Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-25.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-26.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-26.png new file mode 100644 index 0000000000..d1b35f111c Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-26.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-27.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-27.webp new file mode 100644 index 0000000000..9c3ed5d3d3 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-27.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-28.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-28.webp new file mode 100644 index 0000000000..fd66a74d39 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-28.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-29.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-29.png new file mode 100644 index 0000000000..52e42e1853 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-29.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-3.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-3.webp new file mode 100644 index 0000000000..37aa210584 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-3.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-30.png b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-30.png new file mode 100644 index 0000000000..170ea11be4 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-30.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-31.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-31.webp new file mode 100644 index 0000000000..879a7d889e Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-31.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-4.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-4.webp new file mode 100644 index 0000000000..ec6d6bbc0f Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-4.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-5.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-5.webp new file mode 100644 index 0000000000..0e8de5e333 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-5.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-6.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-6.webp new file mode 100644 index 0000000000..3eab18e0cd Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-6.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-7.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-7.webp new file mode 100644 index 0000000000..30ab2bf4ac Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-7.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-8.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-8.webp new file mode 100644 index 0000000000..9e5aa2c73e Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-8.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-market-9.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-9.webp new file mode 100644 index 0000000000..4960336242 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-market-9.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-0.jpg b/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-0.jpg new file mode 100644 index 0000000000..129e3e4e33 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-0.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-1.jpg b/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-1.jpg new file mode 100644 index 0000000000..ae47d44a4a Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-1.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-2.jpg b/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-2.jpg new file mode 100644 index 0000000000..f54e492f3f Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-2.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-3.jpg b/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-3.jpg new file mode 100644 index 0000000000..27a68673ee Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon-showcase-3.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-countdown-0.jpg b/src/.vuepress/public/assets/img/activity/dromara-coscon24-countdown-0.jpg new file mode 100644 index 0000000000..ac02379bab Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-countdown-0.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-0.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-0.webp new file mode 100644 index 0000000000..c234b4826c Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-0.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-1.png b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-1.png new file mode 100644 index 0000000000..d7c8df06d0 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-1.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-10.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-10.webp new file mode 100644 index 0000000000..c152b1e3a1 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-10.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-11.png b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-11.png new file mode 100644 index 0000000000..d7c8df06d0 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-11.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-12.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-12.webp new file mode 100644 index 0000000000..37b2ff4b91 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-12.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-13.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-13.webp new file mode 100644 index 0000000000..d5af7ce93e Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-13.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-14.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-14.webp new file mode 100644 index 0000000000..beb7b7f3c8 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-14.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-15.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-15.webp new file mode 100644 index 0000000000..beb7b7f3c8 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-15.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-16.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-16.webp new file mode 100644 index 0000000000..8dc609a2ce Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-16.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-2.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-2.webp new file mode 100644 index 0000000000..37b2ff4b91 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-2.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-3.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-3.webp new file mode 100644 index 0000000000..eb089ec1c0 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-3.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-4.png b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-4.png new file mode 100644 index 0000000000..d7c8df06d0 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-4.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-5.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-5.webp new file mode 100644 index 0000000000..37b2ff4b91 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-5.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-6.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-6.webp new file mode 100644 index 0000000000..767bea426a Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-6.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-7.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-7.webp new file mode 100644 index 0000000000..3be5e3bbf5 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-7.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-8.png b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-8.png new file mode 100644 index 0000000000..d7c8df06d0 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-8.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-9.webp b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-9.webp new file mode 100644 index 0000000000..37b2ff4b91 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-coscon24-forum-9.webp differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-0.png b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-0.png new file mode 100644 index 0000000000..6b67b3d3c7 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-0.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-1.png b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-1.png new file mode 100644 index 0000000000..a4bb123481 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-1.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-2.png b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-2.png new file mode 100644 index 0000000000..fbfb714c90 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-2.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-3.png b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-3.png new file mode 100644 index 0000000000..39b6e1ffdc Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-3.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-4.png b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-4.png new file mode 100644 index 0000000000..8875e25e13 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-4.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-5.png b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-5.png new file mode 100644 index 0000000000..babfc718f7 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-5.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-6.png b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-6.png new file mode 100644 index 0000000000..8637ffe2c3 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-milvusplus-live-preview-6.png differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-0.jpg b/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-0.jpg new file mode 100644 index 0000000000..2f64109fa3 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-0.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-1.jpg b/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-1.jpg new file mode 100644 index 0000000000..023cf92c8e Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-1.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-2.jpg b/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-2.jpg new file mode 100644 index 0000000000..c8b5260bde Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-2.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-3.jpg b/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-3.jpg new file mode 100644 index 0000000000..d4f84aec9b Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/dromara-wuzhen-summit-2024-3.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-reward-submit-0.gif b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-0.gif new file mode 100644 index 0000000000..acdc7ccde3 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-0.gif differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-reward-submit-1.webp b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-1.webp new file mode 100644 index 0000000000..774c1d2ec3 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-1.webp differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-reward-submit-2.webp b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-2.webp new file mode 100644 index 0000000000..da2f3f524b Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-2.webp differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-reward-submit-3.webp b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-3.webp new file mode 100644 index 0000000000..d80c330621 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-3.webp differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-reward-submit-4.webp b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-4.webp new file mode 100644 index 0000000000..5e9096accc Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-4.webp differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-reward-submit-5.webp b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-5.webp new file mode 100644 index 0000000000..2028ca1d23 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-5.webp differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-reward-submit-6.webp b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-6.webp new file mode 100644 index 0000000000..9993b5c0c2 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-6.webp differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-reward-submit-7.webp b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-7.webp new file mode 100644 index 0000000000..5dad928a46 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-reward-submit-7.webp differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-0.gif b/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-0.gif new file mode 100644 index 0000000000..acdc7ccde3 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-0.gif differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-1.png b/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-1.png new file mode 100644 index 0000000000..3a16bdaa26 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-1.png differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-2.png b/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-2.png new file mode 100644 index 0000000000..9ab81d382f Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-2.png differ diff --git a/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-3.jpg b/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-3.jpg new file mode 100644 index 0000000000..72f856e4f5 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/gstar-shenzhen-developers-3.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-0.gif b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-0.gif new file mode 100644 index 0000000000..acdc7ccde3 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-0.gif differ diff --git a/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-1.png b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-1.png new file mode 100644 index 0000000000..a74afc1350 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-1.png differ diff --git a/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-2.svg b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-2.svg new file mode 100644 index 0000000000..2754e13500 --- /dev/null +++ b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-3.svg b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-3.svg new file mode 100644 index 0000000000..7412d77d69 --- /dev/null +++ b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-4.png b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-4.png new file mode 100644 index 0000000000..64adcea33c Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-4.png differ diff --git a/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-5.png b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-5.png new file mode 100644 index 0000000000..b2b6f0d6bc Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-5.png differ diff --git a/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-6.jpg b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-6.jpg new file mode 100644 index 0000000000..05fc222821 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/harmonyos-tool-gift-6.jpg differ diff --git a/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-0.webp b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-0.webp new file mode 100644 index 0000000000..b23d95d685 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-0.webp differ diff --git a/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-1.webp b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-1.webp new file mode 100644 index 0000000000..60fe26fbf5 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-1.webp differ diff --git a/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-2.webp b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-2.webp new file mode 100644 index 0000000000..ba5c92b2bc Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-2.webp differ diff --git a/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-3.webp b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-3.webp new file mode 100644 index 0000000000..60fe26fbf5 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-3.webp differ diff --git a/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-4.png b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-4.png new file mode 100644 index 0000000000..638f990deb Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-4.png differ diff --git a/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-5.webp b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-5.webp new file mode 100644 index 0000000000..f638b503ce Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-5.webp differ diff --git a/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-6.webp b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-6.webp new file mode 100644 index 0000000000..bbd53f1134 Binary files /dev/null and b/src/.vuepress/public/assets/img/activity/secretflow-3rd-upgrade-showcase-6.webp differ diff --git a/src/.vuepress/public/assets/img/blog.png b/src/.vuepress/public/assets/img/blog.png index d588cf450a..dc9c8ecd60 100644 Binary files a/src/.vuepress/public/assets/img/blog.png and b/src/.vuepress/public/assets/img/blog.png differ diff --git a/src/.vuepress/public/assets/img/blog/HertzBea-collection-works-0.png b/src/.vuepress/public/assets/img/blog/HertzBea-collection-works-0.png new file mode 100644 index 0000000000..343d051684 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/HertzBea-collection-works-0.png differ diff --git a/src/.vuepress/public/assets/img/blog/architect-software-craft-0.webp b/src/.vuepress/public/assets/img/blog/architect-software-craft-0.webp new file mode 100644 index 0000000000..c628de64b2 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/architect-software-craft-0.webp differ diff --git a/src/.vuepress/public/assets/img/blog/dromara-warmflow-assignee-guide-0.webp b/src/.vuepress/public/assets/img/blog/dromara-warmflow-assignee-guide-0.webp new file mode 100644 index 0000000000..6a8e78e71c Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/dromara-warmflow-assignee-guide-0.webp differ diff --git a/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-0.webp b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-0.webp new file mode 100644 index 0000000000..4d7e9c19e5 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-0.webp differ diff --git a/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-1.png b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-1.png new file mode 100644 index 0000000000..d5d2b74a03 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-1.png differ diff --git a/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-2.png b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-2.png new file mode 100644 index 0000000000..3fceba1bbd Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-2.png differ diff --git a/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-3.png b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-3.png new file mode 100644 index 0000000000..2344b78b8b Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-3.png differ diff --git a/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-4.webp b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-4.webp new file mode 100644 index 0000000000..f55cb35981 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-4.webp differ diff --git a/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-5.webp b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-5.webp new file mode 100644 index 0000000000..490ad7925e Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/milvus-easyai-face-java-5.webp differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-0.gif b/src/.vuepress/public/assets/img/blog/os-data-community-team-0.gif new file mode 100644 index 0000000000..41d0a20ab4 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-0.gif differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-10.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-10.jpg new file mode 100644 index 0000000000..da5356aa79 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-10.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-11.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-11.jpg new file mode 100644 index 0000000000..e92d7dbe14 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-11.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-12.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-12.jpg new file mode 100644 index 0000000000..5f986f1d37 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-12.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-13.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-13.jpg new file mode 100644 index 0000000000..410ccd686b Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-13.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-14.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-14.jpg new file mode 100644 index 0000000000..6e302b0d0d Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-14.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-15.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-15.jpg new file mode 100644 index 0000000000..d8a2d51bd0 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-15.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-16.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-16.jpg new file mode 100644 index 0000000000..879ae703b8 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-16.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-17.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-17.jpg new file mode 100644 index 0000000000..b875dc53d6 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-17.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-18.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-18.jpg new file mode 100644 index 0000000000..73b9775ed6 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-18.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-19.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-19.jpg new file mode 100644 index 0000000000..955c4a3d47 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-19.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-20.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-20.jpg new file mode 100644 index 0000000000..16d75ac2e3 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-20.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-21.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-21.jpg new file mode 100644 index 0000000000..0e0d04420c Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-21.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-22.gif b/src/.vuepress/public/assets/img/blog/os-data-community-team-22.gif new file mode 100644 index 0000000000..41d0a20ab4 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-22.gif differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-6.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-6.jpg new file mode 100644 index 0000000000..99c5f88b14 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-6.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/os-data-community-team-9.jpg b/src/.vuepress/public/assets/img/blog/os-data-community-team-9.jpg new file mode 100644 index 0000000000..0196d0e239 Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/os-data-community-team-9.jpg differ diff --git a/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-0.png b/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-0.png new file mode 100644 index 0000000000..afbcfccd5a Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-0.png differ diff --git a/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-1.png b/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-1.png new file mode 100644 index 0000000000..b1cb4f920b Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-1.png differ diff --git a/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-2.png b/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-2.png new file mode 100644 index 0000000000..9908cbfb6b Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/springboot-forest-deepseek-integration-2.png differ diff --git a/src/.vuepress/public/assets/img/blog/warm-flow-1.3.0-0.png b/src/.vuepress/public/assets/img/blog/warm-flow-1.3.0-0.png new file mode 100644 index 0000000000..7b1d0ea71b Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/warm-flow-1.3.0-0.png differ diff --git a/src/.vuepress/public/assets/img/blog/warm-flow-1.3.0-1.webp b/src/.vuepress/public/assets/img/blog/warm-flow-1.3.0-1.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/blog/warm-flow-1.3.0-1.webp differ diff --git a/src/.vuepress/public/assets/img/logo-animation/logo.webp b/src/.vuepress/public/assets/img/logo-animation/logo.webp new file mode 100644 index 0000000000..ec91a0c576 Binary files /dev/null and b/src/.vuepress/public/assets/img/logo-animation/logo.webp differ diff --git a/src/.vuepress/public/assets/img/logo.webp b/src/.vuepress/public/assets/img/logo.webp new file mode 100644 index 0000000000..7cd2f90b7a Binary files /dev/null and b/src/.vuepress/public/assets/img/logo.webp differ diff --git a/src/.vuepress/public/assets/img/logo/Akali.webp b/src/.vuepress/public/assets/img/logo/Akali.webp index b652027e54..0c5449dca7 100644 Binary files a/src/.vuepress/public/assets/img/logo/Akali.webp and b/src/.vuepress/public/assets/img/logo/Akali.webp differ diff --git a/src/.vuepress/public/assets/img/logo/Disjob.webp b/src/.vuepress/public/assets/img/logo/Disjob.webp index de77272c88..c9cb34c407 100644 Binary files a/src/.vuepress/public/assets/img/logo/Disjob.webp and b/src/.vuepress/public/assets/img/logo/Disjob.webp differ diff --git a/src/.vuepress/public/assets/img/logo/J2EEFAST.webp b/src/.vuepress/public/assets/img/logo/J2EEFAST.webp index 9e5af471a0..43ab87f5f0 100644 Binary files a/src/.vuepress/public/assets/img/logo/J2EEFAST.webp and b/src/.vuepress/public/assets/img/logo/J2EEFAST.webp differ diff --git a/src/.vuepress/public/assets/img/logo/MaxKey.webp b/src/.vuepress/public/assets/img/logo/MaxKey.webp index 96433a45e1..274e898183 100644 Binary files a/src/.vuepress/public/assets/img/logo/MaxKey.webp and b/src/.vuepress/public/assets/img/logo/MaxKey.webp differ diff --git a/src/.vuepress/public/assets/img/logo/Neutrino-Proxy.webp b/src/.vuepress/public/assets/img/logo/Neutrino-Proxy.webp index 7397c8e1a2..4b20a5e76a 100644 Binary files a/src/.vuepress/public/assets/img/logo/Neutrino-Proxy.webp and b/src/.vuepress/public/assets/img/logo/Neutrino-Proxy.webp differ diff --git a/src/.vuepress/public/assets/img/logo/TLog-home.webp b/src/.vuepress/public/assets/img/logo/TLog-home.webp new file mode 100644 index 0000000000..4417458e3f Binary files /dev/null and b/src/.vuepress/public/assets/img/logo/TLog-home.webp differ diff --git a/src/.vuepress/public/assets/img/logo/TLog.webp b/src/.vuepress/public/assets/img/logo/TLog.webp index 8cd2a24836..626a41ede7 100644 Binary files a/src/.vuepress/public/assets/img/logo/TLog.webp and b/src/.vuepress/public/assets/img/logo/TLog.webp differ diff --git a/src/.vuepress/public/assets/img/logo/TestHub.webp b/src/.vuepress/public/assets/img/logo/TestHub.webp index 20ea42bff7..18627c0e06 100644 Binary files a/src/.vuepress/public/assets/img/logo/TestHub.webp and b/src/.vuepress/public/assets/img/logo/TestHub.webp differ diff --git a/src/.vuepress/public/assets/img/logo/WeMQ.webp b/src/.vuepress/public/assets/img/logo/WeMQ.webp index 5644a3a51c..7cfcb9938e 100644 Binary files a/src/.vuepress/public/assets/img/logo/WeMQ.webp and b/src/.vuepress/public/assets/img/logo/WeMQ.webp differ diff --git a/src/.vuepress/public/assets/img/logo/cubic-home.webp b/src/.vuepress/public/assets/img/logo/cubic-home.webp new file mode 100644 index 0000000000..4f55c67dbc Binary files /dev/null and b/src/.vuepress/public/assets/img/logo/cubic-home.webp differ diff --git a/src/.vuepress/public/assets/img/logo/cubic.webp b/src/.vuepress/public/assets/img/logo/cubic.webp index e2c9e4369b..797fa82800 100644 Binary files a/src/.vuepress/public/assets/img/logo/cubic.webp and b/src/.vuepress/public/assets/img/logo/cubic.webp differ diff --git a/src/.vuepress/public/assets/img/logo/domain-admin.webp b/src/.vuepress/public/assets/img/logo/domain-admin.webp index b630442dca..52c73b9d83 100644 Binary files a/src/.vuepress/public/assets/img/logo/domain-admin.webp and b/src/.vuepress/public/assets/img/logo/domain-admin.webp differ diff --git a/src/.vuepress/public/assets/img/logo/dynamic-tp.webp b/src/.vuepress/public/assets/img/logo/dynamic-tp.webp index 8f3bde3f82..c9c4b4cbeb 100644 Binary files a/src/.vuepress/public/assets/img/logo/dynamic-tp.webp and b/src/.vuepress/public/assets/img/logo/dynamic-tp.webp differ diff --git a/src/.vuepress/public/assets/img/logo/easy-es.webp b/src/.vuepress/public/assets/img/logo/easy-es.webp index 820000be68..e25b2b0b54 100644 Binary files a/src/.vuepress/public/assets/img/logo/easy-es.webp and b/src/.vuepress/public/assets/img/logo/easy-es.webp differ diff --git a/src/.vuepress/public/assets/img/logo/electron-egg.webp b/src/.vuepress/public/assets/img/logo/electron-egg.webp index 7d5b19a995..1b9ddbdccc 100644 Binary files a/src/.vuepress/public/assets/img/logo/electron-egg.webp and b/src/.vuepress/public/assets/img/logo/electron-egg.webp differ diff --git a/src/.vuepress/public/assets/img/logo/gobrs-async.webp b/src/.vuepress/public/assets/img/logo/gobrs-async.webp index b93210b601..0b0079fed5 100644 Binary files a/src/.vuepress/public/assets/img/logo/gobrs-async.webp and b/src/.vuepress/public/assets/img/logo/gobrs-async.webp differ diff --git a/src/.vuepress/public/assets/img/logo/koalas-rpc.webp b/src/.vuepress/public/assets/img/logo/koalas-rpc.webp index 310283eb3b..2323945acb 100644 Binary files a/src/.vuepress/public/assets/img/logo/koalas-rpc.webp and b/src/.vuepress/public/assets/img/logo/koalas-rpc.webp differ diff --git a/src/.vuepress/public/assets/img/logo/liteFlow.webp b/src/.vuepress/public/assets/img/logo/liteFlow.webp index fbdc11f03e..05e5359dc3 100644 Binary files a/src/.vuepress/public/assets/img/logo/liteFlow.webp and b/src/.vuepress/public/assets/img/logo/liteFlow.webp differ diff --git a/src/.vuepress/public/assets/img/logo/newcar.webp b/src/.vuepress/public/assets/img/logo/newcar.webp index 9e5bdae15e..4aa5594380 100644 Binary files a/src/.vuepress/public/assets/img/logo/newcar.webp and b/src/.vuepress/public/assets/img/logo/newcar.webp differ diff --git a/src/.vuepress/public/assets/img/logo/open-giteye-api.webp b/src/.vuepress/public/assets/img/logo/open-giteye-api.webp index f6ac396893..d9c153a217 100644 Binary files a/src/.vuepress/public/assets/img/logo/open-giteye-api.webp and b/src/.vuepress/public/assets/img/logo/open-giteye-api.webp differ diff --git a/src/.vuepress/public/assets/img/logo/redisfront.png b/src/.vuepress/public/assets/img/logo/redisfront.png new file mode 100644 index 0000000000..afe8ee9e1b Binary files /dev/null and b/src/.vuepress/public/assets/img/logo/redisfront.png differ diff --git a/src/.vuepress/public/assets/img/logo/redisfront.webp b/src/.vuepress/public/assets/img/logo/redisfront.webp index 4c87657c8f..aa8628dd14 100644 Binary files a/src/.vuepress/public/assets/img/logo/redisfront.webp and b/src/.vuepress/public/assets/img/logo/redisfront.webp differ diff --git a/src/.vuepress/public/assets/img/logo/sa-token-home.webp b/src/.vuepress/public/assets/img/logo/sa-token-home.webp new file mode 100644 index 0000000000..985b8eb456 Binary files /dev/null and b/src/.vuepress/public/assets/img/logo/sa-token-home.webp differ diff --git a/src/.vuepress/public/assets/img/logo/sa-token.webp b/src/.vuepress/public/assets/img/logo/sa-token.webp index c75d22cecb..28b9ddd680 100644 Binary files a/src/.vuepress/public/assets/img/logo/sa-token.webp and b/src/.vuepress/public/assets/img/logo/sa-token.webp differ diff --git a/src/.vuepress/public/assets/img/logo/sayOrder.webp b/src/.vuepress/public/assets/img/logo/sayOrder.webp index 293a413c06..eff47d4a04 100644 Binary files a/src/.vuepress/public/assets/img/logo/sayOrder.webp and b/src/.vuepress/public/assets/img/logo/sayOrder.webp differ diff --git a/src/.vuepress/public/assets/img/logo/sureness.webp b/src/.vuepress/public/assets/img/logo/sureness.webp index 88446a9292..9d8027bfcd 100644 Binary files a/src/.vuepress/public/assets/img/logo/sureness.webp and b/src/.vuepress/public/assets/img/logo/sureness.webp differ diff --git a/src/.vuepress/public/assets/img/logo/tianai-captcha.webp b/src/.vuepress/public/assets/img/logo/tianai-captcha.webp index 641bf49659..8fa5e936ea 100644 Binary files a/src/.vuepress/public/assets/img/logo/tianai-captcha.webp and b/src/.vuepress/public/assets/img/logo/tianai-captcha.webp differ diff --git a/src/.vuepress/public/assets/img/logo/x-easypdf.webp b/src/.vuepress/public/assets/img/logo/x-easypdf.webp index 6134d65e9c..3647f7f74d 100644 Binary files a/src/.vuepress/public/assets/img/logo/x-easypdf.webp and b/src/.vuepress/public/assets/img/logo/x-easypdf.webp differ diff --git a/src/.vuepress/public/assets/img/logo/zyplayer-doc.webp b/src/.vuepress/public/assets/img/logo/zyplayer-doc.webp index ab436422f9..ebf73f8ce4 100644 Binary files a/src/.vuepress/public/assets/img/logo/zyplayer-doc.webp and b/src/.vuepress/public/assets/img/logo/zyplayer-doc.webp differ diff --git a/src/.vuepress/public/assets/img/members/hefengen.webp b/src/.vuepress/public/assets/img/members/hefengen.webp index 30b9f621b8..9e82b090c4 100644 Binary files a/src/.vuepress/public/assets/img/members/hefengen.webp and b/src/.vuepress/public/assets/img/members/hefengen.webp differ diff --git a/src/.vuepress/public/assets/img/news.png b/src/.vuepress/public/assets/img/news.png index 08c49ef4b3..4512c96132 100644 Binary files a/src/.vuepress/public/assets/img/news.png and b/src/.vuepress/public/assets/img/news.png differ diff --git a/src/.vuepress/public/assets/img/news/Apache ShenYu-2.7.0-0.png b/src/.vuepress/public/assets/img/news/Apache ShenYu-2.7.0-0.png new file mode 100644 index 0000000000..dbbbc52404 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Apache ShenYu-2.7.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Apache-Hertzbeat-1.6.1-0.png b/src/.vuepress/public/assets/img/news/Apache-Hertzbeat-1.6.1-0.png new file mode 100644 index 0000000000..dae304dc81 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Apache-Hertzbeat-1.6.1-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Apache-Hertzbeat-1.6.1-1.png b/src/.vuepress/public/assets/img/news/Apache-Hertzbeat-1.6.1-1.png new file mode 100644 index 0000000000..93cb1fa7cd Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Apache-Hertzbeat-1.6.1-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Carbon-0-0.png b/src/.vuepress/public/assets/img/news/Carbon-0-0.png new file mode 100644 index 0000000000..82023b8de6 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Carbon-0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Carbon-0-1.png b/src/.vuepress/public/assets/img/news/Carbon-0-1.png new file mode 100644 index 0000000000..b9801d4c0d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Carbon-0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Carbon-2.6.0-0.png b/src/.vuepress/public/assets/img/news/Carbon-2.6.0-0.png new file mode 100644 index 0000000000..f49ff5f74a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Carbon-2.6.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Carbon-v2.5.0-0.png b/src/.vuepress/public/assets/img/news/Carbon-v2.5.0-0.png new file mode 100644 index 0000000000..9b024b7639 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Carbon-v2.5.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Carbon-v2.5.0-1.webp b/src/.vuepress/public/assets/img/news/Carbon-v2.5.0-1.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Carbon-v2.5.0-1.webp differ diff --git a/src/.vuepress/public/assets/img/news/Dante-Cloud-3.3.5.0-0.png b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.3.5.0-0.png new file mode 100644 index 0000000000..13807af106 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.3.5.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Dante-Cloud-3.3.5.0-1.webp b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.3.5.0-1.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.3.5.0-1.webp differ diff --git a/src/.vuepress/public/assets/img/news/Dante-Cloud-3.4.3.3-0.png b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.4.3.3-0.png new file mode 100644 index 0000000000..13807af106 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.4.3.3-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Dante-Cloud-3.5.0.0-0.png b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.5.0.0-0.png new file mode 100644 index 0000000000..13807af106 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.5.0.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Dante-Cloud-3.5.5.0-0.png b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.5.5.0-0.png new file mode 100644 index 0000000000..13807af106 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Dante-Cloud-3.5.5.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/DyJava-0.png b/src/.vuepress/public/assets/img/news/DyJava-0.png new file mode 100644 index 0000000000..7f23b799d0 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/DyJava-0.png differ diff --git a/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-0.png b/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-0.png new file mode 100644 index 0000000000..a7e494a7ed Binary files /dev/null and b/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-1.png b/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-1.png new file mode 100644 index 0000000000..01682f2490 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-2.png b/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-2.png new file mode 100644 index 0000000000..17e39b35cf Binary files /dev/null and b/src/.vuepress/public/assets/img/news/DynamicTp-v1.2.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-0.png b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-0.png new file mode 100644 index 0000000000..d226864c4b Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-1.png b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-1.png new file mode 100644 index 0000000000..6af56792ad Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-2.jpg b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-2.jpg new file mode 100644 index 0000000000..f09886d6b5 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-2.jpg differ diff --git a/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-3.jpg b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-3.jpg new file mode 100644 index 0000000000..f57b302581 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-3.jpg differ diff --git a/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-4.png b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-4.png new file mode 100644 index 0000000000..65b1294872 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-5.jpg b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-5.jpg new file mode 100644 index 0000000000..d4f9968956 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-5.jpg differ diff --git a/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-6.jpg b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-6.jpg new file mode 100644 index 0000000000..cbddf30933 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Easy-Es-2.1.0-6.jpg differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-0.png b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-0.png new file mode 100644 index 0000000000..eeeff3f239 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-0.png differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-1.png b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-1.png new file mode 100644 index 0000000000..bf5f4f9b1c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-1.png differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-10.webp b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-10.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-10.webp differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-2.png b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-2.png new file mode 100644 index 0000000000..25d29f8fd7 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-2.png differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-3.png b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-3.png new file mode 100644 index 0000000000..369fde7e0d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-3.png differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-4.jpg b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-4.jpg new file mode 100644 index 0000000000..f626ffa173 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-4.jpg differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-5.png b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-5.png new file mode 100644 index 0000000000..bedd0b2441 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-5.png differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-6.png b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-6.png new file mode 100644 index 0000000000..3d3be28f6a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-6.png differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-7.png b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-7.png new file mode 100644 index 0000000000..2ea9918752 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-7.png differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-8.png b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-8.png new file mode 100644 index 0000000000..47f9a057f2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-8.png differ diff --git a/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-9.jpg b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-9.jpg new file mode 100644 index 0000000000..3ddcc3ff8f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/EasyAI-v1.3.8-9.jpg differ diff --git a/src/.vuepress/public/assets/img/news/ElectronEgg-v4-0.jpg b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-0.jpg new file mode 100644 index 0000000000..dd43cfa1bc Binary files /dev/null and b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-0.jpg differ diff --git a/src/.vuepress/public/assets/img/news/ElectronEgg-v4-1.jpg b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-1.jpg new file mode 100644 index 0000000000..1335644531 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-1.jpg differ diff --git a/src/.vuepress/public/assets/img/news/ElectronEgg-v4-2.png b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-2.png new file mode 100644 index 0000000000..4087d3d0df Binary files /dev/null and b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-2.png differ diff --git a/src/.vuepress/public/assets/img/news/ElectronEgg-v4-3.png b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-3.png new file mode 100644 index 0000000000..ddc603d4a6 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-3.png differ diff --git a/src/.vuepress/public/assets/img/news/ElectronEgg-v4-4.png b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-4.png new file mode 100644 index 0000000000..843d926abb Binary files /dev/null and b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-4.png differ diff --git a/src/.vuepress/public/assets/img/news/ElectronEgg-v4-5.jpg b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-5.jpg new file mode 100644 index 0000000000..ed9947cc47 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-5.jpg differ diff --git a/src/.vuepress/public/assets/img/news/ElectronEgg-v4-6.png b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-6.png new file mode 100644 index 0000000000..f193c15a4f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-6.png differ diff --git a/src/.vuepress/public/assets/img/news/ElectronEgg-v4-7.webp b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-7.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/ElectronEgg-v4-7.webp differ diff --git a/src/.vuepress/public/assets/img/news/Forest-v1.7-0.png b/src/.vuepress/public/assets/img/news/Forest-v1.7-0.png new file mode 100644 index 0000000000..4f42d90537 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Forest-v1.7-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Gopher-0-0.png b/src/.vuepress/public/assets/img/news/Gopher-0-0.png new file mode 100644 index 0000000000..30988d73e5 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Gopher-0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Gopher-0-1.png b/src/.vuepress/public/assets/img/news/Gopher-0-1.png new file mode 100644 index 0000000000..a227305380 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Gopher-0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/LiteFlow-v2.13.0-0.png b/src/.vuepress/public/assets/img/news/LiteFlow-v2.13.0-0.png new file mode 100644 index 0000000000..2fd0a96efb Binary files /dev/null and b/src/.vuepress/public/assets/img/news/LiteFlow-v2.13.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-0.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-0.png new file mode 100644 index 0000000000..42b6dda8e1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-0.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-1.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-1.png new file mode 100644 index 0000000000..540b330919 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-1.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-2.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-2.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.2-2.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-0.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-0.png new file mode 100644 index 0000000000..42b6dda8e1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-0.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-1.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-1.png new file mode 100644 index 0000000000..540b330919 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-1.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-2.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-2.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.3-2.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-0.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-0.webp new file mode 100644 index 0000000000..0586db24e2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-0.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-1.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-1.png new file mode 100644 index 0000000000..540b330919 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-1.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-2.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-2.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.4-2.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.5-0.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.5-0.webp new file mode 100644 index 0000000000..0586db24e2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.5-0.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.5-1.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.5-1.png new file mode 100644 index 0000000000..540b330919 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.5-1.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-0.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-0.png new file mode 100644 index 0000000000..42b6dda8e1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-0.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-1.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-1.webp new file mode 100644 index 0000000000..0586db24e2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-1.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-2.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-2.png new file mode 100644 index 0000000000..540b330919 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-2.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-3.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-3.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.6-3.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.7-0.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.7-0.webp new file mode 100644 index 0000000000..0586db24e2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.7-0.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.7-1.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.7-1.png new file mode 100644 index 0000000000..540b330919 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.7-1.png differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.8-0.webp b/src/.vuepress/public/assets/img/news/MaxKey-4.1.8-0.webp new file mode 100644 index 0000000000..0586db24e2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.8-0.webp differ diff --git a/src/.vuepress/public/assets/img/news/MaxKey-4.1.8-1.png b/src/.vuepress/public/assets/img/news/MaxKey-4.1.8-1.png new file mode 100644 index 0000000000..540b330919 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MaxKey-4.1.8-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-0.jpg b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-0.jpg new file mode 100644 index 0000000000..eafc714711 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-0.jpg differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-1.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-1.png new file mode 100644 index 0000000000..800feb55f6 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-2.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-2.png new file mode 100644 index 0000000000..68e64507a6 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-3.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-3.png new file mode 100644 index 0000000000..26defa0a53 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-4.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-4.png new file mode 100644 index 0000000000..d625cad30c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.10.0-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-0.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-0.png new file mode 100644 index 0000000000..fccc9cf9b8 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-1.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-1.png new file mode 100644 index 0000000000..bd70460cc5 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-2.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-2.png new file mode 100644 index 0000000000..15835c5505 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-3.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-3.png new file mode 100644 index 0000000000..5a5ce67be0 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-4.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-4.png new file mode 100644 index 0000000000..a58236e957 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-5.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-5.png new file mode 100644 index 0000000000..82b32487ed Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.0-5.png differ diff --git a/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.4-0.png b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.4-0.png new file mode 100644 index 0000000000..364de934e9 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mayfly-Go-1.9.4-0.png differ diff --git a/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-0.png b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-0.png new file mode 100644 index 0000000000..6b4491f529 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-0.png differ diff --git a/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-1.png b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-1.png new file mode 100644 index 0000000000..64a4a3e6e8 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-1.png differ diff --git a/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-2.png b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-2.png new file mode 100644 index 0000000000..6a087f27c4 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-2.png differ diff --git a/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-3.png b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-3.png new file mode 100644 index 0000000000..7d125a99fa Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-3.png differ diff --git a/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-4.png b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-4.png new file mode 100644 index 0000000000..837efd0c9e Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-4.png differ diff --git a/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-5.png b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-5.png new file mode 100644 index 0000000000..6b4491f529 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MayflyGo-1.10.2-5.png differ diff --git a/src/.vuepress/public/assets/img/news/Mica-MQTT-0-0.png b/src/.vuepress/public/assets/img/news/Mica-MQTT-0-0.png new file mode 100644 index 0000000000..9d4837d887 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Mica-MQTT-0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-0.png b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-0.png new file mode 100644 index 0000000000..e8984c5ec3 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-0.png differ diff --git a/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-1.gif b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-1.gif new file mode 100644 index 0000000000..7b20119499 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-1.gif differ diff --git a/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-2.png b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-2.png new file mode 100644 index 0000000000..43145f73c1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-2.png differ diff --git a/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-3.png b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-3.png new file mode 100644 index 0000000000..3c88a7a29e Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-3.png differ diff --git a/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-4.png b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-4.png new file mode 100644 index 0000000000..405ea33947 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/MilvusPlus-v2.2.1-4.png differ diff --git a/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-0.png b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-0.png new file mode 100644 index 0000000000..3ca40ef6ff Binary files /dev/null and b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-0.png differ diff --git a/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-1.png b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-1.png new file mode 100644 index 0000000000..265802f236 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-1.png differ diff --git a/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-2.png b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-2.png new file mode 100644 index 0000000000..9d2b36a875 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-2.png differ diff --git a/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-3.png b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-3.png new file mode 100644 index 0000000000..7d7c5eabb9 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-3.png differ diff --git a/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-4.png b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-4.png new file mode 100644 index 0000000000..706447ddbb Binary files /dev/null and b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-4.png differ diff --git a/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-5.png b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-5.png new file mode 100644 index 0000000000..fead4a4468 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-5.png differ diff --git a/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-6.jpg b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-6.jpg new file mode 100644 index 0000000000..4224bd89a6 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-6.jpg differ diff --git a/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-7.png b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-7.png new file mode 100644 index 0000000000..405ea33947 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/NeutrinoProxy-2.0.2-7.png differ diff --git a/src/.vuepress/public/assets/img/news/RedisFront-0-0.png b/src/.vuepress/public/assets/img/news/RedisFront-0-0.png new file mode 100644 index 0000000000..8b97b21a13 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/RedisFront-0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/RedisFront-0-1.png b/src/.vuepress/public/assets/img/news/RedisFront-0-1.png new file mode 100644 index 0000000000..c534bbb5b3 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/RedisFront-0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/RedisFront-0-2.png b/src/.vuepress/public/assets/img/news/RedisFront-0-2.png new file mode 100644 index 0000000000..ced46290d6 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/RedisFront-0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/RuoYi-Vue-Plus-5.3.0-0.png b/src/.vuepress/public/assets/img/news/RuoYi-Vue-Plus-5.3.0-0.png new file mode 100644 index 0000000000..30c07a5d7a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/RuoYi-Vue-Plus-5.3.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/SMS4j-3.3.4-0.png b/src/.vuepress/public/assets/img/news/SMS4j-3.3.4-0.png new file mode 100644 index 0000000000..be65feffc1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/SMS4j-3.3.4-0.png differ diff --git a/src/.vuepress/public/assets/img/news/SQLREST-0-0.png b/src/.vuepress/public/assets/img/news/SQLREST-0-0.png new file mode 100644 index 0000000000..66c87f8e5a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/SQLREST-0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/SQLREST-0-1.png b/src/.vuepress/public/assets/img/news/SQLREST-0-1.png new file mode 100644 index 0000000000..89e9d91425 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/SQLREST-0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-0.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-0.png new file mode 100644 index 0000000000..bf43673033 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-1.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-1.png new file mode 100644 index 0000000000..6b3002a272 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-2.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-2.png new file mode 100644 index 0000000000..b1fb93b3a1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-3.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-3.png new file mode 100644 index 0000000000..4aa6dc0f5d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-4.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-4.png new file mode 100644 index 0000000000..b5bb15640d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.41.0-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-0.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-0.png new file mode 100644 index 0000000000..cf9bcb9e36 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-1.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-1.png new file mode 100644 index 0000000000..76f63c0def Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-2.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-2.png new file mode 100644 index 0000000000..4df6f106b1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-3.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-3.png new file mode 100644 index 0000000000..1f26396f4a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-4.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-4.png new file mode 100644 index 0000000000..f3f55245cb Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-5.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-5.png new file mode 100644 index 0000000000..b5bb15640d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.42.0-5.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-0.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-0.png new file mode 100644 index 0000000000..0bf6377f70 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-1.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-1.png new file mode 100644 index 0000000000..1a3fe09792 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-2.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-2.png new file mode 100644 index 0000000000..d23f5e87d5 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-3.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-3.png new file mode 100644 index 0000000000..9d32cfa3ea Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-4.png b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-4.png new file mode 100644 index 0000000000..f01db026f6 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Sa-Token-v1.43.0-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-April-update-0.png b/src/.vuepress/public/assets/img/news/Skyeye-April-update-0.png new file mode 100644 index 0000000000..3c697ade2a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-April-update-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-April-update-1.png b/src/.vuepress/public/assets/img/news/Skyeye-April-update-1.png new file mode 100644 index 0000000000..6cc9d9fd7c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-April-update-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-April-update-2.png b/src/.vuepress/public/assets/img/news/Skyeye-April-update-2.png new file mode 100644 index 0000000000..18c5a70f4b Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-April-update-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-0.png b/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-0.png new file mode 100644 index 0000000000..f605e31f72 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-1.png b/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-1.png new file mode 100644 index 0000000000..dfe787e1d4 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-2.png b/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-2.png new file mode 100644 index 0000000000..7e8ca84c64 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v2.5.8-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-0.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-0.png new file mode 100644 index 0000000000..4a6f21ae03 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-1.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-1.png new file mode 100644 index 0000000000..4d9841b360 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-10.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-10.png new file mode 100644 index 0000000000..f033bde064 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-10.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-11.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-11.png new file mode 100644 index 0000000000..b6e7e771ed Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-11.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-12.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-12.png new file mode 100644 index 0000000000..a9abd62d0c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-12.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-13.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-13.png new file mode 100644 index 0000000000..e4a7c63a26 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-13.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-14.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-14.png new file mode 100644 index 0000000000..e279ff8e62 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-14.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-15.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-15.png new file mode 100644 index 0000000000..1498dc0907 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-15.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-16.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-16.png new file mode 100644 index 0000000000..4bd2608186 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-16.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-17.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-17.png new file mode 100644 index 0000000000..405ea33947 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-17.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-2.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-2.png new file mode 100644 index 0000000000..897cd1f54a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-3.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-3.png new file mode 100644 index 0000000000..b57ac69f43 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-4.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-4.png new file mode 100644 index 0000000000..8740a4c1e8 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-5.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-5.png new file mode 100644 index 0000000000..5ffcf86c86 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-5.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-6.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-6.png new file mode 100644 index 0000000000..39bf85ec6f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-6.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-7.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-7.png new file mode 100644 index 0000000000..5d870fabd5 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-7.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-8.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-8.png new file mode 100644 index 0000000000..e981f7d552 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-8.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-9.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-9.png new file mode 100644 index 0000000000..5066d27075 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.14.17-9.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-0.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-0.png new file mode 100644 index 0000000000..4a6f21ae03 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-1.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-1.png new file mode 100644 index 0000000000..4d9841b360 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-10.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-10.png new file mode 100644 index 0000000000..33d42cc468 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-10.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-11.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-11.png new file mode 100644 index 0000000000..4461a95ffd Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-11.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-12.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-12.png new file mode 100644 index 0000000000..45cbbaef33 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-12.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-13.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-13.png new file mode 100644 index 0000000000..145181c19b Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-13.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-14.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-14.png new file mode 100644 index 0000000000..df59f4fa7e Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-14.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-15.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-15.png new file mode 100644 index 0000000000..804e4b1e02 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-15.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-16.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-16.png new file mode 100644 index 0000000000..2aa4b5baee Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-16.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-17.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-17.png new file mode 100644 index 0000000000..6ba33a47b8 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-17.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-2.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-2.png new file mode 100644 index 0000000000..b322443eb8 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-3.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-3.png new file mode 100644 index 0000000000..1b10097424 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-4.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-4.png new file mode 100644 index 0000000000..4e6cd2ccc6 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-5.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-5.png new file mode 100644 index 0000000000..adb6295f72 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-5.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-6.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-6.png new file mode 100644 index 0000000000..5ea313d2ad Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-6.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-7.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-7.png new file mode 100644 index 0000000000..39bf85ec6f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-7.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-8.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-8.png new file mode 100644 index 0000000000..5d870fabd5 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-8.png differ diff --git a/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-9.png b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-9.png new file mode 100644 index 0000000000..e981f7d552 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Skyeye-v3.15.14-9.png differ diff --git a/src/.vuepress/public/assets/img/news/Stream-Query-0-0.gif b/src/.vuepress/public/assets/img/news/Stream-Query-0-0.gif new file mode 100644 index 0000000000..acdc7ccde3 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Stream-Query-0-0.gif differ diff --git a/src/.vuepress/public/assets/img/news/Stream-Query-0-1.jpg b/src/.vuepress/public/assets/img/news/Stream-Query-0-1.jpg new file mode 100644 index 0000000000..073f6118ac Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Stream-Query-0-1.jpg differ diff --git a/src/.vuepress/public/assets/img/news/Stream-Query-0-2.png b/src/.vuepress/public/assets/img/news/Stream-Query-0-2.png new file mode 100644 index 0000000000..c6cfedd25a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Stream-Query-0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Stream-Query-0-3.png b/src/.vuepress/public/assets/img/news/Stream-Query-0-3.png new file mode 100644 index 0000000000..3ef8ccbf97 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Stream-Query-0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Stream-Query-0-4.png b/src/.vuepress/public/assets/img/news/Stream-Query-0-4.png new file mode 100644 index 0000000000..11fa51e7ae Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Stream-Query-0-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Stream-Query-0-5.png b/src/.vuepress/public/assets/img/news/Stream-Query-0-5.png new file mode 100644 index 0000000000..f5067b9dd8 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Stream-Query-0-5.png differ diff --git a/src/.vuepress/public/assets/img/news/Stream-Query-0-6.png b/src/.vuepress/public/assets/img/news/Stream-Query-0-6.png new file mode 100644 index 0000000000..57076afe26 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Stream-Query-0-6.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-0-0.jpg b/src/.vuepress/public/assets/img/news/WGAI-0-0.jpg new file mode 100644 index 0000000000..78bf80f1e8 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-0-0.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-0-1.png b/src/.vuepress/public/assets/img/news/WGAI-0-1.png new file mode 100644 index 0000000000..a4ffecb223 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-0-2.jpg b/src/.vuepress/public/assets/img/news/WGAI-0-2.jpg new file mode 100644 index 0000000000..c3c42c60ed Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-0-2.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-0-3.jpg b/src/.vuepress/public/assets/img/news/WGAI-0-3.jpg new file mode 100644 index 0000000000..91151bd45f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-0-3.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-0-4.jpg b/src/.vuepress/public/assets/img/news/WGAI-0-4.jpg new file mode 100644 index 0000000000..282679a0e1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-0-4.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V2.0-0.png b/src/.vuepress/public/assets/img/news/WGAI-V2.0-0.png new file mode 100644 index 0000000000..cd9dc061db Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V2.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V2.0-1.png b/src/.vuepress/public/assets/img/news/WGAI-V2.0-1.png new file mode 100644 index 0000000000..a4b9910973 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V2.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V2.0-2.png b/src/.vuepress/public/assets/img/news/WGAI-V2.0-2.png new file mode 100644 index 0000000000..4f5cd844de Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V2.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V2.0-3.png b/src/.vuepress/public/assets/img/news/WGAI-V2.0-3.png new file mode 100644 index 0000000000..6d9142aaec Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V2.0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V2.0-4.jpg b/src/.vuepress/public/assets/img/news/WGAI-V2.0-4.jpg new file mode 100644 index 0000000000..c8c29ee93a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V2.0-4.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V3.0-0.png b/src/.vuepress/public/assets/img/news/WGAI-V3.0-0.png new file mode 100644 index 0000000000..bb67c8dcdf Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V3.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V3.0-1.png b/src/.vuepress/public/assets/img/news/WGAI-V3.0-1.png new file mode 100644 index 0000000000..e8c1917499 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V3.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V3.0-2.png b/src/.vuepress/public/assets/img/news/WGAI-V3.0-2.png new file mode 100644 index 0000000000..16ffa0f4ba Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V3.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V3.0-3.png b/src/.vuepress/public/assets/img/news/WGAI-V3.0-3.png new file mode 100644 index 0000000000..e1f0f2fe02 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V3.0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V3.0-4.png b/src/.vuepress/public/assets/img/news/WGAI-V3.0-4.png new file mode 100644 index 0000000000..b8345cbe19 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V3.0-4.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V3.0-5.jpg b/src/.vuepress/public/assets/img/news/WGAI-V3.0-5.jpg new file mode 100644 index 0000000000..737fc487d8 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V3.0-5.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V4.0-0.jpg b/src/.vuepress/public/assets/img/news/WGAI-V4.0-0.jpg new file mode 100644 index 0000000000..2be9f16075 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V4.0-0.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V4.0-1.png b/src/.vuepress/public/assets/img/news/WGAI-V4.0-1.png new file mode 100644 index 0000000000..28f391a83d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V4.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V4.0-2.jpg b/src/.vuepress/public/assets/img/news/WGAI-V4.0-2.jpg new file mode 100644 index 0000000000..77c6bdfbc4 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V4.0-2.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V4.0-3.jpg b/src/.vuepress/public/assets/img/news/WGAI-V4.0-3.jpg new file mode 100644 index 0000000000..8b9cef5ed5 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V4.0-3.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V4.0-4.jpg b/src/.vuepress/public/assets/img/news/WGAI-V4.0-4.jpg new file mode 100644 index 0000000000..236232d5a5 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V4.0-4.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V4.0-5.jpg b/src/.vuepress/public/assets/img/news/WGAI-V4.0-5.jpg new file mode 100644 index 0000000000..77c6bdfbc4 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V4.0-5.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V4.0-6.jpg b/src/.vuepress/public/assets/img/news/WGAI-V4.0-6.jpg new file mode 100644 index 0000000000..abab52b7f3 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V4.0-6.jpg differ diff --git a/src/.vuepress/public/assets/img/news/WGAI-V4.0-7.jpg b/src/.vuepress/public/assets/img/news/WGAI-V4.0-7.jpg new file mode 100644 index 0000000000..f491e0ea92 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/WGAI-V4.0-7.jpg differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-0.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-0.png new file mode 100644 index 0000000000..61f6d95537 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-1.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-1.png new file mode 100644 index 0000000000..55ee0e9b35 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-2.webp b/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-2.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.3.4-2.webp differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-0.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-0.png new file mode 100644 index 0000000000..8aec99fd61 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-1.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-1.png new file mode 100644 index 0000000000..aab6da862d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-2.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-2.png new file mode 100644 index 0000000000..93d2575921 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-3.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-3.png new file mode 100644 index 0000000000..9c02d9ebef Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-4.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-4.png new file mode 100644 index 0000000000..259a75b4ad Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-5.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-5.png new file mode 100644 index 0000000000..2322104931 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-5.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-6.webp b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-6.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.6-6.webp differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-0.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-0.png new file mode 100644 index 0000000000..ec3ba70b31 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-1.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-1.png new file mode 100644 index 0000000000..709edb2a95 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-2.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-2.png new file mode 100644 index 0000000000..2322104931 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.8-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-0.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-0.png new file mode 100644 index 0000000000..e6cc1d1bc1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-1.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-1.png new file mode 100644 index 0000000000..709edb2a95 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-2.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-2.png new file mode 100644 index 0000000000..2322104931 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.6.9-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-0.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-0.png new file mode 100644 index 0000000000..5678636618 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-1.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-1.png new file mode 100644 index 0000000000..5678636618 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-2.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-2.png new file mode 100644 index 0000000000..8030029a1f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-3.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-3.png new file mode 100644 index 0000000000..cb2757eab7 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-4.webp b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-4.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.0-4.webp differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-0.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-0.png new file mode 100644 index 0000000000..04bc3eb2ac Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-1.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-1.png new file mode 100644 index 0000000000..45179b4fc0 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-2.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-2.png new file mode 100644 index 0000000000..746d5b9514 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.7.3-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-0.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-0.png new file mode 100644 index 0000000000..2b4aa0d3a3 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-1.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-1.png new file mode 100644 index 0000000000..42f21d2f1f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-2.png b/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-2.png new file mode 100644 index 0000000000..617c4650f2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-1.8.0-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-0.png b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-0.png new file mode 100644 index 0000000000..d0fe2879a2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-0.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-1.png b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-1.png new file mode 100644 index 0000000000..8aec99fd61 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-1.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-2.png b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-2.png new file mode 100644 index 0000000000..d016f71d44 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-2.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-3.png b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-3.png new file mode 100644 index 0000000000..3ee6171d0b Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-3.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-4.png b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-4.png new file mode 100644 index 0000000000..fb2fa6b797 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-4.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-5.png b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-5.png new file mode 100644 index 0000000000..aee10f7484 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-5.png differ diff --git a/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-6.webp b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-6.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/Warm-Flow-v1.6.7-6.webp differ diff --git a/src/.vuepress/public/assets/img/news/carbon-2.6.2-0.png b/src/.vuepress/public/assets/img/news/carbon-2.6.2-0.png new file mode 100644 index 0000000000..c5bf87529b Binary files /dev/null and b/src/.vuepress/public/assets/img/news/carbon-2.6.2-0.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-0.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-0.png new file mode 100644 index 0000000000..eeeff3f239 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-0.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-1.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-1.png new file mode 100644 index 0000000000..369fde7e0d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-1.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-2.jpg b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-2.jpg new file mode 100644 index 0000000000..ec96a2f254 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-2.jpg differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-3.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-3.png new file mode 100644 index 0000000000..bedd0b2441 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-3.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-4.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-4.png new file mode 100644 index 0000000000..3d3be28f6a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-4.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-5.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-5.png new file mode 100644 index 0000000000..2ea9918752 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-5.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-6.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-6.png new file mode 100644 index 0000000000..47f9a057f2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-6.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-7.jpg b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-7.jpg new file mode 100644 index 0000000000..3ddcc3ff8f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-7.jpg differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-8.webp b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-8.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.6-8.webp differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-0.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-0.png new file mode 100644 index 0000000000..eeeff3f239 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-0.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-1.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-1.png new file mode 100644 index 0000000000..369fde7e0d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-1.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-2.jpg b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-2.jpg new file mode 100644 index 0000000000..f626ffa173 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-2.jpg differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-3.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-3.png new file mode 100644 index 0000000000..bedd0b2441 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-3.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-4.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-4.png new file mode 100644 index 0000000000..3d3be28f6a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-4.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-5.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-5.png new file mode 100644 index 0000000000..2ea9918752 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-5.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-6.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-6.png new file mode 100644 index 0000000000..47f9a057f2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-6.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-7.jpg b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-7.jpg new file mode 100644 index 0000000000..70e125dae0 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-7.jpg differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-8.webp b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-8.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.7-8.webp differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-0.webp b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-0.webp new file mode 100644 index 0000000000..635708c6a1 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-0.webp differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-1.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-1.png new file mode 100644 index 0000000000..369fde7e0d Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-1.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-2.jpg b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-2.jpg new file mode 100644 index 0000000000..f626ffa173 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-2.jpg differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-3.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-3.png new file mode 100644 index 0000000000..bedd0b2441 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-3.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-4.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-4.png new file mode 100644 index 0000000000..3d3be28f6a Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-4.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-5.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-5.png new file mode 100644 index 0000000000..2ea9918752 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-5.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-6.png b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-6.png new file mode 100644 index 0000000000..47f9a057f2 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-6.png differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-7.jpg b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-7.jpg new file mode 100644 index 0000000000..70e125dae0 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-7.jpg differ diff --git a/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-8.webp b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-8.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/easyAI-v1.2.9-8.webp differ diff --git a/src/.vuepress/public/assets/img/news/fastrequest-2024.1.9-0.gif b/src/.vuepress/public/assets/img/news/fastrequest-2024.1.9-0.gif new file mode 100644 index 0000000000..2823dea576 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/fastrequest-2024.1.9-0.gif differ diff --git a/src/.vuepress/public/assets/img/news/fastrequest-2024.1.9-1.webp b/src/.vuepress/public/assets/img/news/fastrequest-2024.1.9-1.webp new file mode 100644 index 0000000000..4518bcd669 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/fastrequest-2024.1.9-1.webp differ diff --git a/src/.vuepress/public/assets/img/news/mica-mqtt-2.4.0-0.png b/src/.vuepress/public/assets/img/news/mica-mqtt-2.4.0-0.png new file mode 100644 index 0000000000..7aa127ca77 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/mica-mqtt-2.4.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/mica-mqtt-2.4.0-1.png b/src/.vuepress/public/assets/img/news/mica-mqtt-2.4.0-1.png new file mode 100644 index 0000000000..405ea33947 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/mica-mqtt-2.4.0-1.png differ diff --git a/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-0.png b/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-0.png new file mode 100644 index 0000000000..56cad7ed2f Binary files /dev/null and b/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-0.png differ diff --git a/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-1.jpg b/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-1.jpg new file mode 100644 index 0000000000..44765a4709 Binary files /dev/null and b/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-1.jpg differ diff --git a/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-2.webp b/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-2.webp new file mode 100644 index 0000000000..57dc0e2b3c Binary files /dev/null and b/src/.vuepress/public/assets/img/news/x-easypdf-v3.3.0-2.webp differ diff --git a/src/.vuepress/public/assets/img/open.png b/src/.vuepress/public/assets/img/open.png index 5a52ccc277..a65d0aeb64 100644 Binary files a/src/.vuepress/public/assets/img/open.png and b/src/.vuepress/public/assets/img/open.png differ diff --git a/src/.vuepress/public/assets/img/slogan.png b/src/.vuepress/public/assets/img/slogan.png index eccabe2a79..a91424ca79 100644 Binary files a/src/.vuepress/public/assets/img/slogan.png and b/src/.vuepress/public/assets/img/slogan.png differ diff --git a/src/.vuepress/public/assets/img/vision.png b/src/.vuepress/public/assets/img/vision.png index 29e54f0324..4b9db4cd61 100644 Binary files a/src/.vuepress/public/assets/img/vision.png and b/src/.vuepress/public/assets/img/vision.png differ diff --git a/src/.vuepress/styles/config.scss b/src/.vuepress/styles/config.scss index 13244a6fb5..27c697eec6 100644 --- a/src/.vuepress/styles/config.scss +++ b/src/.vuepress/styles/config.scss @@ -1,2 +1,2 @@ $colors: #c0392b, #d35400, #f39c12, #27ae60, #16a085, #2980b9, #8e44ad, #2c3e50, - #7f8c8d !default; + #7f8c8d !default; \ No newline at end of file diff --git a/src/.vuepress/styles/index.scss b/src/.vuepress/styles/index.scss index 4b9d0fed93..f4caec0019 100644 --- a/src/.vuepress/styles/index.scss +++ b/src/.vuepress/styles/index.scss @@ -1,14 +1,12 @@ -body { - background-color: #f9fbff; -} - -.bg-default { - background-color: #e9eef8; -} +// body { +// background: linear-gradient(to bottom, #030513, #051455); +// } #navbar { - background-color: #e4eeff; box-shadow: none; + background: #fff0; + backdrop-filter: blur(5px); + border-bottom: 1px solid #2c2d3b; } .nav-item { @@ -37,3 +35,7 @@ body { display: block; } } + +.page-cover img { + display: none; +} \ No newline at end of file diff --git a/src/.vuepress/theme.ts b/src/.vuepress/theme.ts new file mode 100644 index 0000000000..52227fb5b9 --- /dev/null +++ b/src/.vuepress/theme.ts @@ -0,0 +1,6 @@ +import { hopeTheme } from "vuepress-theme-hope"; +export default hopeTheme({ + plugins: { + slimsearch: true, + }, +}); diff --git a/src/about/README.md b/src/about/README.md index 6ebdb73976..db26a19210 100644 --- a/src/about/README.md +++ b/src/about/README.md @@ -1,33 +1,34 @@ ---- -title: About -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - -## Idea - -Let every open source enthusiast, experience the joy of open source. - -## Community - -Technology stack comprehensive open source co-build, maintain community neutrality, harmony and happiness to do open source. - -## Website - -**[https://dromara.org](https://dromara.org)** Is **Dromara** Open Source Community Official Website. - -## Honors - - - - - - +--- +title: About +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +sidebar: false +--- + +## Idea + +Let every open source enthusiast, experience the joy of open source. + +## Community + +Technology stack comprehensive open source co-build, maintain community neutrality, harmony and happiness to do open source. + +## Website + +**[https://dromara.org](https://dromara.org)** Is **Dromara** Open Source Community Official Website. + +## Honors + + + + + + diff --git a/src/activity/Dromara-OSPP-2025.md b/src/activity/Dromara-OSPP-2025.md new file mode 100644 index 0000000000..338e4df8ca --- /dev/null +++ b/src/activity/Dromara-OSPP-2025.md @@ -0,0 +1,106 @@ +--- +title: Open Source Promotion Plan 2025 Student Recruitment | Claim Dromara Projects to Win Generous Rewards. +author: Dromara +date: 2025-05-12 +cover: /assets/img/activity/Dromara-OSPP-2025-0.png +head: + - - meta + - name: Activity +--- + +# Open Source Promotion Plan 2025 + +Open Source Promotion Plan (OSPP) is a summer open source initiative launched and long-term supported by the Open Source Supply Chain Lighting Plan of the Institute of Software, Chinese Academy of Sciences. It aims to encourage students to actively participate in the development and maintenance of open source software, cultivate and discover more excellent developers, promote the vigorous development of outstanding open source software communities, and support the construction of the open source software supply chain. + +![](/assets/img/activity/Dromara-OSPP-2025-0.png) + +## Student Registration Now Open + +Student registration for Open Source Promotion Plan 2025 is officially open! Students can now visit the OSPP official website at https://summer-ospp.ac.cn/ to browse projects, communicate with mentors, prepare project application materials, and submit their applications. + +> With so many project tasks available, are you already eager to give it a try? Whether you're a beginner or an experienced developer, whether you want to contribute code, learn open source technologies and gain development experience, or enhance your resume, OSPP offers opportunities for everyone. +> +> With a spirit of exploration and a thirst for knowledge in open source, we welcome all students to join Open Source Promotion Plan 2025 and explore infinite possibilities this summer! +> +> At OSPP, participating students have the opportunity to interact with numerous open source communities and experienced developer mentors, improving their technical skills and practical abilities while gaining more open source knowledge and expertise. Successful participants will also receive generous project rewards—8,000 RMB for basic difficulty projects and 12,000 RMB for advanced difficulty projects (pre-tax)—and may become members of the Dromara community, enjoying numerous benefits! + +## Introduction to Dromara Community + +Dromara is an open source community formed by top open source project authors in China. + +It provides a series of open source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, scheduling orchestration, and more. The community is committed to full open source collaboration, maintaining community neutrality, and striving to provide microservices cloud-native solutions for global users. + +Dromara aims to make every open source enthusiast experience the joy of open source. The Dromara open source community currently boasts 10+ GVP projects, with a total star count exceeding 100,000. It has built a community of over 10,000 members, with thousands of individuals and teams using Dromara's open source projects. + +## Dromara Community Project Topics + +We are excited to present two projects: **Dromara Open Source Community Integrated Management System** and **Dromara Incubator Website Optimization and Main Website Enhancement**. + +> The project tasks are designed to be accessible, and participants will receive dedicated guidance from Dromara community mentors throughout the process. + +### 1. Dromara Open Source Community Integrated Management System + +With the rapid development of open source, the Dromara open source community continues to expand, with an increasing number of projects and increasingly diverse participants. The current management approach is relatively fragmented, lacking a centralized and efficient system for managing personnel and projects. This makes it difficult to track project incubation processes, unclear the relationships between personnel and projects, and leads to chaotic permission management, significantly affecting community operational efficiency and collaboration. + +**Project Requirements** + +Build a fully functional and user-friendly open source community personnel and project management system to improve community management efficiency, promote better collaboration and communication among community members, and facilitate the smooth development of open source projects. + +**Key Deliverables**: + +1. Complete the front-end and back-end architecture design of the system, including detailed architecture diagrams, module division, interface design, etc. +2. Develop an open source project management module to achieve full lifecycle management of project incubation processes, including project creation, review, progress tracking, document management, and other functions. +3. Develop a community personnel management module to achieve personnel information entry and editing, manage relationships between personnel and projects, and display contributors' information in projects. +4. Complete system testing, including functional testing, performance testing, security testing, etc., and submit test reports. +5. Write system usage documentation, including administrator manuals and user manuals, to facilitate community members' use. + +* Address: https://dromara.org +* Project Source Code: https://github.com/dromara/droer + +**Technical Requirements**: + +1. Familiarity with Java programming language and mastery of common Java development frameworks such as Spring Boot and MyBatis. +2. Understanding of front-end development technologies, including HTML, CSS, JavaScript, and experience with front-end frameworks like Vue.js. +3. Proficiency in database design and development, with experience using relational databases such as MySQL. +4. Ability to design software systems, including reasonable module division and interface design based on requirements. +5. Good coding habits, adherence to code standards, and ability to write code comments. + +* Mentor: Tang Zhenchao (tangzc2@gmail.com) +* Project Repository: https://github.com/dromara/droer +* Application Link: https://summer-ospp.ac.cn/org/prodetail/25ee40202?list=org&navpage=org + +### 2. Dromara Incubator Website Optimization and Main Website Enhancement + +**Project Description** + +Dromara is an open source community spontaneously organized by top open source project maintainers. + +The Dromara open source community currently has 10+ top-level projects and 50+ incubator projects, each with its official website. This project involves unifying the UI design and architecture of both official websites (incubator website and main website), optimizing page content, adding new website features, highlighting the display of top-level and incubator projects, improving the blog submission process, and writing website usage documentation. + +**Deliverables**: + +1. Optimize and refine the UI pages of the incubator website and the main website. +2. Design and improve website content display: + - 2.1: Optimize the UI for displaying top-level project groups and incubator project groups. +3. Unify the technical architecture of the incubator website and the main website, upgrade to the latest corresponding technology versions, and provide build documentation and local deployment guides. +4. Develop other website pages as needed. + +**Technical Requirements**: + +1. Some open source experience is preferred; passion for open source is a plus. +2. Proficiency in Vue, HTML, and CSS. +3. Familiarity with common Git commands. + +* Mentor: Mao Da ren (549477611@qq.com) +* Project Repositories: + - https://github.com/dromara/incubator + - https://github.com/dromara/dromara.github.io +* Application Link: https://summer-ospp.ac.cn/org/prodetail/25ee40277?list=org&navpage=org + +## How to Participate in Open Source Promotion Plan 2025 + +Starting May 9, 2025, the Dromara community will begin accepting student applications for the above project topics. We encourage interested students to contact the respective mentors via the provided联系方式 (contact information) to discuss the projects and prepare application materials. + +![](/assets/img/activity/Dromara-OSPP-2025-1.png) + +![](/assets/img/activity/Dromara-OSPP-2025-2.png) \ No newline at end of file diff --git a/src/activity/PowerData-AI-shanghai-4.19.md b/src/activity/PowerData-AI-shanghai-4.19.md new file mode 100644 index 0000000000..82df406c98 --- /dev/null +++ b/src/activity/PowerData-AI-shanghai-4.19.md @@ -0,0 +1,93 @@ +--- +title: PowerData Digital AI - Shanghai Open Source Tour on April 19th! Registration Open~! +author: April 7, 2025 14:02 +date: 2025-04-07 +cover: /assets/img/activity/PowerData-AI-shanghai-4.19-3.jpg +head: + - - meta + - name: Activity +--- + + +**Digital AI, Hand in Hand with the Future** + +**2025 PowerData Shanghai Open Source Tour** + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-2.gif) + +On April 19, 2025, at 13:30, in the Demo Hall of MOSpace, Xuhui District, Shanghai, a feast focusing on the integration of AI and data technology is about to begin! ✨ + +This event brings together top domestic digital AI technology teams, featuring heavyweight guests such as **Yijia Su from SelectDB**, **Pengfei Ji from Chat2DB**, and **Bibo Wang from DataFocus**. They will provide in-depth analysis on cutting-edge topics like the AI-driven real-time data warehouse, natural language interaction with databases, and building a ChatBI in 15 minutes. ✈️ + +Get up close and personal with experts to gain insights into AI-driven innovations in data infrastructure, industrial decision-making optimization, and the future of intelligent search! Registration is free. Scan the code to secure your spot and explore the boundaries of digital AI technology with hundreds of developers! 🚀 + +The event will also feature **three rounds of lucky draws**, with tons of peripherals available. All participants have a chance to win. Come and join us~ 🎁 + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-3.jpg) + +**01** +**Event Information** + + +**Organizer** + +PowerData Community + + +**Event Time** + +April 19, 2025, 13:30 - 17:30 + + +**Venue** + +Demo Hall, 1st Floor, Building F2, No. 180 Longtai Road, Xuhui District, Shanghai · MOSpace + +**02** +**Four Event Highlights** + + +**【Top-Tier Technical Practices】** +Learn from database vendors like Doris, Chat2DB, OceanBase, KWDB, IoTDB, and DataFocus about their integration of concepts in the AI era. Explore the implementation of technologies like nl2sql and chatbi, and uncover the AI practices of leading companies. + +**【Zero-Distance Exchange】** +Engage in face-to-face conversations with various technical experts, freely exchanging ideas. + +**【Abundant Gifts】** +Lucky draws will be held at registration, during intermission, and at closing. Sponsored by open-source vendors, there will be plenty of peripheral gifts. Moreover, gifts will be distributed simply for participating in interactions. +Our partners have sponsored **60+ peripheral items** for this event, including mugs, stickers, notebooks, stress relievers, badges, clothing, backpacks, hats, and more. Waiting for you to claim! + +**【Beautiful Venue with Expansive Views】** +This event is venue-supported by MOSpace, Shanghai's **first** large-model incubation and acceleration hub. + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-9.jpg) + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-10.jpg) + +**03** +**How to Participate** + + +**Event Registration** + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-12.png) + +🔥 Scan the code to register. Seats are limited, secure yours now! After registration, a volunteer will invite you to join the event group for the latest updates. + +**Live Stream** + +🔥 This event will be live-streamed in full. Click the video account below to预约 (set a reminder). + +**04** +**Best Attendee** + + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-14.png) + +To create a truly vibrant, open, and communicative open-source event, PowerData has specially prepared "Best Attendee" certificates. These will be awarded on the spot during the event to participants who demonstrate outstanding engagement, such as those who ask questions actively, initiate discussions, share insights, or give lightning talks. + +<<< END >>> + +PowerData is an open-source data community formed by a group of data professionals united by their passion, built on the spirit of open source. + +The community group regularly organizes mock interviews, online sharing sessions, industry seminars, offline Meetups, city gatherings, job referrals, and other activities. Within the community group, you can engage in technical discussions, seek advice, and meet more like-minded data enthusiasts. diff --git a/src/activity/README.md b/src/activity/README.md index 084017e351..2b2c7f2e0b 100644 --- a/src/activity/README.md +++ b/src/activity/README.md @@ -1,33 +1,9 @@ ---- -title: Activity -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - - - - - - +--- +title: Activity +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +sidebar: false +layout: siteLayout +--- diff --git a/src/activity/apache-iceberg-meetup2025.md b/src/activity/apache-iceberg-meetup2025.md new file mode 100644 index 0000000000..72b90cd172 --- /dev/null +++ b/src/activity/apache-iceberg-meetup2025.md @@ -0,0 +1,26 @@ +--- +title: Domestic Premiere | Apache Iceberg Meetup 2025 Shenzhen Stop Event Preview +author: January 10, 2025 08:33 +date: 2025-01-10 +cover: /assets/img/activity/apache-iceberg-meetup2025-0.png +head: + - - meta + - name: Activity +--- + +# **Event Introduction** + +2024 was a year of vigorous development for the Lakehouse data architecture. More and more enterprises are choosing data lakes as the unified storage layer and building rich data applications, including BI and AI, on top of them. Apache Iceberg, as a highly popular open-source project in the data lake field, also made significant progress in 2024. The community-driven Rest Catalog is becoming increasingly mature, and the Iceberg V3 format is also趋向成熟 (maturing). Companies including Snowflake, AWS, Apple, and Databricks have adopted Iceberg as their preferred data lake table format, which undoubtedly推动 (propels) Iceberg to gradually become the standard for data lake table formats. + +At the beginning of 2025, we will host the first domestic offline Apache Iceberg Meetup in beautiful Shenzhen. This meetup is jointly organized by the Apache Iceberg community, Tencent Cloud Big Data, and AutoMQ. Focusing on the theme of best user practices on Iceberg, it invites some of China's most experienced Iceberg users & contributors—Tencent Cloud Big Data, AutoMQ, Bilibili, WeChat, and Huawei Terminal Cloud—to share their experiences in using Iceberg to build Lakehouse data architectures. The Apache Iceberg community and partners sincerely invite friends from the Iceberg and other major communities, data enthusiasts, architects, and enterprise representatives to participate. + +● Organizers: Tencent Cloud Big Data, AutoMQ +● Event Time: January 18, 2025, 13:30-17:00 +● Event Address: Tencent Binhai Building 3611, Shenzhen +● Event Format: Primarily offline, with simultaneous online live broadcast and replay + +**Event Agenda** + +![](/assets/img/activity/apache-iceberg-meetup2025-0.png) + +# **- END -** \ No newline at end of file diff --git a/src/activity/coscon24-forum-intro.md b/src/activity/coscon24-forum-intro.md new file mode 100644 index 0000000000..b73c894bac --- /dev/null +++ b/src/activity/coscon24-forum-intro.md @@ -0,0 +1,118 @@ +--- +title: First Look at the Main Forum! Open Source, Open Life - Experience the New Open Source Lifestyle at COSCon'24 +author: October 29, 2024 12:36 +date: 2024-10-29 +cover: /assets/img/activity/coscon24-forum-intro-1.webp +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/coscon24-forum-intro-0.webp) + +![](/assets/img/activity/coscon24-forum-intro-1.webp) + +We sincerely invite you to attend the **9th China Open Source Conference (COSCon'24)**, to join this grand event and witness how open source is changing lives and shaping the future! This year's main forum, themed **"Open Source, Open Life"**, will showcase how open source technology integrates into every aspect of our daily lives through diverse sharing and discussions. + +**How to Register:** Scan the QR code below or copy the link into your browser to register. + +![](/assets/img/activity/coscon24-forum-intro-2.png) + +**Registration Link:** +**https://www.bagevent.com/event/coscon24** + +**1** + + +**Day One:** +**Open Source, Open Life - A Stage for Sharing Stories** + +The theme for the first day is "Open Source, Open Life." Our focus will be on personal open source stories. Through sharing these stories, we hope to inspire more friends—"I can become an open source contributor too!" + +**Opening Ceremony & Welcome Address (9:00~9:20)** + +Let's kick off COSCon 2024 together! Jiang Bo, Chairperson of KaiYuanshe, and Ted Liu, Co-founder and Board Director of KaiYuanshe, will provide forward-looking insights into the new open source lifestyle. Open source is not just about code; it changes how we think, collaborate, and profoundly impacts all aspects of life. + +**Keynote Speech (9:20~9:40)** + +As a platinum sponsor of this conference, Wei Kewei, CTO of Inspur KaiwuDB, will present "KWDB: Embarking on a New Open Source Journey." + +**Keynote Speeches + Dialogue:** +**Stories of How Open Source Changes Lives (9:40~11:40)** + +Four guests from different industries will share their deep connections with open source: from becoming a famous Bilibili UP主 after retirement, to open-sourcing their sports data, to transforming healthcare through open source, and even shaping a new tech-driven life with open source—each story shines with the power of open source. Live dialogues between guests during the speeches, sparking ideas, is one of the innovations of this conference. + +**Lightning Talks:** +**Moments When Open Source Changed Personal Lives (11:40~12:20)** + +Eight open source enthusiasts from diverse backgrounds will use 5-minute lightning talks to share how open source changed their career paths or lifestyles. Whether you are a student, professional, or hobbyist, these inspiring open source stories will ignite your imagination for the future! 【Limited spots available, register quickly! See details in today's second push notification】 + +**2** + + +**Day Two:** +**An Open New Future - Collision of Exploration and Vision** + +The theme for the second day is "An Open New Future." Open source has already and will continue to change the world. Our discussions will focus on the development of open source technology and the myriad changes it brings. Furthermore, on the occasion of KaiYuanshe's 10th anniversary, we will review and celebrate this remarkable decade with all old and new friends, and envision the future together. + +**KaiYuanshe's Vision for the Next Decade (9:00~9:20)** + +KaiYuanshe Chairperson Jiang Bo will outline the vision for KaiYuanshe's next decade, exploring the latest trends in open source in China and globally. Let's step into the next golden decade of open source together! + +**Major Release:** +**China Open Source Annual Report (9:20~9:40)** + +The highly anticipated annual "Open Source Annual Report" will be released live! Professor Wang Wei from East China Normal University will interpret the latest data and development trends in China's open source ecosystem. + +**Panel Discussion:** +**The Future of Open Source and Artificial Intelligence (9:40~10:20)** + +Amid the wave of artificial intelligence, how does the power of open source drive technological change? Leaders of top AI open source projects will gather to discuss a win-win future for open source and AI technology. + +**Panel Discussion:** +**The Power of Open Source Education (10:20~10:50)** + +Open source not only leads trends in technology but also brings变革 (transformation) to education. Let's listen to experts share innovative models of open source education and discuss how to cultivate the next generation of open source talent. + +**Panel Discussion:** +**The Social Responsibility of Open Source (10:50~11:20)** + +Open source is not just a technology but a force for social progress. The "OpenGood Open Source Public Welfare Case Collection," initiated and collected by KaiYuanshe, will be released at this conference. Subsequently, representatives from enterprises, communities, and public welfare organizations will share practical cases of open source solving social problems and discuss how to better fulfill the social responsibility of the open source community. + +**Panel Discussion:** +**My Ten-Year Journey with KaiYuanshe (11:20~12:00)** + +Looking back over the past decade, how did KaiYuanshe grow from a startup community to today's open source benchmark? Founders, core members, and senior fans will tell their stories of growing up with KaiYuanshe. + +**Special Segment:** +**KaiYuanshe Ten-Year Story Wall (12:00~12:30)** + +In this special segment, you will have the opportunity to take the stage and share your story with KaiYuanshe, witnessing the birth of the "KaiYuanshe Ten-Year Story Wall." Let's witness the profound impact of open source on individuals and communities together! Of course, there will also be cake and a group photo to capture this historic moment! + +**COSCon'24 is not just a feast for technical exchange but a carnival for open source enthusiasts!** Whether you are a seasoned open source veteran or a newcomer just getting started, you can find resonance and inspiration here. Come join this annual open source extravaganza, envision the future of open source with like-minded friends, and experience the endless charm of the new open source lifestyle! + +**You can click "Read More" at the end of the article to purchase tickets and register. November 2-3, see you in Beijing! Let's not miss each other!** + +Produced by | COSCon'24 Organizing Committee +Edited by | Mooncake +Designed by | Wang Jun + + +**Related Reading | Related Reading** + +[Forum Introduction | COSCon'24 Open Source AI Forum on AI for Science](http://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247539499&idx=1&sn=9f703834ad629cf93cab40f87aafc6fc&chksm=9fdad015a8ad59034d5c27731fb83ff1d9d4c11b89d085a8b6a93e084f6d97e591a5043529e2&scene=21#wechat_redirect) + +[COSCon'24 Volunteer Recruitment Call: Co-create a New Open Source Life!](http://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247539207&idx=1&sn=a9b09c66f440506fc898cc78113aa0da&chksm=9fdad139a8ad582f3d2a190e9ae65d9c73041483191065c2b069161a7d249217ef4b137889be&scene=21#wechat_redirect) + +**KaiYuanshe** +_**KAIYUANSHE**_ + +![](/assets/img/activity/coscon24-forum-intro-9.webp) + +KaiYuanshe was established in 2014. It is an open source community composed of individual volunteers dedicated to the cause of open source, based on the principles of "Contribution, Consensus, and Co-governance." KaiYuanshe always maintains the理念 (concept) of "Vendor-Neutral, Public Welfare, Non-Profit," with the愿景 (vision) of "Based in China, Contributing to the World, Promoting Open Source as a Lifestyle in the New Era," and the使命 (mission) of "Open Source Governance, International Integration, Community Development, Project Incubation," aiming to co-create a healthy and sustainable open source ecosystem. + +KaiYuanshe actively cooperates closely with open-source-supporting communities, universities, enterprises, and relevant government units. It is also the first Chinese member of the Open Source Initiative (OSI), the global open source license certification organization. + +Since 2016, KaiYuanshe has continuously hosted the China Open Source Conference (COSCon), released the "China Open Source Annual Report," jointly launched initiatives like the "China Open Source Pioneer List" and "China Open Source Code Power List," generating widespread influence both domestically and internationally. + +COSCon'24 29 \ No newline at end of file diff --git a/src/activity/dev-ai-frontier-meet.md b/src/activity/dev-ai-frontier-meet.md new file mode 100644 index 0000000000..f2cfa5fb64 --- /dev/null +++ b/src/activity/dev-ai-frontier-meet.md @@ -0,0 +1,20 @@ +--- +title: '@Developers|GitCode AI Exploration Day: An Opportunity to Meet Industry Experts Face-to-Face...' +author: GitCode Open Source Exploration +date: 2024-10-10 +cover: /assets/img/activity/dev-ai-frontier-meet-0.gif +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/dev-ai-frontier-meet-0.gif) + +AI technology is leading the technological revolution, driving transformation and upgrading across all industries. To delve into the frontiers of AI and foster developer exchange, as the 1024 Programmer's Day approaches, GitCode is joining hands with the Songshan Lake Developer Village to host a special event in the picturesque Songshan Lake European Town. On **October 20th**, the AI Future Exploration Day officially opens! + +The event specially invites multiple distinguished guests with profound expertise in the AI field. They will share exciting insights on cutting-edge topics such as large model applications, breaking new ground in the AGI era, and AI-assisted coding. Additionally, GitCode's AI algorithm engineers will take you on an in-depth exploration of the **GitCode AI Community**. The event also features a lucky draw session, giving developers the chance to gain unexpected surprises and rewards during the exchange. + +Join us as we embark on a journey of deep dialogue and exploration about the future of AI~ + +![](/assets/img/activity/dev-ai-frontier-meet-1.webp) +![](/assets/img/activity/dev-ai-frontier-meet-2.webp) \ No newline at end of file diff --git a/src/activity/dromara-COSCon-letter.md b/src/activity/dromara-COSCon-letter.md new file mode 100644 index 0000000000..0c0ffc653a --- /dev/null +++ b/src/activity/dromara-COSCon-letter.md @@ -0,0 +1,43 @@ +--- +title: A Letter from Dromara Open Source Organization Participating in COSCon +author: Dromara Secretariat +date: 2024-09-27 +cover: /assets/img/activity/dromara-COSCon-letter-0.png +head: + - - meta + - name: Activity +--- + +**About** + +COSCon + +![](/assets/img/activity/dromara-COSCon-letter-1.png) + +China Open Source Conference (COSCon) is one of the most influential open source events in the industry. Since its inception in 2015 by KaiYuanShe, this year marks its ninth edition. With its unique positioning and growing influence, COSCon has not only garnered strong support from an increasing number of domestic and international companies, universities, open source organizations, and communities but has also successfully attracted widespread attention and active participation from open source developers, contributors, and submitters worldwide. Compared to industry conferences typically organized by enterprises, IT media, or industry associations, COSCon boasts broader cross-organizational, cross-project, and cross-community coverage. + +**Location** + +![](/assets/img/activity/dromara-COSCon-letter-3.png) + +Zhongguancun National Independent Innovation Demonstration Zone Conference Center, Beijing + +**A Passion for Open Source in Late Autumn** + +dromara + +![](/assets/img/activity/dromara-COSCon-letter-6.png) + +Dozens of open source authors from the Dromara open source organization will participate in this event. Among them are veterans of open source from the 80s and 90s, as well as rising open source stars from the 00s and 10s. We hope to meet and fully communicate with developers, corporate leaders, and all those who follow the Dromara open source organization offline, sparking the flame of China’s open source atmosphere. + +**Information Collection** + +![](/assets/img/activity/dromara-COSCon-letter-8.png) + +![](/assets/img/activity/dromara-COSCon-letter-9.png) + +We look forward to meeting everyone offline. Participants are kindly requested to fill in their personal information. Based on the number of attendees, we will select a venue and customize Dromara open source peripheral gifts. + +Information submission link: https://docs.qq.com/sheet/DSmNjU1dma3JEVFRk?is_no_hook_redirect=1&tab=BB08J2 + +![](/assets/img/activity/dromara-COSCon-letter-10.png) \ No newline at end of file diff --git a/src/activity/dromara-activites-introduce.md b/src/activity/dromara-activites-introduce.md index 297e2db897..5ae9a59732 100644 --- a/src/activity/dromara-activites-introduce.md +++ b/src/activity/dromara-activites-introduce.md @@ -1,47 +1,47 @@ ---- -title: Dromara Dream Code Book Club Introduction -author: xiaoyu -date: 2020-12-27 -tag: - - DreamCode - - Dromara - - GateWay -cover: /assets/img/activity/dromara-open-soul-01.jpg -head: - - - meta - - name: Activity ---- - -![Dromara Dream Code Book Club Introduction](/assets/img/activite/soul-xmind.png) - -### Dromara Dream Code Book Club(Dromara 2020 event introduction) - -- Date: Sunday, December 27, 2020 - -### Activity background - -- In order to increase the enthusiasm of community participants, promote the construction of the Dromara community, exercise everyone's expressive ability and improve the core strength of technology, the community organized this event in the form of source code reading. - -### Activity purpose, meaning and goal - -- Increase motivation -- Improve technical strength and expand everyone's horizons -- Exercise language skills -- Promote the harmony, unity and progress of the community -- Make the Dromara community bigger and bigger - -### Activity development - -- The activity is divided into multiple phases. First, twelve members are selected for a 12-day source code reading, and two online sharing is carried out during the period. -- In order to improve everyone's consciousness, we have set up a punishment system. First hand over 500 yuan to the administrator. If homework is not submitted at 8 am the next day, 100 yuan will be deducted for sharing latecomers. Those who ask for leave in advance do not need to be punished. -- Each person writes to their homework submission area in text based on the content they read every day. - -### Activity leader and main participants - -#### Principal - -- Cui, Kimming, Xiaoyu - -#### The main participants - -- Dromara community member +--- +title: Dromara Dream Code Book Club Introduction +author: xiaoyu +date: 2020-12-27 +tag: + - DreamCode + - Dromara + - GateWay +cover: /assets/img/activity/dromara-open-soul-01.jpg +head: + - - meta + - name: Activity +--- + +![Dromara Dream Code Book Club Introduction](/assets/img/activite/soul-xmind.png) + +### Dromara Dream Code Book Club(Dromara 2020 event introduction) + +- Date: Sunday, December 27, 2020 + +### Activity background + +- In order to increase the enthusiasm of community participants, promote the construction of the Dromara community, exercise everyone's expressive ability and improve the core strength of technology, the community organized this event in the form of source code reading. + +### Activity purpose, meaning and goal + +- Increase motivation +- Improve technical strength and expand everyone's horizons +- Exercise language skills +- Promote the harmony, unity and progress of the community +- Make the Dromara community bigger and bigger + +### Activity development + +- The activity is divided into multiple phases. First, twelve members are selected for a 12-day source code reading, and two online sharing is carried out during the period. +- In order to improve everyone's consciousness, we have set up a punishment system. First hand over 500 yuan to the administrator. If homework is not submitted at 8 am the next day, 100 yuan will be deducted for sharing latecomers. Those who ask for leave in advance do not need to be punished. +- Each person writes to their homework submission area in text based on the content they read every day. + +### Activity leader and main participants + +#### Principal + +- Cui, Kimming, Xiaoyu + +#### The main participants + +- Dromara community member diff --git a/src/activity/dromara-cloud-native-meet-02.md b/src/activity/dromara-cloud-native-meet-02.md index f97c5c2b4d..35fb50b0e9 100644 --- a/src/activity/dromara-cloud-native-meet-02.md +++ b/src/activity/dromara-cloud-native-meet-02.md @@ -1,43 +1,43 @@ ---- -title: Dromara Soul Source Code 01 Reading Sharing Session 02 -author: xiaoyu -date: 2021-02-06 -tag: - - Soul - - Dromara - - Reactor -cover: /assets/img/activity/dromara-open-soul-03.jpg -head: - - - meta - - name: Activity ---- - -![Dromara Online Activity](/assets/img/activite/soul-xmind.png) - -### Dromara source code reading (Soul 2021 first activity) - -- Date: Sunday, February 6, 2021 -- Time:20:00 – 23:00 -- Location: Tencent Meeting - -### Activity Details - -**20:00 - 20:10 The opening introduces the recent dream code sharing situation by kimming & 崔** - -**20:10 - 20:25 [Introduction to SPI and how Soul SPI is enhanced](https://blog.csdn.net/zm469568595/article/details/113362044) by zhuming** - -**20:25 - 20:50 [Introduction to Reactive Programming](https://zhoutzzz.com/archives/xiang-ying-shi-bian-cheng-reactiveprogramming) by Ztzzz** - -**20:50 - 21:10 [Soul Unit Test](https://www.yuque.com/docs/share/27992671-8d47-4bba-b2dc-c0e39074d649?#) by yangze** - -**21:10 - 21:25 [Fault-tolerant design](http://icyfenix.cn/distribution/traffic-management/failure.html) by jiangwenbo** - -**21:25 - 21:40 [Soul Web Flux loading process and processing request analysis](https://blog.csdn.net/u012180773?t=1) by rwby** - -**21:40 - 21:55 [Soul current limiting and fusing analysis](https://redick01.github.io/redick.github.io/#/blog/sourcecode/soul/soul_19) by liupenghui** - -**21:55 - 22:05 Summary of common Java problems by muou** - -**22:05 - 22:20 How to open a social interface by weikai** - -**22:20 - 22:30 Summary and Community Development Prospects by Xiaoyu** +--- +title: Dromara Soul Source Code 01 Reading Sharing Session 02 +author: xiaoyu +date: 2021-02-06 +tag: + - Soul + - Dromara + - Reactor +cover: /assets/img/activity/dromara-open-soul-03.jpg +head: + - - meta + - name: Activity +--- + +![Dromara Online Activity](/assets/img/activite/soul-xmind.png) + +### Dromara source code reading (Soul 2021 first activity) + +- Date: Sunday, February 6, 2021 +- Time:20:00 – 23:00 +- Location: Tencent Meeting + +### Activity Details + +**20:00 - 20:10 The opening introduces the recent dream code sharing situation by kimming & 崔** + +**20:10 - 20:25 [Introduction to SPI and how Soul SPI is enhanced](https://blog.csdn.net/zm469568595/article/details/113362044) by zhuming** + +**20:25 - 20:50 [Introduction to Reactive Programming](https://zhoutzzz.com/archives/xiang-ying-shi-bian-cheng-reactiveprogramming) by Ztzzz** + +**20:50 - 21:10 [Soul Unit Test](https://www.yuque.com/docs/share/27992671-8d47-4bba-b2dc-c0e39074d649?#) by yangze** + +**21:10 - 21:25 [Fault-tolerant design](http://icyfenix.cn/distribution/traffic-management/failure.html) by jiangwenbo** + +**21:25 - 21:40 [Soul Web Flux loading process and processing request analysis](https://blog.csdn.net/u012180773?t=1) by rwby** + +**21:40 - 21:55 [Soul current limiting and fusing analysis](https://redick01.github.io/redick.github.io/#/blog/sourcecode/soul/soul_19) by liupenghui** + +**21:55 - 22:05 Summary of common Java problems by muou** + +**22:05 - 22:20 How to open a social interface by weikai** + +**22:20 - 22:30 Summary and Community Development Prospects by Xiaoyu** diff --git a/src/activity/dromara-cloud-native-meet.md b/src/activity/dromara-cloud-native-meet.md index ea8a9e1250..4e9c5de5e3 100644 --- a/src/activity/dromara-cloud-native-meet.md +++ b/src/activity/dromara-cloud-native-meet.md @@ -1,43 +1,43 @@ ---- -title: Dromara Soul source code 01 reading sharing session 01 -author: xiaoyu -date: 2021-01-21 -tag: - - Soul - - Dromara - - Reactor -cover: /assets/img/activity/dromara-open-soul-02.jpg -head: - - - meta - - name: Activity ---- - -![Dromara Online Activity](/assets/img/activite/soul-xmind.png) - -### Dromara source code reading (Soul 2021 first activity) - -- Date: Sunday, January 24, 2021 -- Time: 15:00 – 17:00 -- Location: Tencent Meeting - -### Activity Details - -**15:00-15:10 Opening introduction of dream code sharing process by kimming & Cui** - -**15:10-15:25 Soul data synchronization websocket by Ting** - -**15:25-15:50 Http Discovery Sharing by Zhu Ming** - -**15:50-16:10 Analysis based on the Sofa-Rpc protocol by Dongdong** - -**16:10-16:25 Metrics Monitoring by Ge Tianye** - -**16:25-16:40 Http Long Polling Sharing by Du Yuhang** - -**16:40-16:55 Sharing and introducing the overall architecture of data synchronization by Wentao Xia** - -**16:55-17:05 Microkernel Architecture Sharing by Shen Xiangjun** - -**17:05-17:20 Sharing the experience and insights of reading source code by JinZe** - -**17:20-17:30 Summary and Community Development Prospects by Xiaoyu** +--- +title: Dromara Soul source code 01 reading sharing session 01 +author: xiaoyu +date: 2021-01-21 +tag: + - Soul + - Dromara + - Reactor +cover: /assets/img/activity/dromara-open-soul-02.jpg +head: + - - meta + - name: Activity +--- + +![Dromara Online Activity](/assets/img/activite/soul-xmind.png) + +### Dromara source code reading (Soul 2021 first activity) + +- Date: Sunday, January 24, 2021 +- Time: 15:00 – 17:00 +- Location: Tencent Meeting + +### Activity Details + +**15:00-15:10 Opening introduction of dream code sharing process by kimming & Cui** + +**15:10-15:25 Soul data synchronization websocket by Ting** + +**15:25-15:50 Http Discovery Sharing by Zhu Ming** + +**15:50-16:10 Analysis based on the Sofa-Rpc protocol by Dongdong** + +**16:10-16:25 Metrics Monitoring by Ge Tianye** + +**16:25-16:40 Http Long Polling Sharing by Du Yuhang** + +**16:40-16:55 Sharing and introducing the overall architecture of data synchronization by Wentao Xia** + +**16:55-17:05 Microkernel Architecture Sharing by Shen Xiangjun** + +**17:05-17:20 Sharing the experience and insights of reading source code by JinZe** + +**17:20-17:30 Summary and Community Development Prospects by Xiaoyu** diff --git a/src/activity/dromara-coscon-market.md b/src/activity/dromara-coscon-market.md new file mode 100644 index 0000000000..c6114160af --- /dev/null +++ b/src/activity/dromara-coscon-market.md @@ -0,0 +1,252 @@ +--- +title: COSCon'24 Open Source Bazaar Officially Unveiled, Inviting You to Play at the Market Stalls~ +author: November 02, 2024 08:02 +date: 2024-11-02 +cover: /assets/img/activity/dromara-coscon-market-0.webp +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/dromara-coscon-market-0.webp) + +The Open Source Bazaar is a highlight of this year's conference, aiming to build a platform to showcase open source projects and promote cooperation and exchange among open source communities and organizations. Here, you can: + +* Showcase excellent open source projects/activities to enhance the visibility of your project/community/organization; +* Expand partnerships to promote the development of communities/projects/organizations; +* Exchange insights on open source and discuss the development of the open source cause; +* Experience open source culture and share in the open source feast. + +**Open Source Bazaar Stalls** + +**KCC KaiYuanshe City Community** + +![](/assets/img/activity/dromara-coscon-market-3.webp) + +KCC is the abbreviation for Kaiyuanshe City Community. It is composed of open source enthusiasts from various cities or regions at home and abroad who identify with KaiYuanshe's philosophy, referred to as "KCC". Each city or region can apply to establish a KCC. Currently, KCCs have been established in **Shenzhen, Nanjing, Shanghai, Beijing, Chengdu, Hangzhou, Dalian, Guangzhou**, Singapore, and Silicon Valley. More KCCs are expected to be established in the future. + +**SegmentFault & Apache Answer** + +![](/assets/img/activity/dromara-coscon-market-4.webp) +![](/assets/img/activity/dromara-coscon-market-5.webp) + +SegmentFault, a leading technical Q&A community in China. +Apache Answer, an open-source Q&A software initiated by the SegmentFault team. + +**Stall Activity:** Distribution of interactive peripherals. + +**HyperAI 超神经** + +![](/assets/img/activity/dromara-coscon-market-6.webp) + +HyperAI (hyper.ai) is a leading domestic open-source artificial intelligence and high-performance computing community. It aims to assist developers and enthusiasts in data science and AI to learn, understand, and practice by providing services such as accelerated dataset downloads, online tutorial demonstrations, in-depth paper interpretations, and top conference calendar integration, building the future of AI together with the community. + +**Stall Activity:** Interactive lucky draw. + +**Asynchronous Community Carnival** + +![](/assets/img/activity/dromara-coscon-market-7.webp) + +"Asynchronous Community" (www.epubit.com) is an IT professional book community founded by Posts & Telecom Press. Launched in August 2015, it is dedicated to publishing and sharing high-quality content, providing readers with premium learning materials and offering professional publishing services for authors and translators. It enables online interaction between authors and readers and the integrated development of traditional and digital publishing. + +**Stall Activities:** +1. Book giveaway lottery; +2. Book gift for Rubik's cube restoration; +3. New book signing session. + +**COSCUP Open Source Contributor Conference** + +![](/assets/img/activity/dromara-coscon-market-8.webp) + +In 2014, several Taiwanese open source communities jointly initiated the Open Culture Foundation (OCF), hoping to promote open culture with the strength of a legal entity and spark open collaboration among industry, government, academia, and the public. + +**Stall Activity:** Introduce trends in technical open source topics and interactive games. + +**Apache IoTDB** + +![](/assets/img/activity/dromara-coscon-market-9.webp) + +Apache IoTDB (Internet of Things Database) is an integrated software system for collecting, storing, managing, and analyzing IoT time-series data. Apache IoTDB adopts a lightweight architecture with high performance and rich features. + +**Stall Interaction:** Interactive lucky draw. + +**SQLE (Actionopen Open Source Community)** + +![](/assets/img/activity/dromara-coscon-market-10.webp) + +A profound MySQL open source community. Established in 2017, the community is committed to open-sourcing high-quality operation tools, sharing technical干货 (substantive content) daily, and evangelizing database technology; currently open-sourced products include: SQL audit tool SQLE, distributed middleware DBLE, and data transmission component DTLE. + +**Stall Activity:** Interactive lucky draw. + +**PowerData Data Power Community** + +![](/assets/img/activity/dromara-coscon-market-11.png) + +We are a community of data practitioners, united by passion, based on the spirit of open source, forming the PowerData Data Power Community. + +**Stall Activity:** Interactive lucky draw. + +**Dromara Open Source Community Face-to-Face** + +![](/assets/img/activity/dromara-coscon-market-12.png) + +Dromara is an open-source community composed of top open-source project authors in China. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, and scheduling orchestration. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, and is dedicated to providing microservices cloud-native solutions for global users. It aims to allow every participating open-source enthusiast to experience the joy of open-source. + +**Stall Activities:** +1. Interactive Q&A: Visitors to the stall can answer questions correctly to receive Dromara peripherals. +2. Hands-on operation: Visitors can choose to write code hands-on and experience the fun of practical operation. + +**ModelScope Open Source Community** + +![](/assets/img/activity/dromara-coscon-market-13.png) + +ModelScope community was established in June 2022 and is a model open-source community and innovation platform. + +**Stall Activities:** +Showcase ModelScope's open-source journey, partner wall, and community experience interactions. + +**deepin** + +![](/assets/img/activity/dromara-coscon-market-14.png) + +The deepin open-source community is a root community for desktop operating systems and also China's active desktop OS community—committed to providing everyone with a free, open communication platform and the best open-source operating system through community development and collaboration. Currently, the domestic forum has 127,000 registered users, total posts have exceeded 1 million, and code contribution participants have reached 2,000. + +**Stall Interaction:** +Set up corresponding challenge tasks; users can collect stamps after completing tasks and receive different peripheral gifts based on the number of stamps. + +* Level 1: Basic open source general knowledge Q&A + Description: e.g., Who developed the Linux OS, list some popular Linux distributions, list several common open-source licenses, etc. +* Level 2: deepin-IDE basic code challenge + Description: Complete simple programming tasks, such as sorting algorithms, basic data processing. +* Level 3: deepin system interactive experience + Description: Experience deepin 23 on-site and leave your product feedback and suggestions through the experience. + +**Rainbond** + +![](/assets/img/activity/dromara-coscon-market-15.png) + +Rainbond is a cloud-native application management platform. The core is 100% open-source, easy to use, does not require knowledge of containers and Kubernetes, supports managing multiple Kubernetes clusters, and provides enterprise-level application full lifecycle management. + +**Stall Activities:** +1. Interactive giveaway of community stickers, e.g., following the WeChat official account, Github Star, etc. +2. In-depth interaction giveaway of tote bags or notebooks. + +**Banff China** + +![](/assets/img/activity/dromara-coscon-market-16.webp) + +The Banff Mountain Film Festival began in 1976 and is the world's most authoritative and largest outdoor adventure, mountain culture, and environmental theme film festival. It is known as the outdoor "Oscars." Our company, Banff (Beijing) Culture Communication Co., Ltd., was established in 2010. In 2019, we introduced "Free Solo," which won the Oscar for Best Feature Documentary that same year, setting a box office record for documentaries in China. This year, Banff China joins hands with KaiYuanshe to co-create the True·Hackathon segment, contributing to promoting open source as a lifestyle. + +**Stall Activity:** Sports knowledge popularization, peripheral interaction. + +**Intelligent Robot Interactive Experience** + +![](/assets/img/activity/dromara-coscon-market-17.png) + +At the COSCon'24 Open Source Bazaar display stall, we will bring an interactive experience based on intelligent robot technology. Participants can personally experience how intelligent robots use gesture control and human body following technology to achieve intelligent control of physical and virtual cars. The display includes a fully open-source physical car project, from circuit design to hardware implementation, allowing participants to deeply understand its working principle. Meanwhile, the display of the virtual car will同步 (simultaneously) open source,方便 (facilitating) participants to explore and adjust control schemes. + +**Stall Interaction:** +1. Gesture Control & Human Following Race: Participants can control two physical cars for a race through gestures or human following, experiencing the fun of intelligent control technology. +2. Path Drawing Interaction: Use gesture control to draw paths with a virtual car; source code interfaces are provided for on-site adjustments; participants who complete the task will receive精美礼物 (exquisite gifts). +3. Algorithm Q&A Session: Q&A challenge on intelligent robot technology and algorithms; those who answer correctly also have a chance to win gifts. +4. Through these fun interactive activities, participants can not only experience intelligent control but also further understand the underlying technical principles. We also provide detailed source code and learning materials for participants to reference after the event. + +**Apache Doris Quick Q&A** + +![](/assets/img/activity/dromara-coscon-market-18.png) + +Apache Doris is a modern, high-performance, real-time analytical database based on MPP. Known for its extreme speed and ease of use, it can return query results on massive data with sub-second response time, supporting both high-concurrency point query scenarios and high-throughput complex analysis scenarios. + +**Stall Interaction:** +Bring Apache Doris promotional materials, including project introduction, core features, application scenarios, etc. Ask the audience five random questions. Gifts for answering 1, 2-3, 4-5 questions correctly respectively: stickers, laptop stands, T-shirts, etc. Limited quantity daily, first come first served. + +**openInula** + +![](/assets/img/activity/dromara-coscon-market-19.png) + +openInula is a compile-based reactive front-end development framework. Developers can use familiar JSX syntax, enjoy the flexibility of native JavaScript, while achieving higher page performance, less memory usage, and smaller bundle size. + +**Stall Interaction:** The exhibition area can combine sub-forum topics to experience hands-on operation of openInula API2.0. Small gifts prepared to reward developers. + +**Sermant Open Source Community** + +![](/assets/img/activity/dromara-coscon-market-20.png) + +Sermant is an agentless service mesh based on Java bytecode enhancement technology. It uses Java bytecode enhancement technology to provide service governance functions for host applications, solving service governance problems in large-scale microservices scenarios. + +**Stall Interaction:** +* Sermant Plugin Development Experience: Develop plugins based on the template file provided by Sermant official, dynamically implementing bytecode enhancement for Java microservices. +* Share Sermant technical evangelism articles or check-in photos on social media (朋友圈). +* On-site project introduction and Q&A. +* On-site participants can receive prizes provided by the Sermant community, such as tote bags, by participating in the above activities. + +**OpenFDE Open Source Linux Desktop** + +![](/assets/img/activity/dromara-coscon-market-21.png) + +OpenFDE is an open-source Linux desktop. Differently, it is based on the AOSP graphics stack, providing a consistent window graphics environment for Android and Linux applications. + +**Stall Interaction:** Open source knowledge Q&A: Prepare several open source & community-related questions; gifts for correct answers. + +**ZenTao Open Source Edition Project Management Software** + +![](/assets/img/activity/dromara-coscon-market-22.png) + +ZenTao is an open-source full-lifecycle project management software designed based on Agile and CMMI management concepts. It integrates product management, project management, quality management, document management, organizational management, and affair management, fully covering the core processes of R&D project management. + +**Stall Interaction:** Prepare open source questions; give corresponding small gifts based on the participant's correct answer rate. + +**OpenBuild** + +![](/assets/img/activity/dromara-coscon-market-23.png) + +OpenBuild is an open-source community for Web3 developers, dedicated to connecting Web2 and Web3, helping developers transition to the decentralized web. Through open-source collaboration, it provides systematic educational content, open-source tools, and community resources, helping developers build a reputation system and create business opportunities. + +**Stall Interaction:** Interactive lucky draw. + +**NebulaGraph** + +![](/assets/img/activity/dromara-coscon-market-24.webp) + +NebulaGraph is an open-source, distributed, easily scalable native graph database capable of handling ultra-large-scale datasets containing hundreds of billions of vertices and trillions of edges, providing millisecond-level queries. + +**Stall Interaction:** Answer basic questions about NebulaGraph based on the roller banner and official account content. Correct answers win any peripheral; incorrect answers receive a small rubber duck. + +**AIGCOPEN Community x Microsoft Reactor** + +![](/assets/img/activity/dromara-coscon-market-25.png) +![](/assets/img/activity/dromara-coscon-market-26.png) + +AIGC aims to create China's AIGC ecosystem landing. + +**Stall Interaction:** Showcase Microsoft Reactor developer community activities on-site, distribute promotional leaflets for Microsoft AI developer certification guidance, guide downloads of the latest OpenAI Chinese whitepaper, and distribute free learning materials from the AIGCOPEN community. + +**隐语 SecretFlow** + +![](/assets/img/activity/dromara-coscon-market-27.webp) + +SecretFlow is an open-source unified framework for privacy-preserving data analysis and machine learning. + +**Stall Interaction:** Privacy Computing Quick Q&A—An activity initiated by the SecretFlow open-source community, "SecretFlow·Privacy Computing Quick Q&A," focusing on basic knowledge of privacy computing, aiming to promote data security, privacy computing, and related knowledge. + +**IvorySQL Open Source Community** + +![](/assets/img/activity/dromara-coscon-market-28.webp) + +IvorySQL is an open-source Oracle-compatible PostgreSQL led by HighGo. It is dedicated to providing enterprises and developers with a high-performance, scalable, and secure best-in-class solution for Oracle migration. As an international open-source project, the IvorySQL China community has now grown to 2000+ members, completed over 270 commits, and contributed over 900,000+ lines of open-source code. + +**Stall Interaction:** +Participants只需 (only need to) participate in the dart lottery and community interaction for a chance to join an exciting lottery and win generous prizes. + +**Apollo Developer Community** + +![](/assets/img/activity/dromara-coscon-market-29.png) + +The Apollo Developer Community is dedicated to providing a learning and exchange platform for global autonomous driving developers and partners, helping developers quickly understand and use autonomous driving technology. + +**Stall Interaction:** Autonomous driving technology科普互动 (popular science interaction); peripheral interaction. + +The above are the stalls at this Open Source Bazaar. Welcome! + +![](/assets/img/activity/dromara-coscon-market-30.png) +![](/assets/img/activity/dromara-coscon-market-31.webp) \ No newline at end of file diff --git a/src/activity/dromara-coscon-showcase.md b/src/activity/dromara-coscon-showcase.md new file mode 100644 index 0000000000..483f27e693 --- /dev/null +++ b/src/activity/dromara-coscon-showcase.md @@ -0,0 +1,95 @@ +--- +title: Dromara Showcases at COSCon'24 China Open Source Conference, Discussing the Future of Domestic Open Source +author: achao +date: 2024-10-17 +cover: /assets/img/activity/dromara-coscon-showcase-0.jpg +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/dromara-coscon-showcase-0.jpg) + +**Time**: November 2-3, 2024 + +**Location**: Zhongguancun National Independent Innovation Demonstration Zone Exhibition Center, Beijing + +The COSCon'24 China Open Source Conference & KaiYuanshe's 10th Anniversary Carnival, hosted by KaiYuanshe, will be grandly held in Beijing. As an annual event in the domestic open-source community, this conference brings together a large number of open-source enthusiasts, technical experts, and industry leaders to discuss the future development of open-source technology. The conference features multiple sub-forums, with the Middleware/Microservices sub-forum presented by the Dromara open-source organization. The Dromara community will bring exciting technical shares from multiple members, showcasing the strength of domestic open source. + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products including popular tools, enterprise-level authentication, microservices, operation and maintenance monitoring, scheduling orchestration, and distributed systems. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, allowing every participating open-source enthusiast to experience the joy of open source. The community currently boasts over 15 GVP projects, with a total star count exceeding 300K, has built an open-source community of tens of thousands of members, and has thousands of individuals and teams using Dromara's open-source projects. + +At this forum, at least three members of the Dromara community will deliver speeches covering popular technical fields such as AI, data operations, and front-end animation engines. Meanwhile, core authors of many other open-source projects from the Dromara community will also be present on-site for the Dromara community offline gathering, to communicate face-to-face with everyone, share their insights and experiences in their respective fields, and promote the development of open-source technology. + +### 1. Li Dapeng: EasyAi - The Open-Source Java AI Framework with Top-Tier Domestic Influence + +**Speaker**: Li Dapeng, Author of Easy-Ai, holder of multiple AI algorithm invention patents, member of the Dromara Open Source Community Project Committee, Algorithm Engineer at Shandong Well Data Co., Ltd. +As a promoter of domestic AI frameworks, Li Dapeng is dedicated to building out-of-the-box AI solutions with Java, empowering enterprise-level development teams. His work not only fills the gap of Java in the AI field but also provides great convenience and flexibility for developers. This time, he will bring an exciting sharing: + +**Presentation Topic**: 《EasyAi - A 100% JAVA Code Domestic Open-Source AI Framework - The Huge Advantages and Role of Using the Easy Framework to Develop AI Micro-Models Under the Complete JAVA System in Enterprise Business》 + +**Presentation Time**: November 3, 2024 + +**Introduction**: Java, as the main language for enterprise-level development, has always been known for its rich ecosystem, shaped by the mobile internet era. However, with the advent of the artificial intelligence era, it has lost its ecosystem in the AI field. To solve this problem, EasyAi emerged. Its significance for Java is equivalent to the role of Spring in the JavaWeb field—to provide an out-of-the-box solution that enables every developer to use EasyAi to develop small-scale models that meet their artificial intelligence business needs. This is its mission! EasyAi has no dependencies. It is a native Java artificial intelligence algorithm framework. Firstly, it can be seamlessly integrated into our Java projects with Maven in one step, without any additional environment configuration or dependencies, achieving out-of-the-box usability. Secondly, it includes pre-packaged modules for image target detection and AI customer service, as well as underlying algorithm tools for deep learning, machine learning, reinforcement learning, heuristic learning, matrix operations, derivative functions, partial derivative functions, etc. Developers can, with minimal learning, develop small-scale models that deeply align with their business needs. + +**Open Source Project Links**: +Gitee Address: +https://gitee.com/dromara/easyAi +Project Honors: Gitee GVP Project, GitCode G-Star Project + +![](/assets/img/activity/dromara-coscon-showcase-1.jpg) + +* * * + +### 2. A Chao: Stream-Query Unlocks New Ways of Data Operation for Javaers + +**Speaker**: A Chao, 00s open-source enthusiast, Deputy Secretary-General of the Dromara Open Source Organization, 33rd Gitee Cover Person, Author of Stream-Query, Apache StreamPark Committer, Apache ShenYu Committer, Dromara Hutool Member, Baomidou Mybatis-Plus Member, Deputy Technical Director of Heshisiwei (Beijing) Technology Co., Ltd. +A Chao is a highly innovative young developer whose contributions have been widely recognized in multiple open-source projects, especially in the field of simplifying Java data operations. The introduction of Stream-Query enables Java developers to perform data queries more efficiently, reducing development costs. This time, he will share: + +**Presentation Topic**: 《From Complexity to Simplicity: Stream-Query Unlocks New Ways of Data Operation for Javaers》 + +**Presentation Time**: November 3, 2024 + +**Introduction**: Dromara Stream-Query, as an innovative tool library, aims to simplify the data query process and improve development efficiency. It includes two core modules: Stream-Core and Stream-Plugin, which now enhance MybatisPlus functionality, support dynamic Mapper implementation, eliminating the need to manually create Mapper classes, and easily complete CRUD operations. This presentation will detail the core functions, technical architecture, and practical application of Stream-Query in real projects, helping Java developers make full use of this tool to improve work efficiency. + +**Open Source Project Links**: +Gitee Address: +https://gitee.com/dromara/stream-query +Project Honors: Gitee Recommended Project, GitCode G-Star Project, Trusted Open Source Project of China Academy of Information and Communications Technology (CAICT) Trusted Open Source Community Consortium (TWOS) + +![](/assets/img/activity/dromara-coscon-showcase-2.jpg) + +* * * + +### 3. Liu Chenyang: Exploring Programmatic Animation and Open-Source Front-End Engines + +**Speaker**: Liu Chenyang, 05s suspended high school freshman, co-founder of the BugDuck team, member of the Dromara Open Source Community Project Committee, author of the Newcar animation engine. +As a young generation open-source contributor, Liu Chenyang, with his creative development work in the field of front-end animation engines, has become a shining star in the open-source community. Whether it's Newcar or VueMotion, he has demonstrated profound technical skills and unique insights. This time, he will bring three topic shares: + +* • **Topic 1: 《From Newcar to VueMotion: Exploration of Front-End Animation Engines》** + **Introduction**: Introducing Newcar and VueMotion as modern animation engines. VueMotion is an animation engine based on VueJs, providing a variety of components, similar to Manim but with a wider application range than Manim. This speech will discuss the concepts, development stories, and future prospects. +* • **Topic 2: 《LLMVision: The Combination of Programmatic Animation and Large Model Capabilities》** + **Introduction**: Introducing LLMVision as a mathematics and statistics demonstration animation generator based on VueMotion. +* • **Topic 3: 《Four Years of a High School Student with Open Source》** + **Introduction**: I will share my experience with open source and my story of struggling against an unfair fate using open source. + +**Open Source Project Links**: +Gitee Address: +https://gitee.com/dromara/newcar +Project Honors: Gitee Recommended Project + +![](/assets/img/activity/dromara-coscon-showcase-3.jpg) + +* * * + +This COSCon'24 China Open Source Conference, the shares from the Dromara community will bring cutting-edge technical thinking and practical experience to Java developers, AI technology explorers, and front-end enthusiasts. + +Even more excitingly, core authors of multiple projects from the Dromara community will be present on-site to communicate face-to-face with everyone, sharing their development journeys and insights. This is a rare opportunity for open-source enthusiasts to engage in in-depth discussions with these excellent open-source contributors and jointly promote the prosperity and development of the domestic open-source ecosystem! + +**Event Details Link**: +https://kaiyuanshe.cn/activity/COSCon-2024 + +**Registration Link**: +https://www.bagevent.com/event/coscon24 + +**Dromara COSCon**: +https://coscon.dromara.org/ \ No newline at end of file diff --git a/src/activity/dromara-coscon24-countdown.md b/src/activity/dromara-coscon24-countdown.md new file mode 100644 index 0000000000..793498ead9 --- /dev/null +++ b/src/activity/dromara-coscon24-countdown.md @@ -0,0 +1,11 @@ +--- +title: Countdown 3 Days! We're Waiting for You at the 9th China Open Source Conference & KaiYuanshe's 10th Anniversary Carnival! +author: +date: 2024-10-31 +cover: /assets/img/activity/dromara-coscon24-countdown-0.jpg +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/dromara-coscon24-countdown-0.jpg) \ No newline at end of file diff --git a/src/activity/dromara-coscon24-forum.md b/src/activity/dromara-coscon24-forum.md new file mode 100644 index 0000000000..919d1425e7 --- /dev/null +++ b/src/activity/dromara-coscon24-forum.md @@ -0,0 +1,85 @@ +--- +title: Agenda Introduction|Open Source Middleware/Microservices Sub-Forum +author: COSCon'24 Organizing Committee +date: 2024-10-31 +cover: /assets/img/activity/dromara-coscon24-forum-13.webp +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/dromara-coscon24-forum-0.webp) + +The COSCon'24 9th China Open Source Conference & KaiYuanshe's 10th Anniversary Carnival will be held on November 2-3, 2024, at the Zhongguancun National Independent Innovation Demonstration Zone Conference Center. As one of the most influential open-source events in the industry, COSCon was first launched by KaiYuanshe in 2016 and is now in its ninth edition. + +With its unique positioning and growing influence, COSCon has attracted increasing support from domestic and international enterprises, universities, and open-source organizations/communities. It boasts broad coverage across organizations, projects, and communities, drawing the attention and participation of numerous open-source developers and enthusiasts worldwide. In 2024, coinciding with KaiYuanshe's 10th anniversary, many communities will join this grand event, jointly presenting thematic forums on various topics, covering technologies such as web application development, cloud computing, big data, artificial intelligence, and Web3.0, as well as areas like open-source evaluation standards, open-source governance, and open-source talent education. + +**1** + +**Conference & Forum Information** + +**⏰ Conference Time**: November 2-3, 2024. The main forum will be held in the morning, followed by parallel thematic forums in the afternoon. + +**📍 Location**: Zhongguancun National Independent Innovation Demonstration Zone Conference Center, Beijing + +**🙌🏻 Registration**: Scan the QR code below to register +http://coscon24.bagevent.com + +![](/assets/img/activity/dromara-coscon24-forum-3.webp) + +At the COSCon'24 9th China Open Source Conference, the Dromara open-source community will serve as a co-organizing community, presenting a thematic forum on open-source middleware/microservices during the event. + +**2** + +**Forum Co-Organizer** + +
+dromara-coscon24-forum +
+
+Xiao Yu, Mianbi Intelligence +
+ +
+Founder of the Dromara open-source organization +
+ + + + + +**3** + +**Forum Agenda Introduction** + +![](/assets/img/activity/dromara-coscon24-forum-10.webp) + +**4** + +**About the Event** + +The COSCon'24 9th China Open Source Conference & KaiYuanshe's 10th Anniversary Carnival is an annual open-source extravaganza. Over the two-day conference, there will not only be exciting Keynote speeches but also diverse thematic forums/hands-on labs/community gatherings. It is expected that over 2,000 participants will attend this event on-site, with more than 10,000 viewers joining online via live stream. + +![](/assets/img/activity/dromara-coscon24-forum-13.webp) + +Produced by | COSCon'24 Organizing Committee +Edited by | Wang Jun + +**Related Reading | Related Reading** + +[First Look at the Main Forum! Open Source, Open Life - Experience the New Open Source Lifestyle at COSCon'24](http://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247539577&idx=1&sn=12c6cf318e39bf583b8c746a4cdc8123&chksm=9fdad047a8ad595125aab0b3fb10da55e1d3bfff18b7c3f9f97c0e88242fd52342556ad60dca&scene=21#wechat_redirect) + +[Forum Introduction | Open Source Education Sub-Forum (Open Source Summer and Open Source Talent Cultivation)](http://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247539807&idx=1&sn=fa057571874df6208346e394f2a3cbed&chksm=9fdad761a8ad5e779b8467ba4cc5df3119c211b831148503fe9e3a3e0880a33aed9f8e94cfd8&scene=21#wechat_redirect) + +**KaiYuanshe** +_**KAIYUANSHE**_ + +![](/assets/img/activity/dromara-coscon24-forum-16.webp) + +KaiYuanshe was established in 2014. It is an open-source community composed of individual volunteers dedicated to the cause of open source, based on the principles of "Contribution, Consensus, and Co-governance." KaiYuanshe always maintains the concept of "Vendor-Neutral, Public Welfare, Non-Profit," with the vision of "Based in China, Contributing to the World, Promoting Open Source as a Lifestyle in the New Era," and the mission of "Open Source Governance, International Integration, Community Development, Project Incubation," aiming to co-create a healthy and sustainable open-source ecosystem. + +KaiYuanshe actively cooperates closely with open-source-supporting communities, universities, enterprises, and relevant government units. It is also the first Chinese member of the Open Source Initiative (OSI), the global open-source license certification organization. + +Since 2016, KaiYuanshe has continuously hosted the China Open Source Conference (COSCon), released the "China Open Source Annual Report," and jointly launched initiatives like the "China Open Source Pioneer List" and "China Open Source Code Power List," generating widespread influence both domestically and internationally. + +We look forward to meeting you on November 2-3 at the COSCon'24 9th China Open Source Conference! \ No newline at end of file diff --git a/src/activity/dromara-milvusplus-live-preview.md b/src/activity/dromara-milvusplus-live-preview.md new file mode 100644 index 0000000000..5d9e9b2ffa --- /dev/null +++ b/src/activity/dromara-milvusplus-live-preview.md @@ -0,0 +1,32 @@ +--- +title: "Live Stream Preview丨Dromara MilvusPlus: A Vector Database Operations Library Built for Java Developers" +author: December 11, 2024 08:41 +date: 2024-12-11 +cover: /assets/img/activity/dromara-milvusplus-live-preview-0.png +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/dromara-milvusplus-live-preview-0.png) + +![](/assets/img/activity/dromara-milvusplus-live-preview-1.png) + +This episode of the 【User Tech】live stream will kick off on **December 12 at 8:00 PM** on the Zilliz live stream channel. We are honored to invite **Dromara Community Committee Member** Xiong Guochao, who will bring a sharing titled 《Dromara MilvusPlus: A Vector Database Operations Library Built for Java Developers》. MilvusPlus greatly simplifies the process of integrating vector databases into Java applications. This project offers a MybatisPlus-like calling style, enabling developers to manage and query vector data more efficiently and familiarly. + +![](/assets/img/activity/dromara-milvusplus-live-preview-2.png) + +Click below to schedule your reminder now. Please stay tuned for our live stream and join us on this journey of exploration! + +**Recommended Reading** + +How to Use RAG Technology for Document Q&A? Milvus × NetEase Youdao QAnything Reveals the Secrets! + + + +User Tech|First Session of the New Year! Milvus and Ctrip's Vector Exploration Publicly Revealed + + + +![](/assets/img/activity/dromara-milvusplus-live-preview-5.png) +![](/assets/img/activity/dromara-milvusplus-live-preview-6.png) \ No newline at end of file diff --git a/src/activity/dromara-wuzhen-summit-2024.md b/src/activity/dromara-wuzhen-summit-2024.md new file mode 100644 index 0000000000..9f6cceb896 --- /dev/null +++ b/src/activity/dromara-wuzhen-summit-2024.md @@ -0,0 +1,57 @@ +--- +title: Dromara Among the List! 30 Chinese Open Source Achievements Showcased at the 2024 World Internet Conference Wuzhen Summit +author: China Economic Net +date: 2024-11-22 +cover: /assets/img/activity/dromara-wuzhen-summit-2024-0.jpg +head: + - - meta + - name: Activity +--- + +**Great News!** The Dromara open-source community won the **Second Prize** in the "2024 China Internet Development Innovation and Investment Competition (Open Source)" guided by the Cyberspace Administration of China's Information Development Bureau, jointly organized by the China Internet Development Foundation, the China Academy of Cyberspace Studies, and the China Internet Investment Fund, and undertaken by Beijing Chuxin Open Source Technology Co., Ltd. Several core members of the community were recognized! + +Thanks to all the friends in the community for your support, and thank you to the competition organizing committee for your recognition! + +![](/assets/img/activity/dromara-wuzhen-summit-2024-0.jpg) + +Below is the report from China Economic Net. + +**Mr. Song, the head of the competition organizing committee, commented on Dromara: Some open-source communities spontaneously formed by grassroots developers, such as Dromara, are truly touching with their culture of "contributing for the love of open source."** + +On November 21, 2024, at the Internet Public Welfare and Charity Forum of the 2024 World Internet Conference Wuzhen Summit, the 2024 China Internet Development Innovation and Investment Competition (Open Source) concluded successfully. This competition is part of the brand public welfare project "China Internet Development Innovation and Investment Competition." Guided by the Cyberspace Administration of China's Information Development Bureau, it is the second national open-source public welfare competition jointly organized by the China Internet Development Foundation, the China Academy of Cyberspace Studies, and the China Internet Investment Fund. After multiple stages, including preliminary selection, code composition analysis, and final on-site evaluation, the competition finally selected 10 first prizes and 20 second prizes from 97 participating projects. The winning projects cover fields such as RISC-V, operating systems, databases, cloud computing, big data, artificial intelligence, and supply chain security, with some projects reaching internationally advanced levels. + +![](/assets/img/activity/dromara-wuzhen-summit-2024-1.jpg) + +Since its official launch in April 2024, the 2024 China Internet Development Innovation and Investment Competition (Open Source) has attracted widespread attention from professionals in the open-source field. This year's competition was undertaken by Beijing Chuxin Open Source Technology Co., Ltd., with co-organizers including: China Open Source Software Promotion Alliance, Kechuang China Open Source Innovation Consortium, China Software Testing Center, China Engineers Joint Body, China Electronics Technology Group Corporation, China Mobile Communications Group Co., Ltd., China United Network Communications Group Co., Ltd., National Innovation Center of Intelligent and Connected Vehicles, Beijing Open Source Innovation Committee, Shanghai Open Source Technology Information Association, Huawei, ZTE, Tencent, Baidu, Alibaba Damo Academy, WeBank, Ant Group, ByteDance, JD.com, Xiaomi, QiAnXin, and dozens of other industry institutions, as well as research institutes such as the Institute of Computing Technology of the Chinese Academy of Sciences, the Institute of Software of the Chinese Academy of Sciences, the Institutes of Science and Development of the Chinese Academy of Sciences, Tsinghua University, Peking University, Zhejiang University, National University of Defense Technology, Beihang University, Beijing Institute of Technology, Beijing University of Posts and Telecommunications, University of Electronic Science and Technology of China, Sichuan University, East China Normal University, Southern University of Science and Technology, and Southwest Jiaotong University. As the main platform for the competition's publicity, China Economic Net set up a special section to report on the competition's news throughout the event. The Linux Foundation AI&DATA Community, the Apache Software Foundation, and domestic developer communities such as CSDN, InfoQ, and SegmentFault actively contributed to the online publicity of this year's competition and offline promotion activities in Beijing, Shanghai, Chengdu, Guangzhou, Hangzhou, Shenzhen, and the Greater Bay Area. + +Professor Lu Shouqun, a leading figure in China's open-source industry and honorary chairman of the China Open Source Software Promotion Alliance, stated: "China's cybersecurity and information infrastructure and underlying technologies benefit from the global proliferation of open-source technology. World-renowned open-source projects such as Linux, Kubernetes, and RISC-V have cultivated a group of world-class open-source technical talents in China. Talent is the foundational support for achieving a technologically strong nation, national rejuvenation, and winning the initiative in international competition. The open-source competitions continuously held by relevant units of the Cyberspace Administration of China are of extraordinary significance. Open-source competitions are an important channel for discovering, selecting, and cultivating young open-source talents. Enterprises, universities, and research institutions in the open-source ecosystem should regard this as their responsibility, actively support and participate in it, and contribute to cultivating open-source talent and enhancing technological innovation in China." + +It is reported that the competition established a review committee consisting of 60 technical experts and intellectual property experts, setting up a comprehensive indicator evaluation system across four dimensions: "technological innovation, open-source compliance, community operation, and commercial development." During the final stage, all participating projects underwent professional code composition analysis by the China Software Testing Center, QiAnXin Group, and Suzhou Prism Seven Color Information Technology Co., Ltd., providing an objective basis for the final expert review. + +Professor Chen Zhong of the Peking University School of Computer Science, chairman of this year's competition review committee, stated: "Promoting development through competitions has played an extremely important role in many innovative fields, and the open-source field is no exception. It even has broader exemplary significance. It can both encourage developers and related projects to stand out and promote the spirit and culture of open source, killing two birds with one stone." + +**Song Kewei, head of the competition organizing committee and general manager of Beijing Chuxin Open Source Technology Co., Ltd., said: "The diversity of participating projects is a significant feature of this year's competition. Compared to previous competitions, many individual open-source projects emerged in this year's competition. Although their influence may not be extensive, the maturity of their projects and community activity are very high; some open-source communities spontaneously formed by grassroots developers, such as Dromara, are truly touching with their culture of 'contributing for the love of open source'; there are also projects originating from leading tech companies (e.g., Tencent) and donated to the OpenAtom Open Source Foundation, such as OpenTenbase, which are particularly noteworthy."** + +Since open source was incorporated into the national "14th Five-Year Plan and 2035 Long-Range Objectives" in 2021, China's open source has officially entered an "accelerated" development stage. China has become the country with the fastest growth in the number of open-source developers globally and the second-largest in terms of total numbers. Open-source innovation has become an important development path for China's core and cutting-edge technologies. + +The "Decision" of the Third Plenary Session of the 20th Central Committee of the Communist Party of China proposed "improving the systems and mechanisms for developing new quality productive forces according to local conditions. Promoting revolutionary breakthroughs in technology, innovative allocation of production factors, and in-depth transformation and upgrading of industries, promoting the optimized combination and renewal leap of laborers, means of labor, and objects of labor, giving birth to new industries, new models, and new momentum, and developing productive forces characterized by high technology, high efficiency, and high quality." + +The China Internet Development Foundation, the organizer of the competition, has deeply implemented the strategic deployment of building a strong cyber nation, fully utilized the leveraging effect of public welfare funds, and carried out a series of innovation and investment competitions focusing on key technical talent and project innovation. It has successively included an open-source track in the China Internet Development Innovation and Investment Competition in 2022 and 2024. By hosting a series of open-source innovation and investment competitions, it continues to focus on the development of the open-source industry, assists in incubating outstanding open-source projects in China, and contributes to promoting China's information development and consolidating the digital economy. + +"Compared to the 2022 competition, the organizing committee expanded the review committee and included the adaptation of open-source projects to domestic chips and operating systems as one of the assessment items, fully demonstrating the competition's strong support for China's信创 (IT application innovation) industry ecosystem. In addition, unlike previous training on investment and financing for winning projects, this year's competition has added financing roadshow promotion activities for high-quality open-source projects, further reflecting the attributes of an 'innovation and investment competition,'" Song Kewei added. "Since 2024, local governments across the country have纷纷出台 (successively introduced) policies to support the open-source industry, providing targeted support for open-source enterprises, open-source projects, and open-source communities. Through these two open-source competitions, we have selected 120 outstanding Chinese open-source projects and more than 500 outstanding open-source talents, distributed in more than 20 cities across the country. Subsequently, we will continue to focus on the cultivation of open-source talent and the incubation of open-source projects." + +For the detailed list of winners, see: https://bs.bjos.club/hjgg-n183.html + +(Project supported by funds from the China Internet Development Foundation) + +![](/assets/img/activity/dromara-wuzhen-summit-2024-2.jpg) + +![](/assets/img/activity/dromara-wuzhen-summit-2024-3.jpg) + +--- + +About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, and scheduling orchestration. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, and is dedicated to providing microservices cloud-native solutions for global users. It aims to allow every participating open-source enthusiast to experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of members, with thousands of individuals and teams using Dromara's open-source projects. \ No newline at end of file diff --git a/src/activity/gstar-reward-submit.md b/src/activity/gstar-reward-submit.md new file mode 100644 index 0000000000..d5ea148541 --- /dev/null +++ b/src/activity/gstar-reward-submit.md @@ -0,0 +1,25 @@ +--- +title: G-Star Light Guide Initiative Launched | Submit Your GitCode Project Story to Win AirPods Pro, Guaranteed JD.com Gift Card upon Entry! +author: GitCode +date: 2024-11-12 +cover: /assets/img/activity/gstar-reward-submit-0.gif +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/gstar-reward-submit-0.gif) + +![](/assets/img/activity/gstar-reward-submit-1.webp) + +![](/assets/img/activity/gstar-reward-submit-2.webp) + +![](/assets/img/activity/gstar-reward-submit-3.webp) + +![](/assets/img/activity/gstar-reward-submit-4.webp) + +![](/assets/img/activity/gstar-reward-submit-5.webp) + +![](/assets/img/activity/gstar-reward-submit-6.webp) + +![](/assets/img/activity/gstar-reward-submit-7.webp) \ No newline at end of file diff --git a/src/activity/gstar-shenzhen-developers.md b/src/activity/gstar-shenzhen-developers.md new file mode 100644 index 0000000000..ae9211db7e --- /dev/null +++ b/src/activity/gstar-shenzhen-developers.md @@ -0,0 +1,35 @@ +--- +title: 【Event Registration】G-Star Gathering Day Shenzhen Stop|A Exclusive Party for Developers, Coming to Shenzhen! +author: November 13, 2024 08:30 +date: 2024-11-13 +cover: /assets/img/activity/gstar-shenzhen-developers-0.gif +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/gstar-shenzhen-developers-0.gif) + +**G-Star Gathering Day** is a face-to-face gathering crafted by GitCode for open-source project developers. We have prepared a venue with a great view, delicious pizza, and beer to freely discuss the passion and joy of open-source development, share experiences, and spark inspiration. This is not only a technical exchange event but also a beautiful testament to the friendship among developers, working together to contribute to the prosperity of the open-source ecosystem. + +**G-Star Gathering Day Shenzhen Stop** is about to kick off! We sincerely invite every developer who loves open source to gather in Shenzhen on **November 30** to explore the infinite possibilities of open-source technology together. Continue reading to learn more about the event details and join us for this exclusive party for developers! + +![](/assets/img/activity/gstar-shenzhen-developers-1.png) + +**G-Star Gathering Day Shenzhen Stop** +📅 Event Time: November 30, 14:00 +📍 Event Location: Nanshan District, Shenzhen + +“ + +**Event Details and Registration Method** + +![](/assets/img/activity/gstar-shenzhen-developers-2.png) + +This event not only invites several industry experts to bring diverse technical sharing but also includes interactive sessions and lucky draws! It is a rare opportunity for technical exchange and networking, where both newcomers and veterans can gain a lot. Scan the QR code above or click the mini-program below to register and embark on a journey of open-source technology exploration! Limited spots available, don’t miss out! + +As China’s hub of technological innovation, Shenzhen has always been at the forefront of technological innovation and exchange, and it has also given birth to many high-quality G-Star projects. We believe that this G-Star Gathering Day Shenzhen Stop will undoubtedly inspire more innovative ideas! + +**November 30, see you in Shenzhen!** + +![](/assets/img/activity/gstar-shenzhen-developers-3.jpg) \ No newline at end of file diff --git a/src/activity/harmonyos-tool-gift.md b/src/activity/harmonyos-tool-gift.md new file mode 100644 index 0000000000..b1f30a3146 --- /dev/null +++ b/src/activity/harmonyos-tool-gift.md @@ -0,0 +1,33 @@ +--- +title: HarmonyOS Developers, Assemble! Contribute Your Development Tools and Win Gifts at the Grand Bazaar +author: January 23, 2025 10:24 +date: 2025-01-23 +cover: /assets/img/activity/harmonyos-tool-gift-0.gif +head: + - - meta + - name: Activity +--- + +![](/assets/img/activity/harmonyos-tool-gift-0.gif) + +Amid the wave of technology, the HarmonyOS ecosystem is advancing rapidly with breakthrough momentum, flourishing vigorously. The HarmonyOS Development Tools Square Community, as a gathering place for practical HarmonyOS development tools, has always been committed to creating a resource-rich, efficient, and convenient exchange platform for developers. + +At the beginning of 2025, we sincerely invite developers to participate in the "**HarmonyOS Development Tools Grand Bazaar**" activity. Turn your usual HarmonyOS development experience into tools to help more developers efficiently build HarmonyOS applications. Welcome to **scan the QR code in the poster below to join this event** and contribute to the prosperity of the HarmonyOS ecosystem together! + +![](/assets/img/activity/harmonyos-tool-gift-1.png) + +![](/assets/img/activity/harmonyos-tool-gift-2.svg) + +**Recommended Reading** + +![](/assets/img/activity/harmonyos-tool-gift-3.svg) + +[![](/assets/img/activity/harmonyos-tool-gift-4.png)](https://mp.weixin.qq.com/s?__biz=MzkyNjY0MDY1Ng==&mid=2247488718&idx=1&sn=5da5632275a58f1b743e875e2ad9ffc3&scene=21#wechat_redirect) + +Announcement of Winners for the Cangjie Programming Language Essay Contest + +[![](/assets/img/activity/harmonyos-tool-gift-5.png)](https://mp.weixin.qq.com/s?__biz=MzkyNjY0MDY1Ng==&mid=2247488488&idx=1&sn=d5cb681cc3eec11f0e56785dbab75f3e&scene=21#wechat_redirect) + +CodeMaster Featured Personality - Issue 1 + +![](/assets/img/activity/harmonyos-tool-gift-6.jpg) \ No newline at end of file diff --git a/src/activity/secretflow-3rd-upgrade-showcase.md b/src/activity/secretflow-3rd-upgrade-showcase.md new file mode 100644 index 0000000000..f7425af79e --- /dev/null +++ b/src/activity/secretflow-3rd-upgrade-showcase.md @@ -0,0 +1,138 @@ +--- +title: Three-Year Promise, Gathering in Beijing! Join Us to Witness the All-New SecretFlow Evolving into a "Full-Stack Technology Community for Data Circulation" +author: SecretFlow Open Source Community +date: 2025-07-29 +cover: /assets/img/activity/secretflow-3rd-upgrade-showcase-0.webp +head: + - - meta + - name: Activity +--- + +At a critical stage of the in-depth development of the digital economy, national data infrastructure serves as a vital carrier for implementing data foundation systems and advanced technologies. It requires the integration and co-creation of technological and industrial ecosystems to break down barriers to interoperability. Seizing this period of accelerated development, the SecretFlow open source community is celebrating its third anniversary—a significant milestone! + +Three years ago, the SecretFlow open source community started with privacy-preserving computing technology, embarking on a journey of exploration and practice to ensure secure and trustworthy data circulation. Today, the community has gathered over 20,000 followers and developers, collaborated with 60+ academic and research institutions, and partnered with 70+ industrial organizations, collectively advancing key technologies for data element circulation and their application in various industries. + +In response to national data infrastructure policies, the SecretFlow open source community is not only born from the trend but also evolves with the times! We look forward to having you join us in witnessing SecretFlow’s transformation from a "privacy-preserving computing technology community" to a "full-stack technology community for data circulation." + + + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-0.webp) + +On August 14, 2025, the event "Technical Interconnection · Infinite Value—SecretFlow Third Anniversary & Community Upgrade Ceremony" will take place! This event is jointly hosted by Ant Cryptocomputing Technology Co., Ltd., the Key Laboratory of Blockchain Technology and Data Security of the Ministry of Industry and Information Technology, the State Key Laboratory of Blockchain and Data Security at Zhejiang University, and China Electronics Data Industry Group Co., Ltd. It is co-organized by the Beijing International Big Data Exchange and the Digital Economy Professional Committee of the China Electronics Information Industry Federation. + +The aim is to collaborate with partners from industry, academia, and research to advance the construction of data element circulation infrastructure, accelerate innovation in data element application scenarios, and unleash industrial value. + +Scan the QR code to reserve your【on-site seat】 + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-1.webp) + +A three-year promise—SecretFlow meets you in Beijing! + + + +**🌟 Event Highlights: A Sneak Peek** + +**1. SecretFlow Community Upgrade: Technological Ecosystem Unites for Progress** + +A single tree does not make a forest. The SecretFlow community will "break boundaries" beyond privacy-preserving computing, uniting the technological and industrial ecosystem to build an integrated technological ecosystem covering trusted data spaces, data components, data networks, blockchain, and privacy-preserving computing. This evolution from a privacy-preserving computing technology community to a full-stack technology community for data element circulation will foster a collaborative ecosystem for national data infrastructure. + +Simultaneously, the community will promote interoperability across the data element industry. Leveraging the open-source nature of SecretFlow, it will build an ecological community involving multiple stakeholders from industry, academia, research, and application, breaking down industry silos, facilitating cross-domain technological collaboration and standard co-creation, enabling efficient cross-domain data element circulation, and stimulating innovation in industrial data. Stay tuned for the inaugural lineup! + +**2. Technology + Industry: A Feast of Colliding Ideas Driven by Dual Forces** + +We believe that cutting-edge technology is not a "solo performance." To bring hardcore code into real business scenarios, this event will feature exchanges and collisions through keynote sharing and in-depth roundtable discussions. Topics will include the technological evolution of data circulation infrastructure construction and benchmark scenarios of industrial data circulation, exploring how open source drives innovation, how technology empowers business, and how industries accelerate incubation. + +When上层建筑 (superstructure) meets基层建设 (infrastructure), and when tech geeks meet industry practitioners, we will break down barriers between cognition and practice,碰撞出 (collide to generate) practical paths for driving data value mining and industrial application落地 (implementation). + +**3. Data x Industries: Insights into Real Industrial Application Challenges** + +Industry is the best training ground for technology, and application scenarios are the fundamental drivers of data value. At this event, we will invite industry pioneers from various fields such as government, finance, insurance, and transportation to share practical cases of data element circulation and value release. + +We look forward to industrial organizations playing a leading role, jointly exploring innovative applications of data in various scenarios, promoting value mining and cross-entity reuse of data elements, and achieving complementary advantages, experience sharing, knowledge diffusion, and value multiplication through ecological exchange and cooperation. + + + +**🎓 Agenda: First Public Release** + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-2.webp) + +**🌟 Event Highlights: A Sneak Peek** + +Sign up now for the "Technical Interconnection · Infinite Value—SecretFlow Third Anniversary Special Event" to secure your exclusive seat! Gain in-depth insights into the latest technologies and practices in data circulation infrastructure, meet industry experts face-to-face, and unlock limited-edition community gifts! + +🔍 **How to Register** + +Scan the QR code above or below to register. Add SecretFlow Assistant Calor (WeChat ID: SecretFlow04) and reply with "Third Anniversary" to check your registration status. + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-3.webp) + +🎁 **Support Benefits** + +**1. Participation Period: From now until 23:59 on August 13, 2025** + +**2. Participation Rules and Reward Mechanism**: + +① Invite friends for online support: + +Step 1: Scan the registration QR code via WeChat to enter the "Support SecretFlow" page. + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-4.png) + +Step 2: Share the support link with friends, and invited friends click to support online. + +Step 3: After the inviter checks in at the event, they can receive designated gifts based on the cumulative number of friends invited. + +Reward Mechanism: + +- 10 ≤ Cumulative number of friends invited < 20: Receive a SecretFlow custom umbrella. +- Cumulative number of friends invited ≥ 20: Receive a SecretFlow custom travel cup. + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-5.webp) + +② Invite friends to register and attend onsite: + +Step 1: Scan the registration QR code via WeChat to enter the "Support SecretFlow" page. + +Step 2: Share the support link with friends, and invited friends register for the event and check in onsite. + +Step 3: The inviter can receive designated gifts based on the number of friends who check in onsite (Note: The inviter does not need to be onsite; the invited friends' onsite check-ins will count toward the cumulative number). + +Reward Mechanism: + +a) 3 ≤ Number of friends who check in onsite < 5: Receive a SecretFlow third-anniversary custom T-shirt. + +b) 5 ≤ Number of friends who check in onsite < 8: Receive a SecretFlow custom umbrella and a nap blanket. + +c) Number of friends who check in onsite ≥ 8: Receive a SecretFlow third-anniversary custom T-shirt and an eye massager. + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-6.webp) + +Note: In this support activity, users with the same ID number, mobile phone number, or WeChat account are considered the same user. Please refrain from using any unfair means that undermine the fairness of the activity. Violators will be disqualified from rewards. + + + +**Open-Source Trusted Privacy-Preserving Computing Framework—SecretFlow** + +Supports mainstream privacy-preserving computing technologies such as MPC, FL, and TEE. Integrates the co-creation capabilities of industry, academia, and research ecosystems to facilitate the broader application of privacy-preserving computing in AI, data analysis, and other scenarios, addressing pain points such as privacy protection and data silos across industries. + +**Website**: https://www.secretflow.org.cn + +**View Source Code**: https://github.com/secretflow/secretflow + + + +**Open-Source Secure and Trusted System Software Stack—Asterinas** + +An open-source community focused on secure and trusted technology software stacks, providing a solid security foundation for general computing and confidential computing scenarios, safeguarding emerging safety-critical and privacy-sensitive applications. + +**Website**: https://asterinas.github.io/ + +**View Source Code**: https://github.com/asterinas + + + +**Contact Us** + +**Official Account**: SecretFlow’s Little Theater (隐语的小剧场) + +**Assistant**: SecretFlow04 \ No newline at end of file diff --git a/src/blog/ApacheShenYu-2.6.1.md b/src/blog/ApacheShenYu-2.6.1.md index 9a00d83565..8d88e4a4a2 100644 --- a/src/blog/ApacheShenYu-2.6.1.md +++ b/src/blog/ApacheShenYu-2.6.1.md @@ -2,8 +2,6 @@ title: Apache ShenYu 2.6.1 Released author: Ho Fung Eun date: 2024-01-24 -tag: - - cover: /assets/img/blog/ApacheShenYu-2.6.1-0.png head: - - meta diff --git a/src/blog/DyJava.md b/src/blog/DyJava.md index abae02fffc..b19b7eaaf6 100644 --- a/src/blog/DyJava.md +++ b/src/blog/DyJava.md @@ -3,7 +3,7 @@ title: Here it comes! TikTok Development Tool DyJava Joins Dromara Open Source C author: danmo date: 2024-04-29 tag: - - + - DyJava cover: /assets/img/blog/DyJava-0.png head: - - meta @@ -68,6 +68,6 @@ For businesses that want to open a store on TikTok, DyJava provides a complete b > as the 1 Java development kit specially built for tremolo, DyJava will undoubtedly become the preferred tool for tremolo back-end development with its rich functional modules, simple API design, efficient performance and perfect support system. Let's join hands with DyJava to open a new chapter in the back-end development of Douyin! -**In this era full of infinite possibilities, let us use DyJava to create more wonderful Douyin applications and bring users a richer entertainment experience! * * +\*_In this era full of infinite possibilities, let us use DyJava to create more wonderful Douyin applications and bring users a richer entertainment experience! _ \* -**Warehouse address: https://gitee.com/dromara/dy-java** \ No newline at end of file +**Warehouse address: https://gitee.com/dromara/dy-java** diff --git a/src/blog/Fast Request-0.md b/src/blog/Fast Request-0.md index e93206ab2b..062f99b4b4 100644 --- a/src/blog/Fast Request-0.md +++ b/src/blog/Fast Request-0.md @@ -2,8 +2,6 @@ title: Script library syntax hint support, Fast Request 2024.1.5 release author: Kings date: 2024-05-15 -tag: - - cover: /assets/img/blog/Fast Request-0-1.webp head: - - meta diff --git a/src/blog/FastRequest.md b/src/blog/FastRequest.md index 279d815e94..737c6241da 100644 --- a/src/blog/FastRequest.md +++ b/src/blog/FastRequest.md @@ -2,8 +2,6 @@ title: Multi-tab open API,Fast Request 2024.1.6 release author: Kings date: 2024-06-26 -tag: - - cover: /assets/img/blog/FastRequest-1.webp head: - - meta diff --git a/src/blog/HertzBea-collection-works.md b/src/blog/HertzBea-collection-works.md new file mode 100644 index 0000000000..1e6058ce08 --- /dev/null +++ b/src/blog/HertzBea-collection-works.md @@ -0,0 +1,130 @@ +--- +title: "Behind the Scenes of HertzBeat: How Metric Collection Works" +author: 2025年04月03日 11:19 +date: 2025-04-03 +cover: /assets/img/blog/HertzBea-collection-works-0.png +head: + - - meta + - name: 博客 +--- + + + +> 来自Apache HertzBeat 社区韩国朋友 @JuJinPark 的文章,写的很棒,这里就直接贴原文不翻译了. + +> HertzBeat is an open-source, real-time monitoring system designed for flexibility and ease of use. But how exactly does it collect, process, and store metrics from various systems? + +In this post, we’ll walk through the internal architecture behind **HertzBeat’s metric collection pipeline** — from job distribution to alerting and storage — with the help of a high-level system diagram. + +* * * + +### HertzBeat’s Metric Collection Architecture + + + +![](/assets/img/blog/HertzBea-collection-works-0.png) + +> **Figure**: High-level architecture of HertzBeat's metric collection system. The Manager handles job scheduling, alerting, and storage, while Collectors (external or internal) perform the actual metric collection. Communication between the Manager and Collectors uses a custom Netty TCP protocol. + +* * * + +### 1\. Job Distribution: Assigning What to Monitor + +When the **Manager** component starts, it loads monitoring targets from the database. These targets define the host, collection interval, and other parameters. + +To distribute the workload, the Manager sends jobs to **external Collectors** over a custom **Netty-based TCP protocol**. The  module handles this logic using **consistent hashing**, ensuring jobs are evenly distributed across collectors.`CollectJobScheduling` + +> 💡 HertzBeat also includes a built-in **main collector** (identified as ) that runs directly inside the Manager. This allows HertzBeat to operate in **standalone mode** without requiring any external collectors.`MAIN_COLLECTOR_NODE` + +* * * + +### 2\. Task Scheduling: When to Monitor + +Once a Collector receives a job, it registers it with the **`TimerDispatch`** system. + +* For **external collectors**, the Manager sends the task via the TCP connection. + +* For the **main collector**, the Manager directly invokes  within the same process.`CollectJobService` + + +Each Collector runs a **`Timer`** in a background thread, which schedules tasks according to their configured intervals. When the time is up, the timer triggers a  to begin metric collection.`TimerTask` + +* * * + +### 3\. Task Execution: How Metrics Are Collected + +When a  is triggered, it creates a  task and passes it to , which places it in the **`MetricsCollectorQueue`**.`TimerTask``MetricsCollect``MetricsTaskDispatch` + +* A dedicated thread () continuously polls this queue.`CommonDispatcher` + +* Tasks are executed by a **worker thread pool**, allowing multiple metric collections to run concurrently. + +* Each task uses a specific **collector strategy** (e.g., HTTP, JDBC, SSH) to fetch metrics from the target system. + + +* * * + +### 4\. Result Processing: What Happens to Collected Data + +Once metrics are collected, the results are processed by the **`CollectDataDispatch`** module. + +* If the task is recurring, it is rescheduled via .`TimerDispatch` + +* Results are added to a **`CommonDataQueue`** for further handling. + + +For external collectors, results are sent **back to the Manager** via the Netty TCP connection. For the main collector, results are forwarded **directly** to the next processing stage without network overhead. + +* * * + +### 5\. Alerting & Storage: Making Metrics Useful + +The Manager receives metric data and pushes it into the , where it is processed through two main pipelines:`MetricsDataToAlertQueue` + +#### 🔔 Alerting + +* The  consumes metrics from the alert queue.`RealTimeAlertCalculator` + +* It checks each metric against user-defined alert rules and triggers alerts if conditions are met. + + +#### 🧠 Storage + +* After alert evaluation, metrics are added to the .`MetricsDataToStorageQueue` + +* A background thread () processes this queue and stores the metrics in a database for long-term analysis and dashboard visualization.`DataStorageDispatch` + + +* * * + +### Standalone Mode: No External Collectors Required + +Thanks to the built-in **main collector**, HertzBeat can operate entirely in standalone mode. This is especially useful for testing, small deployments, or quick setup. All core components — job scheduling, collection, alerting, and storage — run within a single process. + +* * * + +### 🧠 Conclusion + +HertzBeat’s metric collection system is designed for **performance, scalability, and flexibility**. With its: + +* **Queue-based, multi-threaded architecture** + +* **Persistent TCP connections** for reliable job/result flow + +* **Built-in main collector** for standalone operation + + +it handles large-scale monitoring workloads with minimal overhead and high efficiency. + +* * * + +### 🙌 What’s Next? + +If you're curious to explore more: + +* ⭐️ Star the project on GitHub  + +* 🤝 Contribute or open an issue  + + +https://github.com/apache/hertzbeat \ No newline at end of file diff --git a/src/blog/HertzBeat-v1.6.0 .md b/src/blog/HertzBeat-v1.6.0 .md index 8b4beaccbe..154ef52b4f 100644 --- a/src/blog/HertzBeat-v1.6.0 .md +++ b/src/blog/HertzBeat-v1.6.0 .md @@ -2,8 +2,6 @@ title: The first Apache version v1.6.0 was released in HertzBeat! author: tom date: 2024-06-17 -tag: - - cover: /assets/img/blog/HertzBeat-v1.6.0-0.png head: - - meta diff --git a/src/blog/LiteFlow-2.12.0.md b/src/blog/LiteFlow-2.12.0.md index 56b1f6e843..57fb81f44e 100644 --- a/src/blog/LiteFlow-2.12.0.md +++ b/src/blog/LiteFlow-2.12.0.md @@ -2,8 +2,6 @@ title: Decision routing features hit, LiteFlow release of version 2.12.0, Make your code amaing! author: Platinum East date: 2024-04-16 -tag: - - cover: /assets/img/blog/LiteFlow-2.12.0-0.png head: - - meta diff --git a/src/blog/LiteFlow-v2.12.1.md b/src/blog/LiteFlow-v2.12.1.md index bfb0a0fd66..b5acc80ed8 100644 --- a/src/blog/LiteFlow-v2.12.1.md +++ b/src/blog/LiteFlow-v2.12.1.md @@ -2,8 +2,6 @@ title: The rule engine LiteFlow released the v2.12.1 version. How hard did it use to know author: Platinum East date: 2024-06-04 -tag: - - cover: /assets/img/qrcode_zsxq.webp head: - - meta diff --git a/src/blog/README.md b/src/blog/README.md index bdea2cba06..df3b36c555 100644 --- a/src/blog/README.md +++ b/src/blog/README.md @@ -1,33 +1,9 @@ ---- -title: Blog -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - - - - - - +--- +title: Blog +pageInfo: false +contributors: false +editLink: false +sidebar: false +lastUpdated: false +layout: siteLayout +--- diff --git a/src/blog/RuoYi-Vue-Plus-5.2.0.md b/src/blog/RuoYi-Vue-Plus-5.2.0.md index 59ec604318..8f8e87fcb4 100644 --- a/src/blog/RuoYi-Vue-Plus-5.2.0.md +++ b/src/blog/RuoYi-Vue-Plus-5.2.0.md @@ -2,8 +2,6 @@ title: RuoYi-Vue-Plus Releases 5.2.0-BETA Public Test Version Workflow! author: RuoYi date: 2024-05-22 -tag: - - cover: /assets/img/blog/RuoYi-Vue-Plus-5.2.0-0.png head: - - meta diff --git a/src/blog/Sa-Token-v1.38.0.md b/src/blog/Sa-Token-v1.38.0.md index cf1af1974c..1a8b3d1e81 100644 --- a/src/blog/Sa-Token-v1.38.0.md +++ b/src/blog/Sa-Token-v1.38.0.md @@ -2,8 +2,6 @@ title: Sa-Token v1.38.0 Release, heavily refactor SSO modules author: click33 date: 2024-05-13 -tag: - - cover: /assets/img/blog/Sa-Token-v1.38.0-0.png head: - - meta diff --git a/src/blog/Sa-Token.md b/src/blog/Sa-Token.md index 5f41cca2de..7cca7c883f 100644 --- a/src/blog/Sa-Token.md +++ b/src/blog/Sa-Token.md @@ -2,8 +2,6 @@ title: Commemoration:1541Days, Sa-Token Top 1 of Gitee Recommendation List author: click33 date: 2024-04-25 -tag: - - cover: /assets/img/blog/Sa-Token-0.png head: - - meta diff --git a/src/blog/architect-software-craft.md b/src/blog/architect-software-craft.md new file mode 100644 index 0000000000..55b3290c4f --- /dev/null +++ b/src/blog/architect-software-craft.md @@ -0,0 +1,101 @@ +--- +title: "The Architect: Master Craftsmen in the Software Edifice" +author: Cat Lord is Annoyed +date: 2024-12-26 +cover: /assets/img/blog/architect-software-craft-0.webp +head: + - - meta + - name: Blog +--- + +In this era driven by software and artificial intelligence, our daily lives are inextricably linked with software. In this skyscraper of the software world, architects are like those master builders. They are not only proficient in stacking code but also understand how to use the bricks and tiles of code and logic to construct skyscrapers that are both sturdy and aesthetically pleasing, ensuring these structures can withstand the test of time. This article explores how architects, wielding keyboards and mice in the software world, play multiple roles in the process of building software. Let's read with a light heart, think with a smile, and feel free to offer your valuable opinions after a knowing chuckle. + +## Requirements Analysis (Project Manager) + +When a new engineering project is initiated, the project manager first needs to clarify the project's budget, timeline, and requirements. At this stage, objective, rational, and scientific analysis is crucial. +The project manager needs to negotiate for more resources with the boss based on facts and the timeline, and determine reasonable deliverables and deadlines. + +* Humorous Case + + +Boss: "We need to build an e-commerce skyscraper, just like Taobao would be best." +Xiao Wang: "Boss, what's your budget?" +Boss: "100,000, not a penny more. Finish it within a month." +Xiao Wang: "Okay, got it, OKK, guaranteed completion." *Mutters to himself: Guess I'll have to use plastic and foam as building materials (Better buy a set online or find one in an open-source community to modify).* +Boss: "Can you show me the design drawings later?" +Xiao Wang: "What, we need design drawings too? Okay, no problem," *turns around and sends over some materials found online.* +Boss: "Xiao Wang, quality and safety must be guaranteed; we need maintenance later too." +Xiao Wang: "OKK, no problem" +After the project goes live: Quality? Impossible. Safety? Nonsense. Maintenance? Dream on. Unit tests, integration tests, performance tests? Not a chance. +Further development? Impossible. Lack the skill and energy. Just delete the database and run away. + +## Site Selection & Technology Selection (Feng Shui Master) + +When a new engineering project is initiated, the project manager first needs to clarify the project's budget, timeline, and requirements. +At this stage, objective, rational, and scientific analysis is crucial. The project manager needs to negotiate for more resources with the boss based on facts and the timeline, and determine reasonable deliverables and deadlines. +For example: + +* Programming Language Selection: Choose Java for its stability and mature ecosystem, Python for its flexibility, or PHP, hailed as the best language in the world? +* Database Selection: Choose MySQL, reputed to struggle beyond 20 million rows per table, or PostgreSQL with its more powerful features and higher scalability? +* Message Queue Selection: Choose Kafka, known for being extremely fast but potentially losing messages, or the recently popular Pulsar? + +Every selection needs concrete implementation, guided by the `first principle of pursuing efficiency`. +For instance, can using Java enable rapid development and easier maintenance later? +Does MySQL really fail at 20 million rows per table? +Why is Kafka so fast, what's its principle, does it really lose messages, what features does it have? +These questions require in-depth study. Don't follow blindly; you must delve into the principles, consult experts, and even examine the source code implementation—after all, there are no secrets in front of the source code. + +## Design Drawings & Architecture Diagrams (Architect) + +Architects use pencils and rulers to draw blueprints, while software architects use code and logic to build architecture diagrams. +This is not random scribbling; every stroke relates to the software's survival. +If the diagram is wrong, the disaster is unimaginable. Architects should have a high degree of technical foresight in their designs, adhering to the principle of `staying one step ahead`. + +During the design process, it's not about immediately designing for trillions-level traffic or multi-site disaster recovery from the start. Instead, architecture diagrams should be drawn according to the actual situation and team resources, adapting to local conditions. +Every component in the diagram—its purpose, the scenarios for its use, its fault tolerance, the interaction protocols and interfaces between them—needs careful consideration. +Then, draw the lines and trajectories; plainly put, it's a logical flow diagram of data movement. + +Of course, during this process, the architect also needs to reserve some space to consider the system's extensibility for adding more features later. Secondly, in case the boss is dissatisfied one day, the cost of replacement can be minimized as much as possible. +Finally, the architect needs to communicate and review with the project team members in an easy-to-understand manner, ensuring everyone understands what they are doing and how to do it. + +## Construction & Management (Foreman) + +If developing a software edifice is compared to a small battle, then the architect is undoubtedly the general on the battlefield. +Understanding the combat effectiveness, technical level, and temperaments of the people below is key to deploying troops and utilizing their strengths. +This way, even when faced with constantly changing requirements, flexible adjustments can be made. When encountering technical challenges, the architect's motto should be: `"Brothers, follow me!" (not "Brothers, charge for me!")`, leading the charge to overcome technical difficulties. +Don't浮躁 (be impetuous) floating on "PPT," drawing a diagram here, connecting a line there, and thinking the problem is solved. When it comes to actual development, you'll find it's completely different—how difficult it is and how much time it really takes. + +* During the development process: + 1. First, the architect should estimate the工期 (timeline) for each module reasonably and scientifically based on the requirements and architectural design. Always reserve some buffer time. (Sometimes, encountering a single problem can take developers days to solve). + 2. Write core code, establish coding standards, complete interface protocol design, define unit tests, integration tests, E2E tests, etc. Simultaneously, communicate with team members about why this is done and what the benefits are. + 3. Secondly, the architect should constantly monitor the project's progress. When bricklaying (coding) is slow, consider encapsulating some wheels (reusable components) or leveraging AI capabilities to speed up the bricklaying. + 4. Finally,验收 (acceptance) needs to be done for each feature, code needs to be reviewed, and an obsession with quality must be maintained. + When there are disagreements in understanding the code, you might even need to personally refactor the code and then communicate and discuss with the team members. + +* In management and communication: + 1. A good technical team doesn't really need management. The ancients said, 文人相轻 (scholars tend to despise each other). Actually, the programmer community is similar. They won't follow you because of your title or various management tactics, + but rather, based on what they can learn and how they can improve by working with you. Only then will they truly unite and genuinely love the work. + 2. Regarding communication: internally, you are a whole, a team. Problems must be communicated clearly face-to-face, ensuring every member understands your thoughts. Externally, + you must have your own technical convictions and personality. When faced with unreasonable demands, communicate promptly, even argue, to ensure the project stays on track. Don't let people think you are a + pushover or a soft persimmon (easy to bully). Of course, this might also give the impression that you are difficult to communicate with. So what? The responsibility is to get the project done well. + +## Delivery & Operations (Property Management) + +Now the building is constructed. How to safely hand over the house to the owner and let the owner live安心 (with peace of mind)? In this process, the architect should play +the role of property management and should clarify the following: + +* Documentation + Documentation is the most critical and core deliverable in the entire handover process. + First, be responsible for managing documentation and knowledge, ensuring all important architectural decisions, design documents, and operational manuals are properly recorded and managed. + Ensure the documentation `is like a recipe; even if the chef changes, the same flavor can be produced`. + +* DevOps + Imagine if the building needs repairs or parts replacement, + architects can quickly respond to needs and iterate continuously through automated CI/CD (Continuous Integration/Continuous Deployment) processes. + If encountering a problem like a broken elevator, besides considering fault-tolerant strategies, you also need to consider how to perform rolling, canary updates. + +* Observability + The community's operational status needs to be constantly monitored and observed. Ensure that everything that should be monitored *is* monitored, and that monitoring data can reconstruct the entire chain when needed. + Predict system risks in advance and proactively avoid abnormal situations. + +In this era full of changes, architects need to continuously learn new technologies, adapt to new environments, and solve new problems. They also need to maintain curiosity, be full of desire to explore the unknown, and be passionate about new technologies. Be brave in innovation, dare to try, and don't fear failure. This work is full of challenges but also full of fun. This architects' alliance is a collision of ideas,更像 (even more like) a meeting of heroes. Borrowing the quote from Master Yi in League of Legends (LOL): `"A true master is an eternal student."` Hope all architects and engineers aspiring to become architects can contribute their bricks and tiles to China's software edifice. \ No newline at end of file diff --git a/src/blog/dromara-open-source-community.md b/src/blog/dromara-open-source-community.md new file mode 100644 index 0000000000..9e24c6257d --- /dev/null +++ b/src/blog/dromara-open-source-community.md @@ -0,0 +1,149 @@ +--- +title: How to Join an Open Source Community and Enjoy Open Source +author: tom +date: 2022-11-16 +cover: /logo.svg +head: + - - meta + - name: Blog +--- + +Many students have the idea of participating in open source communities or projects. A well-structured open source community with a clear growth path can be particularly appealing. + +Growing together with a community—while one may walk fast alone, a group will walk farther together. + +### First, an Introduction to the Dromara Community + +The Dromara community is an open source community formed by top open source project authors in China. It offers a range of open source products, solutions, consulting, technical support, and training certification services, including distributed transactions, logging, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, scheduling orchestration, and more. The community is committed to comprehensive open source collaboration and maintaining community neutrality, aiming to provide global users with microservices and cloud-native solutions. It allows every open source enthusiast involved to experience the joy of open source. + +The community currently boasts more than 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open source community of tens of thousands of members, with thousands of individuals and teams using Dromara’s open source projects. + +### Community Philosophy + +**Let every open source enthusiast involved experience the joy of open source**. + +We understand that open source contribution is a voluntary act—no one pays open source project developers. Therefore, the open source community will never force you to undertake any tasks or features you are not interested in. Contributors voluntarily pick up tasks that interest them, or when tasks need to be assigned, the PMC will consult contributors to see if they are interested, fully respecting contributors' opinions. We cannot guarantee that participating in open source will be 100% joyful, but we can guarantee that it will never be 100% unpleasant. + +Although no one is paying, the open source community strives to provide as many benefits as possible to developers, such as community gifts and merchandise, and this year’s "Open Source Summer" event by the Chinese Academy of Sciences (with a 12,000 RMB reward). + +You might participate purely out of a love for open source, or because you believe in the project’s growth potential and want to grow together, or to enhance your resume with open source experience, or for internal enterprise development, or to realize self-worth. We highly respect all motivations for participating in open source projects and do our best to provide the utmost support within our means. + +### Open Source Project Growth Path + +**Contributor** (code, documentation, etc.) -> **Committer** (sustained project contribution or outstanding contributors, nominated by the PMC) -> **PMC** (after being a Committer for 2+ months, with sustained contributions and active maintenance of the open source project, nominated by the PMC) + +Community projects have a well-defined growth path. Contributions are not limited to **code**—a **unit test case**, **documentation improvement**, or even fixing a **punctuation error** in the documentation can count as a project contribution. Your first successful contribution makes you a **Project Contributor**. Those who contribute consistently or make significant feature contributions can be nominated by the PMC to become a **Project Committer** (with no objections), automatically becoming a **Dromara Community Member**. Committers who continue to contribute and actively maintain the project can be nominated by the PMC and, after a vote, become a **Project PMC**. After becoming a **Project PMC**, they can be voted into the **Dromara Committee** by the **Dromara Council**. + +## Joining the Organization + +**Dromara** welcomes all open source enthusiasts to join. We provide a well-rounded platform for community governance development and member growth. + +### Dromara Community Members + +#### How to Become a Community Member? + +1. You can contribute to open source projects under the Dromara community (through code, documentation, examples, and other forms of contribution). Once elected as a **Project Committer**, you automatically become a **Dromara Community Member**. + +#### Community Member Rights and Responsibilities + +**Community Member Rights** + +1. The community will display member information and honors on the official website, repositories, etc. +2. Dromara community exclusive email, e.g., `lili@dromara.org`. +3. Free invitation as a guest to Dromara’s paid knowledge planet. +4. Participation in community internal meetings, development plans, events, offline gatherings, etc. +5. Annual community gifts and merchandise (e.g., sweatshirts and wrist rests in 2022). +6. Various types of community support in open source, work, etc. (there are many experts in the community!). + +**Community Member Responsibilities** + +1. Do not engage in illegal activities or actions that harm the community and open source projects. +2. Maintain the community’s image and actively promote it. + +### Dromara Committee Members + +1. You can contribute consistently to open source projects under the Dromara community, become a **Project PMC**, and after being voted in by the Dromara Committee, become a **Dromara Committee Member**. +2. Alternatively, you can donate your open source project directly. Upon successful donation, you automatically become a **Dromara Committee Member**. + +**Committee Member Rights** + +1. All rights enjoyed by the above `Community Members`. +2. Voting rights on community matters. +3. Listing on the official website, repositories, etc., as a Dromara Committee Member. +4. Nominations for new open source projects, committee member nominations, voting, etc. +5. Opportunities for presentations, project promotions, and other community resource support. + +**Committee Member Responsibilities** + +1. Do not engage in illegal activities or actions that harm the community and open source projects. +2. Proactively maintain and promote the community. + +## Step-by-Step Guide to Participating in Open Source + +> Here, we use the open source project HertzBeat under the Dromara community as an example. + +#### Understand and Get Familiar with the Open Source Project + +- Visit the project repository https://github.com/dromara/hertzbeat or the official website https://hertzbeat.com/ to learn about the project. +- Use or start the project based on the documentation to familiarize yourself with its features. + +#### Find Tasks That Interest You + +- Visit the project repository’s Issue list and find tasks that interest you or are marked as [TASK]. If you’d like to try one, simply comment below to claim it, and you can get started! Of course, you can also directly join the communication group or contact WeChat tan-cloud to express your interest in participating, and they will recommend tasks that suit you. +- We recommend starting with small tasks for your first contribution, such as writing unit test cases. + +#### Submit a Pull Request + +1. First, you need to Fork the target repository hertzbeat: https://github.com/dromara/hertzbeat. +2. Then, use the git command to clone the code locally: + ``` + git clone git@github.com:${YOUR_USERNAME}/hertzbeat.git + ``` +3. After cloning, refer to the target repository’s getting started guide or README file to initialize the project. +4. Next, you can use the following commands to commit code, switch to a new branch, and start development: + ``` + git checkout -b a-feature-branch + ``` +5. Commit your changes. The commit message should follow the convention: [module name or type name]feature or bugfix or doc: custom message. + ``` + git add + git commit -m '[docs]feature: necessary instructions' + ``` +6. Push to the remote repository: + ``` + git push origin a-feature-branch + ``` +7. Then, you can create a new PR (Pull Request) on GitHub. + +Please ensure the PR title and content include necessary information to help Committers and other contributors with code review. + +#### Wait for the PR to Be Merged + +After submitting a PR, Committers or community members will review your code (Code Review), provide feedback, or engage in discussions. Please keep an eye on your PR. + +Note: **If后续需要改动,不需要发起一个新的 PR,在原有的分支上提交 commit 并推送到远程仓库后,PR会自动更新**。(If后续需要改动,不需要发起一个新的 PR,在原有的分支上提交 commit 并推送到远程仓库后,PR会自动更新。) + +Additionally, the project has a standardized CI check process. After submitting a PR, CI will be triggered. Please ensure it passes the CI checks. + +Finally, a Committer can merge the PR into the **main DEV branch**. + +#### After the Code Is Merged + +Once the code is merged, you can delete the development branch locally and remotely: +``` +git branch -d a-dev-branch +git push origin --delete a-dev-branch +``` + +On the main branch, you can sync with the upstream repository using: +``` +git remote add upstream https://github.com/dromara/hertzbeat.git #Bind the remote warehouse, if it has been executed, it does not need to be executed again +git checkout master +git pull upstream master +``` + +By following these steps, you become a HertzBeat contributor. Repeat the process, stay active in the community, and persist—you can become a Committer -> PMC! + +## Final Thoughts + +When it comes to programmers, the stereotype often includes plaid shirts, being earnest and a bit dull—we are often the ones behind the scenes. The open source community hopes that through open source, developers can have the opportunity to step into the spotlight and showcase themselves, using open source code to "package" themselves. Imagine the project you contributed to being used or deployed by thousands of teams—that’s really cool. When interviewing, you won’t need to spend a month cramming stereotyped essay writing to prove yourself to the interviewer; your Github/Gitee account will already show them that you are awesome and reliable! \ No newline at end of file diff --git a/src/blog/dromara-warmflow-assignee-guide.md b/src/blog/dromara-warmflow-assignee-guide.md new file mode 100644 index 0000000000..0011ff60f7 --- /dev/null +++ b/src/blog/dromara-warmflow-assignee-guide.md @@ -0,0 +1,55 @@ +--- +title: Dromara WarmFlow Workflow Dynamically Assigning Approvers +author: WarmFlow +date: 2024-12-03 +cover: /assets/img/blog/dromara-warmflow-assignee-guide-0.webp +head: + - - meta + - name: Blog +--- + +### Background: + +The assignee of an approval task is usually predefined in the process designer. But what if you need to assign an approver dynamically during the process? This is where Dromara's WarmFlow workflow comes into play. It effectively addresses this issue through its assignee variable expressions. + +##### Solution Approach + +* 1. During process design, configure the assignee variable expression `${handler1}` for nodes that require dynamic assignee assignment. +* 2. When the previous task is being processed, pass the value of `${handler1}` in the process variables. +* 3. Once the task is completed, the current node task will be generated, achieving dynamic assignment. + +![](/assets/img/blog/dromara-warmflow-assignee-guide-0.webp) + +Backend code to set variables: + +```java +// Process variables +Map variable = new HashMap<>(); +variable.put("handler1", "100"); +flowParams.variable(variable); + +Instance instance = insService.skipByInsId(testLeave.getInstanceId(), flowParams); +``` + +##### Advanced Usage + +* Supports dynamically assigning a group of people. +* Supports SpEL expressions. +* Supports expression extensions. + +Change the code from `"100"` to `Arrays.asList(4, "5", 100L)` to dynamically assign a group of people: + +```java +// Process variables +Map variable = new HashMap<>(); +variable.put("handler1", Arrays.asList(4, "5", 100L)); +flowParams.variable(variable); + +Instance instance = insService.skipByInsId(testLeave.getInstanceId(), flowParams); +``` + +##### Conclusion + +Doesn't WarmFlow workflow make it easy to manage your approval processes? Don't hesitate to give it a try! ^v^ + +Additionally, the Dromara organization offers many other useful projects. Feel free to explore them! \ No newline at end of file diff --git a/src/blog/hmily_current.md b/src/blog/hmily_current.md index e2206ae8f5..877c267f0b 100644 --- a/src/blog/hmily_current.md +++ b/src/blog/hmily_current.md @@ -1,172 +1,172 @@ ---- -title: Hmily:Easy Handle Highly Concurrent Distributed Transactions -author: xiaoyu -date: 2018-11-14 -tag: - - hmily -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: Blog ---- - -### Handling Highly Concurrent Transactions with Hmily - -Let's start with a quick advertisement. Hmily is participating in the Open Source China Annual Popularity Poll at [this link](https://www.oschina.net/project/top_cn_2018?origin=zhzd). Click the link, search for Hmily, and cast your vote. It's the second one in the 11th row. Thank you, everyone, for your support! Feel free to follow us and submit pull requests to make Hmily even better and more perfect. - -GitHub: [https://github.com/yu199195/hmily] -Gitee: [https://gitee.com/dromara/hmily] - -Now, let's address some questions from the community and clear up some areas of confusion. - -### 1. Performance Issues with Hmily? - -Answer: Hmily uses AOP aspect to bind with your RPC methods. It essentially saves logs (using asynchronous disruptor) and passes some parameters when you make an RPC call. Both confirm and cancel operations are now asynchronous, so its performance is similar to that of your RPC. Remember, Hmily doesn't create transactions; it's just a facilitator for distributed transactions. In the past, Hmily had a performance drop due to a locking mechanism in the AOP aspect, as discussed in an article by the Spring Cloud China community. This issue has been resolved now, and everything is asynchronous. The testing scenario was somewhat unreasonable, as it was a demo under default configurations. In the following sections, I'll explain how to improve Hmily's performance. - -### 2. How Does Hmily Handle RPC Call Timeouts? - -Answer: In a distributed environment, when you invoke an RPC method and it exceeds the timeout, let's say the Dubbo timeout is set to 100ms but your method takes 140ms, your method has succeeded, but for the caller, it appears as a failure. In this case, a rollback is needed. Hmily's approach is as follows: if the caller thinks the operation failed due to a timeout, it won't include the operation in the rollback chain. So, for an RPC interface that times out, it handles its own rollback. A scheduled task handles the rollback because the log is in the "try" phase, and the cancel method is invoked for rollback, achieving eventual consistency. - -### 3. Hmily's Support for Cluster Deployment and Log Recovery in Cluster Environments? - -Answer: Hmily is naturally compatible with cluster deployment as it's bound to your application via AOP aspect. Log recovery in a clustered environment is rarely an issue, unless your entire cluster crashes simultaneously. If your cluster goes down simultaneously and recovers, the logs have a version field; only those that are successfully updated undergo recovery. - -### 4. Hmily Asynchronously Saves Logs, What If a Drastic Event Occurs Before Logging? - -Answer: If you're having such thoughts, you probably haven't delved into the source code or didn't fully understand it. In the AOP aspect, logs are first saved asynchronously, with the state being PRE_TRY. After the try phase execution completes, it's updated to "try". Even in scenarios like sudden JVM exit or power loss right after this line of code is executed, the mechanism stands. Even if you're testing scenarios like stopping the JVM abruptly or killing the service, keep in mind that Hmily can't account for all accidental events. It's best not to put excessive effort into solving these rare occurrences; the ideal solution is to not focus on them. - -### Hmily Configuration Tuning for High-Concurrency Scenarios - -The following parameters can be optimized for high-concurrency scenarios in Hmily: - -- `serializer`: I recommend using Kryo. Hmily also supports Hessian, Protostuff, and JDK serialization. In our tests, the performance was in the order: Kryo > Hessian > Protostuff > JDK. - -- `recoverDelayTime`: Delay time for the recovery task (in seconds, default is 120). This parameter should be greater than the timeout set for your RPC calls. - -- `retryMax`: Maximum retry count (default is 3). When your service goes down, the recovery task will execute your cancel or confirm method for a maximum of retryMax times. - -- `bufferSize`: Disruptor's buffer size. Increase this for high-concurrency scenarios; it should be a power of 2. - -- `consumerThreads`: Number of threads for Disruptor's consumer. Increase this for high-concurrency scenarios. - -- `started`: Set this to true for the initiator side and false for the participant side. - -- `asyncThreads`: Size of the thread pool for asynchronous execution of confirm and cancel methods. Increase this for high-concurrency scenarios. - -Next, the most important aspect: configuring the storage of transaction logs. In our stress tests, I recommend using MongoDB, where the performance ranked as follows: MongoDB > Redis Cluster > MySQL > ZooKeeper. - -- If you're using MongoDB for log storage, configure as follows: - -```xml - - - - - - - - - - - - - - - - - - - - - -``` - -- Here, I recommend using Kryo. Of course, Hmily also supports Hessian, Protostuff, and JDK serialization. In our tests, the performance was in the order: Kryo > Hessian > Protostuff > JDK. - -- recoverDelayTime :Delay time for the recovery task (in seconds, default is 120). This parameter should be greater than the timeout set for your RPC calls. - -- retryMax : Maximum retry count (default is 3). When your service goes down, the recovery task will execute your cancel or confirm method for a maximum of retryMax times. - -- Disruptor's buffer size. Increase this for high-concurrency scenarios; it should be a power of 2. - -- consumerThreads: Disruptor's consumer thread count. Increase this for high-concurrency scenarios. - -- started: Set this to true for the initiator side and false for the participant side. - -- asyncThreads: Size of the thread pool for asynchronous execution of confirm and cancel methods. Increase this for high-concurrency scenarios. - -- Next is the most important aspect: configuring the storage of transaction logs. In our stress tests, I recommend using MongoDB, where the performance ranked as follows: MongoDB > Redis Cluster > MySQL > ZooKeeper. - -- If you're using MongoDB for log storage, configure as follows: - -```xml - - - - - - - - - -``` - -- If you're using Redis for log storage, configure as follows: - - - Single node Redis: - -```xml - - - - - - - - -``` - -- Redis sentinel mode cluster: - -```xml - - - - - - - - - -``` - -- Redis cluster mode: - -```xml - - - - - - - - -``` - -- If you're using ZooKeeper for log storage, configure as follows: - -```xml - - - - - - - - -``` - -- The database configuration has been provided above, and I won't introduce the file-based storage approach. -- The above is the content shared today, an annotation, and a few configuration lines to easily handle high-concurrency distributed transactions! +--- +title: Hmily:Easy Handle Highly Concurrent Distributed Transactions +author: xiaoyu +date: 2018-11-14 +tag: + - hmily +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: Blog +--- + +### Handling Highly Concurrent Transactions with Hmily + +Let's start with a quick advertisement. Hmily is participating in the Open Source China Annual Popularity Poll at [this link](https://www.oschina.net/project/top_cn_2018?origin=zhzd). Click the link, search for Hmily, and cast your vote. It's the second one in the 11th row. Thank you, everyone, for your support! Feel free to follow us and submit pull requests to make Hmily even better and more perfect. + +GitHub: [https://github.com/yu199195/hmily] +Gitee: [https://gitee.com/dromara/hmily] + +Now, let's address some questions from the community and clear up some areas of confusion. + +### 1. Performance Issues with Hmily? + +Answer: Hmily uses AOP aspect to bind with your RPC methods. It essentially saves logs (using asynchronous disruptor) and passes some parameters when you make an RPC call. Both confirm and cancel operations are now asynchronous, so its performance is similar to that of your RPC. Remember, Hmily doesn't create transactions; it's just a facilitator for distributed transactions. In the past, Hmily had a performance drop due to a locking mechanism in the AOP aspect, as discussed in an article by the Spring Cloud China community. This issue has been resolved now, and everything is asynchronous. The testing scenario was somewhat unreasonable, as it was a demo under default configurations. In the following sections, I'll explain how to improve Hmily's performance. + +### 2. How Does Hmily Handle RPC Call Timeouts? + +Answer: In a distributed environment, when you invoke an RPC method and it exceeds the timeout, let's say the Dubbo timeout is set to 100ms but your method takes 140ms, your method has succeeded, but for the caller, it appears as a failure. In this case, a rollback is needed. Hmily's approach is as follows: if the caller thinks the operation failed due to a timeout, it won't include the operation in the rollback chain. So, for an RPC interface that times out, it handles its own rollback. A scheduled task handles the rollback because the log is in the "try" phase, and the cancel method is invoked for rollback, achieving eventual consistency. + +### 3. Hmily's Support for Cluster Deployment and Log Recovery in Cluster Environments? + +Answer: Hmily is naturally compatible with cluster deployment as it's bound to your application via AOP aspect. Log recovery in a clustered environment is rarely an issue, unless your entire cluster crashes simultaneously. If your cluster goes down simultaneously and recovers, the logs have a version field; only those that are successfully updated undergo recovery. + +### 4. Hmily Asynchronously Saves Logs, What If a Drastic Event Occurs Before Logging? + +Answer: If you're having such thoughts, you probably haven't delved into the source code or didn't fully understand it. In the AOP aspect, logs are first saved asynchronously, with the state being PRE_TRY. After the try phase execution completes, it's updated to "try". Even in scenarios like sudden JVM exit or power loss right after this line of code is executed, the mechanism stands. Even if you're testing scenarios like stopping the JVM abruptly or killing the service, keep in mind that Hmily can't account for all accidental events. It's best not to put excessive effort into solving these rare occurrences; the ideal solution is to not focus on them. + +### Hmily Configuration Tuning for High-Concurrency Scenarios + +The following parameters can be optimized for high-concurrency scenarios in Hmily: + +- `serializer`: I recommend using Kryo. Hmily also supports Hessian, Protostuff, and JDK serialization. In our tests, the performance was in the order: Kryo > Hessian > Protostuff > JDK. + +- `recoverDelayTime`: Delay time for the recovery task (in seconds, default is 120). This parameter should be greater than the timeout set for your RPC calls. + +- `retryMax`: Maximum retry count (default is 3). When your service goes down, the recovery task will execute your cancel or confirm method for a maximum of retryMax times. + +- `bufferSize`: Disruptor's buffer size. Increase this for high-concurrency scenarios; it should be a power of 2. + +- `consumerThreads`: Number of threads for Disruptor's consumer. Increase this for high-concurrency scenarios. + +- `started`: Set this to true for the initiator side and false for the participant side. + +- `asyncThreads`: Size of the thread pool for asynchronous execution of confirm and cancel methods. Increase this for high-concurrency scenarios. + +Next, the most important aspect: configuring the storage of transaction logs. In our stress tests, I recommend using MongoDB, where the performance ranked as follows: MongoDB > Redis Cluster > MySQL > ZooKeeper. + +- If you're using MongoDB for log storage, configure as follows: + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +- Here, I recommend using Kryo. Of course, Hmily also supports Hessian, Protostuff, and JDK serialization. In our tests, the performance was in the order: Kryo > Hessian > Protostuff > JDK. + +- recoverDelayTime :Delay time for the recovery task (in seconds, default is 120). This parameter should be greater than the timeout set for your RPC calls. + +- retryMax : Maximum retry count (default is 3). When your service goes down, the recovery task will execute your cancel or confirm method for a maximum of retryMax times. + +- Disruptor's buffer size. Increase this for high-concurrency scenarios; it should be a power of 2. + +- consumerThreads: Disruptor's consumer thread count. Increase this for high-concurrency scenarios. + +- started: Set this to true for the initiator side and false for the participant side. + +- asyncThreads: Size of the thread pool for asynchronous execution of confirm and cancel methods. Increase this for high-concurrency scenarios. + +- Next is the most important aspect: configuring the storage of transaction logs. In our stress tests, I recommend using MongoDB, where the performance ranked as follows: MongoDB > Redis Cluster > MySQL > ZooKeeper. + +- If you're using MongoDB for log storage, configure as follows: + +```xml + + + + + + + + + +``` + +- If you're using Redis for log storage, configure as follows: + + - Single node Redis: + +```xml + + + + + + + + +``` + +- Redis sentinel mode cluster: + +```xml + + + + + + + + + +``` + +- Redis cluster mode: + +```xml + + + + + + + + +``` + +- If you're using ZooKeeper for log storage, configure as follows: + +```xml + + + + + + + + +``` + +- The database configuration has been provided above, and I won't introduce the file-based storage approach. +- The above is the content shared today, an annotation, and a few configuration lines to easily handle high-concurrency distributed transactions! diff --git a/src/blog/hmily_introduction.md b/src/blog/hmily_introduction.md index cb786963fc..56d650d482 100644 --- a/src/blog/hmily_introduction.md +++ b/src/blog/hmily_introduction.md @@ -1,628 +1,627 @@ ---- -title: Hmily:High-Performance Asynchronous Distributed Transaction TCC Framework -author: xiaoyu -date: 2018-09-25 -tag: - - hmily - - TCC -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: Blog ---- - -# Hmily Framework Features [https://github.com/yu199195/hmily] - -- Seamlessly integrates with Spring and Spring Boot. - -- Seamlessly integrates with Dubbo, Spring Cloud, Motan, and other RPC frameworks. - -- Supports various transaction log storage methods (Redis, MongoDB, MySQL, etc.). - -- Offers multiple serialization methods for different types of logs (Kryo, Protostuff, Hessian). - -- Provides automatic transaction recovery. - -- Supports embedded transaction dependency propagation. - -- Zero-intrusion code and flexible configuration. - -# Why is Hmily So High-Performance? - -### 1. Asynchronous Read/Write of Transaction Logs Using Disruptor (Disruptor is a Lock-Free, GC-Free Concurrency Framework) - -```java -package com.hmily.tcc.core.disruptor.publisher; - -import com.hmily.tcc.common.bean.entity.TccTransaction; -import com.hmily.tcc.common.enums.EventTypeEnum; -import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory; -import com.hmily.tcc.core.coordinator.CoordinatorService; -import com.hmily.tcc.core.disruptor.event.HmilyTransactionEvent; -import com.hmily.tcc.core.disruptor.factory.HmilyTransactionEventFactory; -import com.hmily.tcc.core.disruptor.handler.HmilyConsumerDataHandler; -import com.hmily.tcc.core.disruptor.translator.HmilyTransactionEventTranslator; -import com.lmax.disruptor.BlockingWaitStrategy; -import com.lmax.disruptor.IgnoreExceptionHandler; -import com.lmax.disruptor.RingBuffer; -import com.lmax.disruptor.dsl.Disruptor; -import com.lmax.disruptor.dsl.ProducerType; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * event publisher. - * - * @author xiaoyu(Myth) - */ -@Component -public class HmilyTransactionEventPublisher implements DisposableBean { - - private Disruptor disruptor; - - private final CoordinatorService coordinatorService; - - @Autowired - public HmilyTransactionEventPublisher(final CoordinatorService coordinatorService) { - this.coordinatorService = coordinatorService; - } - - /** - * disruptor start. - * - * @param bufferSize this is disruptor buffer size. - * @param threadSize this is disruptor consumer thread size. - */ - public void start(final int bufferSize, final int threadSize) { - disruptor = new Disruptor<>(new HmilyTransactionEventFactory(), bufferSize, r -> { - AtomicInteger index = new AtomicInteger(1); - return new Thread(null, r, "disruptor-thread-" + index.getAndIncrement()); - }, ProducerType.MULTI, new BlockingWaitStrategy()); - - final Executor executor = new ThreadPoolExecutor(threadSize, threadSize, 0, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - HmilyThreadFactory.create("hmily-log-disruptor", false), - new ThreadPoolExecutor.AbortPolicy()); - - HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize]; - for (int i = 0; i < threadSize; i++) { - consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService); - } - disruptor.handleEventsWithWorkerPool(consumers); - disruptor.setDefaultExceptionHandler(new IgnoreExceptionHandler()); - disruptor.start(); - } - - /** - * publish disruptor event. - * - * @param tccTransaction {@linkplain com.hmily.tcc.common.bean.entity.TccTransaction } - * @param type {@linkplain EventTypeEnum} - */ - public void publishEvent(final TccTransaction tccTransaction, final int type) { - final RingBuffer ringBuffer = disruptor.getRingBuffer(); - ringBuffer.publishEvent(new HmilyTransactionEventTranslator(type), tccTransaction); - } - - @Override - public void destroy() { - disruptor.shutdown(); - } -} -``` - -- The default value of bufferSize here is 4094 \* 4, which can be configured based on the user's requirements. - -```java - - HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize]; - for (int i = 0; i < threadSize; i++) { - consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService); - } - disruptor.handleEventsWithWorkerPool(consumers); -``` - -- Multiple consumers are employed here to process tasks in the queue. - -### 2.Asynchronous Execution of Confirm and Cancel Methods - -```java -package com.hmily.tcc.core.service.handler; - -import com.hmily.tcc.common.bean.context.TccTransactionContext; -import com.hmily.tcc.common.bean.entity.TccTransaction; -import com.hmily.tcc.common.enums.TccActionEnum; -import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory; -import com.hmily.tcc.core.service.HmilyTransactionHandler; -import com.hmily.tcc.core.service.executor.HmilyTransactionExecutor; -import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * this is transaction starter. - * - * @author xiaoyu - */ -@Component -public class StarterHmilyTransactionHandler implements HmilyTransactionHandler { - - private static final int MAX_THREAD = Runtime.getRuntime().availableProcessors() << 1; - - private final HmilyTransactionExecutor hmilyTransactionExecutor; - - private final Executor executor = new ThreadPoolExecutor(MAX_THREAD, MAX_THREAD, 0, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - HmilyThreadFactory.create("hmily-execute", false), - new ThreadPoolExecutor.AbortPolicy()); - - @Autowired - public StarterHmilyTransactionHandler(final HmilyTransactionExecutor hmilyTransactionExecutor) { - this.hmilyTransactionExecutor = hmilyTransactionExecutor; - } - - @Override - public Object handler(final ProceedingJoinPoint point, final TccTransactionContext context) - throws Throwable { - Object returnValue; - try { - TccTransaction tccTransaction = hmilyTransactionExecutor.begin(point); - try { - //execute try - returnValue = point.proceed(); - tccTransaction.setStatus(TccActionEnum.TRYING.getCode()); - hmilyTransactionExecutor.updateStatus(tccTransaction); - } catch (Throwable throwable) { - //if exception ,execute cancel - final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction(); - executor.execute(() -> hmilyTransactionExecutor - .cancel(currentTransaction)); - throw throwable; - } - //execute confirm - final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction(); - executor.execute(() -> hmilyTransactionExecutor.confirm(currentTransaction)); - } finally { - hmilyTransactionExecutor.remove(); - } - return returnValue; - } -} -``` - -- When an exception occurs in the try method's AOP aspect, the cancel method is executed asynchronously using a thread pool. If there is no exception, the confirm method is executed. - -### A question might arise: What if the cancel or confirm methods themselves throw exceptions? - -Answer: This scenario is quite rare because you've just finished executing the try phase moments ago. Moreover, if such an exception arises, the framework has a built-in scheduling thread pool for recovery, so there's no need to worry. - -### Another question might arise: What if there's an exception during log storage? - -Answer: First, this is an edge case; second, the log configuration parameters are required during framework startup. Even if log storage fails during runtime, the framework will utilize cached logs, ensuring correct program execution. Lastly, if log storage fails and the system crashes under extremely rare circumstances, well, congratulations, you can consider buying a lottery ticket. The best solution is to not overly concern yourself with such a scenario. - -### 3.Use of ThreadLocal Cache - -```java - /** - * transaction begin. - * - * @param point cut point. - * @return TccTransaction - */ - public TccTransaction begin(final ProceedingJoinPoint point) { - LogUtil.debug(LOGGER, () -> "......hmily transaction!start...."); - //build tccTransaction - final TccTransaction tccTransaction = buildTccTransaction(point, TccRoleEnum.START.getCode(), null); - //save tccTransaction in threadLocal - CURRENT.set(tccTransaction); - //publishEvent - hmilyTransactionEventPublisher.publishEvent(tccTransaction, EventTypeEnum.SAVE.getCode()); - //set TccTransactionContext this context transfer remote - TccTransactionContext context = new TccTransactionContext(); - //set action is try - context.setAction(TccActionEnum.TRYING.getCode()); - context.setTransId(tccTransaction.getTransId()); - context.setRole(TccRoleEnum.START.getCode()); - TransactionContextLocal.getInstance().set(context); - return tccTransaction; - } -``` - -- It's important to understand that the ThreadLocal cache holds transaction information for the initiator method. RPC calls form a chain of invocation, ensuring proper storage. - -```java - -/** - * add participant. - * - * @param participant {@linkplain Participant} - */ - public void enlistParticipant(final Participant participant) { - if (Objects.isNull(participant)) { - return; - } - Optional.ofNullable(getCurrentTransaction()) - .ifPresent(c -> { - c.registerParticipant(participant); - updateParticipant(c); - }); - } -``` - -### 4.Usage of Guava Cache - -```java -package com.hmily.tcc.core.cache; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.cache.Weigher; -import com.hmily.tcc.common.bean.entity.TccTransaction; -import com.hmily.tcc.core.coordinator.CoordinatorService; -import com.hmily.tcc.core.helper.SpringBeanUtils; -import org.apache.commons.lang3.StringUtils; - -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -/** - * use google guava cache. - * @author xiaoyu - */ -public final class TccTransactionCacheManager { - - private static final int MAX_COUNT = 10000; - - private static final LoadingCache LOADING_CACHE = - CacheBuilder.newBuilder().maximumWeight(MAX_COUNT) - .weigher((Weigher) (string, tccTransaction) -> getSize()) - .build(new CacheLoader() { - @Override - public TccTransaction load(final String key) { - return cacheTccTransaction(key); - } - }); - - private static CoordinatorService coordinatorService = SpringBeanUtils.getInstance().getBean(CoordinatorService.class); - - private static final TccTransactionCacheManager TCC_TRANSACTION_CACHE_MANAGER = new TccTransactionCacheManager(); - - private TccTransactionCacheManager() { - - } - - /** - * TccTransactionCacheManager. - * - * @return TccTransactionCacheManager - */ - public static TccTransactionCacheManager getInstance() { - return TCC_TRANSACTION_CACHE_MANAGER; - } - - private static int getSize() { - return (int) LOADING_CACHE.size(); - } - - private static TccTransaction cacheTccTransaction(final String key) { - return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction()); - } - - /** - * cache tccTransaction. - * - * @param tccTransaction {@linkplain TccTransaction} - */ - public void cacheTccTransaction(final TccTransaction tccTransaction) { - LOADING_CACHE.put(tccTransaction.getTransId(), tccTransaction); - } - - /** - * acquire TccTransaction. - * - * @param key this guava key. - * @return {@linkplain TccTransaction} - */ - public TccTransaction getTccTransaction(final String key) { - try { - return LOADING_CACHE.get(key); - } catch (ExecutionException e) { - return new TccTransaction(); - } - } - - /** - * remove guava cache by key. - * @param key guava cache key. - */ - public void removeByKey(final String key) { - if (StringUtils.isNotEmpty(key)) { - LOADING_CACHE.invalidate(key); - } - } - -} -``` - -- Among the participants, we used ThreadLocal, but why don't we use it among the participants? - There are actually two reasons: First, because try and confirm will not be in the same thread, which will cause ThreadLocal to fail. When considering RPC clusters, it may be load balanced to different machines. Here is a detail: - -```java - private static TccTransaction cacheTccTransaction(final String key) { - return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction()); - } -``` - -When the Guava Cache doesn't have a particular entry, it queries the log for that entry, ensuring support for clustered environments. - -### These four aspects collectively make Hmily an asynchronous, high-performance distributed TCC framework. - -### How to Use Hmily?(https://github.com/yu199195/hmily/tree/master/hmily-tcc-demo) - -Due to package naming issues, the framework package hasn't been uploaded to the Maven Central Repository. Therefore, users need to clone the code, compile it, and deploy it to their private repository. - -### 1.For Dubbo Users - -- Include in your API project: - -```xml - - - com.hmily.tcc - hmily-tcc-annotation - {you version} - -``` - -- Include in your service provider project: - -```xml - - com.hmily.tcc - hmily-tcc-dubbo - {you version} - -``` - -- Configure the startup bean in your XML configuration. - -```xml - - - - - - - - - - - - - - - - - - - - - - -``` - -- Of course there are many configuration properties, here I only gave a demo. For details, you can refer to this class: - -```java -package com.hmily.tcc.common.config; - -import com.hmily.tcc.common.enums.RepositorySupportEnum; -import lombok.Data; - -/** - * hmily config. - * - * @author xiaoyu - */ -@Data -public class TccConfig { - - - /** - * Resource suffix this parameter please fill in about is the transaction store path. - * If it's a table store this is a table suffix, it's stored the same way. - * If this parameter is not filled in, the applicationName of the application is retrieved by default - */ - private String repositorySuffix; - - /** - * log serializer. - * {@linkplain com.hmily.tcc.common.enums.SerializeEnum} - */ - private String serializer = "kryo"; - - /** - * scheduledPool Thread size. - */ - private int scheduledThreadMax = Runtime.getRuntime().availableProcessors() << 1; - - /** - * scheduledPool scheduledDelay unit SECONDS. - */ - private int scheduledDelay = 60; - - /** - * retry max. - */ - private int retryMax = 3; - - /** - * recoverDelayTime Unit seconds - * (note that this time represents how many seconds after the local transaction was created before execution). - */ - private int recoverDelayTime = 60; - - /** - * Parameters when participants perform their own recovery. - * 1.such as RPC calls time out - * 2.such as the starter down machine - */ - private int loadFactor = 2; - - /** - * repositorySupport. - * {@linkplain RepositorySupportEnum} - */ - private String repositorySupport = "db"; - - /** - * disruptor bufferSize. - */ - private int bufferSize = 4096 * 2 * 2; - - /** - * this is disruptor consumerThreads. - */ - private int consumerThreads = Runtime.getRuntime().availableProcessors() << 1; - - /** - * db config. - */ - private TccDbConfig tccDbConfig; - - /** - * mongo config. - */ - private TccMongoConfig tccMongoConfig; - - /** - * redis config. - */ - private TccRedisConfig tccRedisConfig; - - /** - * zookeeper config. - */ - private TccZookeeperConfig tccZookeeperConfig; - - /** - * file config. - */ - private TccFileConfig tccFileConfig; - -} -``` - -### SpringCloud Users - -```xml - - com.hmily.tcc - hmily-tcc-springcloud - {you version} - -``` - -### Motan Users - -```xml - - com.hmily.tcc - hmily-tcc-motan - {you version} - -``` - -### hmily-spring-boot-start - this makes it even easier, you just need to import different jar packages according to your RPC framework. - -- For Dubbo users, add: - -```xml - - com.hmily.tcc - hmily-tcc-spring-boot-starter-dubbo - ${your version} - -``` - -- For Spring Cloud users, add: - -```xml - - com.hmily.tcc - hmily-tcc-spring-boot-starter-springcloud - ${your version} - -``` - -- For Motan users, add: - -```xml - - com.hmily.tcc - hmily-tcc-spring-boot-starter-motan - ${your version} - -``` - -- Next, configure the settings in your YML file: - -```yml -hmily: - tcc: - serializer: kryo - recoverDelayTime: 128 - retryMax: 3 - scheduledDelay: 128 - scheduledThreadMax: 10 - repositorySupport: db - tccDbConfig: - driverClassName: com.mysql.jdbc.Driver - url: jdbc:mysql://192.168.1.98:3306/tcc?useUnicode=true&characterEncoding=utf8 - username: root - password: 123456 - - #repositorySupport : redis - #tccRedisConfig: - #masterName: mymaster - #sentinel : true - #sentinelUrl : 192.168.1.91:26379;192.168.1.92:26379;192.168.1.93:26379 - #password : foobaredbbexONE123 - - # repositorySupport : zookeeper - # host : 92.168.1.73:2181 - # sessionTimeOut : 100000 - # rootPath : /tcc - - # repositorySupport : mongodb - # mongoDbUrl : 192.168.1.68:27017 - # mongoDbName : happylife - # mongoUserName : xiaoyu - # mongoUserPwd : 123456 - - # repositorySupport : file - # path : /account - # prefix : account -``` - -- Using Hmily is simple. Just annotate your interface methods with @Tcc, and you're good to go. - -- Please note that due to space constraints, some intricate details have been summarized. For those interested, you can star and fork the project on GitHub and join the WeChat group or QQ group for discussions. - -- GitHub repository: https://github.com/yu199195/hmily - -- Thank you once again! If you're interested, you're welcome to provide any valuable PR contributions." +--- +title: Hmily:High-Performance Asynchronous Distributed Transaction TCC Framework +author: xiaoyu +date: 2018-09-25 +tag: + - hmily + - TCC +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: Blog +--- + +# Hmily Framework Features [https://github.com/yu199195/hmily] + +- Seamlessly integrates with Spring and Spring Boot. + +- Seamlessly integrates with Dubbo, Spring Cloud, Motan, and other RPC frameworks. + +- Supports various transaction log storage methods (Redis, MongoDB, MySQL, etc.). + +- Offers multiple serialization methods for different types of logs (Kryo, Protostuff, Hessian). + +- Provides automatic transaction recovery. + +- Supports embedded transaction dependency propagation. + +- Zero-intrusion code and flexible configuration. + +# Why is Hmily So High-Performance? + +### 1. Asynchronous Read/Write of Transaction Logs Using Disruptor (Disruptor is a Lock-Free, GC-Free Concurrency Framework) + +```java +package com.hmily.tcc.core.disruptor.publisher; + +import com.hmily.tcc.common.bean.entity.TccTransaction; +import com.hmily.tcc.common.enums.EventTypeEnum; +import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory; +import com.hmily.tcc.core.coordinator.CoordinatorService; +import com.hmily.tcc.core.disruptor.event.HmilyTransactionEvent; +import com.hmily.tcc.core.disruptor.factory.HmilyTransactionEventFactory; +import com.hmily.tcc.core.disruptor.handler.HmilyConsumerDataHandler; +import com.hmily.tcc.core.disruptor.translator.HmilyTransactionEventTranslator; +import com.lmax.disruptor.BlockingWaitStrategy; +import com.lmax.disruptor.IgnoreExceptionHandler; +import com.lmax.disruptor.RingBuffer; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.dsl.ProducerType; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * event publisher. + * + * @author xiaoyu(Myth) + */ +@Component +public class HmilyTransactionEventPublisher implements DisposableBean { + + private Disruptor disruptor; + + private final CoordinatorService coordinatorService; + + @Autowired + public HmilyTransactionEventPublisher(final CoordinatorService coordinatorService) { + this.coordinatorService = coordinatorService; + } + + /** + * disruptor start. + * + * @param bufferSize this is disruptor buffer size. + * @param threadSize this is disruptor consumer thread size. + */ + public void start(final int bufferSize, final int threadSize) { + disruptor = new Disruptor<>(new HmilyTransactionEventFactory(), bufferSize, r -> { + AtomicInteger index = new AtomicInteger(1); + return new Thread(null, r, "disruptor-thread-" + index.getAndIncrement()); + }, ProducerType.MULTI, new BlockingWaitStrategy()); + + final Executor executor = new ThreadPoolExecutor(threadSize, threadSize, 0, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + HmilyThreadFactory.create("hmily-log-disruptor", false), + new ThreadPoolExecutor.AbortPolicy()); + + HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize]; + for (int i = 0; i < threadSize; i++) { + consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService); + } + disruptor.handleEventsWithWorkerPool(consumers); + disruptor.setDefaultExceptionHandler(new IgnoreExceptionHandler()); + disruptor.start(); + } + + /** + * publish disruptor event. + * + * @param tccTransaction {@linkplain com.hmily.tcc.common.bean.entity.TccTransaction } + * @param type {@linkplain EventTypeEnum} + */ + public void publishEvent(final TccTransaction tccTransaction, final int type) { + final RingBuffer ringBuffer = disruptor.getRingBuffer(); + ringBuffer.publishEvent(new HmilyTransactionEventTranslator(type), tccTransaction); + } + + @Override + public void destroy() { + disruptor.shutdown(); + } +} +``` + +- The default value of bufferSize here is 4094 \* 4, which can be configured based on the user's requirements. + +```java + + HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize]; + for (int i = 0; i < threadSize; i++) { + consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService); + } + disruptor.handleEventsWithWorkerPool(consumers); +``` + +- Multiple consumers are employed here to process tasks in the queue. + +### 2.Asynchronous Execution of Confirm and Cancel Methods + +```java +package com.hmily.tcc.core.service.handler; + +import com.hmily.tcc.common.bean.context.TccTransactionContext; +import com.hmily.tcc.common.bean.entity.TccTransaction; +import com.hmily.tcc.common.enums.TccActionEnum; +import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory; +import com.hmily.tcc.core.service.HmilyTransactionHandler; +import com.hmily.tcc.core.service.executor.HmilyTransactionExecutor; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * this is transaction starter. + * + * @author xiaoyu + */ +@Component +public class StarterHmilyTransactionHandler implements HmilyTransactionHandler { + + private static final int MAX_THREAD = Runtime.getRuntime().availableProcessors() << 1; + + private final HmilyTransactionExecutor hmilyTransactionExecutor; + + private final Executor executor = new ThreadPoolExecutor(MAX_THREAD, MAX_THREAD, 0, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + HmilyThreadFactory.create("hmily-execute", false), + new ThreadPoolExecutor.AbortPolicy()); + + @Autowired + public StarterHmilyTransactionHandler(final HmilyTransactionExecutor hmilyTransactionExecutor) { + this.hmilyTransactionExecutor = hmilyTransactionExecutor; + } + + @Override + public Object handler(final ProceedingJoinPoint point, final TccTransactionContext context) + throws Throwable { + Object returnValue; + try { + TccTransaction tccTransaction = hmilyTransactionExecutor.begin(point); + try { + //execute try + returnValue = point.proceed(); + tccTransaction.setStatus(TccActionEnum.TRYING.getCode()); + hmilyTransactionExecutor.updateStatus(tccTransaction); + } catch (Throwable throwable) { + //if exception ,execute cancel + final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction(); + executor.execute(() -> hmilyTransactionExecutor + .cancel(currentTransaction)); + throw throwable; + } + //execute confirm + final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction(); + executor.execute(() -> hmilyTransactionExecutor.confirm(currentTransaction)); + } finally { + hmilyTransactionExecutor.remove(); + } + return returnValue; + } +} +``` + +- When an exception occurs in the try method's AOP aspect, the cancel method is executed asynchronously using a thread pool. If there is no exception, the confirm method is executed. + +### A question might arise: What if the cancel or confirm methods themselves throw exceptions? + +Answer: This scenario is quite rare because you've just finished executing the try phase moments ago. Moreover, if such an exception arises, the framework has a built-in scheduling thread pool for recovery, so there's no need to worry. + +### Another question might arise: What if there's an exception during log storage? + +Answer: First, this is an edge case; second, the log configuration parameters are required during framework startup. Even if log storage fails during runtime, the framework will utilize cached logs, ensuring correct program execution. Lastly, if log storage fails and the system crashes under extremely rare circumstances, well, congratulations, you can consider buying a lottery ticket. The best solution is to not overly concern yourself with such a scenario. + +### 3.Use of ThreadLocal Cache + +```java + /** + * transaction begin. + * + * @param point cut point. + * @return TccTransaction + */ + public TccTransaction begin(final ProceedingJoinPoint point) { + LogUtil.debug(LOGGER, () -> "......hmily transaction!start...."); + //build tccTransaction + final TccTransaction tccTransaction = buildTccTransaction(point, TccRoleEnum.START.getCode(), null); + //save tccTransaction in threadLocal + CURRENT.set(tccTransaction); + //publishEvent + hmilyTransactionEventPublisher.publishEvent(tccTransaction, EventTypeEnum.SAVE.getCode()); + //set TccTransactionContext this context transfer remote + TccTransactionContext context = new TccTransactionContext(); + //set action is try + context.setAction(TccActionEnum.TRYING.getCode()); + context.setTransId(tccTransaction.getTransId()); + context.setRole(TccRoleEnum.START.getCode()); + TransactionContextLocal.getInstance().set(context); + return tccTransaction; + } +``` + +- It's important to understand that the ThreadLocal cache holds transaction information for the initiator method. RPC calls form a chain of invocation, ensuring proper storage. + +```java + +/** + * add participant. + * + * @param participant {@linkplain Participant} + */ + public void enlistParticipant(final Participant participant) { + if (Objects.isNull(participant)) { + return; + } + Optional.ofNullable(getCurrentTransaction()) + .ifPresent(c -> { + c.registerParticipant(participant); + updateParticipant(c); + }); + } +``` + +### 4.Usage of Guava Cache + +```java +package com.hmily.tcc.core.cache; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.Weigher; +import com.hmily.tcc.common.bean.entity.TccTransaction; +import com.hmily.tcc.core.coordinator.CoordinatorService; +import com.hmily.tcc.core.helper.SpringBeanUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +/** + * use google guava cache. + * @author xiaoyu + */ +public final class TccTransactionCacheManager { + + private static final int MAX_COUNT = 10000; + + private static final LoadingCache LOADING_CACHE = + CacheBuilder.newBuilder().maximumWeight(MAX_COUNT) + .weigher((Weigher) (string, tccTransaction) -> getSize()) + .build(new CacheLoader() { + @Override + public TccTransaction load(final String key) { + return cacheTccTransaction(key); + } + }); + + private static CoordinatorService coordinatorService = SpringBeanUtils.getInstance().getBean(CoordinatorService.class); + + private static final TccTransactionCacheManager TCC_TRANSACTION_CACHE_MANAGER = new TccTransactionCacheManager(); + + private TccTransactionCacheManager() { + + } + + /** + * TccTransactionCacheManager. + * + * @return TccTransactionCacheManager + */ + public static TccTransactionCacheManager getInstance() { + return TCC_TRANSACTION_CACHE_MANAGER; + } + + private static int getSize() { + return (int) LOADING_CACHE.size(); + } + + private static TccTransaction cacheTccTransaction(final String key) { + return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction()); + } + + /** + * cache tccTransaction. + * + * @param tccTransaction {@linkplain TccTransaction} + */ + public void cacheTccTransaction(final TccTransaction tccTransaction) { + LOADING_CACHE.put(tccTransaction.getTransId(), tccTransaction); + } + + /** + * acquire TccTransaction. + * + * @param key this guava key. + * @return {@linkplain TccTransaction} + */ + public TccTransaction getTccTransaction(final String key) { + try { + return LOADING_CACHE.get(key); + } catch (ExecutionException e) { + return new TccTransaction(); + } + } + + /** + * remove guava cache by key. + * @param key guava cache key. + */ + public void removeByKey(final String key) { + if (StringUtils.isNotEmpty(key)) { + LOADING_CACHE.invalidate(key); + } + } + +} +``` + +- Among the participants, we used ThreadLocal, but why don't we use it among the participants? + There are actually two reasons: First, because try and confirm will not be in the same thread, which will cause ThreadLocal to fail. When considering RPC clusters, it may be load balanced to different machines. Here is a detail: + +```java + private static TccTransaction cacheTccTransaction(final String key) { + return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction()); + } +``` + +When the Guava Cache doesn't have a particular entry, it queries the log for that entry, ensuring support for clustered environments. + +### These four aspects collectively make Hmily an asynchronous, high-performance distributed TCC framework. + +### How to Use Hmily?(https://github.com/yu199195/hmily/tree/master/hmily-tcc-demo) + +Due to package naming issues, the framework package hasn't been uploaded to the Maven Central Repository. Therefore, users need to clone the code, compile it, and deploy it to their private repository. + +### 1.For Dubbo Users + +- Include in your API project: + +```xml + + com.hmily.tcc + hmily-tcc-annotation + {you version} + +``` + +- Include in your service provider project: + +```xml + + com.hmily.tcc + hmily-tcc-dubbo + {you version} + +``` + +- Configure the startup bean in your XML configuration. + +```xml + + + + + + + + + + + + + + + + + + + + + + +``` + +- Of course there are many configuration properties, here I only gave a demo. For details, you can refer to this class: + +```java +package com.hmily.tcc.common.config; + +import com.hmily.tcc.common.enums.RepositorySupportEnum; +import lombok.Data; + +/** + * hmily config. + * + * @author xiaoyu + */ +@Data +public class TccConfig { + + + /** + * Resource suffix this parameter please fill in about is the transaction store path. + * If it's a table store this is a table suffix, it's stored the same way. + * If this parameter is not filled in, the applicationName of the application is retrieved by default + */ + private String repositorySuffix; + + /** + * log serializer. + * {@linkplain com.hmily.tcc.common.enums.SerializeEnum} + */ + private String serializer = "kryo"; + + /** + * scheduledPool Thread size. + */ + private int scheduledThreadMax = Runtime.getRuntime().availableProcessors() << 1; + + /** + * scheduledPool scheduledDelay unit SECONDS. + */ + private int scheduledDelay = 60; + + /** + * retry max. + */ + private int retryMax = 3; + + /** + * recoverDelayTime Unit seconds + * (note that this time represents how many seconds after the local transaction was created before execution). + */ + private int recoverDelayTime = 60; + + /** + * Parameters when participants perform their own recovery. + * 1.such as RPC calls time out + * 2.such as the starter down machine + */ + private int loadFactor = 2; + + /** + * repositorySupport. + * {@linkplain RepositorySupportEnum} + */ + private String repositorySupport = "db"; + + /** + * disruptor bufferSize. + */ + private int bufferSize = 4096 * 2 * 2; + + /** + * this is disruptor consumerThreads. + */ + private int consumerThreads = Runtime.getRuntime().availableProcessors() << 1; + + /** + * db config. + */ + private TccDbConfig tccDbConfig; + + /** + * mongo config. + */ + private TccMongoConfig tccMongoConfig; + + /** + * redis config. + */ + private TccRedisConfig tccRedisConfig; + + /** + * zookeeper config. + */ + private TccZookeeperConfig tccZookeeperConfig; + + /** + * file config. + */ + private TccFileConfig tccFileConfig; + +} +``` + +### SpringCloud Users + +```xml + + com.hmily.tcc + hmily-tcc-springcloud + {you version} + +``` + +### Motan Users + +```xml + + com.hmily.tcc + hmily-tcc-motan + {you version} + +``` + +### hmily-spring-boot-start - this makes it even easier, you just need to import different jar packages according to your RPC framework. + +- For Dubbo users, add: + +```xml + + com.hmily.tcc + hmily-tcc-spring-boot-starter-dubbo + ${your version} + +``` + +- For Spring Cloud users, add: + +```xml + + com.hmily.tcc + hmily-tcc-spring-boot-starter-springcloud + ${your version} + +``` + +- For Motan users, add: + +```xml + + com.hmily.tcc + hmily-tcc-spring-boot-starter-motan + ${your version} + +``` + +- Next, configure the settings in your YML file: + +```yml +hmily: + tcc: + serializer: kryo + recoverDelayTime: 128 + retryMax: 3 + scheduledDelay: 128 + scheduledThreadMax: 10 + repositorySupport: db + tccDbConfig: + driverClassName: com.mysql.jdbc.Driver + url: jdbc:mysql://192.168.1.98:3306/tcc?useUnicode=true&characterEncoding=utf8 + username: root + password: 123456 + + #repositorySupport : redis + #tccRedisConfig: + #masterName: mymaster + #sentinel : true + #sentinelUrl : 192.168.1.91:26379;192.168.1.92:26379;192.168.1.93:26379 + #password : foobaredbbexONE123 + + # repositorySupport : zookeeper + # host : 92.168.1.73:2181 + # sessionTimeOut : 100000 + # rootPath : /tcc + + # repositorySupport : mongodb + # mongoDbUrl : 192.168.1.68:27017 + # mongoDbName : happylife + # mongoUserName : xiaoyu + # mongoUserPwd : 123456 + + # repositorySupport : file + # path : /account + # prefix : account +``` + +- Using Hmily is simple. Just annotate your interface methods with @Tcc, and you're good to go. + +- Please note that due to space constraints, some intricate details have been summarized. For those interested, you can star and fork the project on GitHub and join the WeChat group or QQ group for discussions. + +- GitHub repository: https://github.com/yu199195/hmily + +- Thank you once again! If you're interested, you're welcome to provide any valuable PR contributions." diff --git a/src/blog/milvus-easyai-face-java.md b/src/blog/milvus-easyai-face-java.md new file mode 100644 index 0000000000..2c82019600 --- /dev/null +++ b/src/blog/milvus-easyai-face-java.md @@ -0,0 +1,131 @@ +--- +title: "Milvus × EasyAi: How to Build a Face Recognition Application from Scratch with Java" +author: December 26, 2024, 08:36 +date: 2024-12-26 +cover: /assets/img/blog/milvus-easyai-face-java-0.webp +head: + - - meta + - name: Blog +--- + +![](/assets/img/blog/milvus-easyai-face-java-0.webp) + +![](/assets/img/blog/milvus-easyai-face-java-1.png) + +**How to build a face recognition application from scratch? Try the combination of native Java artificial intelligence algorithms: EasyAi + Milvus**. + +**The software and tools used in this article include**: + +* EasyAi: Face feature vector extraction +* Milvus: Vector database for efficient storage and retrieval of data. + +**01**. + +**EasyAi: The Most Popular Java AI Algorithm Framework in China** + +As a pure Java framework for developing AI applications, EasyAi has no dependencies. It is a native Java artificial intelligence algorithm framework. First, it can be seamlessly integrated into our Java project via Maven with one click, requiring no additional environment configuration or dependencies, making it ready to use out of the box. Furthermore, it includes pre-packaged modules for image target detection and AI customer service, as well as underlying algorithm tools for deep learning, machine learning, reinforcement learning, heuristic learning, matrix operations, derivative functions, partial derivative functions, and more. With minimal learning, developers can deeply customize micro-models that fit their business needs. + +**02**. + +**EasyAi-Face: A Face Recognition Application Based on Easy-Ai** + +**1. Generate an average human face**: Scale all face samples to a uniform size, crop excess parts from the top and bottom, pad zeros where insufficient, sum all pixel channels, calculate the average, and output the average face. + +**2. Use a pre-trained face localization fastYolo model** to locate faces in the target photo, setting a threshold so that only detections with confidence above this threshold are considered faces. + +**3. Obtain the face bounding box with the highest confidence in the target photo** and perform a secondary correction on the face position based on this bounding box. + +_Secondary correction solution:_ + +* Use particle swarm optimization with four feature dimensions to seek the optimal solution: the x and y coordinates of the top-left corner of the face and the width and height. The adaptive function returns the value set to minimize for optimality. The activity range of the xy and width-height four-dimensional particles is adjusted, with upper and lower limits set to ±50 pixels around the coordinates and dimensions from the initial localization (adjustable). +* The adaptation function calculation process involves cropping the face based on the coordinates locked by the four-dimensional particles, scaling it to a specified smaller size using the same scaling method as before, and converting their grayscale channels into probability distributions via softMax for the entire matrix. +* Compare the Euclidean distance between the average face and the grayscale probability image of the face locked by the particles at this moment, and return it. Let the particles explore (within a specified number of iterations) for the minimum optimal solution. + +**4. Extract facial features**: Obtain the optimal coordinates found by the particles, crop the image based on these coordinates, and take the top 70% of the height (discarding the mouth area, as it is less stable). Extract the LBP (Local Binary Pattern) texture features of the resulting image. + +**03**. + +**Building a Face Recognition Application with EasyAi-Face + Milvus** + +**3.1 Extracting Facial Features** + +**Add Dependency** +```xml + + org.dromara.easyai + seeFace + 1.0.5 + +``` +**Initialize Face** +```java +@Bean +public Face face(FaceConfig faceConfig ){ + if (StringUtils.isNotBlank(faceConfig.getAvgFace()) && StringUtils.isNotBlank(faceConfig.getFaceModel())){ + return FaceFactory.getFace(faceConfig.getAvgFace(), faceConfig.getFaceModel()); + } + return FaceFactory.getFace(); +} +``` +**Extract Facial Features** +```java +private List getFloats(InputStream inputStream) { + ThreeChannelMatrix m = Picture.getThreeMatrix(inputStream, false); + ErrorMessage errorMessage = face.look(m, idWorker.nextId(), 30); + final Matrix feature = errorMessage.getFaceMessage().getFeature(); + return MatrixUtil.matrixToFloatList(feature); +} +``` +**3.2 Store in Vector Database** +```java +public void initUserVector(UserDTO userDTO, List features) { + List names = Collections.singletonList(userDTO.getUserName()); + List userIds = Collections.singletonList(userDTO.getUserId()); + List getFaceUrl = Collections.singletonList(userDTO.getFaceUrl()); + List getFaceFeatureUrl = Collections.singletonList(userDTO.getFaceFeatureUrl()); + List> vectors = Collections.singletonList(features); + List fields = new ArrayList(); + fields.add(new Field("vector", vectors)); + fields.add(new Field("face_url", getFaceUrl)); + fields.add(new Field("face_feature_url", getFaceFeatureUrl)); + fields.add(new Field("user_id", userIds)); + fields.add(new Field("user_name", names)); + InsertParam insertParam = InsertParam.newBuilder().withCollectionName(milvusConfig.getCollectionName()).withFields(fields).build(); + this.milvusClient.insert(insertParam); +} +``` +**3.3 [Recognize Faces] L2 Similarity Search for Facial Features** +```java +public List search(List floatList, Integer topK) { + final List idScoreList = vectorService.search(floatList, topK); + List list = new ArrayList<>(); + idScoreList.forEach(idScore -> { + UserDTO imageDTO = new UserDTO(); + final float score = idScore.getScore(); + final Map fieldValues = idScore.getFieldValues(); + imageDTO.setAutoId(Long.valueOf(String.valueOf( fieldValues.getOrDefault("Auto_id", "-1")))); + imageDTO.setUserId(Long.valueOf(String.valueOf( fieldValues.getOrDefault("user_id", "-1")))); + imageDTO.setUserName(String.valueOf((fieldValues.getOrDefault("user_name", "")))); + imageDTO.setFaceUrl(String.valueOf((fieldValues.getOrDefault("face_url", "")))); + imageDTO.setFaceFeatureUrl(String.valueOf((fieldValues.getOrDefault("face_feature_url", "")))); + imageDTO.setScore(Math.sqrt(score)); + list.add(imageDTO); + }); + return list; +} +``` +**04**. + +**Conclusion** + +This article demonstrates how to build a face recognition application using **EasyAi and Milvus**. By leveraging the strengths of the Java ecosystem's EasyAi and Milvus's vector search capabilities, we can quickly set up our own face recognition project using Java. We hope this article has been helpful. At the same time, we encourage you to use EasyAi and vector search in your own projects to explore more possibilities. The code involved in this article can be obtained via **Gitee**: Easy-Ai-Face_(https://gitee.com/fushoujiang/easy-ai-face)_. + +**Recommended Reading** + +[![](/assets/img/blog/milvus-easyai-face-java-2.png)](https://mp.weixin.qq.com/s?__biz=MzUzMDI5OTA5NQ==&mid=2247507230&idx=1&sn=3dc46b15f39e6ac1666614bb7aa338d6&scene=21#wechat_redirect) + +[![](/assets/img/blog/milvus-easyai-face-java-3.png)](https://mp.weixin.qq.com/s?__biz=MzUzMDI5OTA5NQ==&mid=2247507157&idx=1&sn=0ac4343d81b7ef655e358e49c496e37b&scene=21#wechat_redirect) + +![](/assets/img/blog/milvus-easyai-face-java-4.webp) + +![](/assets/img/blog/milvus-easyai-face-java-5.webp) \ No newline at end of file diff --git a/src/blog/northstar-7.0.md b/src/blog/northstar-7.0.md index 5790ea59fa..a281a07b61 100644 --- a/src/blog/northstar-7.0.md +++ b/src/blog/northstar-7.0.md @@ -2,8 +2,6 @@ title: Quantitative software industry spoiler, northstar 7.0 official version of the strong debut author: northstar date: 2024-02-01 -tag: - - cover: /assets/img/blog/northstar-7.0-0.png head: - - meta diff --git a/src/blog/os-data-community-team.md b/src/blog/os-data-community-team.md new file mode 100644 index 0000000000..a3853a0dff --- /dev/null +++ b/src/blog/os-data-community-team.md @@ -0,0 +1,221 @@ +--- +title: China's Open Source Data Community Operations Dream Team +author: December 20, 2024, 13:59 +date: 2024-12-20 +cover: /assets/img/blog/os-data-community-team-0.gif +head: + - - meta + - name: Blog +--- + +![](/assets/img/blog/os-data-community-team-0.gif) + +**PowerData** + +The Power of Data, Extraordinary Imagination + +■ ■ ■ + +Think • Exchange • Contribute • Win-Win + +○ + +○ + +**Full text: 4632 words | Suggested reading time: 10 minutes** + +**Article Guide /** Company Nature + +Recently, I read an article by Teacher Yin Haiwen titled ["Database Operations Dream Team Plus"](https://mp.weixin.qq.com/s?__biz=Mzg3MTk4MzYyMQ==&mid=2247486110&idx=1&sn=dd60d01a97e6f8aaa1fbbc9315009959&scene=21#wechat_redirect), which sparked an idea. The open-source data community also has many capable, good-looking, and thoughtful operations colleagues. Hence, I wrote this introduction to China's open-source data operations dream team. + +Author: PowerData - Li Qifeng | Editor: PowerData - Li Zhao + +**Chapter 01** + +**DolphinScheduler** + +Zeng Hui + +Apache DolphinScheduler Committer, mainly responsible for the community operations of Apache DolphinScheduler and SeaTunnel. Leads the project's ecosystem development, maintains developer relations, enhances the global influence of the "open-source project," and focuses on internal community building. Dedicated to spreading open-source culture. If you're interested in open-source big data technology events, 🙏 welcome 👏👏 to contact me to collaborate! Usually enjoy music, outdoor activities, and playing badminton! + +**Operations Insight**: The core of community operations lies in activating developers, creating value, and building connections. First, understand developer needs and attract user participation through high-quality content and activities, such as technical sharing, case studies, and contribution incentives. Second, lower the barrier to entry by providing clear contribution guidelines and tutorials to help newcomers quickly get started and integrate into the community. It is also particularly important to focus on cultivating and incentivizing core contributors, giving users a sense of identity and honor to form long-term motivation for contribution. Regularly collect user feedback, respond to issues quickly, and enhance the sense of belonging and trust in the community. Finally, maintain synergy with technology and the ecosystem, allowing the community to bring practical value to products and business, forming a virtuous cycle. + +![](/assets/img/blog/os-data-community-team-6.jpg) + +**Brother Hui is widely recognized in the industry for his great voice in the open-source community (I remember at an open-source dinner, his performance of "海阔天空" directly won over the entire room). In my heart, he is the great open-source operations big brother—professional, passionate, and humble. Since the founding of PowerData, I have been organizing events with Brother Hui. Until now, although we have never met in person, I truly admire him. Brother Hui now has both a son and a daughter, a happy and fulfilling family, and a thriving open-source career. It's really enviable!** + +**Chapter 02** + +**SelectDB** + +Ye Zi (Coconut) + +An event operations specialist, a 24g internet surfer who loves researching and paying attention to fun and interesting activities from various communities. My favorite thing is collecting souvenirs from offline events! + +**Operations Insight**: Operations must have a human touch. Based on clear community goals and user needs, organize activities to create unique community labels. Focus on interaction with users, encourage active participation in community activities, and provide certain incentives. + +**I met Xiran同学 offline at China Open Source Conference and even asked her to hold a PD sign for a photo to endorse PD. Getting back on topic, I have collaborated with Xiran同学 on many online and offline events. She is a very fun, professional, and enthusiastic operations colleague. (She says she is socially anxious, but actually she's not—she's somewhere between an introvert and an extrovert).** + +Yang Suli + +Apache Doris is the first database open-source project I have truly been involved in operating. Having been involved with the project for nearly three years, I am responsible for the content operations of the Doris community & Flywheel Tech, including technical analysis, user case studies, solution articles, as well as the planning, compilation, and promotion of e-books and case collections. So far, I have planned and produced 100+ original articles, garnering 500w+ reads across all channels, and planned a WeChat article with 40k+ reads. Over these three years, I have grown alongside the community. The strength of the Doris community has made me who I am, and I have done my best to contribute to the community's prosperity. + +**Operations Insight**: It might be too early to talk about insights. Operations work requires both innovation and insight, as well as strong execution and communication skills. Understanding the business and basing actions on user needs and experiences are also fundamental abilities for operations. The open-source user segment, with its strong technical literacy and unique perspectives, requires operations to be more open, inclusive, practical, and respectful. In content operations, addressing user needs and solving practical problems should be the top priority. Creating valuable, high-quality content, combined with effective operational strategies and persistence, is the way to achieve goals. + +![](/assets/img/blog/os-data-community-team-9.jpg) + +![](/assets/img/blog/os-data-community-team-10.jpg) + +**Suli同学 is too modest. As SelectDB's content operations specialist, when I lack inspiration while typesetting the official WeChat account, I look at SelectDB's official account (for reference, not plagiarism, manual doge head). I met her in person at Doris Summit in 2023—she is very approachable and enthusiastic. I hope to have the opportunity to communicate offline again in the future.** + +**Chapter 03** + +**NebulaGraph** + +Zhang Hui + +NebulaGraph Developer Relations Lead, CNCF Ambassador. Technical background, skilled in engaging with developers from their perspective within the community. + +**Operations Insight**: People-oriented, Trust is everything. + +![](/assets/img/blog/os-data-community-team-11.jpg) + +![](/assets/img/blog/os-data-community-team-12.jpg) + +![](/assets/img/blog/os-data-community-team-13.jpg) + +**I just met Sister Hui recently. During our exchanges, I felt her passion for open source and her sense of responsibility towards community operations. We can organize more interesting open-source activities together in the future. While compiling this article, Sister Hui proactively helped me invite TiDB community operations representative '表妹' (cousin). Thank you very much~** + +**Chapter 04** + +**IoTDB** + +Qin Chuqing + +Hello everyone, I'm Chuqing. I handle the operations for the IoTDB community and am also the operations lead for Timecho Tech. IoTDB is a time-series database initiated and developed by Tsinghua University. It graduated smoothly from the Apache Incubator and became a top-level project in 2020. Having operated the IoTDB community for over three years, we have organized or participated in nearly fifty events, connected and communicated with thousands of open-source users, and are committed to making IoTDB a warm and rewarding open-source community. + +**Operations Insight**: A community is a magical group. Partners scattered across the country, even the globe, collaborate to form a community—a spontaneously formed, even weakly connected behavior. Operating a community, in my opinion, involves two key tasks: targeting the audience and identifying needs. Step one, targeting the audience, means deeply understanding the community group and analyzing its characteristics. Clearly defining the audience and having a deep understanding of the community is the foundation of community operations. Step two is identifying needs. This means recognizing where the community needs help from the operators. For example, if community users need a deeper understanding of the product architecture, then relevant sharing sessions or blog writing can be organized. + +![](/assets/img/blog/os-data-community-team-14.jpg) + +**Sister Chuqing, Tsinghua top student,气质美女 (elegant beauty), operations expert. This year, I invited Sister Chuqing to share and communicate at the Open Source Conference. She has a thorough understanding of the technical details related to IoTDB and shoulders responsibilities in both market and operations—very comprehensive. PD has collaborated with IoTDB multiple times this year. I feel that the IoTDB team members are very humble, professional, and communicating with them is like a breath of fresh air.** + +**Chapter 05** + +**Pulsar** + +Fu Teng + +Hi,大家好 (hello everyone), I'm Fu Teng. I'm currently working at Anliu Tech on Pulsar commercialization and community operations. It's been 12 years in the industry in a flash, already whitening the young man's head. I started firmly on the Programmer path, later moved into products and projects, and now I'm doing operations and support. I love open source, am passionate about running and fitness, reject internal strife, and hope for peace. Nearing forty, what I value most is my declining health and my hope for a vibrant family. Of course, I still really like working—truly and effectively. Hope to get to know everyone. + +**Operations Insight**: As a rapidly developing open-source messaging and stream processing platform (well, it actually has a 12-year history), Apache Pulsar has penetrated many leading large companies domestically and internationally. Open source and the community have played a crucial role here. Since the open-source wave began in 2000, it has swept across the global software infrastructure like a whirlwind. I wonder, how does everyone view open source? From our perspective in open-source commercialization, infrastructure, due to its complexity, has actually achieved collaboration among global domain experts through open source, rapidly淘汰 (phasing out) many closed-source "black boxes" with unparalleled efficiency. This大大降低了 (greatly reduces) sales costs. Therefore, operating an open-source community is actually a job that combines branding, marketing, and product into one. It's really a tough job because you have to do everything. But it's also a great job because through open source, you can reach big shots in various industries. Everyone can benefit each other and get through the winter. If I had to say what insights I have, it's that you must give first, consistently and steadfastly! + +![](/assets/img/blog/os-data-community-team-15.jpg) + +**Brother Teng, always pushing me to work: "Qifeng, don't forget to repost the article," "Qifeng, come support the Beijing event," "Qifeng, endorse Pulsar." And he's always very polite. I've said we're buddies,哎 (ai), after all, Brother Teng is getting older, but he has been trying hard to integrate into the PD atmosphere recently. This year, I organized many events with Brother Teng, and the collaboration was particularly pleasant. He has also been very supportive of PD. Let's continue this!** + +**Chapter 06** + +**KAIYUANSHE (Open Source Society)** + +Dong Jifu + +Initiator of KCC@Nanjing (KAIYUANSHE Nanjing City Community). Upholding the宗旨 (purpose) of KAIYUANSHE, with the philosophy of "Open Source Knowledge and Action, Steadfast and Robust," he promotes open-source culture in Nanjing, jointly organizes offline events with various communities, and co-builds the open-source ecosystem and inheritance. + +**Operations Insight**: Everything comes from open source, everything goes back to open source. I have made many friends through open source, and at the same time, I have learned a lot of new knowledge by participating in open-source activities. Take action, reap joy. + +![](/assets/img/blog/os-data-community-team-16.jpg) + +![](/assets/img/blog/os-data-community-team-17.jpg) + +![](/assets/img/blog/os-data-community-team-18.jpg) + +**Brother Jifu, my close open-source partner in Nanjing, along with Brother Mazheng and Brother Daqing in the picture above—we are the Nanjing open-source task force. Since 2023, we have organized an event in Nanjing every year. Brother Jifu, as the lead organizer and recorder, writes post-event summary articles that are, in my heart, the no.1 open-source event summary articles. They are detailed, share materials, and incorporate his own thoughts, making it feel like listening to the sharing session again. Attached is one of Brother Jifu's event summary articles: [AI开源南京分享会回顾录 (AI Open Source Nanjing Sharing Session Review)](https://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247542008&idx=1&sn=6209251a4a343932e5451cc04d35461e&scene=21#wechat_redirect)** + +**Chapter 07** + +**Dromara** + +Li Nan + +Hello everyone, I am Li Nan (Nannan) from KAIYUANSHE & Dromara open-source community. I like comics, binge-watching web dramas, am very curious, and enjoy tinkering around—a "free-spirited" woman. + +**Operations Insight**: Interact more, listen more, encourage everyone to communicate and share, and respond to questions promptly. Promote more to attract more people to join. Also, maintain community rules well, keep the atmosphere friendly, so everyone can find a sense of belonging. + +![](/assets/img/blog/os-data-community-team-19.jpg) + +**I met Li Nan同学 offline during PD's Hangzhou Open Source Tour. She is a very lovely and enthusiastic girl. Li Nan同学 does not have a technical background but has been volunteering in open-source operations. This year's PD Open Source Tour also received a lot of support from the community. I hope Li Nan同学 can stay lovely forever~** + +**Logically, this article is an introduction to operations personnel in the open-source data community, but I still included Brother Jifu and Li Nan同学 here. I want everyone to see that there are such sincere and lovely people in the open-source field.** + +**Chapter 08** + +**ByConity** + +Liu Xiaomi + +An open-source operations girl, sometimes a bit socially anxious ENFP. I've been working in the open-source circle since my internship before graduation. Over the years, I've been exposed to various big shots and learn a lot of knowledge every day. I hope to continue contributing my part to community building in the future 🐮 + +**Operations Insight**: In my opinion, the most important aspect of community operations is maintaining developer relations. By building strong relationships with developers, the community can leverage extensive developer expertise, increase product iteration speed, and quickly obtain community feedback. By analyzing the project's status from aspects like maturity and infrastructure, and then using the right operational methods to maintain developer relations, a better environment can be created for the community's developers. + +![](/assets/img/blog/os-data-community-team-20.jpg) + +**Xiaomi同学, we haven't met in person, but we have collaborated on many events. This year, for PD's city tour, Xiaomi同学 provided venue, speaker, and souvenir support for multiple events. She is very sincere, no wonder she excels in maintaining developer relations. Not having communicated offline with Xiaomi同学 is one of my regrets. I hope to have the opportunity to请教 (seek advice from) her in person next year.** + +**Chapter 09** + +**TiDB** + +Huang Manshen (Biǎo mèi - Cousin) + +In this era of rapid digital development, open-source communities have become important platforms for technical exchange and knowledge sharing. In my heart, the ideal domestic open-source community should be a vibrant, open, inclusive, and collaborative win-win ecosystem. It is not just a gathering place for technology but also a convergence point for dreams and passion. + +**Operations Insight**: In the TiDB community, all decision-making processes, technology roadmaps, and development plans will be公开 (open) to community members, allowing everyone to understand how the community operates and its future direction. This transparency not only enhances the trust of community members but also attracts more outstanding talents to join. Everyone is encouraged to提出 (put forward) their ideas and suggestions, whether it's improvements to existing features or visions for future development—all can become valuable assets for the community's development. + +![](/assets/img/blog/os-data-community-team-21.jpg) + +**In the field of open-source data community operations, the figure of TiDB's Biǎo mèi (I should probably call her Biǎo jiě - older cousin sister) is indispensable. The活跃 (activeness) of the TiDB community is离不开 (inseparable from) Cousin's professionalism and dedication. As an outsider, I also learned a lot of practical and actionable operational knowledge from her article ["What is the Ideal Open Source Community Like? A Shallow Understanding from TiDB Community Operations Cousin"](https://mp.weixin.qq.com/s?__biz=MzA4MTgzNDQzMQ==&mid=2651502145&idx=1&sn=5225ec33313a4284d72143296441a8aa&scene=21#wechat_redirect). I hope to have the opportunity to seek advice from Cousin in person in the future~** + +**Chapter 10** + +**ActionTech** + +Guan Changlong + +ActionTech Open Source Community Operations Manager. From server rooms to training podiums, passionate about open-source technology evangelism. + +**Operations Insight**: As a community operator, passion and patience are required. Establish efficient communication channels to facilitate developer exchange. Actively respond to member questions and create a friendly atmosphere. If there is an opportunity for offline communication, the effect is definitely the best. + +I met Brother Long at an open-source event in Shanghai and had in-depth exchanges at the China Open Source Conference, where PD's and ActionTech's booths were next to each other. As an operations expert with a technical background, he has a natural advantage in technology evangelism. Combined with Brother Long's eloquent expression and patient, steady tone, you will unconsciously be attracted to him. Moreover, he is very helpful and took many handsome photos of me during the event. Brother Long is also somewhere between an introvert and an extrovert—an introvert when无事 (nothing's happening), but特别 e (very extroverted) when there's work to be done. + +<<< Summary >>> + +The open-source community is full of strong players—not only developers but also operations personnel, each with their own unique skills and extraordinary talents. I hope today's article allows you to see the professionalism, enthusiasm, handsomeness, and beauty of open-source operations personnel. + +**Past Wonderful Article Collections** + +![](/assets/img/blog/os-data-community-team-22.gif) + +【Open Source Figures Column】 + +[Open Source Figure - Wang Chunsheng of ZenTao Community: Making Project Management More Convenient](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488278&idx=1&sn=f3e915780246a3e0084fb398b5f392e5&scene=21#wechat_redirect) + +[Open Source Figure—Qiao Jialin of IoTDB: Conquering Industrial Internet Data](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488845&idx=1&sn=101879acffe0007ab7a5db28a26b3f2f&scene=21#wechat_redirect) + +【Technical Articles Column】 + +[Kafka Source Code Learning (1) Producer Source Code](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247487990&idx=1&sn=5b98967588a71af796130e85801f86b4&scene=21#wechat_redirect) + +[Kafka Source Code Learning (2) Server Source Code](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488095&idx=1&sn=b3ae348379c7ab827e2b1828b48cc344&scene=21#wechat_redirect) + +[Kafka Source Code Learning (3) Consumer Source Code](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488219&idx=1&sn=01636401250533e1bd5b44a7975e8cb1&scene=21#wechat_redirect) + +[【Technical Practice】Doris Data Query Performance Analysis: In-Depth Application of Explain and Profile Functions](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488755&idx=1&sn=42bdf28a3ba55ddb9a6d416b315a4b44&scene=21#wechat_redirect) + +【Community Activities Column】 + +[Event Review | 【Digital Economy·City Pulse】PowerData Xi'an Open Source Tour](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247487699&idx=1&sn=f1ccdcf2e0681d9dcca47e91838a64a4&scene=21#wechat_redirect) + +[Event Review | 【Digital Economy·City Pulse】PowerData Hangzhou Open Source Tour](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247487817&idx=1&sn=8e1a9e621032a95d74b9881bdb4f2498&scene=21#wechat_redirect) + +<<< END >>> \ No newline at end of file diff --git a/src/blog/soul_resource_learning_07_admin.md b/src/blog/soul_resource_learning_07_admin.md index e750e30de6..6acd739cf6 100644 --- a/src/blog/soul_resource_learning_07_admin.md +++ b/src/blog/soul_resource_learning_07_admin.md @@ -1,145 +1,145 @@ ---- -title: Soul Gateway Learning Admin Source Code Analysis -author: zenglinhui -date: 2021-01-20 -tag: - - Soul -cover: /img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -# Source Code Analysis - -## Page Operation Source Code Analysis - -Before analyzing the source code, let's take a look at the image below. The plugin list displayed on the page corresponds to requests made to the backend. Based on these backend requests, the corresponding controller class is identified. - -![Insert Image Description Here](https://img-blog.csdnimg.cn/20210117034006267.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -Then, we find the corresponding method. In the image above, it can be seen that here, we access the mapping that is empty by default in the "plugin". We pass in pagination-related parameters and then query the corresponding plugin records in the database. - -![Insert Image Description Here](https://img-blog.csdnimg.cn/20210117034215738.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -The corresponding table in the database is shown in the image below. The "divide" status is enabled. In the previous article, this plugin was used to test the gateway. - -![Insert Image Description Here](https://img-blog.csdnimg.cn/20210117035235400.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -At the same time, a selector is also requested. The requested controller can be seen in the image below. In the previous demonstration, we directly perform CRUD operations on the conditions in the selector on the page. These changes can be reflected in the gateway in real time without the need to restart the gateway. Therefore, in addition to the "query" method, the "create", "delete", and "update" methods have been added. After saving to the database, a "publishEvent" method is triggered. This event allows users to configure rules directly in the Soul backend, achieving real-time effectiveness. - -![Insert Image Description Here](https://img-blog.csdnimg.cn/20210117040000892.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -``` -public int createOrUpdate(final SelectorDTO selectorDTO) { - int selectorCount; - SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO); - List selectorConditionDTOs = selectorDTO.getSelectorConditions(); - if (StringUtils.isEmpty(selectorDTO.getId())) { - selectorCount = selectorMapper.insertSelective(selectorDO); - selectorConditionDTOs.forEach(selectorConditionDTO -> { - selectorConditionDTO.setSelectorId(selectorDO.getId()); - selectorConditionMapper.insertSelective(SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO)); - }); - } else { - selectorCount = selectorMapper.updateSelective(selectorDO); - //delete rule condition then add - selectorConditionMapper.deleteByQuery(new SelectorConditionQuery(selectorDO.getId())); - selectorConditionDTOs.forEach(selectorConditionDTO -> { - selectorConditionDTO.setSelectorId(selectorDO.getId()); - SelectorConditionDO selectorConditionDO = SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO); - selectorConditionMapper.insertSelective(selectorConditionDO); - }); - } - publishEvent(selectorDO, selectorConditionDTOs); - return selectorCount; - } -``` - -2. **Synchronization with soul-bootstrap (WebSocket) Source Code Analysis** - -Previously, it was explained how data is saved to the database after performing operations on the admin page. Spring's built-in reactive programming is used to synchronize the data with the bootstrap project, achieving dynamic refreshing of gateway rules and plugins without requiring a restart. -When soul-bootstrap starts, the following log entry is displayed: - -``` -2021-01-21 00:33:39.620 INFO 14276 --- [0.0-9095-exec-5] o.d.s.a.l.websocket.WebsocketCollector : websocket on open successful.... -``` - -The question is, which entity is it connecting to using WebSockets and how does the connection happen? By examining the code that generates this log entry, we can gain insights. Here's where the log entry is generated: -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121004835890.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -First, let's analyze this code: - -- Get the requested address from the websocketConfig configuration, which is of course configured in the location shown in the following figure. -- After obtaining this configuration address, a timed thread pool is created with a size of urls.length, and a daemon thread with a thread name prefix of "websocket-connect" is created. Why use daemon threads? Because this is just to ensure that the websocket connections of bootstrap and admin are constantly maintained, similar to the function of a heartbeat, so a daemon thread is the best choice. -- According to the created client, one by one, go to the address configured in the configuration file, and then print the previously found logs. -- Finally, start a thread to check if the client is closed. If it is closed, it will reconnect (the initial interval is 10 seconds, and then it will check every 30 seconds, so if you see multiple connection success logs printed in the console, it means that reconnection has occurred). - ![Insert picture description here](https://img-blog.csdnimg.cn/20210121005138940.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -- Next, let's take a look at how the data operated in the admin background is synchronized to bootstrap. Previously, it was mentioned that after saving or updating data in the background, the publishEvent method is called. This is a method of spring's built-in reactive programming. Since it is reactive, it is event-based, and therefore requires a listener. - ![Insert picture description here](https://img-blog.csdnimg.cn/20210121011826544.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -Sure enough, the red box in the above picture is familiar, it is a listener related to websocket. If you still don't understand the connection between the listener and the previous publishEvent, then put breakpoints in the listener's code and debug it. For convenience, I clicked on this synchronization of all data here. -![Insert picture description here](https://img-blog.csdnimg.cn/20210121012123937.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -This enters the DataChangedEventDispatcher class, calls the event-related methods, and in the lower left corner, you can see familiar methods. Yes, it is the aforementioned publishEvent. - -- Then it will jump to the WebsocketDataChangedListener class. Here, pay attention to the send method in the debugging method. - ![Insert picture description here](https://img-blog.csdnimg.cn/2021012101272614.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -- Use the send method to send the updated data to bootstrap. At this point, how admin synchronizes data to bootstrap is revealed. - ![Insert picture description here](https://img-blog.csdnimg.cn/20210121013002688.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -3. **Soul-bootstrap data synchronization (zookeeper) source code analysis** - -Without further ado, let's first look at the picture. Comment out the websocket configuration, open the zookeeper configuration, and start the local or remote zookeeper service. Then start soul-admin. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121152407500.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -First, enter the run method of the ZookeeperDataInit class. After this method is executed, the strange thing is that it jumps to the WebsocketDataChangedListener class. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121233759520.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -I don't understand this point. After the onPluginChanged method in this class is executed, it returns to the ZookeeperDataChangedListener class. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234036652.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -If it is not deleted, the zkNode node data will be updated. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234326495.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -Method for updating zk node. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234628900.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -Moreover, the onSelectorChanged, onMetaDataChanged, and onRuleChanged methods will all first go to the corresponding methods in the WebsocketDataChangedListener class, and then enter the methods in the ZookeeperDataChangedListener class. If the plugin data is changed, it will go through the above steps again. -The problem of entering two Listener classes for synchronous data has not been solved yet. Suddenly, I thought that there was a dependency on websocket in the pom file, because the websocket configuration in the application.yml file had been commented out (not enable=false), so I commented out this dependency first and then compiled the code. I found that the code did not pass the compilation. Another way is to change websocket to disabled. After the modification, I found that it would not jump to the websocket-related class again. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122000547192.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) 4. **Analysis of soul-bootstrap data synchronization (http) source code** - -As usual, modify the configuration in the yml file, and then set a breakpoint in the corresponding listener class. If http is used here, the websocket-related class will still be accessed, so it cannot be commented out directly. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122005803207.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -Let's take a look at the code inside: -There is a constructor here, which instantiates a clients array blocking queue with a size of 1024. A timed task thread pool with a thread number of 1 and a name prefix of "long-polling" background daemon thread (as can be seen from the name, this is used for long polling). A related property configuration -In the initialization method, a timed thread is started. After 5 minutes, the refreshLocalCache method for refreshing the local cache is executed every 5 minutes. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122004153175.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -``` - private void refreshLocalCache() { - this.updateAppAuthCache(); - this.updatePluginCache(); - this.updateRuleCache(); - this.updateSelectorCache(); - this.updateMetaDataCache(); - } -``` - -If the data is manually synchronized, the following related methods will be executed, and they will also be executed through the timed thread pool, but they will be executed immediately. -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122010007881.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -Five minutes later, execute the corresponding refresh method, and print the log. - -``` -2021-01-22 01:00:19.007 INFO 20800 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config start. -2021-01-22 01:00:19.010 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[APP_AUTH], old: {group='APP_AUTH', md5='d751713988987e9331980363e24189ce', lastModifyTime=1611248118794}, updated: {group='APP_AUTH', md5='d751713988987e9331980363e24189ce', lastModifyTime=1611248419010} -2021-01-22 01:00:19.012 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[PLUGIN], old: {group='PLUGIN', md5='70b269257d47f0f6404ae7b7e976d8f1', lastModifyTime=1611248295740}, updated: {group='PLUGIN', md5='70b269257d47f0f6404ae7b7e976d8f1', lastModifyTime=1611248419012} -2021-01-22 01:00:19.069 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[RULE], old: {group='RULE', md5='5811b56257e31109621976d39fc226aa', lastModifyTime=1611248301607}, updated: {group='RULE', md5='5811b56257e31109621976d39fc226aa', lastModifyTime=1611248419069} -2021-01-22 01:00:19.075 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[SELECTOR], old: {group='SELECTOR', md5='70bad5ebb1cf6e3fc55278eef2df42f3', lastModifyTime=1611248299419}, updated: {group='SELECTOR', md5='70bad5ebb1cf6e3fc55278eef2df42f3', lastModifyTime=1611248419075} -2021-01-22 01:00:19.077 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[META_DATA], old: {group='META_DATA', md5='5f79d821e3b601330631a2d53294fb34', lastModifyTime=1611248302571}, updated: {group='META_DATA', md5='5f79d821e3b601330631a2d53294fb34', lastModifyTime=1611248419077} -2021-01-22 01:00:19.077 INFO 20800 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config success. -``` - -5. There are other methods for synchronizing data in soul, which will be analyzed later if there is energy. This is the end of the analysis of the soul-admin source code. If further analysis is conducted, another article will be written separately. - -# Summary - -There are still many features in soul-admin that have not been used yet, and there are many interesting things. This article will be continuously updated, and the source code inside will be analyzed in detail when it is used. - -1. On January 20, 2021, analyzed how soul-admin synchronizes data to soul-bootstrap using websocket. -2. On January 21, 2021, analyzed how soul-admin synchronizes data to soul-bootstrap using zookeeper. -3. On January 21, 2021, analyzed how soul-admin synchronizes data to soul-bootstrap using http. +--- +title: Soul Gateway Learning Admin Source Code Analysis +author: zenglinhui +date: 2021-01-20 +tag: + - Soul +cover: /img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +# Source Code Analysis + +## Page Operation Source Code Analysis + +Before analyzing the source code, let's take a look at the image below. The plugin list displayed on the page corresponds to requests made to the backend. Based on these backend requests, the corresponding controller class is identified. + +![Insert Image Description Here](https://img-blog.csdnimg.cn/20210117034006267.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +Then, we find the corresponding method. In the image above, it can be seen that here, we access the mapping that is empty by default in the "plugin". We pass in pagination-related parameters and then query the corresponding plugin records in the database. + +![Insert Image Description Here](https://img-blog.csdnimg.cn/20210117034215738.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +The corresponding table in the database is shown in the image below. The "divide" status is enabled. In the previous article, this plugin was used to test the gateway. + +![Insert Image Description Here](https://img-blog.csdnimg.cn/20210117035235400.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +At the same time, a selector is also requested. The requested controller can be seen in the image below. In the previous demonstration, we directly perform CRUD operations on the conditions in the selector on the page. These changes can be reflected in the gateway in real time without the need to restart the gateway. Therefore, in addition to the "query" method, the "create", "delete", and "update" methods have been added. After saving to the database, a "publishEvent" method is triggered. This event allows users to configure rules directly in the Soul backend, achieving real-time effectiveness. + +![Insert Image Description Here](https://img-blog.csdnimg.cn/20210117040000892.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +``` +public int createOrUpdate(final SelectorDTO selectorDTO) { + int selectorCount; + SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO); + List selectorConditionDTOs = selectorDTO.getSelectorConditions(); + if (StringUtils.isEmpty(selectorDTO.getId())) { + selectorCount = selectorMapper.insertSelective(selectorDO); + selectorConditionDTOs.forEach(selectorConditionDTO -> { + selectorConditionDTO.setSelectorId(selectorDO.getId()); + selectorConditionMapper.insertSelective(SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO)); + }); + } else { + selectorCount = selectorMapper.updateSelective(selectorDO); + //delete rule condition then add + selectorConditionMapper.deleteByQuery(new SelectorConditionQuery(selectorDO.getId())); + selectorConditionDTOs.forEach(selectorConditionDTO -> { + selectorConditionDTO.setSelectorId(selectorDO.getId()); + SelectorConditionDO selectorConditionDO = SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO); + selectorConditionMapper.insertSelective(selectorConditionDO); + }); + } + publishEvent(selectorDO, selectorConditionDTOs); + return selectorCount; + } +``` + +2. **Synchronization with soul-bootstrap (WebSocket) Source Code Analysis** + +Previously, it was explained how data is saved to the database after performing operations on the admin page. Spring's built-in reactive programming is used to synchronize the data with the bootstrap project, achieving dynamic refreshing of gateway rules and plugins without requiring a restart. +When soul-bootstrap starts, the following log entry is displayed: + +``` +2021-01-21 00:33:39.620 INFO 14276 --- [0.0-9095-exec-5] o.d.s.a.l.websocket.WebsocketCollector : websocket on open successful.... +``` + +The question is, which entity is it connecting to using WebSockets and how does the connection happen? By examining the code that generates this log entry, we can gain insights. Here's where the log entry is generated: +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121004835890.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +First, let's analyze this code: + +- Get the requested address from the websocketConfig configuration, which is of course configured in the location shown in the following figure. +- After obtaining this configuration address, a timed thread pool is created with a size of urls.length, and a daemon thread with a thread name prefix of "websocket-connect" is created. Why use daemon threads? Because this is just to ensure that the websocket connections of bootstrap and admin are constantly maintained, similar to the function of a heartbeat, so a daemon thread is the best choice. +- According to the created client, one by one, go to the address configured in the configuration file, and then print the previously found logs. +- Finally, start a thread to check if the client is closed. If it is closed, it will reconnect (the initial interval is 10 seconds, and then it will check every 30 seconds, so if you see multiple connection success logs printed in the console, it means that reconnection has occurred). + ![Insert picture description here](https://img-blog.csdnimg.cn/20210121005138940.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +- Next, let's take a look at how the data operated in the admin background is synchronized to bootstrap. Previously, it was mentioned that after saving or updating data in the background, the publishEvent method is called. This is a method of spring's built-in reactive programming. Since it is reactive, it is event-based, and therefore requires a listener. + ![Insert picture description here](https://img-blog.csdnimg.cn/20210121011826544.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +Sure enough, the red box in the above picture is familiar, it is a listener related to websocket. If you still don't understand the connection between the listener and the previous publishEvent, then put breakpoints in the listener's code and debug it. For convenience, I clicked on this synchronization of all data here. +![Insert picture description here](https://img-blog.csdnimg.cn/20210121012123937.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +This enters the DataChangedEventDispatcher class, calls the event-related methods, and in the lower left corner, you can see familiar methods. Yes, it is the aforementioned publishEvent. + +- Then it will jump to the WebsocketDataChangedListener class. Here, pay attention to the send method in the debugging method. + ![Insert picture description here](https://img-blog.csdnimg.cn/2021012101272614.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +- Use the send method to send the updated data to bootstrap. At this point, how admin synchronizes data to bootstrap is revealed. + ![Insert picture description here](https://img-blog.csdnimg.cn/20210121013002688.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +3. **Soul-bootstrap data synchronization (zookeeper) source code analysis** + +Without further ado, let's first look at the picture. Comment out the websocket configuration, open the zookeeper configuration, and start the local or remote zookeeper service. Then start soul-admin. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121152407500.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +First, enter the run method of the ZookeeperDataInit class. After this method is executed, the strange thing is that it jumps to the WebsocketDataChangedListener class. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121233759520.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +I don't understand this point. After the onPluginChanged method in this class is executed, it returns to the ZookeeperDataChangedListener class. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234036652.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +If it is not deleted, the zkNode node data will be updated. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234326495.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +Method for updating zk node. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234628900.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +Moreover, the onSelectorChanged, onMetaDataChanged, and onRuleChanged methods will all first go to the corresponding methods in the WebsocketDataChangedListener class, and then enter the methods in the ZookeeperDataChangedListener class. If the plugin data is changed, it will go through the above steps again. +The problem of entering two Listener classes for synchronous data has not been solved yet. Suddenly, I thought that there was a dependency on websocket in the pom file, because the websocket configuration in the application.yml file had been commented out (not enable=false), so I commented out this dependency first and then compiled the code. I found that the code did not pass the compilation. Another way is to change websocket to disabled. After the modification, I found that it would not jump to the websocket-related class again. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122000547192.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) 4. **Analysis of soul-bootstrap data synchronization (http) source code** + +As usual, modify the configuration in the yml file, and then set a breakpoint in the corresponding listener class. If http is used here, the websocket-related class will still be accessed, so it cannot be commented out directly. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122005803207.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +Let's take a look at the code inside: +There is a constructor here, which instantiates a clients array blocking queue with a size of 1024. A timed task thread pool with a thread number of 1 and a name prefix of "long-polling" background daemon thread (as can be seen from the name, this is used for long polling). A related property configuration +In the initialization method, a timed thread is started. After 5 minutes, the refreshLocalCache method for refreshing the local cache is executed every 5 minutes. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122004153175.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +``` + private void refreshLocalCache() { + this.updateAppAuthCache(); + this.updatePluginCache(); + this.updateRuleCache(); + this.updateSelectorCache(); + this.updateMetaDataCache(); + } +``` + +If the data is manually synchronized, the following related methods will be executed, and they will also be executed through the timed thread pool, but they will be executed immediately. +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122010007881.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +Five minutes later, execute the corresponding refresh method, and print the log. + +``` +2021-01-22 01:00:19.007 INFO 20800 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config start. +2021-01-22 01:00:19.010 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[APP_AUTH], old: {group='APP_AUTH', md5='d751713988987e9331980363e24189ce', lastModifyTime=1611248118794}, updated: {group='APP_AUTH', md5='d751713988987e9331980363e24189ce', lastModifyTime=1611248419010} +2021-01-22 01:00:19.012 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[PLUGIN], old: {group='PLUGIN', md5='70b269257d47f0f6404ae7b7e976d8f1', lastModifyTime=1611248295740}, updated: {group='PLUGIN', md5='70b269257d47f0f6404ae7b7e976d8f1', lastModifyTime=1611248419012} +2021-01-22 01:00:19.069 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[RULE], old: {group='RULE', md5='5811b56257e31109621976d39fc226aa', lastModifyTime=1611248301607}, updated: {group='RULE', md5='5811b56257e31109621976d39fc226aa', lastModifyTime=1611248419069} +2021-01-22 01:00:19.075 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[SELECTOR], old: {group='SELECTOR', md5='70bad5ebb1cf6e3fc55278eef2df42f3', lastModifyTime=1611248299419}, updated: {group='SELECTOR', md5='70bad5ebb1cf6e3fc55278eef2df42f3', lastModifyTime=1611248419075} +2021-01-22 01:00:19.077 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[META_DATA], old: {group='META_DATA', md5='5f79d821e3b601330631a2d53294fb34', lastModifyTime=1611248302571}, updated: {group='META_DATA', md5='5f79d821e3b601330631a2d53294fb34', lastModifyTime=1611248419077} +2021-01-22 01:00:19.077 INFO 20800 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config success. +``` + +5. There are other methods for synchronizing data in soul, which will be analyzed later if there is energy. This is the end of the analysis of the soul-admin source code. If further analysis is conducted, another article will be written separately. + +# Summary + +There are still many features in soul-admin that have not been used yet, and there are many interesting things. This article will be continuously updated, and the source code inside will be analyzed in detail when it is used. + +1. On January 20, 2021, analyzed how soul-admin synchronizes data to soul-bootstrap using websocket. +2. On January 21, 2021, analyzed how soul-admin synchronizes data to soul-bootstrap using zookeeper. +3. On January 21, 2021, analyzed how soul-admin synchronizes data to soul-bootstrap using http. diff --git a/src/blog/soul_source_learning_01.md b/src/blog/soul_source_learning_01.md index a6f11cbfde..438597a215 100644 --- a/src/blog/soul_source_learning_01.md +++ b/src/blog/soul_source_learning_01.md @@ -1,224 +1,224 @@ ---- -title: Soul Learning(1) Environment Configuration -author: chenxi -date: 2021-01-15 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -# Analysis of soul (1) Set up soul environment - -> soul is a High-Performance Java API Gateway -> -> GitHub:https://github.com/dromara/soul -> -> document:https://dromara.org/zh-cn/docs/soul/soul.html - -## 1. Prepare source code - -### 1.1. Fork [dromara/soul](https://github.com/dromara/soul.git) repository to my github [cchenxi/soul](https://github.com/cchenxi/soul.git) - -### 1.2. Clone the repository - -```shell -git clone https://github.com/cchenxi/soul.git -``` - -### 1.3.Open the source code with idea - -### 1.4. Compile the soul source code - -You can compile the project as follows. - -```shell -mvn clean package install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Drat.skip=true -Dcheckstyle.skip=true -``` - -待补,文章内部有报错 - -## 2. Startup `soul` - -### 2.1. Startup `soul-admin` module - -> `soul-admin` is the management system for soul. - -Choose to use `MySQL` to storage gateway data and modify the datasource config. - -待补,文章内部有报错 - -Run `org.dromara.soul.admin.SoulAdminBootstrap`. - -When success, please visit the website `http://localhost:9095/`, then jump to the login page, and input the corresponding user name and password to log in. - -The user name is `admin` and the password is `123456`. - -待补,文章内部有报错 - -待补,文章内部有报错 - -### 2.2. Startup `soul-bootstrap` module - -> `soul-bootstrap` is the core of soul. - -Check the configuration of `soul-bootstrap`. - -待补,文章内部有报错 - -Please make sure the ip and the port has been configured for `soul-admin`. - -If the console output as follows, it means the startup is successful. - -```plain text -2021-01-14 15:01:15.832 INFO 17943 --- [ main] b.s.s.d.w.WebsocketSyncDataConfiguration : you use websocket sync soul data....... -2021-01-14 15:01:15.924 INFO 17943 --- [ main] o.d.s.p.s.d.w.WebsocketSyncDataService : websocket connection is successful..... -2021-01-14 15:01:16.113 INFO 17943 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' -log4j:WARN No appenders could be found for logger (com.alibaba.dubbo.common.logger.LoggerFactory). -log4j:WARN Please initialize the log4j system properly. -log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. -2021-01-14 15:01:17.150 INFO 17943 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195 -2021-01-14 15:01:17.154 INFO 17943 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 5.508 seconds (JVM running for 6.762) -``` - -## 3. Test - -> Add the `soul-examples` module to soul's pom.xml for test. - -### 3.1. Startup an HTTP backend service - -Startup `soul-examples-http` - -You can see the dependency in `soul-examples-http`'s pom.xml. - -```xml - - org.dromara - soul-spring-boot-starter-client-springmvc - ${soul.version} - -``` - -Configure the `application.yml` - -```yaml -soul: - http: - adminUrl: http://localhost:9095 - port: 8188 - contextPath: /http - appName: http - full: false -``` - -If `soul.http.full`=false, you need to add the `@SoulSpringMvcClient` annotation in controller or controller method. - -#### 3.1.1. Test the service - -Visit `http://localhost:8188/test/findByUserId?userId=1` and the result as follows. - -待补,文章内部有报错 - -#### 3.1.2. Test forward HTTP request - -Visit `http://localhost:9195/http/test/findByUserId?userId=1` and the result as follows. - -待补,文章内部有报错 - -You can see the following information in the console of `soul-bootstrap`. It means the forward HTTP request is successful. - -```shell -2021-01-14 20:42:57.123 INFO 29812 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:42:57.125 INFO 29812 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:42:57.126 INFO 29812 --- [work-threads-11] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -``` - -### 3.2. Startup two HTTP backend services to simulate load balance - -Choose `Allow parallel run` - -Change the port to `8189` - -Startup `soul-examples-http` again - -待补,文章内部有报错 - -#### 3.2.1. Test the service - -Visit `http://localhost:8189/test/findByUserId?userId=1` and the result as follows. - -待补,文章内部有报错 - -#### 3.2.2. Test load balance - -待补,文章内部有报错 - -Configure two HTTP service in selector - -Visit `http://localhost:9195/http/test/findByUserId?userId=1` more and more and result as follows. - -待补,文章内部有报错 -You can see the following information in the console of `soul-bootstrap`. It means the load balance is successful. - -```shell -2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8189/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:38.755 INFO 29812 --- [work-threads-23] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:38.756 INFO 29812 --- [work-threads-23] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:38.756 INFO 29812 --- [work-threads-23] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8189/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:40.976 INFO 29812 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:40.976 INFO 29812 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:40.977 INFO 29812 --- [-work-threads-1] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -``` - -#### 3.2.3. Press test - -Use `wrk` to press test and compare the two ways as follows. - -1. Visit the backend service directly. -2. Visit the service via soul. - -The performance drops slightly after using the gateway, probably because of the extra layer of forwarding. - -```shell -➜ soul git:(master) ✗ wrk -t8 -c40 -d30s http://localhost:8189/test/findByUserId\?userId\=1 -Running 30s test @ http://localhost:8189/test/findByUserId?userId=1 - 8 threads and 40 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 6.06ms 28.81ms 442.25ms 98.22% - Req/Sec 2.05k 493.86 2.84k 74.82% - 486269 requests in 30.05s, 51.01MB read -Requests/sec: 16179.68 -Transfer/sec: 1.70MB -➜ soul git:(master) ✗ wrk -t8 -c40 -d30s http://localhost:9195/http/test/findByUserId\?userId\=1 -Running 30s test @ http://localhost:9195/http/test/findByUserId?userId=1 - 8 threads and 40 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 14.37ms 18.11ms 255.66ms 93.06% - Req/Sec 459.41 139.11 1.01k 74.23% - 109533 requests in 30.09s, 11.49MB read -Requests/sec: 3639.60 -Transfer/sec: 390.98KB -``` - -#### 3.2.4. Problem in the process - -When startup the port of `8189`,but the output of console is still `8188`. - -待补,文章内部有报错 - -After modify the value of `soul.http.port`, the problem solved. - -待补,文章内部有报错 +--- +title: Soul Learning(1) Environment Configuration +author: chenxi +date: 2021-01-15 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +# Analysis of soul (1) Set up soul environment + +> soul is a High-Performance Java API Gateway +> +> GitHub:https://github.com/dromara/soul +> +> document:https://dromara.org/zh-cn/docs/soul/soul.html + +## 1. Prepare source code + +### 1.1. Fork [dromara/soul](https://github.com/dromara/soul.git) repository to my github [cchenxi/soul](https://github.com/cchenxi/soul.git) + +### 1.2. Clone the repository + +```shell +git clone https://github.com/cchenxi/soul.git +``` + +### 1.3.Open the source code with idea + +### 1.4. Compile the soul source code + +You can compile the project as follows. + +```shell +mvn clean package install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Drat.skip=true -Dcheckstyle.skip=true +``` + +待补,文章内部有报错 + +## 2. Startup `soul` + +### 2.1. Startup `soul-admin` module + +> `soul-admin` is the management system for soul. + +Choose to use `MySQL` to storage gateway data and modify the datasource config. + +待补,文章内部有报错 + +Run `org.dromara.soul.admin.SoulAdminBootstrap`. + +When success, please visit the website `http://localhost:9095/`, then jump to the login page, and input the corresponding user name and password to log in. + +The user name is `admin` and the password is `123456`. + +待补,文章内部有报错 + +待补,文章内部有报错 + +### 2.2. Startup `soul-bootstrap` module + +> `soul-bootstrap` is the core of soul. + +Check the configuration of `soul-bootstrap`. + +待补,文章内部有报错 + +Please make sure the ip and the port has been configured for `soul-admin`. + +If the console output as follows, it means the startup is successful. + +```plain text +2021-01-14 15:01:15.832 INFO 17943 --- [ main] b.s.s.d.w.WebsocketSyncDataConfiguration : you use websocket sync soul data....... +2021-01-14 15:01:15.924 INFO 17943 --- [ main] o.d.s.p.s.d.w.WebsocketSyncDataService : websocket connection is successful..... +2021-01-14 15:01:16.113 INFO 17943 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' +log4j:WARN No appenders could be found for logger (com.alibaba.dubbo.common.logger.LoggerFactory). +log4j:WARN Please initialize the log4j system properly. +log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. +2021-01-14 15:01:17.150 INFO 17943 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195 +2021-01-14 15:01:17.154 INFO 17943 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 5.508 seconds (JVM running for 6.762) +``` + +## 3. Test + +> Add the `soul-examples` module to soul's pom.xml for test. + +### 3.1. Startup an HTTP backend service + +Startup `soul-examples-http` + +You can see the dependency in `soul-examples-http`'s pom.xml. + +```xml + + org.dromara + soul-spring-boot-starter-client-springmvc + ${soul.version} + +``` + +Configure the `application.yml` + +```yaml +soul: + http: + adminUrl: http://localhost:9095 + port: 8188 + contextPath: /http + appName: http + full: false +``` + +If `soul.http.full`=false, you need to add the `@SoulSpringMvcClient` annotation in controller or controller method. + +#### 3.1.1. Test the service + +Visit `http://localhost:8188/test/findByUserId?userId=1` and the result as follows. + +待补,文章内部有报错 + +#### 3.1.2. Test forward HTTP request + +Visit `http://localhost:9195/http/test/findByUserId?userId=1` and the result as follows. + +待补,文章内部有报错 + +You can see the following information in the console of `soul-bootstrap`. It means the forward HTTP request is successful. + +```shell +2021-01-14 20:42:57.123 INFO 29812 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:42:57.125 INFO 29812 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:42:57.126 INFO 29812 --- [work-threads-11] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +``` + +### 3.2. Startup two HTTP backend services to simulate load balance + +Choose `Allow parallel run` + +Change the port to `8189` + +Startup `soul-examples-http` again + +待补,文章内部有报错 + +#### 3.2.1. Test the service + +Visit `http://localhost:8189/test/findByUserId?userId=1` and the result as follows. + +待补,文章内部有报错 + +#### 3.2.2. Test load balance + +待补,文章内部有报错 + +Configure two HTTP service in selector + +Visit `http://localhost:9195/http/test/findByUserId?userId=1` more and more and result as follows. + +待补,文章内部有报错 +You can see the following information in the console of `soul-bootstrap`. It means the load balance is successful. + +```shell +2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8189/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:38.755 INFO 29812 --- [work-threads-23] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:38.756 INFO 29812 --- [work-threads-23] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:38.756 INFO 29812 --- [work-threads-23] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8189/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:40.976 INFO 29812 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:40.976 INFO 29812 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:40.977 INFO 29812 --- [-work-threads-1] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +``` + +#### 3.2.3. Press test + +Use `wrk` to press test and compare the two ways as follows. + +1. Visit the backend service directly. +2. Visit the service via soul. + +The performance drops slightly after using the gateway, probably because of the extra layer of forwarding. + +```shell +➜ soul git:(master) ✗ wrk -t8 -c40 -d30s http://localhost:8189/test/findByUserId\?userId\=1 +Running 30s test @ http://localhost:8189/test/findByUserId?userId=1 + 8 threads and 40 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 6.06ms 28.81ms 442.25ms 98.22% + Req/Sec 2.05k 493.86 2.84k 74.82% + 486269 requests in 30.05s, 51.01MB read +Requests/sec: 16179.68 +Transfer/sec: 1.70MB +➜ soul git:(master) ✗ wrk -t8 -c40 -d30s http://localhost:9195/http/test/findByUserId\?userId\=1 +Running 30s test @ http://localhost:9195/http/test/findByUserId?userId=1 + 8 threads and 40 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 14.37ms 18.11ms 255.66ms 93.06% + Req/Sec 459.41 139.11 1.01k 74.23% + 109533 requests in 30.09s, 11.49MB read +Requests/sec: 3639.60 +Transfer/sec: 390.98KB +``` + +#### 3.2.4. Problem in the process + +When startup the port of `8189`,but the output of console is still `8188`. + +待补,文章内部有报错 + +After modify the value of `soul.http.port`, the problem solved. + +待补,文章内部有报错 diff --git a/src/blog/soul_source_learning_02_divide_plugin.md b/src/blog/soul_source_learning_02_divide_plugin.md index 9d34ddf4ff..dd1598ca2b 100644 --- a/src/blog/soul_source_learning_02_divide_plugin.md +++ b/src/blog/soul_source_learning_02_divide_plugin.md @@ -1,134 +1,134 @@ ---- -title: Soul Learning(2) Use Divide Plugin -author: yuanjie -date: 2021-01-16 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -# The Divide plug-in uses - -## I. Initiation of the Project - -Start the soul-bootstrap (9195) and soul-admin (9095) modules. We can see from the bootstrap configuration file that the two modules perform data synchronization through the Web Socket protocol: - -![picture](https://uploader.shimo.im/f/nGr4Gtt1RDaxFZhp.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -You can also see from the bootstrap log: - -![picture](https://uploader.shimo.im/f/cvJNUI1WLaJEk0Pe.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -The so-called data synchronization refers to synchronizing the data configured in soul-admin to the JVM memory in the soul cluster, which is the key to the high performance of the gateway. - -After we start the two projects, we can test the divide plug-in through the background management system. - -## II. Introduction to divide Plug-in - -The divide plug-in is the core processing plug-in for the gateway to process HTTP protocol requests, and is also the only plug-in that is enabled by default: - -![picture](https://uploader.shimo.im/f/0CIBpm0YatPSWUUu.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -We can imagine what the gateway does and guess what the divide plug-in might do to handle HTTP requests? - -First of all, as a micro-service gateway, there must be a distributed micro-service cluster with multiple business lines behind it, and as a unified entrance to all services, the gateway must have the ability of traffic distribution/routing/load balancing, and the word divide, as its name implies, means distribution and distribution. So we can guess that the divide plug-in is to route and forward HTTP requests according to various rules, which is also the most basic capability of the gateway. - -When we open the list of plug-ins on the management interface, we can see that all plug-ins are composed of two parts: ** Selector ** (selector) and ** Selector rule **. - -The plug-in design idea is the core design idea of soul gateway, and the two concepts of selector and rule are also the soul of soul gateway. In theory, if we master it well, we can manage the traffic of any access gateway. - -A plug-in has multiple selectors, and one selector corresponds to multiple rules. The selector is equivalent to the first screening of traffic, and the rule is the final screening. - -### Selector - -![picture](https://uploader.shimo.im/f/KlNWtqo6shqyJYWZ.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - - * ** Name **: Give your selector an easily distinguishable name - * ** Type **: custom flow is a custom flow. Full flow is full flow. Custom traffic means that the request will follow your matching methods and conditions below. Full flow does not go. - * ** Match mode **: and or or means that the following multiple conditions are combined in the way of and or or. - * ** Condition **: - * URI: It means that you filter traffic according to the way of URI, and the way of match supports fuzzy matching (/**) - * Header: refers to filtering traffic based on the fields in the request header. - * Query: refers to filtering traffic based on the query criteria of the URI. - * IP: Refers to filtering traffic based on the real IP you request. - * Host: refers to filtering traffic based on the real host you request. - * Post: Not recommended. - * Condition matching: - * Match: Fuzzy matching, recommended and URI condition collocation, support restful style matching. (/test/**) - * =: The preceding and following values must be equal to match. - * RegEx: Regular match, which means that the previous value matches the following regular expression. - * Like: string fuzzy match. - * ** Whether to open or not **: Open to take effect - * ** Print the log **: When opened, the match log is printed when a match is made. - * ** Order of execution **: When there are multiple selectors, the one with the smaller execution order is executed first. - -### Selector rule - -![picture](https://uploader.shimo.im/f/If4ekdjZ1T0j11fy.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -![picture](https://uploader.shimo.im/f/CTJJ5j55VhfIxVsS.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -As you can see, the configuration of rules is similar to that of selectors, which can be understood as a more fine-grained custom configuration. - -## III. Use of divide plug-in - -Without further ado, let's just run the examples module provided by soul to demonstrate the divide plugin. - -![picture](https://uploader.shimo.im/f/8i3YFAMvzXsKJg7o.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -Notice that we ended up running the soul-examples-http module. The configuration file can use the default or customize the contextPath and appName, as shown in the figure above. - -We need to note that the contextPath attribute is very important, which is equivalent to the namespace of all our HTTP requests, and the selector is aligned one by one. Generally speaking, we can configure a service to correspond to a contextPath, and multiple service instances configured with the same contextPath under a service will be automatically mapped to the same selector for load balancing. - -After we start the process with port 8188, we can find that the selector and rule corresponding to this instance are automatically configured in the divide plug-in list of the management console: - -![picture](https://uploader.shimo.im/f/ozvPWCqVaXEGwG2E.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -You can see that the 8188 project address I started is automatically registered: - -![picture](https://uploader.shimo.im/f/MzTmhBkyZSRIiPAp.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -### Test gateway routing - -Test the forwarding without gateway through postman first: - -```plain -http://localhost:8188/order/findById?id=1 -``` - -![picture](https://uploader.shimo.im/f/OJi1lpFiwlHN53EE.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -Then test the forwarding to this interface through the gateway: - -```plain -http://localhost:9195/my-http/order/findById?id=1 -``` - -![picture](https://uploader.shimo.im/f/8p4u4OKuWp3inEVh.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -Looking at the log, we found that it was indeed forwarded to the 8188 interface address through the gateway: - -![picture](https://uploader.shimo.im/f/iE6V4aNqbaaUQz2K.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -### Test load balancing - -We change the port to 8189 and start the second process. - -![picture](https://uploader.shimo.im/f/arghWSgrccJ5262m.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -Note that IDEA needs to remove the restriction of Single instance only: - -![picture](https://uploader.shimo.im/f/cMdvwK0RI7AxmLf6.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -We enter the management console again and find that two configuration addresses appear under the my-http selector: - -![picture](https://uploader.shimo.im/f/nC35SJOlCZnNIrAz.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -At this point, we continue to test and find that the load balancing strategy does work: - -![picture](https://uploader.shimo.im/f/int2660TqS1nAkYB.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -Today, I just demonstrated the most basic configuration of the divide plug-in, and there are various other rule configurations that can be tried later. +--- +title: Soul Learning(2) Use Divide Plugin +author: yuanjie +date: 2021-01-16 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +# The Divide plug-in uses + +## I. Initiation of the Project + +Start the soul-bootstrap (9195) and soul-admin (9095) modules. We can see from the bootstrap configuration file that the two modules perform data synchronization through the Web Socket protocol: + +![picture](https://uploader.shimo.im/f/nGr4Gtt1RDaxFZhp.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +You can also see from the bootstrap log: + +![picture](https://uploader.shimo.im/f/cvJNUI1WLaJEk0Pe.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +The so-called data synchronization refers to synchronizing the data configured in soul-admin to the JVM memory in the soul cluster, which is the key to the high performance of the gateway. + +After we start the two projects, we can test the divide plug-in through the background management system. + +## II. Introduction to divide Plug-in + +The divide plug-in is the core processing plug-in for the gateway to process HTTP protocol requests, and is also the only plug-in that is enabled by default: + +![picture](https://uploader.shimo.im/f/0CIBpm0YatPSWUUu.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +We can imagine what the gateway does and guess what the divide plug-in might do to handle HTTP requests? + +First of all, as a micro-service gateway, there must be a distributed micro-service cluster with multiple business lines behind it, and as a unified entrance to all services, the gateway must have the ability of traffic distribution/routing/load balancing, and the word divide, as its name implies, means distribution and distribution. So we can guess that the divide plug-in is to route and forward HTTP requests according to various rules, which is also the most basic capability of the gateway. + +When we open the list of plug-ins on the management interface, we can see that all plug-ins are composed of two parts: ** Selector ** (selector) and ** Selector rule **. + +The plug-in design idea is the core design idea of soul gateway, and the two concepts of selector and rule are also the soul of soul gateway. In theory, if we master it well, we can manage the traffic of any access gateway. + +A plug-in has multiple selectors, and one selector corresponds to multiple rules. The selector is equivalent to the first screening of traffic, and the rule is the final screening. + +### Selector + +![picture](https://uploader.shimo.im/f/KlNWtqo6shqyJYWZ.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + + * ** Name **: Give your selector an easily distinguishable name + * ** Type **: custom flow is a custom flow. Full flow is full flow. Custom traffic means that the request will follow your matching methods and conditions below. Full flow does not go. + * ** Match mode **: and or or means that the following multiple conditions are combined in the way of and or or. + * ** Condition **: + * URI: It means that you filter traffic according to the way of URI, and the way of match supports fuzzy matching (/**) + * Header: refers to filtering traffic based on the fields in the request header. + * Query: refers to filtering traffic based on the query criteria of the URI. + * IP: Refers to filtering traffic based on the real IP you request. + * Host: refers to filtering traffic based on the real host you request. + * Post: Not recommended. + * Condition matching: + * Match: Fuzzy matching, recommended and URI condition collocation, support restful style matching. (/test/**) + * =: The preceding and following values must be equal to match. + * RegEx: Regular match, which means that the previous value matches the following regular expression. + * Like: string fuzzy match. + * ** Whether to open or not **: Open to take effect + * ** Print the log **: When opened, the match log is printed when a match is made. + * ** Order of execution **: When there are multiple selectors, the one with the smaller execution order is executed first. + +### Selector rule + +![picture](https://uploader.shimo.im/f/If4ekdjZ1T0j11fy.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +![picture](https://uploader.shimo.im/f/CTJJ5j55VhfIxVsS.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +As you can see, the configuration of rules is similar to that of selectors, which can be understood as a more fine-grained custom configuration. + +## III. Use of divide plug-in + +Without further ado, let's just run the examples module provided by soul to demonstrate the divide plugin. + +![picture](https://uploader.shimo.im/f/8i3YFAMvzXsKJg7o.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +Notice that we ended up running the soul-examples-http module. The configuration file can use the default or customize the contextPath and appName, as shown in the figure above. + +We need to note that the contextPath attribute is very important, which is equivalent to the namespace of all our HTTP requests, and the selector is aligned one by one. Generally speaking, we can configure a service to correspond to a contextPath, and multiple service instances configured with the same contextPath under a service will be automatically mapped to the same selector for load balancing. + +After we start the process with port 8188, we can find that the selector and rule corresponding to this instance are automatically configured in the divide plug-in list of the management console: + +![picture](https://uploader.shimo.im/f/ozvPWCqVaXEGwG2E.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +You can see that the 8188 project address I started is automatically registered: + +![picture](https://uploader.shimo.im/f/MzTmhBkyZSRIiPAp.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +### Test gateway routing + +Test the forwarding without gateway through postman first: + +```plain +http://localhost:8188/order/findById?id=1 +``` + +![picture](https://uploader.shimo.im/f/OJi1lpFiwlHN53EE.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +Then test the forwarding to this interface through the gateway: + +```plain +http://localhost:9195/my-http/order/findById?id=1 +``` + +![picture](https://uploader.shimo.im/f/8p4u4OKuWp3inEVh.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +Looking at the log, we found that it was indeed forwarded to the 8188 interface address through the gateway: + +![picture](https://uploader.shimo.im/f/iE6V4aNqbaaUQz2K.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +### Test load balancing + +We change the port to 8189 and start the second process. + +![picture](https://uploader.shimo.im/f/arghWSgrccJ5262m.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +Note that IDEA needs to remove the restriction of Single instance only: + +![picture](https://uploader.shimo.im/f/cMdvwK0RI7AxmLf6.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +We enter the management console again and find that two configuration addresses appear under the my-http selector: + +![picture](https://uploader.shimo.im/f/nC35SJOlCZnNIrAz.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +At this point, we continue to test and find that the load balancing strategy does work: + +![picture](https://uploader.shimo.im/f/int2660TqS1nAkYB.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +Today, I just demonstrated the most basic configuration of the divide plug-in, and there are various other rule configurations that can be tried later. diff --git a/src/blog/soul_source_learning_02_divide_plugin_source.md b/src/blog/soul_source_learning_02_divide_plugin_source.md index 80ab7c5fcb..f7d4790d7c 100644 --- a/src/blog/soul_source_learning_02_divide_plugin_source.md +++ b/src/blog/soul_source_learning_02_divide_plugin_source.md @@ -1,15 +1,15 @@ ---- -title: 待补,文章内部有报错 -author: 季鹏 -date: 2021-01-17 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -# Divide 插件如何转发 http 请求 - -待补,文章内部有报错 +--- +title: 待补,文章内部有报错 +author: 季鹏 +date: 2021-01-17 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +# Divide 插件如何转发 http 请求 + +待补,文章内部有报错 diff --git a/src/blog/soul_source_learning_02_http_client_register.md b/src/blog/soul_source_learning_02_http_client_register.md index 87f953c944..a905b59f78 100644 --- a/src/blog/soul_source_learning_02_http_client_register.md +++ b/src/blog/soul_source_learning_02_http_client_register.md @@ -1,427 +1,427 @@ ---- -title: Soul Gateway Learning (2) HTTP Client Access Source Code Parsing -author: fanjinpeng -date: 2021-01-18 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -# Logic Analysis of HTTP User Accessing to Soul Gateway - -## 1. Registration portal - -When the HTTP user accesses the Soul Gateway, it will call the soul-admin interface to register the interface that needs to be managed by the Soul Gateway. Let's see what we have done today. - -First look at the interface information called as follows: - -```java -// SpringMvcClientBeanPostProcessor.java -/** - * Instantiates a new Soul client bean post processor. - * - * @param soulSpringMvcConfig the soul spring mvc config - */ -public SpringMvcClientBeanPostProcessor(final SoulSpringMvcConfig soulSpringMvcConfig) { - ValidateUtils.validate(soulSpringMvcConfig); - this.soulSpringMvcConfig = soulSpringMvcConfig; - url = soulSpringMvcConfig.getAdminUrl() + "/soul-client/springmvc-register"; - executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); -} -``` - -## 2. Spring mvc-register interface logic - -Search "springmvc-register" globally and find the SoulClientController under the soul-admin module. See here. Are we familiar with those who often write CRUD? Ha-ha - -```java -// SoulClientController.java -/** - * Register spring mvc string. - * - * @param springMvcRegisterDTO the spring mvc register dto - * @return the string - */ -@PostMapping("/springmvc-register") -public String registerSpringMvc(@RequestBody final SpringMvcRegisterDTO springMvcRegisterDTO) { - return soulClientRegisterService.registerSpringMvc(springMvcRegisterDTO); -} -``` - -Service layer implementation class: - -```java -// SoulClientRegisterServiceImpl.java -@Override -@Transactional -public String registerSpringMvc(final SpringMvcRegisterDTO dto) { - if (dto.isRegisterMetaData()) { - MetaDataDO exist = metaDataMapper.findByPath(dto.getPath()); - if (Objects.isNull(exist)) { - saveSpringMvcMetaData(dto); - } - } - String selectorId = handlerSpringMvcSelector(dto); - handlerSpringMvcRule(selectorId, dto); - return SoulResultMessage.SUCCESS; -} -``` - -Dto. IsRegister MetaData () is used to determine whether to register metadata information. I don't know when to use it, and I have doubts.//TODO, go down first. - -### 2.1 Take a look at the method handlerSpringMvcSelector to handle the Selector. - -```java -// SoulClientRegisterServiceImpl.java -private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) { - String contextPath = dto.getContext(); - // 根据 contextPath 到数据库里查询,是否已经注册过。 - SelectorDO selectorDO = selectorService.findByName(contextPath); - String selectorId; - String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort())); - if (Objects.isNull(selectorDO)) { - // 还没有注册过 - selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri); - } else { - // 已经注册过,业务系统重启了会到这里 - selectorId = selectorDO.getId(); - //update upstream - String handle = selectorDO.getHandle(); - String handleAdd; - DivideUpstream addDivideUpstream = buildDivideUpstream(uri); - SelectorData selectorData = selectorService.buildByName(contextPath); - if (StringUtils.isBlank(handle)) { - handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream)); - } else { - List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class); - for (DivideUpstream upstream : exist) { - if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) { - return selectorId; - } - } - exist.add(addDivideUpstream); - handleAdd = GsonUtils.getInstance().toJson(exist); - } - selectorDO.setHandle(handleAdd); - selectorData.setHandle(handleAdd); - // update db - selectorMapper.updateSelective(selectorDO); - // submit upstreamCheck - upstreamCheckService.submit(contextPath, addDivideUpstream); - // publish change event. - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, - Collections.singletonList(selectorData))); - } - return selectorId; -} -``` - -#### 2.1.1 First Access to Soul Gateway - -For the new access, the selectorDO cannot be found in the database. Enter the registerSelector method to carefully see which database tables have been inserted with data. - -```java -// SoulClientRegisterServiceImpl.java -private String registerSelector(final String contextPath, final String rpcType, final String appName, final String uri) { - SelectorDTO selectorDTO = SelectorDTO.builder() - .name(contextPath) - .type(SelectorTypeEnum.CUSTOM_FLOW.getCode()) - .matchMode(MatchModeEnum.AND.getCode()) - .enabled(Boolean.TRUE) - .loged(Boolean.TRUE) - .continued(Boolean.TRUE) - .sort(1) - .build(); - if (RpcTypeEnum.DUBBO.getName().equals(rpcType)) { - selectorDTO.setPluginId(getPluginId(PluginEnum.DUBBO.getName())); - } else if (RpcTypeEnum.SPRING_CLOUD.getName().equals(rpcType)) { - selectorDTO.setPluginId(getPluginId(PluginEnum.SPRING_CLOUD.getName())); - selectorDTO.setHandle(GsonUtils.getInstance().toJson(buildSpringCloudSelectorHandle(appName))); - } else if (RpcTypeEnum.SOFA.getName().equals(rpcType)) { - selectorDTO.setPluginId(getPluginId(PluginEnum.SOFA.getName())); - selectorDTO.setHandle(appName); - } else if (RpcTypeEnum.TARS.getName().equals(rpcType)) { - selectorDTO.setPluginId(getPluginId(PluginEnum.TARS.getName())); - selectorDTO.setHandle(appName); - } else { - //is divide - DivideUpstream divideUpstream = buildDivideUpstream(uri); - String handler = GsonUtils.getInstance().toJson(Collections.singletonList(divideUpstream)); - selectorDTO.setHandle(handler); - selectorDTO.setPluginId(getPluginId(PluginEnum.DIVIDE.getName())); - upstreamCheckService.submit(selectorDTO.getName(), divideUpstream); - } - SelectorConditionDTO selectorConditionDTO = new SelectorConditionDTO(); - selectorConditionDTO.setParamType(ParamTypeEnum.URI.getName()); - selectorConditionDTO.setParamName("/"); - selectorConditionDTO.setOperator(OperatorEnum.MATCH.getAlias()); - selectorConditionDTO.setParamValue(contextPath + "/**"); - selectorDTO.setSelectorConditions(Collections.singletonList(selectorConditionDTO)); - return selectorService.register(selectorDTO); -} -``` - -Are you excited to see so many if else? You can think about how to optimize so many if else and PR ^-^. - -Having written so much, it is nothing more than encapsulating the Selector DTO object, and finally calling the selectorS ervice. Register (Selector DTO) into the library, and continuing to follow. - -```java -// SelectorServiceImpl.java -@Override -public String register(final SelectorDTO selectorDTO) { - SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO); - List selectorConditionDTOs = selectorDTO.getSelectorConditions(); - if (StringUtils.isEmpty(selectorDTO.getId())) { - selectorMapper.insertSelective(selectorDO); - selectorConditionDTOs.forEach(selectorConditionDTO -> { - selectorConditionDTO.setSelectorId(selectorDO.getId()); - // 这里在 for 循环里调用 dao 层插入数据,是不是可以考虑挪出去一次性批量插入? - selectorConditionMapper.insertSelective(SelectorConditionDO - .buildSelectorConditionDO(selectorConditionDTO)); - }); - } - publishEvent(selectorDO, selectorConditionDTOs); - return selectorDO.getId(); -} -``` - -You can see that there are two warehousing methods, which insert data into the selector and selector \_ condition tables respectively. Here we will not discuss the structure and business significance of the table in detail, and we will add it later. - -The publishEvent method, which involves the ApplicationEventPublisher interface, is an implementation of the observer pattern. After the event is published, the subsequent operations are completed through the listener. Here, press No Table first, and then write an article for analysis. - -#### 2.1.2 Soul Gateway has been accessed - -Just like Inception, we go back two layers of dreams and go back to the other branch of inserting data. It can be imagined that the system that has been connected to the Soul gateway restarts, or the new node starts to go. - -Post the previous code again: - -```java -// SoulClientRegisterServiceImpl.java -private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) { - String contextPath = dto.getContext(); - // 根据 contextPath 到数据库里查询,是否已经注册过。 - SelectorDO selectorDO = selectorService.findByName(contextPath); - String selectorId; - String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort())); - if (Objects.isNull(selectorDO)) { - // 还没有注册过 - selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri); - } else { - // 已接入的业务系统重启,或新节点启动,会到这里 - selectorId = selectorDO.getId(); - //update upstream - // handle 字段存储这个接口真实节点信息,可能存在多台机器需要负载均衡的场景 - String handle = selectorDO.getHandle(); - String handleAdd; - DivideUpstream addDivideUpstream = buildDivideUpstream(uri); - SelectorData selectorData = selectorService.buildByName(contextPath); - if (StringUtils.isBlank(handle)) { - // 这个接口虽然之前注册过,但第1个服务器节点接入 Soul 时会进来 - handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream)); - } else { - // 如果已经至少有1个服务器节点已接入,会进到这里,判断是否是同一个节点(使用 upstreamUrl 区分),如果相同直接返回 - List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class); - for (DivideUpstream upstream : exist) { - if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) { - return selectorId; - } - } - // 如果不是同一个节点,把新节点加入到 handle 字段中 - exist.add(addDivideUpstream); - handleAdd = GsonUtils.getInstance().toJson(exist); - } - selectorDO.setHandle(handleAdd); - selectorData.setHandle(handleAdd); - // update db 更新数据库 - selectorMapper.updateSelective(selectorDO); - // submit upstreamCheck - upstreamCheckService.submit(contextPath, addDivideUpstream); - // publish change event. - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, - Collections.singletonList(selectorData))); - } - return selectorId; -} -``` - -Because the database table structure design has not been studied, according to some known guesses, one selector corresponds to one divide plug-in, which is identified by contextPath (here is "/HTTP"), and one contextPath can deploy multiple server nodes. The node information is stored in the handle field as JSON. - -```json -// handle/handleAdd 数据格式 -[ - { - "upstreamHost": "localhost", - "protocol": "http://", - "upstreamUrl": "10.0.0.12:8188", - "weight": 50, - "status": true, - "timestamp": 0, - "warmup": 0 - } -] -``` - -The next step is to update the database update Selective. - -upstreamCheckService.submit(contextPath, addDivideUpstream); The real server node information is cached in a Map (UPSTREAM \_ MAP), and there are regular tasks to detect the activity. If the service node is found to be down, it will be eliminated to prevent the request from being sent to the node that has been down. - -Then there is the eventPublisher. PublishEvent (), which, like the previous publishEvent method, publishes the event and completes the subsequent operations through the listener. Here, the message of data SelectorData modification is sent through the web socket long connection established with the Soul gateway. The Soul gateway modifies the data according to the message. What data is modified and how to modify it will be analyzed later. - -At this point, the handlerSpringMvcSelector method is finally analyzed. - -### 2.Let's take a look at the method handlerSpringMvcRule, which handles the Rule. - -```java -// SoulClientRegisterServiceImpl.java -private void handlerSpringMvcRule(final String selectorId, final SpringMvcRegisterDTO dto) { - RuleDO ruleDO = ruleMapper.findByName(dto.getRuleName()); - if (Objects.isNull(ruleDO)) { - registerRule(selectorId, dto.getPath(), dto.getRpcType(), dto.getRuleName()); - } -} -``` - -First, take the name of the rule and go to the rule table to get the data. If the table name has been registered, there is no operation. - -Look at the database data, which is the interface address under the business system. - -```bash -mysql> use soul; -Database changed - -mysql> select * from rule where name = '/http/order/findById' \G -*************************** 1. row *************************** - id: 1349650371868782592 - selector_id: 1349650371302551552 - match_mode: 0 - name: /http/order/findById - enabled: 1 - loged: 1 - sort: 1 - handle: {"loadBalance":"random","retry":0,"timeout":3000} -date_created: 2021-01-14 17:31:39 -date_updated: 2021-01-14 17:31:39 -1 row in set (0.00 sec) -``` - -If you don't get the data, register this rule. - -```java -// SoulClientRegisterServiceImpl.java -private void registerRule(final String selectorId, final String path, final String rpcType, final String ruleName) { - RuleHandle ruleHandle = RuleHandleFactory.ruleHandle(RpcTypeEnum.acquireByName(rpcType), path); - RuleDTO ruleDTO = RuleDTO.builder() - .selectorId(selectorId) - .name(ruleName) - .matchMode(MatchModeEnum.AND.getCode()) - .enabled(Boolean.TRUE) - .loged(Boolean.TRUE) - .sort(1) - .handle(ruleHandle.toJson()) - .build(); - RuleConditionDTO ruleConditionDTO = RuleConditionDTO.builder() - .paramType(ParamTypeEnum.URI.getName()) - .paramName("/") - .paramValue(path) - .build(); - if (path.indexOf("*") > 1) { - ruleConditionDTO.setOperator(OperatorEnum.MATCH.getAlias()); - } else { - ruleConditionDTO.setOperator(OperatorEnum.EQ.getAlias()); - } - ruleDTO.setRuleConditions(Collections.singletonList(ruleConditionDTO)); - ruleService.register(ruleDTO); -} -``` - -In the first line, the corresponding RuleHandle is obtained according to rpcType ( "HTTP"). Here, three types are built in by default. Here, HTTP corresponds to DivideRuleHandle. - -```java -// RuleHandleFactory.java -public final class RuleHandleFactory { - - /** - * The RpcType to RuleHandle class map. - */ - private static final Map> RPC_TYPE_TO_RULE_HANDLE_CLASS = new ConcurrentHashMap<>(); - - /** - * The default RuleHandle. - */ - private static final Class DEFAULT_RULE_HANDLE = SpringCloudRuleHandle.class; - - static { - RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.HTTP, DivideRuleHandle.class); - RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.DUBBO, DubboRuleHandle.class); - RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.SOFA, SofaRuleHandle.class); - } - - /** - * Get a RuleHandle object with given rpc type and path. - * @param rpcType rpc type. - * @param path path. - * @return RuleHandle object. - */ - public static RuleHandle ruleHandle(final RpcTypeEnum rpcType, final String path) { - if (Objects.isNull(rpcType)) { - return null; - } - Class clazz = RPC_TYPE_TO_RULE_HANDLE_CLASS.getOrDefault(rpcType, DEFAULT_RULE_HANDLE); - try { - return clazz.newInstance().createDefault(path); - } catch (InstantiationException | IllegalAccessException e) { - throw new SoulException( - String.format("Init RuleHandle failed with rpc type: %s, rule class: %s, exception: %s", - rpcType, - clazz.getSimpleName(), - e.getMessage())); - } - } -} -``` - -Let's construct the RuleDTO object and register the rules. - -```java -// RuleServiceImpl.java -@Override -public String register(final RuleDTO ruleDTO) { - RuleDO ruleDO = RuleDO.buildRuleDO(ruleDTO); - List ruleConditions = ruleDTO.getRuleConditions(); - if (StringUtils.isEmpty(ruleDTO.getId())) { - ruleMapper.insertSelective(ruleDO); - ruleConditions.forEach(ruleConditionDTO -> { - ruleConditionDTO.setRuleId(ruleDO.getId()); - // 这里在 for 循环里调用 dao 层插入数据,是不是可以考虑挪出去一次性批量插入? - ruleConditionMapper.insertSelective(RuleConditionDO - .buildRuleConditionDO(ruleConditionDTO)); - }); - } - publishEvent(ruleDO, ruleConditions); - return ruleDO.getId(); -} -``` - -Insert data into the rule and rule \_ condition tables, respectively. - -The publishEvent () method sends RuleData data to the Soul gateway through the web socket long connection. - -## 3.Sum up - -At this point, the logical analysis of calling the "/soul-client/springmvc-register" interface is finished, and we summarize as follows: - -- Process the selector - - Add or modify selector and selector \_ condition table data, and persist them to MySQL. - - Send data change information to Soul gateway through websocket. -- Process the rule - - Add or modify the data of rule and rule \_ condition tables, and persist them to MySQL. - - Send data change information to Soul gateway through websocket. - -The table structure and field meaning need further study and research. After the websocket is sent to the Soul gateway, what the gateway has done also needs follow-up analysis. - -At this point, the registration logic of the HTTP user accessing the Soul gateway is analyzed. - -If you have the need to use the gateway in your work, or you have the pursuit of learning the gateway, welcome to analyze and learn with me. Soul Gateway, you deserve it. +--- +title: Soul Gateway Learning (2) HTTP Client Access Source Code Parsing +author: fanjinpeng +date: 2021-01-18 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +# Logic Analysis of HTTP User Accessing to Soul Gateway + +## 1. Registration portal + +When the HTTP user accesses the Soul Gateway, it will call the soul-admin interface to register the interface that needs to be managed by the Soul Gateway. Let's see what we have done today. + +First look at the interface information called as follows: + +```java +// SpringMvcClientBeanPostProcessor.java +/** + * Instantiates a new Soul client bean post processor. + * + * @param soulSpringMvcConfig the soul spring mvc config + */ +public SpringMvcClientBeanPostProcessor(final SoulSpringMvcConfig soulSpringMvcConfig) { + ValidateUtils.validate(soulSpringMvcConfig); + this.soulSpringMvcConfig = soulSpringMvcConfig; + url = soulSpringMvcConfig.getAdminUrl() + "/soul-client/springmvc-register"; + executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); +} +``` + +## 2. Spring mvc-register interface logic + +Search "springmvc-register" globally and find the SoulClientController under the soul-admin module. See here. Are we familiar with those who often write CRUD? Ha-ha + +```java +// SoulClientController.java +/** + * Register spring mvc string. + * + * @param springMvcRegisterDTO the spring mvc register dto + * @return the string + */ +@PostMapping("/springmvc-register") +public String registerSpringMvc(@RequestBody final SpringMvcRegisterDTO springMvcRegisterDTO) { + return soulClientRegisterService.registerSpringMvc(springMvcRegisterDTO); +} +``` + +Service layer implementation class: + +```java +// SoulClientRegisterServiceImpl.java +@Override +@Transactional +public String registerSpringMvc(final SpringMvcRegisterDTO dto) { + if (dto.isRegisterMetaData()) { + MetaDataDO exist = metaDataMapper.findByPath(dto.getPath()); + if (Objects.isNull(exist)) { + saveSpringMvcMetaData(dto); + } + } + String selectorId = handlerSpringMvcSelector(dto); + handlerSpringMvcRule(selectorId, dto); + return SoulResultMessage.SUCCESS; +} +``` + +Dto. IsRegister MetaData () is used to determine whether to register metadata information. I don't know when to use it, and I have doubts.//TODO, go down first. + +### 2.1 Take a look at the method handlerSpringMvcSelector to handle the Selector. + +```java +// SoulClientRegisterServiceImpl.java +private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) { + String contextPath = dto.getContext(); + // 根据 contextPath 到数据库里查询,是否已经注册过。 + SelectorDO selectorDO = selectorService.findByName(contextPath); + String selectorId; + String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort())); + if (Objects.isNull(selectorDO)) { + // 还没有注册过 + selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri); + } else { + // 已经注册过,业务系统重启了会到这里 + selectorId = selectorDO.getId(); + //update upstream + String handle = selectorDO.getHandle(); + String handleAdd; + DivideUpstream addDivideUpstream = buildDivideUpstream(uri); + SelectorData selectorData = selectorService.buildByName(contextPath); + if (StringUtils.isBlank(handle)) { + handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream)); + } else { + List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class); + for (DivideUpstream upstream : exist) { + if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) { + return selectorId; + } + } + exist.add(addDivideUpstream); + handleAdd = GsonUtils.getInstance().toJson(exist); + } + selectorDO.setHandle(handleAdd); + selectorData.setHandle(handleAdd); + // update db + selectorMapper.updateSelective(selectorDO); + // submit upstreamCheck + upstreamCheckService.submit(contextPath, addDivideUpstream); + // publish change event. + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, + Collections.singletonList(selectorData))); + } + return selectorId; +} +``` + +#### 2.1.1 First Access to Soul Gateway + +For the new access, the selectorDO cannot be found in the database. Enter the registerSelector method to carefully see which database tables have been inserted with data. + +```java +// SoulClientRegisterServiceImpl.java +private String registerSelector(final String contextPath, final String rpcType, final String appName, final String uri) { + SelectorDTO selectorDTO = SelectorDTO.builder() + .name(contextPath) + .type(SelectorTypeEnum.CUSTOM_FLOW.getCode()) + .matchMode(MatchModeEnum.AND.getCode()) + .enabled(Boolean.TRUE) + .loged(Boolean.TRUE) + .continued(Boolean.TRUE) + .sort(1) + .build(); + if (RpcTypeEnum.DUBBO.getName().equals(rpcType)) { + selectorDTO.setPluginId(getPluginId(PluginEnum.DUBBO.getName())); + } else if (RpcTypeEnum.SPRING_CLOUD.getName().equals(rpcType)) { + selectorDTO.setPluginId(getPluginId(PluginEnum.SPRING_CLOUD.getName())); + selectorDTO.setHandle(GsonUtils.getInstance().toJson(buildSpringCloudSelectorHandle(appName))); + } else if (RpcTypeEnum.SOFA.getName().equals(rpcType)) { + selectorDTO.setPluginId(getPluginId(PluginEnum.SOFA.getName())); + selectorDTO.setHandle(appName); + } else if (RpcTypeEnum.TARS.getName().equals(rpcType)) { + selectorDTO.setPluginId(getPluginId(PluginEnum.TARS.getName())); + selectorDTO.setHandle(appName); + } else { + //is divide + DivideUpstream divideUpstream = buildDivideUpstream(uri); + String handler = GsonUtils.getInstance().toJson(Collections.singletonList(divideUpstream)); + selectorDTO.setHandle(handler); + selectorDTO.setPluginId(getPluginId(PluginEnum.DIVIDE.getName())); + upstreamCheckService.submit(selectorDTO.getName(), divideUpstream); + } + SelectorConditionDTO selectorConditionDTO = new SelectorConditionDTO(); + selectorConditionDTO.setParamType(ParamTypeEnum.URI.getName()); + selectorConditionDTO.setParamName("/"); + selectorConditionDTO.setOperator(OperatorEnum.MATCH.getAlias()); + selectorConditionDTO.setParamValue(contextPath + "/**"); + selectorDTO.setSelectorConditions(Collections.singletonList(selectorConditionDTO)); + return selectorService.register(selectorDTO); +} +``` + +Are you excited to see so many if else? You can think about how to optimize so many if else and PR ^-^. + +Having written so much, it is nothing more than encapsulating the Selector DTO object, and finally calling the selectorS ervice. Register (Selector DTO) into the library, and continuing to follow. + +```java +// SelectorServiceImpl.java +@Override +public String register(final SelectorDTO selectorDTO) { + SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO); + List selectorConditionDTOs = selectorDTO.getSelectorConditions(); + if (StringUtils.isEmpty(selectorDTO.getId())) { + selectorMapper.insertSelective(selectorDO); + selectorConditionDTOs.forEach(selectorConditionDTO -> { + selectorConditionDTO.setSelectorId(selectorDO.getId()); + // 这里在 for 循环里调用 dao 层插入数据,是不是可以考虑挪出去一次性批量插入? + selectorConditionMapper.insertSelective(SelectorConditionDO + .buildSelectorConditionDO(selectorConditionDTO)); + }); + } + publishEvent(selectorDO, selectorConditionDTOs); + return selectorDO.getId(); +} +``` + +You can see that there are two warehousing methods, which insert data into the selector and selector \_ condition tables respectively. Here we will not discuss the structure and business significance of the table in detail, and we will add it later. + +The publishEvent method, which involves the ApplicationEventPublisher interface, is an implementation of the observer pattern. After the event is published, the subsequent operations are completed through the listener. Here, press No Table first, and then write an article for analysis. + +#### 2.1.2 Soul Gateway has been accessed + +Just like Inception, we go back two layers of dreams and go back to the other branch of inserting data. It can be imagined that the system that has been connected to the Soul gateway restarts, or the new node starts to go. + +Post the previous code again: + +```java +// SoulClientRegisterServiceImpl.java +private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) { + String contextPath = dto.getContext(); + // 根据 contextPath 到数据库里查询,是否已经注册过。 + SelectorDO selectorDO = selectorService.findByName(contextPath); + String selectorId; + String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort())); + if (Objects.isNull(selectorDO)) { + // 还没有注册过 + selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri); + } else { + // 已接入的业务系统重启,或新节点启动,会到这里 + selectorId = selectorDO.getId(); + //update upstream + // handle 字段存储这个接口真实节点信息,可能存在多台机器需要负载均衡的场景 + String handle = selectorDO.getHandle(); + String handleAdd; + DivideUpstream addDivideUpstream = buildDivideUpstream(uri); + SelectorData selectorData = selectorService.buildByName(contextPath); + if (StringUtils.isBlank(handle)) { + // 这个接口虽然之前注册过,但第1个服务器节点接入 Soul 时会进来 + handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream)); + } else { + // 如果已经至少有1个服务器节点已接入,会进到这里,判断是否是同一个节点(使用 upstreamUrl 区分),如果相同直接返回 + List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class); + for (DivideUpstream upstream : exist) { + if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) { + return selectorId; + } + } + // 如果不是同一个节点,把新节点加入到 handle 字段中 + exist.add(addDivideUpstream); + handleAdd = GsonUtils.getInstance().toJson(exist); + } + selectorDO.setHandle(handleAdd); + selectorData.setHandle(handleAdd); + // update db 更新数据库 + selectorMapper.updateSelective(selectorDO); + // submit upstreamCheck + upstreamCheckService.submit(contextPath, addDivideUpstream); + // publish change event. + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, + Collections.singletonList(selectorData))); + } + return selectorId; +} +``` + +Because the database table structure design has not been studied, according to some known guesses, one selector corresponds to one divide plug-in, which is identified by contextPath (here is "/HTTP"), and one contextPath can deploy multiple server nodes. The node information is stored in the handle field as JSON. + +```json +// handle/handleAdd 数据格式 +[ + { + "upstreamHost": "localhost", + "protocol": "http://", + "upstreamUrl": "10.0.0.12:8188", + "weight": 50, + "status": true, + "timestamp": 0, + "warmup": 0 + } +] +``` + +The next step is to update the database update Selective. + +upstreamCheckService.submit(contextPath, addDivideUpstream); The real server node information is cached in a Map (UPSTREAM \_ MAP), and there are regular tasks to detect the activity. If the service node is found to be down, it will be eliminated to prevent the request from being sent to the node that has been down. + +Then there is the eventPublisher. PublishEvent (), which, like the previous publishEvent method, publishes the event and completes the subsequent operations through the listener. Here, the message of data SelectorData modification is sent through the web socket long connection established with the Soul gateway. The Soul gateway modifies the data according to the message. What data is modified and how to modify it will be analyzed later. + +At this point, the handlerSpringMvcSelector method is finally analyzed. + +### 2.Let's take a look at the method handlerSpringMvcRule, which handles the Rule. + +```java +// SoulClientRegisterServiceImpl.java +private void handlerSpringMvcRule(final String selectorId, final SpringMvcRegisterDTO dto) { + RuleDO ruleDO = ruleMapper.findByName(dto.getRuleName()); + if (Objects.isNull(ruleDO)) { + registerRule(selectorId, dto.getPath(), dto.getRpcType(), dto.getRuleName()); + } +} +``` + +First, take the name of the rule and go to the rule table to get the data. If the table name has been registered, there is no operation. + +Look at the database data, which is the interface address under the business system. + +```bash +mysql> use soul; +Database changed + +mysql> select * from rule where name = '/http/order/findById' \G +*************************** 1. row *************************** + id: 1349650371868782592 + selector_id: 1349650371302551552 + match_mode: 0 + name: /http/order/findById + enabled: 1 + loged: 1 + sort: 1 + handle: {"loadBalance":"random","retry":0,"timeout":3000} +date_created: 2021-01-14 17:31:39 +date_updated: 2021-01-14 17:31:39 +1 row in set (0.00 sec) +``` + +If you don't get the data, register this rule. + +```java +// SoulClientRegisterServiceImpl.java +private void registerRule(final String selectorId, final String path, final String rpcType, final String ruleName) { + RuleHandle ruleHandle = RuleHandleFactory.ruleHandle(RpcTypeEnum.acquireByName(rpcType), path); + RuleDTO ruleDTO = RuleDTO.builder() + .selectorId(selectorId) + .name(ruleName) + .matchMode(MatchModeEnum.AND.getCode()) + .enabled(Boolean.TRUE) + .loged(Boolean.TRUE) + .sort(1) + .handle(ruleHandle.toJson()) + .build(); + RuleConditionDTO ruleConditionDTO = RuleConditionDTO.builder() + .paramType(ParamTypeEnum.URI.getName()) + .paramName("/") + .paramValue(path) + .build(); + if (path.indexOf("*") > 1) { + ruleConditionDTO.setOperator(OperatorEnum.MATCH.getAlias()); + } else { + ruleConditionDTO.setOperator(OperatorEnum.EQ.getAlias()); + } + ruleDTO.setRuleConditions(Collections.singletonList(ruleConditionDTO)); + ruleService.register(ruleDTO); +} +``` + +In the first line, the corresponding RuleHandle is obtained according to rpcType ( "HTTP"). Here, three types are built in by default. Here, HTTP corresponds to DivideRuleHandle. + +```java +// RuleHandleFactory.java +public final class RuleHandleFactory { + + /** + * The RpcType to RuleHandle class map. + */ + private static final Map> RPC_TYPE_TO_RULE_HANDLE_CLASS = new ConcurrentHashMap<>(); + + /** + * The default RuleHandle. + */ + private static final Class DEFAULT_RULE_HANDLE = SpringCloudRuleHandle.class; + + static { + RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.HTTP, DivideRuleHandle.class); + RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.DUBBO, DubboRuleHandle.class); + RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.SOFA, SofaRuleHandle.class); + } + + /** + * Get a RuleHandle object with given rpc type and path. + * @param rpcType rpc type. + * @param path path. + * @return RuleHandle object. + */ + public static RuleHandle ruleHandle(final RpcTypeEnum rpcType, final String path) { + if (Objects.isNull(rpcType)) { + return null; + } + Class clazz = RPC_TYPE_TO_RULE_HANDLE_CLASS.getOrDefault(rpcType, DEFAULT_RULE_HANDLE); + try { + return clazz.newInstance().createDefault(path); + } catch (InstantiationException | IllegalAccessException e) { + throw new SoulException( + String.format("Init RuleHandle failed with rpc type: %s, rule class: %s, exception: %s", + rpcType, + clazz.getSimpleName(), + e.getMessage())); + } + } +} +``` + +Let's construct the RuleDTO object and register the rules. + +```java +// RuleServiceImpl.java +@Override +public String register(final RuleDTO ruleDTO) { + RuleDO ruleDO = RuleDO.buildRuleDO(ruleDTO); + List ruleConditions = ruleDTO.getRuleConditions(); + if (StringUtils.isEmpty(ruleDTO.getId())) { + ruleMapper.insertSelective(ruleDO); + ruleConditions.forEach(ruleConditionDTO -> { + ruleConditionDTO.setRuleId(ruleDO.getId()); + // 这里在 for 循环里调用 dao 层插入数据,是不是可以考虑挪出去一次性批量插入? + ruleConditionMapper.insertSelective(RuleConditionDO + .buildRuleConditionDO(ruleConditionDTO)); + }); + } + publishEvent(ruleDO, ruleConditions); + return ruleDO.getId(); +} +``` + +Insert data into the rule and rule \_ condition tables, respectively. + +The publishEvent () method sends RuleData data to the Soul gateway through the web socket long connection. + +## 3.Sum up + +At this point, the logical analysis of calling the "/soul-client/springmvc-register" interface is finished, and we summarize as follows: + +- Process the selector + - Add or modify selector and selector \_ condition table data, and persist them to MySQL. + - Send data change information to Soul gateway through websocket. +- Process the rule + - Add or modify the data of rule and rule \_ condition tables, and persist them to MySQL. + - Send data change information to Soul gateway through websocket. + +The table structure and field meaning need further study and research. After the websocket is sent to the Soul gateway, what the gateway has done also needs follow-up analysis. + +At this point, the registration logic of the HTTP user accessing the Soul gateway is analyzed. + +If you have the need to use the gateway in your work, or you have the pursuit of learning the gateway, welcome to analyze and learn with me. Soul Gateway, you deserve it. diff --git a/src/blog/soul_source_learning_05_plugin.md b/src/blog/soul_source_learning_05_plugin.md index 74ee2d6c29..259e779fa5 100644 --- a/src/blog/soul_source_learning_05_plugin.md +++ b/src/blog/soul_source_learning_05_plugin.md @@ -1,492 +1,492 @@ ---- -title: Soul Gateway learning plugin chain and load balancing analysis -author: zhuming -date: 2021-01-15 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -# Plug-in chain summary - -Start with a class diagram: - -![plugin diagram](/assets/img/01.png) - -Two of the most basic plug-in classes are: - -- SoulPlugin: Defines the interface of the plug-in responsibility. The key method `execute()` is called by the upper layer. `skip()` The method can cause some plug-ins to be skipped in some requests. - -- AbstractPlugin: An abstract class that implements an interface `execute()`, defines a common execution process, and uses the design pattern of the template method to provide `doExecute()` an abstract method for the implementation class to write its own logic. - -## AbstractSoulPlugin - -Specific analysis of the `execute()` following `AbstractSoulPlugin` categories: - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - String pluginName = named(); - final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); - // If pluginData.getEnabled() is false, it will skip to the next plugin, only a few plugins will enter this condition (DividePlugin, AlibabaDubboPlugin, etc.) - if (pluginData != null && pluginData.getEnabled()) { - // Get all selectors on the plugin - final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); - if (CollectionUtils.isEmpty(selectors)) { - return CheckUtils.checkSelector(pluginName, exchange, chain); - } - // Check whether the request path in the context matches the selector and get the only matching selector data - final SelectorData selectorData = matchSelector(exchange, selectors); - if (Objects.isNull(selectorData)) { - if (PluginEnum.WAF.getName().equals(pluginName)) { - return doExecute(exchange, chain, null, null); - } - return CheckUtils.checkSelector(pluginName, exchange, chain); - } - if (selectorData.getLoged()) { - log.info("{} selector success match , selector name :{}", pluginName, selectorData.getName()); - } - // Gets the individual resource rules in the selector - final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); - if (CollectionUtils.isEmpty(rules)) { - if (PluginEnum.WAF.getName().equals(pluginName)) { - return doExecute(exchange, chain, null, null); - } - return CheckUtils.checkRule(pluginName, exchange, chain); - } - RuleData rule; - if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { - rule = rules.get(rules.size() - 1); - } else { - // Match the path to obtain a unique rule - rule = matchRule(exchange, rules); - } - if (Objects.isNull(rule)) { - return CheckUtils.checkRule(pluginName, exchange, chain); - } - if (rule.getLoged()) { - log.info("{} rule success match ,rule name :{}", pluginName, rule.getName()); - } - // Execute methods of subclasses - return doExecute(exchange, chain, selectorData, rule); - } - // Execute the next plug-in on the plug-in chain - return chain.execute(exchange); -} -``` - -Through code analysis, some conclusions can be drawn: - -- Execute () has two logics: one is the matching of the request path with the selector and the rule, which finally confirms a unique rule and calls the subclass doExecute (); The second is to execute the next plug-in in the plug-in chain. -- The execute () actually abstracts a set of rule matching logic, which is used by all the "forwarding type" plug-ins. Currently, I know the forwarding type plug-ins are `DividePlugin` (HTTP request) and `AlibabaDubboPlugin` (dubbo request). Other types of plug-ins that do not override the execute () method will go directly to the next plug-in. - -## SoulPluginChain - -Another point here is the formation and chain call of the plug-in chain. Let's analyze `SoulPluginChain` this part: - -![plugin02](/assets/img/02.png) - -The SoulPluginChain interface also defines `execute()` methods for the caller to use, and its only subclass, DefaultSoulPluginChain, implements chained calls: - -```java -public Mono execute(final ServerWebExchange exchange) { - return Mono.defer(() -> { - // plugins contains all plugins loaded by the gateway - if (this.index < plugins.size()) { - // Each time the execute() method is called, the index index increases and is called to the next plug-in - SoulPlugin plugin = plugins.get(this.index++); - // Determine whether the current plug-in needs to be skipped based on the context - Boolean skip = plugin.skip(exchange); - if (skip) { - return this.execute(exchange); - } else { - return plugin.execute(exchange, this); - } - } else { - return Mono.empty(); - } - }); -} -``` - -It's curious `plugins` to see where this list of plug-ins comes from. Here's an explanation. DefaultSoulPluginChain is a static inner class of SoulWebHandler. Is `plugins` an attribute in the Soul Web Handle: - -```java -public final class SoulWebHandler implements WebHandler { - - private List plugins; - - public SoulWebHandler(final List plugins) { - this.plugins = plugins; - // ... - } - - @Override - public Mono handle(@NonNull final ServerWebExchange exchange) { - // ... - return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler) - .doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time))); - } - - private static class DefaultSoulPluginChain implements SoulPluginChain { - } -} -``` - -So where did the `plugins` SoulWeb Handler come from? You can continue to trace back to where its constructor was called: - -```java -@Configuration -public class SoulConfiguration { - - @Bean("webHandler") - public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { - List pluginList = plugins.getIfAvailable(Collections::emptyList); - final List soulPlugins = pluginList.stream() - .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); - soulPlugins.forEach(soulPlugin -> log.info("loader plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); - return new SoulWebHandler(soulPlugins); - } -} -``` - -It can be seen that the writing `plugins` is started by means of Spring Bean, that is, when the container starts, all plug-ins are loaded. Here, the entry parameter is used `ObjectProvider` to lazily load all beans of the SoulPlugin type (if none of them are used, no error will be reported) and inject them into the SoulWebHandler. - -** There's a little hole to watch out for! ** - -All plug-ins, including DividePlugin, AlibabaDubboPlugin, etc., are configured by the XX PluginConfiguration class in their respective `soul-spring-boot-starter-plugin-xx` projects to register their own plug-ins as beans, similar to the following example: - -```java -@Configuration -public class DividePluginConfiguration { - - @Bean - public SoulPlugin dividePlugin() { - return new DividePlugin(); - } -} -``` - -Therefore, in the gateway project `soul-bootstrap`, if you need to use a plug-in, you not only need to open the plug-in in the management background, but also need to confirm whether there is a dependency of the `soul-spring-boot-starter-plugin-xx` relevant plug-in in the following `soul-bootstrap` `pom.xml`, for example: - -```xml - - org.dromara - soul-spring-boot-starter-plugin-divide - ${project.version} - -``` - -If you have a comment here or it doesn't exist, don't expect to see it on the plugin chain.. - -## Plug-in project structure - -Finally, briefly describe the functions of each plug-in project: - -1. The first is the spring bean startup class project just mentioned, listing a general idea: - - ``` - soul-spring-boot-starter-plugin-alibaba-dubbo - soul-spring-boot-starter-plugin-apache-dubbo - soul-spring-boot-starter-plugin-context-path - soul-spring-boot-starter-plugin-divide - soul-spring-boot-starter-plugin-global - soul-spring-boot-starter-plugin-httpclient - soul-spring-boot-starter-plugin-hystrix - soul-spring-boot-starter-plugin-monitor - soul-spring-boot-starter-plugin-ratelimiter - soul-spring-boot-starter-plugin-resilience4j - soul-spring-boot-starter-plugin-rewrite - soul-spring-boot-starter-plugin-sentinel - soul-spring-boot-starter-plugin-sign - soul-spring-boot-starter-plugin-sofa - soul-spring-boot-starter-plugin-springcloud - soul-spring-boot-starter-plugin-tars - soul-spring-boot-starter-plugin-waf - ``` - - Their main functions have just been mentioned, registering their own SoulPlugin subclasses as spring beans, and registering spring beans to the PluginData Handler interface called in AbstractSoulPlugin. Provide its own implementation subclass, such as DividePluginDataHandler. - -2. Specific plug-in class project: - - ``` - soul-plugin-alibaba-dubbo - soul-plugin-apache-dubbo - soul-plugin-api - soul-plugin-base - soul-plugin-context-path - soul-plugin-divide - soul-plugin-global - soul-plugin-httpclient - soul-plugin-hystrix - soul-plugin-monitor - soul-plugin-ratelimiter - soul-plugin-resilience4j - soul-plugin-rewrite - soul-plugin-sentinel - soul-plugin-sign - soul-plugin-sofa - soul-plugin-springcloud - soul-plugin-tars - soul-plugin-waf - ``` - - Take the `soul-plugin-divide` DividePlugin and DividePluginDataHandler mentioned just now as examples. And the project also has node information cache manager Upstream Cache Manager, load balancing strategy class LoadBalance and so on. - -# DividePlugin - -The function of DividePlugin is to match Http requests. Since there are Http requests, there are naturally forwarding downstream and returning responses. So here we will analyze three plug-ins: DividePlugin, WebClientPlugin, WebClientResponsePlugin. - -Let's start with that implementation in `doExecute()` DividePlugin, where I just keep the core point: - -```java -@Override -protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); - // Get the cluster of service nodes in the cache by selector ID - final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); - // Call the load balancing method and pass in the policy type to get a unique node - DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); - // Get the real url of the node and put it in the exchange context - String domain = buildDomain(divideUpstream); - String realURL = buildRealURL(domain, soulContext, exchange); - exchange.getAttributes().put(Constants.HTTP_URL, realURL); - // Continue to call the next plug-in - return chain.execute(exchange); -} -``` - -As you can see, after executing the DividePlugin `doExecute()` method, we already have the real path of the downstream service node in the ServerWeb Exchange context, and we just need to request it. But don't worry, the load balancing strategy here is also the key point, and then analyze. - -## Load balancing - -How to execute the load balancing of Soul Gateway involves not only various strategies (hasn, random, polling), but also the concept of "weight score". The specific configuration of the management background is as follows: - -待补,文章内部有报错 - -待补,文章内部有报错 - -待补,文章内部有报错 - -After showing the background configuration, let's take a look at the code implementation of each strategy. - -## Hash - -```java -public DivideUpstream doSelect(final List upstreamList, final String ip) { - final ConcurrentSkipListMap treeMap = new ConcurrentSkipListMap<>(); - for (DivideUpstream address : upstreamList) { - // Each node *VIRTUAL_NODE_NUM(default 5) to make the hash more uniform - for (int i = 0; i < VIRTUAL_NODE_NUM; i++) { - long addressHash = hash("SOUL-" + address.getUpstreamUrl() + "-HASH-" + i); - treeMap.put(addressHash, address); - } - } - // Obtain a hash value from the current ip address and compare treemap(ordered) to find a location greater than the hash value - long hash = hash(String.valueOf(ip)); - SortedMap lastRing = treeMap.tailMap(hash); - // As long as the service node does not increase or decrease, the node obtained by the same ip address can remain unchanged - if (!lastRing.isEmpty()) { - return lastRing.get(lastRing.firstKey()); - } - return treeMap.firstEntry().getValue(); -} -``` - -The load balancing of the hash algorithm does not use the concept of "weight score", that is to say, for each unknown IP, the probability of each node being accessed is the same. (Of course, multiple calls to the same IP will only access the same node.) - -## RandomLoadBalance - -```java -public DivideUpstream doSelect(final List upstreamList, final String ip) { - // Total number - int length = upstreamList.size(); - // Total weight - int totalWeight = 0; - // Whether the weights are the same - boolean sameWeight = true; - for (int i = 0; i < length; i++) { - int weight = upstreamList.get(i).getWeight(); - // Cumulative total weight - totalWeight += weight; - if (sameWeight && i > 0 - && weight != upstreamList.get(i - 1).getWeight()) { - // Calculate whether the ownership weight is the same - sameWeight = false; - } - } - if (totalWeight > 0 && !sameWeight) { - // If the weights are not the same and the weights are greater than 0, random by the total weights -int offset = RANDOM.nextInt(totalWeight); -// and determine which segment the random value falls on - for (DivideUpstream divideUpstream : upstreamList) { - offset -= divideUpstream.getWeight(); - if (offset < 0) { - return divideUpstream; - } - } - } - // Equally random if the weight is the same or if the weight is 0 - return upstreamList.get(RANDOM.nextInt(length)); -} -``` - -When the rule is used `random`, all the node weights are accumulated and the number is obtained randomly, depending on the weight fragment of the node; If the score is 0 or the same, it is straightforward to randomize the cluster length. - -## RoundRobinLoadBalance - -```java -public DivideUpstream doSelect(final List upstreamList, final String ip) { - String key = upstreamList.get(0).getUpstreamUrl(); - ConcurrentMap map = methodWeightMap.get(key); - if (map == null) { - methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<>(16)); - map = methodWeightMap.get(key); - } - int totalWeight = 0; - long maxCurrent = Long.MIN_VALUE; - long now = System.currentTimeMillis(); - DivideUpstream selectedInvoker = null; - WeightedRoundRobin selectedWRR = null; - for (DivideUpstream upstream : upstreamList) { - String rKey = upstream.getUpstreamUrl(); - // Retrieves the node information in the cache - WeightedRoundRobin weightedRoundRobin = map.get(rKey); - int weight = upstream.getWeight(); - if (weightedRoundRobin == null) { - weightedRoundRobin = new WeightedRoundRobin(); - weightedRoundRobin.setWeight(weight); - map.putIfAbsent(rKey, weightedRoundRobin); - } - if (weight != weightedRoundRobin.getWeight()) { - weightedRoundRobin.setWeight(weight); - } - // Here is the first key: the score in the cache increases the weight score of the current node - long cur = weightedRoundRobin.increaseCurrent(); - weightedRoundRobin.setLastUpdate(now); - // Select the node with a high cache score - if (cur > maxCurrent) { - maxCurrent = cur; - selectedInvoker = upstream; - selectedWRR = weightedRoundRobin; - } - totalWeight += weight; - } - if (!updateLock.get() && upstreamList.size() != map.size() && updateLock.compareAndSet(false, true)) { - try { - ConcurrentMap newMap = new ConcurrentHashMap<>(map); - newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > recyclePeriod); - methodWeightMap.put(key, newMap); - } finally { - updateLock.set(false); - } - } - if (selectedInvoker != null) { - // Here is the second key: the score in the cache, reducing the total node weight score - selectedWRR.sel(totalWeight); - return selectedInvoker; - } - return upstreamList.get(0); -} -``` - -This algorithm is a bit complicated. Let me explain the core aspect of calculating weights: - -- Two nodes with a score of 2 and 100 respectively enter, and each of them is kept in the cache, with the score starting from 0. -- After the for loop, the scores of the two nodes in the cache will increase based on themselves. Assuming that the following steps are not performed, the cache will be 2 and 100 for the first time, 4 and 200 for the second time, and so on. -- The third key step is to select the node cache with the highest score and take "punishment" measures to reduce the cumulative score of all nodes, that is, 102. - -According to the steps of this algorithm, nodes that have not been selected, as "growth rewards", will continue to increase on their own basis. The selected node, as a "penalty," reduces the sum of the weights of the other nodes. - -It can be predicted that a node with a small weight will not be selected until a long time later. However, at that moment, it will be punished with great strength, which will lead to a long accumulation of strength once it returns to the pre-liberation period. For nodes with large weight scores, the penalty for being selected each time is very small. Even if the score is too low to be selected after many times, his reward score (itself) is particularly high, and one increase far surpasses other nodes. - -## WebClientPlugin - -After the DividePlugin plug-in is called, the downstream service node path is determined, and then the Web ClientPlugin plug-in comes into play. It implements the SoulPlugin interface directly and implements the `execute()` methods (keeping only the core code): - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - String urlPath = exchange.getAttribute(Constants.HTTP_URL); - // Request type: Get request, orPost request, etc - HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue()); - // Build a shell of the request object and inject the request type and URL - WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath); - return handleRequestBody(requestBodySpec, exchange, timeout, chain); -} - -private Mono handleRequestBody(final WebClient.RequestBodySpec requestBodySpec, - final ServerWebExchange exchange, - final long timeout, - final SoulPluginChain chain) { - return requestBodySpec.headers(httpHeaders -> { - // Add the request header in context... Later is also to add some attributes, do not go into details - httpHeaders.addAll(exchange.getRequest().getHeaders()); - httpHeaders.remove(HttpHeaders.HOST); - }) - .contentType(buildMediaType(exchange)) - .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody())) - // Start asynchronous http calls to downstream services - .exchange() - .doOnError(e -> log.error(e.getMessage())) - .timeout(Duration.ofMillis(timeout)) - // Callback receives the return value - .flatMap(e -> doNext(e, exchange, chain)); -} - -// Here is an asynchronous callback method that works in another thread -private Mono doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) { - // ... - // Continue to complete the remaining plug-in chain calls - return chain.execute(exchange); -} -``` - -Take a quick look at `handleRequestBody()` the implementation of this method in `exchange()`, here are the key Http calls: - -```java -class DefaultWebClient implements WebClient { - @Override - public Mono exchange() { - ClientRequest request = (this.inserter != null ? - initRequestBuilder().body(this.inserter).build() : - initRequestBuilder().build()); - // Here is the critical call, which will go to spring-web-reactive - return Mono.defer(() -> exchangeFunction.exchange(request) - .checkpoint("Request to " + this.httpMethod.name() + " " + this.uri + " [DefaultWebClient]") - .switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR)); - } -} -``` - -To sum up, the processing of Web ClientPlugin will call the downstream service asynchronously, wait for the response, and then execute the subsequent plug-in chain call in another thread. - -## WebClientResponseClient - -Finally, the plug-in chain goes to the Web ClientResponseClient link to encapsulate the response information: - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - return chain.execute(exchange).then(Mono.defer(() -> { - // Gets the response information stored in the context - ServerHttpResponse response = exchange.getResponse(); - ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR); - if (Objects.isNull(clientResponse) - || response.getStatusCode() == HttpStatus.BAD_GATEWAY - || response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) { - Object error = SoulResultWarp.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } else if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { - Object error = SoulResultWarp.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // Various assembly - response.setStatusCode(clientResponse.statusCode()); - response.getCookies().putAll(clientResponse.cookies()); - response.getHeaders().putAll(clientResponse.headers().asHttpHeaders()); - return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())); - })); -} -``` +--- +title: Soul Gateway learning plugin chain and load balancing analysis +author: zhuming +date: 2021-01-15 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +# Plug-in chain summary + +Start with a class diagram: + +![plugin diagram](/assets/img/01.png) + +Two of the most basic plug-in classes are: + +- SoulPlugin: Defines the interface of the plug-in responsibility. The key method `execute()` is called by the upper layer. `skip()` The method can cause some plug-ins to be skipped in some requests. + +- AbstractPlugin: An abstract class that implements an interface `execute()`, defines a common execution process, and uses the design pattern of the template method to provide `doExecute()` an abstract method for the implementation class to write its own logic. + +## AbstractSoulPlugin + +Specific analysis of the `execute()` following `AbstractSoulPlugin` categories: + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + String pluginName = named(); + final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); + // If pluginData.getEnabled() is false, it will skip to the next plugin, only a few plugins will enter this condition (DividePlugin, AlibabaDubboPlugin, etc.) + if (pluginData != null && pluginData.getEnabled()) { + // Get all selectors on the plugin + final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); + if (CollectionUtils.isEmpty(selectors)) { + return CheckUtils.checkSelector(pluginName, exchange, chain); + } + // Check whether the request path in the context matches the selector and get the only matching selector data + final SelectorData selectorData = matchSelector(exchange, selectors); + if (Objects.isNull(selectorData)) { + if (PluginEnum.WAF.getName().equals(pluginName)) { + return doExecute(exchange, chain, null, null); + } + return CheckUtils.checkSelector(pluginName, exchange, chain); + } + if (selectorData.getLoged()) { + log.info("{} selector success match , selector name :{}", pluginName, selectorData.getName()); + } + // Gets the individual resource rules in the selector + final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); + if (CollectionUtils.isEmpty(rules)) { + if (PluginEnum.WAF.getName().equals(pluginName)) { + return doExecute(exchange, chain, null, null); + } + return CheckUtils.checkRule(pluginName, exchange, chain); + } + RuleData rule; + if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { + rule = rules.get(rules.size() - 1); + } else { + // Match the path to obtain a unique rule + rule = matchRule(exchange, rules); + } + if (Objects.isNull(rule)) { + return CheckUtils.checkRule(pluginName, exchange, chain); + } + if (rule.getLoged()) { + log.info("{} rule success match ,rule name :{}", pluginName, rule.getName()); + } + // Execute methods of subclasses + return doExecute(exchange, chain, selectorData, rule); + } + // Execute the next plug-in on the plug-in chain + return chain.execute(exchange); +} +``` + +Through code analysis, some conclusions can be drawn: + +- Execute () has two logics: one is the matching of the request path with the selector and the rule, which finally confirms a unique rule and calls the subclass doExecute (); The second is to execute the next plug-in in the plug-in chain. +- The execute () actually abstracts a set of rule matching logic, which is used by all the "forwarding type" plug-ins. Currently, I know the forwarding type plug-ins are `DividePlugin` (HTTP request) and `AlibabaDubboPlugin` (dubbo request). Other types of plug-ins that do not override the execute () method will go directly to the next plug-in. + +## SoulPluginChain + +Another point here is the formation and chain call of the plug-in chain. Let's analyze `SoulPluginChain` this part: + +![plugin02](/assets/img/02.png) + +The SoulPluginChain interface also defines `execute()` methods for the caller to use, and its only subclass, DefaultSoulPluginChain, implements chained calls: + +```java +public Mono execute(final ServerWebExchange exchange) { + return Mono.defer(() -> { + // plugins contains all plugins loaded by the gateway + if (this.index < plugins.size()) { + // Each time the execute() method is called, the index index increases and is called to the next plug-in + SoulPlugin plugin = plugins.get(this.index++); + // Determine whether the current plug-in needs to be skipped based on the context + Boolean skip = plugin.skip(exchange); + if (skip) { + return this.execute(exchange); + } else { + return plugin.execute(exchange, this); + } + } else { + return Mono.empty(); + } + }); +} +``` + +It's curious `plugins` to see where this list of plug-ins comes from. Here's an explanation. DefaultSoulPluginChain is a static inner class of SoulWebHandler. Is `plugins` an attribute in the Soul Web Handle: + +```java +public final class SoulWebHandler implements WebHandler { + + private List plugins; + + public SoulWebHandler(final List plugins) { + this.plugins = plugins; + // ... + } + + @Override + public Mono handle(@NonNull final ServerWebExchange exchange) { + // ... + return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler) + .doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time))); + } + + private static class DefaultSoulPluginChain implements SoulPluginChain { + } +} +``` + +So where did the `plugins` SoulWeb Handler come from? You can continue to trace back to where its constructor was called: + +```java +@Configuration +public class SoulConfiguration { + + @Bean("webHandler") + public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { + List pluginList = plugins.getIfAvailable(Collections::emptyList); + final List soulPlugins = pluginList.stream() + .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); + soulPlugins.forEach(soulPlugin -> log.info("loader plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); + return new SoulWebHandler(soulPlugins); + } +} +``` + +It can be seen that the writing `plugins` is started by means of Spring Bean, that is, when the container starts, all plug-ins are loaded. Here, the entry parameter is used `ObjectProvider` to lazily load all beans of the SoulPlugin type (if none of them are used, no error will be reported) and inject them into the SoulWebHandler. + +** There's a little hole to watch out for! ** + +All plug-ins, including DividePlugin, AlibabaDubboPlugin, etc., are configured by the XX PluginConfiguration class in their respective `soul-spring-boot-starter-plugin-xx` projects to register their own plug-ins as beans, similar to the following example: + +```java +@Configuration +public class DividePluginConfiguration { + + @Bean + public SoulPlugin dividePlugin() { + return new DividePlugin(); + } +} +``` + +Therefore, in the gateway project `soul-bootstrap`, if you need to use a plug-in, you not only need to open the plug-in in the management background, but also need to confirm whether there is a dependency of the `soul-spring-boot-starter-plugin-xx` relevant plug-in in the following `soul-bootstrap` `pom.xml`, for example: + +```xml + + org.dromara + soul-spring-boot-starter-plugin-divide + ${project.version} + +``` + +If you have a comment here or it doesn't exist, don't expect to see it on the plugin chain.. + +## Plug-in project structure + +Finally, briefly describe the functions of each plug-in project: + +1. The first is the spring bean startup class project just mentioned, listing a general idea: + + ``` + soul-spring-boot-starter-plugin-alibaba-dubbo + soul-spring-boot-starter-plugin-apache-dubbo + soul-spring-boot-starter-plugin-context-path + soul-spring-boot-starter-plugin-divide + soul-spring-boot-starter-plugin-global + soul-spring-boot-starter-plugin-httpclient + soul-spring-boot-starter-plugin-hystrix + soul-spring-boot-starter-plugin-monitor + soul-spring-boot-starter-plugin-ratelimiter + soul-spring-boot-starter-plugin-resilience4j + soul-spring-boot-starter-plugin-rewrite + soul-spring-boot-starter-plugin-sentinel + soul-spring-boot-starter-plugin-sign + soul-spring-boot-starter-plugin-sofa + soul-spring-boot-starter-plugin-springcloud + soul-spring-boot-starter-plugin-tars + soul-spring-boot-starter-plugin-waf + ``` + + Their main functions have just been mentioned, registering their own SoulPlugin subclasses as spring beans, and registering spring beans to the PluginData Handler interface called in AbstractSoulPlugin. Provide its own implementation subclass, such as DividePluginDataHandler. + +2. Specific plug-in class project: + + ``` + soul-plugin-alibaba-dubbo + soul-plugin-apache-dubbo + soul-plugin-api + soul-plugin-base + soul-plugin-context-path + soul-plugin-divide + soul-plugin-global + soul-plugin-httpclient + soul-plugin-hystrix + soul-plugin-monitor + soul-plugin-ratelimiter + soul-plugin-resilience4j + soul-plugin-rewrite + soul-plugin-sentinel + soul-plugin-sign + soul-plugin-sofa + soul-plugin-springcloud + soul-plugin-tars + soul-plugin-waf + ``` + + Take the `soul-plugin-divide` DividePlugin and DividePluginDataHandler mentioned just now as examples. And the project also has node information cache manager Upstream Cache Manager, load balancing strategy class LoadBalance and so on. + +# DividePlugin + +The function of DividePlugin is to match Http requests. Since there are Http requests, there are naturally forwarding downstream and returning responses. So here we will analyze three plug-ins: DividePlugin, WebClientPlugin, WebClientResponsePlugin. + +Let's start with that implementation in `doExecute()` DividePlugin, where I just keep the core point: + +```java +@Override +protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); + // Get the cluster of service nodes in the cache by selector ID + final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); + // Call the load balancing method and pass in the policy type to get a unique node + DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); + // Get the real url of the node and put it in the exchange context + String domain = buildDomain(divideUpstream); + String realURL = buildRealURL(domain, soulContext, exchange); + exchange.getAttributes().put(Constants.HTTP_URL, realURL); + // Continue to call the next plug-in + return chain.execute(exchange); +} +``` + +As you can see, after executing the DividePlugin `doExecute()` method, we already have the real path of the downstream service node in the ServerWeb Exchange context, and we just need to request it. But don't worry, the load balancing strategy here is also the key point, and then analyze. + +## Load balancing + +How to execute the load balancing of Soul Gateway involves not only various strategies (hasn, random, polling), but also the concept of "weight score". The specific configuration of the management background is as follows: + +待补,文章内部有报错 + +待补,文章内部有报错 + +待补,文章内部有报错 + +After showing the background configuration, let's take a look at the code implementation of each strategy. + +## Hash + +```java +public DivideUpstream doSelect(final List upstreamList, final String ip) { + final ConcurrentSkipListMap treeMap = new ConcurrentSkipListMap<>(); + for (DivideUpstream address : upstreamList) { + // Each node *VIRTUAL_NODE_NUM(default 5) to make the hash more uniform + for (int i = 0; i < VIRTUAL_NODE_NUM; i++) { + long addressHash = hash("SOUL-" + address.getUpstreamUrl() + "-HASH-" + i); + treeMap.put(addressHash, address); + } + } + // Obtain a hash value from the current ip address and compare treemap(ordered) to find a location greater than the hash value + long hash = hash(String.valueOf(ip)); + SortedMap lastRing = treeMap.tailMap(hash); + // As long as the service node does not increase or decrease, the node obtained by the same ip address can remain unchanged + if (!lastRing.isEmpty()) { + return lastRing.get(lastRing.firstKey()); + } + return treeMap.firstEntry().getValue(); +} +``` + +The load balancing of the hash algorithm does not use the concept of "weight score", that is to say, for each unknown IP, the probability of each node being accessed is the same. (Of course, multiple calls to the same IP will only access the same node.) + +## RandomLoadBalance + +```java +public DivideUpstream doSelect(final List upstreamList, final String ip) { + // Total number + int length = upstreamList.size(); + // Total weight + int totalWeight = 0; + // Whether the weights are the same + boolean sameWeight = true; + for (int i = 0; i < length; i++) { + int weight = upstreamList.get(i).getWeight(); + // Cumulative total weight + totalWeight += weight; + if (sameWeight && i > 0 + && weight != upstreamList.get(i - 1).getWeight()) { + // Calculate whether the ownership weight is the same + sameWeight = false; + } + } + if (totalWeight > 0 && !sameWeight) { + // If the weights are not the same and the weights are greater than 0, random by the total weights +int offset = RANDOM.nextInt(totalWeight); +// and determine which segment the random value falls on + for (DivideUpstream divideUpstream : upstreamList) { + offset -= divideUpstream.getWeight(); + if (offset < 0) { + return divideUpstream; + } + } + } + // Equally random if the weight is the same or if the weight is 0 + return upstreamList.get(RANDOM.nextInt(length)); +} +``` + +When the rule is used `random`, all the node weights are accumulated and the number is obtained randomly, depending on the weight fragment of the node; If the score is 0 or the same, it is straightforward to randomize the cluster length. + +## RoundRobinLoadBalance + +```java +public DivideUpstream doSelect(final List upstreamList, final String ip) { + String key = upstreamList.get(0).getUpstreamUrl(); + ConcurrentMap map = methodWeightMap.get(key); + if (map == null) { + methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<>(16)); + map = methodWeightMap.get(key); + } + int totalWeight = 0; + long maxCurrent = Long.MIN_VALUE; + long now = System.currentTimeMillis(); + DivideUpstream selectedInvoker = null; + WeightedRoundRobin selectedWRR = null; + for (DivideUpstream upstream : upstreamList) { + String rKey = upstream.getUpstreamUrl(); + // Retrieves the node information in the cache + WeightedRoundRobin weightedRoundRobin = map.get(rKey); + int weight = upstream.getWeight(); + if (weightedRoundRobin == null) { + weightedRoundRobin = new WeightedRoundRobin(); + weightedRoundRobin.setWeight(weight); + map.putIfAbsent(rKey, weightedRoundRobin); + } + if (weight != weightedRoundRobin.getWeight()) { + weightedRoundRobin.setWeight(weight); + } + // Here is the first key: the score in the cache increases the weight score of the current node + long cur = weightedRoundRobin.increaseCurrent(); + weightedRoundRobin.setLastUpdate(now); + // Select the node with a high cache score + if (cur > maxCurrent) { + maxCurrent = cur; + selectedInvoker = upstream; + selectedWRR = weightedRoundRobin; + } + totalWeight += weight; + } + if (!updateLock.get() && upstreamList.size() != map.size() && updateLock.compareAndSet(false, true)) { + try { + ConcurrentMap newMap = new ConcurrentHashMap<>(map); + newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > recyclePeriod); + methodWeightMap.put(key, newMap); + } finally { + updateLock.set(false); + } + } + if (selectedInvoker != null) { + // Here is the second key: the score in the cache, reducing the total node weight score + selectedWRR.sel(totalWeight); + return selectedInvoker; + } + return upstreamList.get(0); +} +``` + +This algorithm is a bit complicated. Let me explain the core aspect of calculating weights: + +- Two nodes with a score of 2 and 100 respectively enter, and each of them is kept in the cache, with the score starting from 0. +- After the for loop, the scores of the two nodes in the cache will increase based on themselves. Assuming that the following steps are not performed, the cache will be 2 and 100 for the first time, 4 and 200 for the second time, and so on. +- The third key step is to select the node cache with the highest score and take "punishment" measures to reduce the cumulative score of all nodes, that is, 102. + +According to the steps of this algorithm, nodes that have not been selected, as "growth rewards", will continue to increase on their own basis. The selected node, as a "penalty," reduces the sum of the weights of the other nodes. + +It can be predicted that a node with a small weight will not be selected until a long time later. However, at that moment, it will be punished with great strength, which will lead to a long accumulation of strength once it returns to the pre-liberation period. For nodes with large weight scores, the penalty for being selected each time is very small. Even if the score is too low to be selected after many times, his reward score (itself) is particularly high, and one increase far surpasses other nodes. + +## WebClientPlugin + +After the DividePlugin plug-in is called, the downstream service node path is determined, and then the Web ClientPlugin plug-in comes into play. It implements the SoulPlugin interface directly and implements the `execute()` methods (keeping only the core code): + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + String urlPath = exchange.getAttribute(Constants.HTTP_URL); + // Request type: Get request, orPost request, etc + HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue()); + // Build a shell of the request object and inject the request type and URL + WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath); + return handleRequestBody(requestBodySpec, exchange, timeout, chain); +} + +private Mono handleRequestBody(final WebClient.RequestBodySpec requestBodySpec, + final ServerWebExchange exchange, + final long timeout, + final SoulPluginChain chain) { + return requestBodySpec.headers(httpHeaders -> { + // Add the request header in context... Later is also to add some attributes, do not go into details + httpHeaders.addAll(exchange.getRequest().getHeaders()); + httpHeaders.remove(HttpHeaders.HOST); + }) + .contentType(buildMediaType(exchange)) + .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody())) + // Start asynchronous http calls to downstream services + .exchange() + .doOnError(e -> log.error(e.getMessage())) + .timeout(Duration.ofMillis(timeout)) + // Callback receives the return value + .flatMap(e -> doNext(e, exchange, chain)); +} + +// Here is an asynchronous callback method that works in another thread +private Mono doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) { + // ... + // Continue to complete the remaining plug-in chain calls + return chain.execute(exchange); +} +``` + +Take a quick look at `handleRequestBody()` the implementation of this method in `exchange()`, here are the key Http calls: + +```java +class DefaultWebClient implements WebClient { + @Override + public Mono exchange() { + ClientRequest request = (this.inserter != null ? + initRequestBuilder().body(this.inserter).build() : + initRequestBuilder().build()); + // Here is the critical call, which will go to spring-web-reactive + return Mono.defer(() -> exchangeFunction.exchange(request) + .checkpoint("Request to " + this.httpMethod.name() + " " + this.uri + " [DefaultWebClient]") + .switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR)); + } +} +``` + +To sum up, the processing of Web ClientPlugin will call the downstream service asynchronously, wait for the response, and then execute the subsequent plug-in chain call in another thread. + +## WebClientResponseClient + +Finally, the plug-in chain goes to the Web ClientResponseClient link to encapsulate the response information: + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + return chain.execute(exchange).then(Mono.defer(() -> { + // Gets the response information stored in the context + ServerHttpResponse response = exchange.getResponse(); + ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR); + if (Objects.isNull(clientResponse) + || response.getStatusCode() == HttpStatus.BAD_GATEWAY + || response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) { + Object error = SoulResultWarp.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } else if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { + Object error = SoulResultWarp.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // Various assembly + response.setStatusCode(clientResponse.statusCode()); + response.getCookies().putAll(clientResponse.cookies()); + response.getHeaders().putAll(clientResponse.headers().asHttpHeaders()); + return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())); + })); +} +``` diff --git a/src/blog/soul_source_learning_08_httplongpolling_01.md b/src/blog/soul_source_learning_08_httplongpolling_01.md index 72e5f141dd..492ad9c82b 100644 --- a/src/blog/soul_source_learning_08_httplongpolling_01.md +++ b/src/blog/soul_source_learning_08_httplongpolling_01.md @@ -1,384 +1,384 @@ ---- -title: Soul Gateway Learns Http Long Polling Analysis 01 -author: zhuming -date: 2021-01-25 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -## Data synchronization between background and gateway (Http long polling) - -### Configuration - -** Background information mode switching ** - -In the previous analysis of Zookeeper synchronization ([ Soul Gateway Source Code Analysis-Issue 11 ](https://blog.csdn.net/zm469568595/article/details/113065463)), we switched through the DataSyncConfiguration configuration class. This time, we have experience and paste the configuration directly. - -```yml -soul: - sync: - websocket: - enabled: false - http: - enabled: true -``` - -** Gateway information mode switching ** - -After the background mode switching is completed, the next step is the gateway. Continue to find the parameter settings on the key configuration class. The gateway configuration is also directly posted here. - -```yml -soul: - sync: -# websocket: -# urls: ws://localhost:9095/websocket - http: - url: http://localhost:9095 -``` - -### Data ChangedListener system - -Background data initialization Data SyncConfiguration configures key beans. Take a look at the Http long polling Bean here. - -```java -@Configuration -public class DataSyncConfiguration { - - @Configuration - @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true") - @EnableConfigurationProperties(HttpSyncProperties.class) - static class HttpLongPollingListener { - - @Bean - @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class) - public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) { - return new HttpLongPollingDataChangedListener(httpSyncProperties); - } - } -} -``` - -Http LongPollingData ChangedListener inherit from AbstractData ChangedListener, which are implemented from the interface DataChangedListener. - -We should be very familiar with the DataChangedListener interface, which provides many methods of different data type changes for the DataChangedEventDispatcher to call, and this class is an "old friend" as a transit station. Diligent ** Handle event classification and distribution for data synchronization ** - -```java -public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { - // Hold the DataChangedListener collection - private List listeners; - - // Method to notify the DataChangedListener of different event types when an event changes - public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - case APP_AUTH: - listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); - break; - case PLUGIN: - listener.onPluginChanged((List) event.getSource(), event.getEventType()); - break; - case RULE: - listener.onRuleChanged((List) event.getSource(), event.getEventType()); - break; - case SELECTOR: - listener.onSelectorChanged((List) event.getSource(), event.getEventType()); - break; - case META_DATA: - listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); - break; - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } - } -} -``` - -```java -public interface DataChangedListener { - - default void onAppAuthChanged(List changed, DataEventTypeEnum eventType) {} - - default void onPluginChanged(List changed, DataEventTypeEnum eventType) {} - - default void onSelectorChanged(List changed, DataEventTypeEnum eventType) {} - - default void onMetaDataChanged(List changed, DataEventTypeEnum eventType) {} - - default void onRuleChanged(List changed, DataEventTypeEnum eventType) {} -} -``` - -After understanding the functions of these two, what does AbstractData ChangedListener do? Take an example of onPluginChanged (): - -```java -public abstract class AbstractDataChangedListener implements DataChangedListener, InitializingBean { - - protected static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); - - @Override - public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) { - if (CollectionUtils.isEmpty(changed)) { - return; - } - this.updatePluginCache(); - this.afterPluginChanged(changed, eventType); - } - - // Modify cache (overwritable) - protected void updatePluginCache() { - this.updateCache(ConfigGroupEnum.PLUGIN, pluginService.listAll()); - } - - protected void updateCache(final ConfigGroupEnum group, final List data) { - String json = GsonUtils.getInstance().toJson(data); - ConfigDataCache newVal = new ConfigDataCache(group.name(), json, Md5Utils.md5(json), System.currentTimeMillis()); - ConfigDataCache oldVal = CACHE.put(newVal.getGroup(), newVal); - log.info("update config cache[{}], old: {}, updated: {}", group, oldVal, newVal); - } - - // Hook, customize what to do after ending data changes (rewritable) - protected void afterPluginChanged(final List changed, final DataEventTypeEnum eventType) { - } -} -``` - -For a plug-in data change method (onPluginChanged), AbstractDataChangedListener actually defines a template, so that the subclass can work according to the specified steps, and the details of each step can be implemented by the subclass itself. - -Second, if you do not override its cache updates, it is maintained by the class in CACHE. - -### What are the other synchronization strategies doing at this time? - -After the DataChange dEventDispatcher calls onPluginChanged (), how does the long polling module work? ** Think about what other synchronization methods are doing at this point. ** - -The web socket pattern, for example, rewrites onPluginChanged () itself to send the websocket information to the holding session, which has a gateway. - -```java -public class WebsocketDataChangedListener implements DataChangedListener { - - @Override - public void onPluginChanged(final List pluginDataList, final DataEventTypeEnum eventType) { - WebsocketData websocketData = - new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); - } -} -``` - -Looking at the zookeeper pattern, it also rewrites onPluginChanged () to modify the node information on the zookeeper so that the gateway side will hear their node changes. - -```java -public class ZookeeperDataChangedListener implements DataChangedListener { - - @Override - public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) { - for (PluginData data : changed) { - String pluginPath = ZkPathConstants.buildPluginPath(data.getName()); - // delete - if (eventType == DataEventTypeEnum.DELETE) { - deleteZkPathRecursive(pluginPath); - String selectorParentPath = ZkPathConstants.buildSelectorParentPath(data.getName()); - deleteZkPathRecursive(selectorParentPath); - String ruleParentPath = ZkPathConstants.buildRuleParentPath(data.getName()); - deleteZkPathRecursive(ruleParentPath); - continue; - } - //create or update - insertZkNode(pluginPath, data); - } - } -} -``` - -As you can see, at this juncture, other synchronization strategies are already busy notifying gateways, so Http long polling must also do this. - -These two strategies are also different in the way of notification, websocket is a good person to do to the end, directly find the session session to send the information in person. After the zookeeper changes the node information, the gateway monitors the change and then synchronizes. - -So how does our Http long polling now notify the gateway? Keep looking.. - -### Thinking on the Implementation of Long Polling - -First think about how I can design long polling by myself? - -Normal long polling implementation should be actively requested by the gateway. The background receives the request and holds it. If there is an update, it will return directly. If not, it will be blocked for a certain period of time. And the background is to do a good job of updating the data, hold the time to check whether the data has changed. - -There are three points involved here: - -1. How do you know if the data has changed? Do you set a last update time and compare it with the request time of the gateway to see if there is any data modification? -2. After holding, how does the background know whether the data is updated, repeated traversal or blocked waiting? -3. Where is the data used for updating? In the case of caching, consider how the background cache interacts with the database. - -### Http LongPollingData ChangedListener Long Polling Implementation - -Around our thinking, look at how Http LongPollingData ChangedListener is achieved. Let's take a look at the implementation of the parent onPluginChanged (). - -```java -public class HttpLongPollingDataChangedListener extends AbstractDataChangedListener { - - private final ScheduledExecutorService scheduler; - - @Override - protected void afterPluginChanged(final List changed, final DataEventTypeEnum eventType) { - scheduler.execute(new DataChangeTask(ConfigGroupEnum.PLUGIN)); - } -} -``` - -Http long polling does not directly override onPluginChanged (), but directly uses its parent class, which means that its CACHE is used. In the end, our information acquisition must also be analyzed. Put it aside for the time being. - -The following logic will call the afterPluginChanged () method of our implementation, where a timed thread pool is used to run a Runnable task DataChangeTask. - -```java -class DataChangeTask implements Runnable { - - @Override - public void run() { - // Iterate through clients - for (Iterator iter = clients.iterator(); iter.hasNext();) { - LongPollingClient client = iter.next(); - iter.remove(); - // Description Complete response The response is complete - client.sendResponse(Collections.singletonList(groupKey)); - log.info("send response with the changed group,ip={}, group={}, changeTime={}", client.ip, groupKey, changeTime); - } - } -} -``` - -After the data is changed, the thread pool is used to call this method, take all `clients` the elements while traversing, and call the method sendResponse (), like marking that the response has been completed. - -Let me guess what it does. `clients` It's likely that the request is held by the gateway, and send response () is likely to actually add response information to the request context. Another key action is to end the hold, allowing the gateway to receive the response and remove the request from the collection. - -We now trace the following `client` generation, which is a BlockingQueue blocking queue in the HttpLongPollingData ChangedListener, which is periodically detected in the LongPolling Client. - -```java -class LongPollingClient implements Runnable { - - @Override - public void run() { - this.asyncTimeoutFuture = scheduler.schedule(() -> { - clients.remove(LongPollingClient.this); - List changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); - sendResponse(changedGroups); - }, timeoutTime, TimeUnit.MILLISECONDS); - // Here is the key, indicating the source - clients.add(this); - } -} -``` - -Instead of analyzing the detection code block of remove (), you can see the add () in the last sentence, which is `clients` the source of the data. - -Find where LongPollingClient is called. HttpLongPollingData ChangedListener # doLongPolling - -```java -public void doLongPolling(final HttpServletRequest request, final HttpServletResponse response) { - - // ... - - // listen for configuration changed. - // Enable synchronous blocking requests - final AsyncContext asyncContext = request.startAsync(); - - // AsyncContext.settimeout() does not timeout properly, so you have to control it yourself - asyncContext.setTimeout(0L); - - // block client's thread. - // The thread pool calls LongPollingClient#run - scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); -} -``` - -The last sentence here will be called and added `client`, and there is a key line of code that blocks the request: - -```java -final AsyncContext asyncContext = request.startAsync(); -``` - -In the LongPolling Client # sendResponse, it has just been analyzed that, in addition to wrapping the injected response information, the held request will also be released. - -```java -class LongPollingClient implements Runnable { - - void sendResponse(final List changedGroups) { - // cancel scheduler - if (null != asyncTimeoutFuture) { - asyncTimeoutFuture.cancel(false); - } - generateResponse((HttpServletResponse) asyncContext.getResponse(), changedGroups); - // The synchronization is complete - asyncContext.complete(); - } -} -``` - -After this analysis, we go back to doLongPolling (), where the thread pool calls another key point. - -```java -scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); -``` - -The timeout time of 60s is passed to the LongPolling Client here. What is it used for? Remember that piece of code we skipped over at LongPolling Client # run? - -```java -class LongPollingClient implements Runnable { - - @Override - public void run() { - // Start time. The delay time is based on timeoutTime - this.asyncTimeoutFuture = scheduler.schedule(() -> { - // Remove the managed connection - clients.remove(LongPollingClient.this); - List changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); - // This method releases the blocked request - sendResponse(changedGroups); - }, timeoutTime, TimeUnit.MILLISECONDS); - - clients.add(this); - } -} -``` - -Here we have understood the implementation of the long polling process in the background. Finally, we will see how doLongPolling () is called and find the calling class ConfigController. - -```java -@ConditionalOnBean(HttpLongPollingDataChangedListener.class) -@RestController -@RequestMapping("/configs") -@Slf4j -public class ConfigController { - - @PostMapping(value = "/listener") - public void listener(final HttpServletRequest request, final HttpServletResponse response) { - longPollingListener.doLongPolling(request, response); - } -} -``` - -It is also basically clear that the background exposes the HTTP path through this Controller for the gateway to call and listen to data changes. - -### Sum up - -- The background exposes the API to the gateway through the Controller layer. When the gateway requests the background, the background does not immediately return a response (whether the data has changed), but holds the request for a maximum of 60 seconds. These held requests are added to the blocking queue as an in-memory cache. -- If there is any data change in these 60 seconds, it will be distributed to our HttpLongPollingData ChangedListener through the DataChangedEventDispatcher. All held requests are traversed ** Invoke the thread pool immediately ** in the blocking queue, stuffed with response information and released. -- If there is still no data change after 60 seconds, the held request will be released and the corresponding request object of the blocking queue will be removed. - -At this point, we have sorted out its most basic long polling logic, then corresponding to the next beginning of thinking, see what conclusions or doubts. - -> 1. How do you know if the data has changed? Do you set a last update time and compare it with the request time of the gateway to see if there is any data modification? -> 2. After holding, how does the background know whether the data is updated, repeated traversal or blocked waiting? -> 3. Where is the data used for updating? In the case of caching, consider how the background cache interacts with the database. - -In response to point 1, how do we know that the data has changed? - -- At present, the data change source of our analysis is DataChangedEventDispatcher, which not only informs us when the data changes, but also calls it immediately every time we manually click the background synchronization. - - Then there must be something like new and old data comparison. Otherwise, every call will directly release the blocking request of the gateway. This is not possible. White IO consumption is certainly not a good design. - -For the second point, we now know that the mode is blocking and waiting, which is `AsyncContext` used in this way. I have not understood this part, and I will discuss it in an extra chapter. - -For the third point, we know that the background configuration must be modified to the database, so the interaction between this cache and the database is also a point worth analyzing. I will continue to analyze these questions in the next chapter. +--- +title: Soul Gateway Learns Http Long Polling Analysis 01 +author: zhuming +date: 2021-01-25 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +## Data synchronization between background and gateway (Http long polling) + +### Configuration + +** Background information mode switching ** + +In the previous analysis of Zookeeper synchronization ([ Soul Gateway Source Code Analysis-Issue 11 ](https://blog.csdn.net/zm469568595/article/details/113065463)), we switched through the DataSyncConfiguration configuration class. This time, we have experience and paste the configuration directly. + +```yml +soul: + sync: + websocket: + enabled: false + http: + enabled: true +``` + +** Gateway information mode switching ** + +After the background mode switching is completed, the next step is the gateway. Continue to find the parameter settings on the key configuration class. The gateway configuration is also directly posted here. + +```yml +soul: + sync: +# websocket: +# urls: ws://localhost:9095/websocket + http: + url: http://localhost:9095 +``` + +### Data ChangedListener system + +Background data initialization Data SyncConfiguration configures key beans. Take a look at the Http long polling Bean here. + +```java +@Configuration +public class DataSyncConfiguration { + + @Configuration + @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true") + @EnableConfigurationProperties(HttpSyncProperties.class) + static class HttpLongPollingListener { + + @Bean + @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class) + public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) { + return new HttpLongPollingDataChangedListener(httpSyncProperties); + } + } +} +``` + +Http LongPollingData ChangedListener inherit from AbstractData ChangedListener, which are implemented from the interface DataChangedListener. + +We should be very familiar with the DataChangedListener interface, which provides many methods of different data type changes for the DataChangedEventDispatcher to call, and this class is an "old friend" as a transit station. Diligent ** Handle event classification and distribution for data synchronization ** + +```java +public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { + // Hold the DataChangedListener collection + private List listeners; + + // Method to notify the DataChangedListener of different event types when an event changes + public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + case APP_AUTH: + listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); + break; + case PLUGIN: + listener.onPluginChanged((List) event.getSource(), event.getEventType()); + break; + case RULE: + listener.onRuleChanged((List) event.getSource(), event.getEventType()); + break; + case SELECTOR: + listener.onSelectorChanged((List) event.getSource(), event.getEventType()); + break; + case META_DATA: + listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); + break; + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } + } +} +``` + +```java +public interface DataChangedListener { + + default void onAppAuthChanged(List changed, DataEventTypeEnum eventType) {} + + default void onPluginChanged(List changed, DataEventTypeEnum eventType) {} + + default void onSelectorChanged(List changed, DataEventTypeEnum eventType) {} + + default void onMetaDataChanged(List changed, DataEventTypeEnum eventType) {} + + default void onRuleChanged(List changed, DataEventTypeEnum eventType) {} +} +``` + +After understanding the functions of these two, what does AbstractData ChangedListener do? Take an example of onPluginChanged (): + +```java +public abstract class AbstractDataChangedListener implements DataChangedListener, InitializingBean { + + protected static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); + + @Override + public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) { + if (CollectionUtils.isEmpty(changed)) { + return; + } + this.updatePluginCache(); + this.afterPluginChanged(changed, eventType); + } + + // Modify cache (overwritable) + protected void updatePluginCache() { + this.updateCache(ConfigGroupEnum.PLUGIN, pluginService.listAll()); + } + + protected void updateCache(final ConfigGroupEnum group, final List data) { + String json = GsonUtils.getInstance().toJson(data); + ConfigDataCache newVal = new ConfigDataCache(group.name(), json, Md5Utils.md5(json), System.currentTimeMillis()); + ConfigDataCache oldVal = CACHE.put(newVal.getGroup(), newVal); + log.info("update config cache[{}], old: {}, updated: {}", group, oldVal, newVal); + } + + // Hook, customize what to do after ending data changes (rewritable) + protected void afterPluginChanged(final List changed, final DataEventTypeEnum eventType) { + } +} +``` + +For a plug-in data change method (onPluginChanged), AbstractDataChangedListener actually defines a template, so that the subclass can work according to the specified steps, and the details of each step can be implemented by the subclass itself. + +Second, if you do not override its cache updates, it is maintained by the class in CACHE. + +### What are the other synchronization strategies doing at this time? + +After the DataChange dEventDispatcher calls onPluginChanged (), how does the long polling module work? ** Think about what other synchronization methods are doing at this point. ** + +The web socket pattern, for example, rewrites onPluginChanged () itself to send the websocket information to the holding session, which has a gateway. + +```java +public class WebsocketDataChangedListener implements DataChangedListener { + + @Override + public void onPluginChanged(final List pluginDataList, final DataEventTypeEnum eventType) { + WebsocketData websocketData = + new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); + } +} +``` + +Looking at the zookeeper pattern, it also rewrites onPluginChanged () to modify the node information on the zookeeper so that the gateway side will hear their node changes. + +```java +public class ZookeeperDataChangedListener implements DataChangedListener { + + @Override + public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) { + for (PluginData data : changed) { + String pluginPath = ZkPathConstants.buildPluginPath(data.getName()); + // delete + if (eventType == DataEventTypeEnum.DELETE) { + deleteZkPathRecursive(pluginPath); + String selectorParentPath = ZkPathConstants.buildSelectorParentPath(data.getName()); + deleteZkPathRecursive(selectorParentPath); + String ruleParentPath = ZkPathConstants.buildRuleParentPath(data.getName()); + deleteZkPathRecursive(ruleParentPath); + continue; + } + //create or update + insertZkNode(pluginPath, data); + } + } +} +``` + +As you can see, at this juncture, other synchronization strategies are already busy notifying gateways, so Http long polling must also do this. + +These two strategies are also different in the way of notification, websocket is a good person to do to the end, directly find the session session to send the information in person. After the zookeeper changes the node information, the gateway monitors the change and then synchronizes. + +So how does our Http long polling now notify the gateway? Keep looking.. + +### Thinking on the Implementation of Long Polling + +First think about how I can design long polling by myself? + +Normal long polling implementation should be actively requested by the gateway. The background receives the request and holds it. If there is an update, it will return directly. If not, it will be blocked for a certain period of time. And the background is to do a good job of updating the data, hold the time to check whether the data has changed. + +There are three points involved here: + +1. How do you know if the data has changed? Do you set a last update time and compare it with the request time of the gateway to see if there is any data modification? +2. After holding, how does the background know whether the data is updated, repeated traversal or blocked waiting? +3. Where is the data used for updating? In the case of caching, consider how the background cache interacts with the database. + +### Http LongPollingData ChangedListener Long Polling Implementation + +Around our thinking, look at how Http LongPollingData ChangedListener is achieved. Let's take a look at the implementation of the parent onPluginChanged (). + +```java +public class HttpLongPollingDataChangedListener extends AbstractDataChangedListener { + + private final ScheduledExecutorService scheduler; + + @Override + protected void afterPluginChanged(final List changed, final DataEventTypeEnum eventType) { + scheduler.execute(new DataChangeTask(ConfigGroupEnum.PLUGIN)); + } +} +``` + +Http long polling does not directly override onPluginChanged (), but directly uses its parent class, which means that its CACHE is used. In the end, our information acquisition must also be analyzed. Put it aside for the time being. + +The following logic will call the afterPluginChanged () method of our implementation, where a timed thread pool is used to run a Runnable task DataChangeTask. + +```java +class DataChangeTask implements Runnable { + + @Override + public void run() { + // Iterate through clients + for (Iterator iter = clients.iterator(); iter.hasNext();) { + LongPollingClient client = iter.next(); + iter.remove(); + // Description Complete response The response is complete + client.sendResponse(Collections.singletonList(groupKey)); + log.info("send response with the changed group,ip={}, group={}, changeTime={}", client.ip, groupKey, changeTime); + } + } +} +``` + +After the data is changed, the thread pool is used to call this method, take all `clients` the elements while traversing, and call the method sendResponse (), like marking that the response has been completed. + +Let me guess what it does. `clients` It's likely that the request is held by the gateway, and send response () is likely to actually add response information to the request context. Another key action is to end the hold, allowing the gateway to receive the response and remove the request from the collection. + +We now trace the following `client` generation, which is a BlockingQueue blocking queue in the HttpLongPollingData ChangedListener, which is periodically detected in the LongPolling Client. + +```java +class LongPollingClient implements Runnable { + + @Override + public void run() { + this.asyncTimeoutFuture = scheduler.schedule(() -> { + clients.remove(LongPollingClient.this); + List changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); + sendResponse(changedGroups); + }, timeoutTime, TimeUnit.MILLISECONDS); + // Here is the key, indicating the source + clients.add(this); + } +} +``` + +Instead of analyzing the detection code block of remove (), you can see the add () in the last sentence, which is `clients` the source of the data. + +Find where LongPollingClient is called. HttpLongPollingData ChangedListener # doLongPolling + +```java +public void doLongPolling(final HttpServletRequest request, final HttpServletResponse response) { + + // ... + + // listen for configuration changed. + // Enable synchronous blocking requests + final AsyncContext asyncContext = request.startAsync(); + + // AsyncContext.settimeout() does not timeout properly, so you have to control it yourself + asyncContext.setTimeout(0L); + + // block client's thread. + // The thread pool calls LongPollingClient#run + scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); +} +``` + +The last sentence here will be called and added `client`, and there is a key line of code that blocks the request: + +```java +final AsyncContext asyncContext = request.startAsync(); +``` + +In the LongPolling Client # sendResponse, it has just been analyzed that, in addition to wrapping the injected response information, the held request will also be released. + +```java +class LongPollingClient implements Runnable { + + void sendResponse(final List changedGroups) { + // cancel scheduler + if (null != asyncTimeoutFuture) { + asyncTimeoutFuture.cancel(false); + } + generateResponse((HttpServletResponse) asyncContext.getResponse(), changedGroups); + // The synchronization is complete + asyncContext.complete(); + } +} +``` + +After this analysis, we go back to doLongPolling (), where the thread pool calls another key point. + +```java +scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); +``` + +The timeout time of 60s is passed to the LongPolling Client here. What is it used for? Remember that piece of code we skipped over at LongPolling Client # run? + +```java +class LongPollingClient implements Runnable { + + @Override + public void run() { + // Start time. The delay time is based on timeoutTime + this.asyncTimeoutFuture = scheduler.schedule(() -> { + // Remove the managed connection + clients.remove(LongPollingClient.this); + List changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); + // This method releases the blocked request + sendResponse(changedGroups); + }, timeoutTime, TimeUnit.MILLISECONDS); + + clients.add(this); + } +} +``` + +Here we have understood the implementation of the long polling process in the background. Finally, we will see how doLongPolling () is called and find the calling class ConfigController. + +```java +@ConditionalOnBean(HttpLongPollingDataChangedListener.class) +@RestController +@RequestMapping("/configs") +@Slf4j +public class ConfigController { + + @PostMapping(value = "/listener") + public void listener(final HttpServletRequest request, final HttpServletResponse response) { + longPollingListener.doLongPolling(request, response); + } +} +``` + +It is also basically clear that the background exposes the HTTP path through this Controller for the gateway to call and listen to data changes. + +### Sum up + +- The background exposes the API to the gateway through the Controller layer. When the gateway requests the background, the background does not immediately return a response (whether the data has changed), but holds the request for a maximum of 60 seconds. These held requests are added to the blocking queue as an in-memory cache. +- If there is any data change in these 60 seconds, it will be distributed to our HttpLongPollingData ChangedListener through the DataChangedEventDispatcher. All held requests are traversed ** Invoke the thread pool immediately ** in the blocking queue, stuffed with response information and released. +- If there is still no data change after 60 seconds, the held request will be released and the corresponding request object of the blocking queue will be removed. + +At this point, we have sorted out its most basic long polling logic, then corresponding to the next beginning of thinking, see what conclusions or doubts. + +> 1. How do you know if the data has changed? Do you set a last update time and compare it with the request time of the gateway to see if there is any data modification? +> 2. After holding, how does the background know whether the data is updated, repeated traversal or blocked waiting? +> 3. Where is the data used for updating? In the case of caching, consider how the background cache interacts with the database. + +In response to point 1, how do we know that the data has changed? + +- At present, the data change source of our analysis is DataChangedEventDispatcher, which not only informs us when the data changes, but also calls it immediately every time we manually click the background synchronization. + + Then there must be something like new and old data comparison. Otherwise, every call will directly release the blocking request of the gateway. This is not possible. White IO consumption is certainly not a good design. + +For the second point, we now know that the mode is blocking and waiting, which is `AsyncContext` used in this way. I have not understood this part, and I will discuss it in an extra chapter. + +For the third point, we know that the background configuration must be modified to the database, so the interaction between this cache and the database is also a point worth analyzing. I will continue to analyze these questions in the next chapter. diff --git a/src/blog/soul_source_learning_09_httplongpolling_02.md b/src/blog/soul_source_learning_09_httplongpolling_02.md index 4b3427eb0a..e64a4420c0 100644 --- a/src/blog/soul_source_learning_09_httplongpolling_02.md +++ b/src/blog/soul_source_learning_09_httplongpolling_02.md @@ -1,264 +1,264 @@ ---- -title: Soul Gateway Learns Http Long Polling Analysis 02 -author: zhuming -date: 2021-01-27 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -## Data synchronization between background and gateway (Http long polling) - -The last chapter of long polling analysis summarizes the implementation of long polling on the gateway side and the way of data flow. - -The overall process of long polling at the gateway end is also divided into two modules: one is pulling at startup, and the other is polling to monitor changes. - -## Pull data on gateway startup - -After the gateway is started, it will call the interface provided by the background to pull data and send the data to the data processing class of each plug-in - -The following shows the processing flow for the gateway to start pulling data: ![01](/assets/img/blog1/01.png) - -These several processing steps are dispersed into the method collaborations of the following classes: - -![02](/assets/img/blog1/02.png) - -HttpS yncData Service # start: When the gateway is started, the HttpS yncData Service initialization will call `start()` a method, which will call the background to pull data and start multiple threads for polling and monitoring (this part will be analyzed in the next module) - -```java -public class HttpSyncDataService implements SyncDataService, AutoCloseable { - - private void start() { - // Prevents the CAS operation from being invoked twice - if (RUNNING.compareAndSet(false, true)) { - // Here is the focus of the process, calling the method to pull data - this.fetchGroupConfig(ConfigGroupEnum.values()); - int threadSize = serverList.size(); - // This will be analyzed in the next module, which will enable thread polling listening according to the background cluster - this.executor = new ThreadPoolExecutor(threadSize, threadSize, 60L, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), - SoulThreadFactory.create("http-long-polling", true)); - this.serverList.forEach(server -> this.executor.execute(new HttpLongPollingTask(server))); - } else { - log.info("soul http long polling was started, executor=[{}]", executor); - } - } -} -``` - -HttpS yncData Service # fetchGroup Config: It is only used to repeatedly call the pull data method according to the data type (for the same background, it will be requested many times, and the information of a certain data type will be pulled each time). The data type here refers to plugin, rule, selector, and so on - -```java -private void fetchGroupConfig(final ConfigGroupEnum... groups) throws SoulException { - for (int index = 0; index < this.serverList.size(); index++) { - String server = serverList.get(index); - try { - // Call the pull data method multiple times according to the passed data type enumeration - this.doFetchGroupConfig(server, groups); - break; - } catch (SoulException e) { - if (index >= serverList.size() - 1) { - throw e; - } - log.warn("fetch config fail, try another one: {}", serverList.get(index + 1)); - } - } -} -``` - -HttpS yncData Service # doFetchGroup Config: Request the `/configs/fetch` background interface, get a certain type of data, and update the cache. Before updating the cache, it will check whether it has changed, and if it has changed, it will end. \*\* \*\* (Since it is the first time to start, the cache will definitely be updated when the data is empty, so it will end directly.) - -```java -private void doFetchGroupConfig(final String server, final ConfigGroupEnum... groups) { - StringBuilder params = new StringBuilder(); - for (ConfigGroupEnum groupKey : groups) { - params.append("groupKeys").append("=").append(groupKey.name()).append("&"); - } - // Construct the specific request path to fetch background data - String url = server + "/configs/fetch?" + StringUtils.removeEnd(params.toString(), "&"); - log.info("request configs: [{}]", url); - String json = null; - try { - json = this.httpClient.getForObject(url, String.class); - } catch (RestClientException e) { - String message = String.format("fetch config fail from server[%s], %s", url, e.getMessage()); - log.warn(message); - throw new SoulException(message, e); - } - // Update cache information - boolean updated = this.updateCacheWithJson(json); - // If there are updates, end the process - if (updated) { - log.info("get latest configs: [{}]", json); - return; - } - log.info("The config of the server[{}] has not been updated or is out of date. Wait for 30s to listen for changes again.", server); - ThreadUtils.sleep(TimeUnit.SECONDS, 30); -} -``` - -HttpS yncData Service # update Cache WithJson: Take out the changed data information from `data` the response information and send it to the DataRefresh Factory - -```java -private DataRefreshFactory factory; - -public HttpSyncDataService(...){ - this.factory = new DataRefreshFactory(pluginDataSubscriber, metaDataSubscribers, authDataSubscribers); -} - -private boolean updateCacheWithJson(final String json) { - JsonObject jsonObject = GSON.fromJson(json, JsonObject.class); - JsonObject data = jsonObject.getAsJsonObject("data"); - return factory.executor(data); -} -``` - -DataRefreshFactory # executor: Send the data to all kinds of data refresh classes (the information type is not distinguished here, but all data refresh classes are notified, and optimization can be considered) - -```java -public final class DataRefreshFactory { - - private static final EnumMap ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class); - - public DataRefreshFactory(final PluginDataSubscriber pluginDataSubscriber, - final List metaDataSubscribers, - final List authDataSubscribers) { - // 注入各类型订阅器到 MAP 中 - ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataRefresh(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataRefresh(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataRefresh(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AppAuthDataRefresh(authDataSubscribers)); - ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataRefresh(metaDataSubscribers)); - } - - public boolean executor(final JsonObject data) { - final boolean[] success = {false}; - // Tureen: 所有数据类型的 DataRefresh 全调用 - ENUM_MAP.values().parallelStream().forEach(dataRefresh -> success[0] = dataRefresh.refresh(data)); - return success[0]; - } -} -``` - -AbstractData Refresh # refresh: Determine whether to update the cache, and if so, call the method of each type `refresh()` - -```java -@Override -public Boolean refresh(final JsonObject data) { - boolean updated = false; - JsonObject jsonObject = convert(data); - if (null != jsonObject) { - ConfigData result = fromJson(jsonObject); - if (this.updateCacheIfNeed(result)) { - updated = true; - // Turren: 调用 refresh - refresh(result.getData()); - } - } - return updated; -} -``` - -PluginData Refresh # refresh: Invokes the plugin's subscriber, which in turn notifies all extension related events of the change - -```java -@Override -protected void refresh(final List data) { - if (CollectionUtils.isEmpty(data)) { - log.info("clear all plugin data cache"); - pluginDataSubscriber.refreshPluginDataAll(); - } else { - pluginDataSubscriber.refreshPluginDataAll(); - // Turren: HTTP synchronization is used, calling the plugin data subscriber - data.forEach(pluginDataSubscriber::onSubscribe); - } -} -``` - -## The gateway polls to listen for changes - -When the gateway is started, the thread is also started to make a background monitoring request. The monitoring request makes a while endless loop to poll, and the request will be hijacked on the background side. This is specifically analyzed in the background summary ([后台与网关数据同步 (Http 长轮询篇 <二>)](https://blog.csdn.net/zm469568595/article/details/113207367)). - -The following shows the overall process of monitoring data changes by the gateway: - -![03](/assets/img/blog1/03.png) - -The corresponding actual code implementation is as follows: - -![04](/assets/img/blog1/04.png) - -The monitoring process on the ** gateway side is implemented in the HttpSyncDataService class, and will be `doFetchGroupConfig()` passed to various subscribers at the end. The following process is the same ** as that at startup - -HttpS yncData Service # start: Start the thread to execute the Http LongPollingTask Runnable - -Http LongPolling Task # run: Turn on cyclic call to poll method. - -```java -@Override -public void run() { - while (RUNNING.get()) { - for (int time = 1; time <= retryTimes; time++) { - try { - doLongPolling(server); - } catch (Exception e) { - if (time < retryTimes) { - log.warn("Long polling failed, tried {} times, {} times left, will be suspended for a while! {}", - time, retryTimes - time, e.getMessage()); - ThreadUtils.sleep(TimeUnit.SECONDS, 5); - continue; - } - log.error("Long polling failed, try again after 5 minutes!", e); - ThreadUtils.sleep(TimeUnit.MINUTES, 5); - } - } - } -} -``` - -Http LongPolling Task # doLongPolling: Get the response result of the listening request. If there is a changed type in the return value, call the data pulling method. - -```java -private void doLongPolling(final String server) { - // Retrieve data from the cache - MultiValueMap params = new LinkedMultiValueMap<>(8); - for (ConfigGroupEnum group : ConfigGroupEnum.values()) { - ConfigData cacheConfig = factory.cacheConfigData(group); - String value = String.join(",", cacheConfig.getMd5(), String.valueOf(cacheConfig.getLastModifyTime())); - params.put(group.name(), Lists.newArrayList(value)); - } - // Build the HTTP request information - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - HttpEntity httpEntity = new HttpEntity(params, headers); - String listenerUrl = server + "/configs/listener"; - log.debug("request listener configs: [{}]", listenerUrl); - JsonArray groupJson = null; - try { - String json = this.httpClient.postForEntity(listenerUrl, httpEntity, String.class).getBody(); - groupJson = GSON.fromJson(json, JsonObject.class).getAsJsonArray("data"); - } catch (RestClientException e) { - String message = String.format("listener configs fail, server:[%s], %s", server, e.getMessage()); - throw new SoulException(message, e); - } - // Obtain the changed types - if (groupJson != null) { - ConfigGroupEnum[] changedGroups = GSON.fromJson(groupJson, ConfigGroupEnum[].class); - if (ArrayUtils.isNotEmpty(changedGroups)) { - log.info("Group config changed: {}", Arrays.toString(changedGroups)); - // Retrieve data of corresponding types from the background - this.doFetchGroupConfig(server, changedGroups); - } - } -} -``` - -LongPollingClient#doFetchGroupConfig: - -This piece of code was analyzed in the previous startup, and the biggest difference between it and the startup is that \*\* \*\*.. - -What do you mean? If the gateway goes to `fetch` the background data and takes it back for comparison, it is found that it has been cheated! There is no change. Just wait for 30s to start the next monitoring. During this period, if there is a data change in the background, there is no way to notify the gateway. - -Why is the gateway doing this? Naturally, in order to prevent a large number of useless pull cycles, if there is a problem in the background and the data is constantly notified to change, but there is no actual change, then the gateway will generate a large number of useless network IO and data exchange with the background without delay. +--- +title: Soul Gateway Learns Http Long Polling Analysis 02 +author: zhuming +date: 2021-01-27 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +## Data synchronization between background and gateway (Http long polling) + +The last chapter of long polling analysis summarizes the implementation of long polling on the gateway side and the way of data flow. + +The overall process of long polling at the gateway end is also divided into two modules: one is pulling at startup, and the other is polling to monitor changes. + +## Pull data on gateway startup + +After the gateway is started, it will call the interface provided by the background to pull data and send the data to the data processing class of each plug-in + +The following shows the processing flow for the gateway to start pulling data: ![01](/assets/img/blog1/01.png) + +These several processing steps are dispersed into the method collaborations of the following classes: + +![02](/assets/img/blog1/02.png) + +HttpS yncData Service # start: When the gateway is started, the HttpS yncData Service initialization will call `start()` a method, which will call the background to pull data and start multiple threads for polling and monitoring (this part will be analyzed in the next module) + +```java +public class HttpSyncDataService implements SyncDataService, AutoCloseable { + + private void start() { + // Prevents the CAS operation from being invoked twice + if (RUNNING.compareAndSet(false, true)) { + // Here is the focus of the process, calling the method to pull data + this.fetchGroupConfig(ConfigGroupEnum.values()); + int threadSize = serverList.size(); + // This will be analyzed in the next module, which will enable thread polling listening according to the background cluster + this.executor = new ThreadPoolExecutor(threadSize, threadSize, 60L, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + SoulThreadFactory.create("http-long-polling", true)); + this.serverList.forEach(server -> this.executor.execute(new HttpLongPollingTask(server))); + } else { + log.info("soul http long polling was started, executor=[{}]", executor); + } + } +} +``` + +HttpS yncData Service # fetchGroup Config: It is only used to repeatedly call the pull data method according to the data type (for the same background, it will be requested many times, and the information of a certain data type will be pulled each time). The data type here refers to plugin, rule, selector, and so on + +```java +private void fetchGroupConfig(final ConfigGroupEnum... groups) throws SoulException { + for (int index = 0; index < this.serverList.size(); index++) { + String server = serverList.get(index); + try { + // Call the pull data method multiple times according to the passed data type enumeration + this.doFetchGroupConfig(server, groups); + break; + } catch (SoulException e) { + if (index >= serverList.size() - 1) { + throw e; + } + log.warn("fetch config fail, try another one: {}", serverList.get(index + 1)); + } + } +} +``` + +HttpS yncData Service # doFetchGroup Config: Request the `/configs/fetch` background interface, get a certain type of data, and update the cache. Before updating the cache, it will check whether it has changed, and if it has changed, it will end. \*\* \*\* (Since it is the first time to start, the cache will definitely be updated when the data is empty, so it will end directly.) + +```java +private void doFetchGroupConfig(final String server, final ConfigGroupEnum... groups) { + StringBuilder params = new StringBuilder(); + for (ConfigGroupEnum groupKey : groups) { + params.append("groupKeys").append("=").append(groupKey.name()).append("&"); + } + // Construct the specific request path to fetch background data + String url = server + "/configs/fetch?" + StringUtils.removeEnd(params.toString(), "&"); + log.info("request configs: [{}]", url); + String json = null; + try { + json = this.httpClient.getForObject(url, String.class); + } catch (RestClientException e) { + String message = String.format("fetch config fail from server[%s], %s", url, e.getMessage()); + log.warn(message); + throw new SoulException(message, e); + } + // Update cache information + boolean updated = this.updateCacheWithJson(json); + // If there are updates, end the process + if (updated) { + log.info("get latest configs: [{}]", json); + return; + } + log.info("The config of the server[{}] has not been updated or is out of date. Wait for 30s to listen for changes again.", server); + ThreadUtils.sleep(TimeUnit.SECONDS, 30); +} +``` + +HttpS yncData Service # update Cache WithJson: Take out the changed data information from `data` the response information and send it to the DataRefresh Factory + +```java +private DataRefreshFactory factory; + +public HttpSyncDataService(...){ + this.factory = new DataRefreshFactory(pluginDataSubscriber, metaDataSubscribers, authDataSubscribers); +} + +private boolean updateCacheWithJson(final String json) { + JsonObject jsonObject = GSON.fromJson(json, JsonObject.class); + JsonObject data = jsonObject.getAsJsonObject("data"); + return factory.executor(data); +} +``` + +DataRefreshFactory # executor: Send the data to all kinds of data refresh classes (the information type is not distinguished here, but all data refresh classes are notified, and optimization can be considered) + +```java +public final class DataRefreshFactory { + + private static final EnumMap ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class); + + public DataRefreshFactory(final PluginDataSubscriber pluginDataSubscriber, + final List metaDataSubscribers, + final List authDataSubscribers) { + // 注入各类型订阅器到 MAP 中 + ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataRefresh(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataRefresh(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataRefresh(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AppAuthDataRefresh(authDataSubscribers)); + ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataRefresh(metaDataSubscribers)); + } + + public boolean executor(final JsonObject data) { + final boolean[] success = {false}; + // Tureen: 所有数据类型的 DataRefresh 全调用 + ENUM_MAP.values().parallelStream().forEach(dataRefresh -> success[0] = dataRefresh.refresh(data)); + return success[0]; + } +} +``` + +AbstractData Refresh # refresh: Determine whether to update the cache, and if so, call the method of each type `refresh()` + +```java +@Override +public Boolean refresh(final JsonObject data) { + boolean updated = false; + JsonObject jsonObject = convert(data); + if (null != jsonObject) { + ConfigData result = fromJson(jsonObject); + if (this.updateCacheIfNeed(result)) { + updated = true; + // Turren: 调用 refresh + refresh(result.getData()); + } + } + return updated; +} +``` + +PluginData Refresh # refresh: Invokes the plugin's subscriber, which in turn notifies all extension related events of the change + +```java +@Override +protected void refresh(final List data) { + if (CollectionUtils.isEmpty(data)) { + log.info("clear all plugin data cache"); + pluginDataSubscriber.refreshPluginDataAll(); + } else { + pluginDataSubscriber.refreshPluginDataAll(); + // Turren: HTTP synchronization is used, calling the plugin data subscriber + data.forEach(pluginDataSubscriber::onSubscribe); + } +} +``` + +## The gateway polls to listen for changes + +When the gateway is started, the thread is also started to make a background monitoring request. The monitoring request makes a while endless loop to poll, and the request will be hijacked on the background side. This is specifically analyzed in the background summary ([后台与网关数据同步 (Http 长轮询篇 <二>)](https://blog.csdn.net/zm469568595/article/details/113207367)). + +The following shows the overall process of monitoring data changes by the gateway: + +![03](/assets/img/blog1/03.png) + +The corresponding actual code implementation is as follows: + +![04](/assets/img/blog1/04.png) + +The monitoring process on the ** gateway side is implemented in the HttpSyncDataService class, and will be `doFetchGroupConfig()` passed to various subscribers at the end. The following process is the same ** as that at startup + +HttpS yncData Service # start: Start the thread to execute the Http LongPollingTask Runnable + +Http LongPolling Task # run: Turn on cyclic call to poll method. + +```java +@Override +public void run() { + while (RUNNING.get()) { + for (int time = 1; time <= retryTimes; time++) { + try { + doLongPolling(server); + } catch (Exception e) { + if (time < retryTimes) { + log.warn("Long polling failed, tried {} times, {} times left, will be suspended for a while! {}", + time, retryTimes - time, e.getMessage()); + ThreadUtils.sleep(TimeUnit.SECONDS, 5); + continue; + } + log.error("Long polling failed, try again after 5 minutes!", e); + ThreadUtils.sleep(TimeUnit.MINUTES, 5); + } + } + } +} +``` + +Http LongPolling Task # doLongPolling: Get the response result of the listening request. If there is a changed type in the return value, call the data pulling method. + +```java +private void doLongPolling(final String server) { + // Retrieve data from the cache + MultiValueMap params = new LinkedMultiValueMap<>(8); + for (ConfigGroupEnum group : ConfigGroupEnum.values()) { + ConfigData cacheConfig = factory.cacheConfigData(group); + String value = String.join(",", cacheConfig.getMd5(), String.valueOf(cacheConfig.getLastModifyTime())); + params.put(group.name(), Lists.newArrayList(value)); + } + // Build the HTTP request information + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + HttpEntity httpEntity = new HttpEntity(params, headers); + String listenerUrl = server + "/configs/listener"; + log.debug("request listener configs: [{}]", listenerUrl); + JsonArray groupJson = null; + try { + String json = this.httpClient.postForEntity(listenerUrl, httpEntity, String.class).getBody(); + groupJson = GSON.fromJson(json, JsonObject.class).getAsJsonArray("data"); + } catch (RestClientException e) { + String message = String.format("listener configs fail, server:[%s], %s", server, e.getMessage()); + throw new SoulException(message, e); + } + // Obtain the changed types + if (groupJson != null) { + ConfigGroupEnum[] changedGroups = GSON.fromJson(groupJson, ConfigGroupEnum[].class); + if (ArrayUtils.isNotEmpty(changedGroups)) { + log.info("Group config changed: {}", Arrays.toString(changedGroups)); + // Retrieve data of corresponding types from the background + this.doFetchGroupConfig(server, changedGroups); + } + } +} +``` + +LongPollingClient#doFetchGroupConfig: + +This piece of code was analyzed in the previous startup, and the biggest difference between it and the startup is that \*\* \*\*.. + +What do you mean? If the gateway goes to `fetch` the background data and takes it back for comparison, it is found that it has been cheated! There is no change. Just wait for 30s to start the next monitoring. During this period, if there is a data change in the background, there is no way to notify the gateway. + +Why is the gateway doing this? Naturally, in order to prevent a large number of useless pull cycles, if there is a problem in the background and the data is constantly notified to change, but there is no actual change, then the gateway will generate a large number of useless network IO and data exchange with the background without delay. diff --git a/src/blog/soul_source_learning_10_websocket.md b/src/blog/soul_source_learning_10_websocket.md index 225674d466..9ff91f79fa 100644 --- a/src/blog/soul_source_learning_10_websocket.md +++ b/src/blog/soul_source_learning_10_websocket.md @@ -1,782 +1,782 @@ ---- -title: Soul Gateway Learns WebSocket Data Synchronization Analysis -author: fanjinpeng,zhuming -date: 2021-01-22 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -> Fan Jinpeng - -# 1.Previously on - -In Part 4, we analyzed that after the HTTP user service system accesses the Soul gateway, it will call the registration interface of soul-admin, register all the interface information that needs to be proxied by the gateway to soul-admin, and finally, it will connect through the web socket. Synchronize the interface information received by soul-admin to Soul Gateway (soul-bootstrap). Today, we will continue to analyze how the data is synchronized to soul-bootstrap. - -If you don't know the process, you can go out and turn left to see the fourth article. - -# 2.Soul-admin and soul-bootstrap data synchronization - -In order to verify the data synchronization process, there is no need to start the business system. You can just start the soul-admin and soul-bootstrap systems. You can open or close the plug-in on the page to see how this process is implemented. - -Link to the official website of data synchronization strategy - -## 2.1 Start 2 systems - -They are all started by default according to the project, and no configuration files need to be modified. - -## 2.2. Page operation search interface - -Start the divide plug-in here, F12, and see which interface soul-admin will be called in the foreground. - -![ open_divide_plugin ](/assets/img/blog3/open_divide_plugin.png) - -You can see that the foreground sends a PUT request to the background: http://localhost:9095/plugin/5. - -## 2.3 Background interface - -Search for this interface in the project - -```java -// PluginController.java -@RestController -@RequestMapping("/plugin") -public class PluginController { - -... - - /** - * update plugin. - * - * @param id primary key. - * @param pluginDTO plugin. - * @return {@linkplain SoulAdminResult} - */ - @PutMapping("/{id}") - public SoulAdminResult updatePlugin(@PathVariable("id") final String id, @RequestBody final PluginDTO pluginDTO) { - Objects.requireNonNull(pluginDTO); - pluginDTO.setId(id); - final String result = pluginService.createOrUpdate(pluginDTO); - if (StringUtils.isNoneBlank(result)) { - return SoulAdminResult.error(result); - } - return SoulAdminResult.success(SoulResultMessage.UPDATE_SUCCESS); - } - -... - -} -``` - -Into the implementation class. - -```java -// PluginServiceImpl.java -/** - * create or update plugin. - * - * @param pluginDTO {@linkplain PluginDTO} - * @return rows - */ - @Override - @Transactional(rollbackFor = Exception.class) - public String createOrUpdate(final PluginDTO pluginDTO) { - final String msg = checkData(pluginDTO); - if (StringUtils.isNoneBlank(msg)) { - return msg; - } - PluginDO pluginDO = PluginDO.buildPluginDO(pluginDTO); - DataEventTypeEnum eventType = DataEventTypeEnum.CREATE; - if (StringUtils.isBlank(pluginDTO.getId())) { - pluginMapper.insertSelective(pluginDO); - } else { - eventType = DataEventTypeEnum.UPDATE; - pluginMapper.updateSelective(pluginDO); - } - - // publish change event. - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType, - Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO)))); - return StringUtils.EMPTY; - } -``` - -It can be seen here that the first half is to operate the database and persist the relevant information; the second half is to publish an event. - -## 2.4 Publish the event - -The event published here is encapsulated by DataChangedEvent, and there is an enumeration in it. There are many types here: - -```java -/** - * configuration group. - * - * @author huangxiaofeng - */ -public enum ConfigGroupEnum { - - APP_AUTH, - - PLUGIN, - - RULE, - - SELECTOR, - - META_DATA; - -... - -} -``` - -Seeing these types, if you still have an impression of the fourth article, you can see that the types of events sent at that time were SELECTOR and RULE, and now it is PLUGIN. Although the types are different, it does not affect us to continue to analyze the logic behind. Let's continue. - -Another eventType is also an enumeration. There are five types: DELETE, CREATE, UPDATE, REFRESH, and MYSELF. In this case, it is UPDATE. - -```java -/** - * The enum Data event type. - * - * @author xiaoyu - */ -public enum DataEventTypeEnum { - /** - * delete event. - */ - DELETE, - /** - * insert event. - */ - CREATE, - /** - * update event. - */ - UPDATE, - /** - * REFRESH data event type enum. - */ - REFRESH, - /** - * Myself data event type enum. - */ - MYSELF; - -... - -} -``` - -## 2.5 Listen for events - -Locate the code that listens for events: - -```java -// DataChangedEventDispatcher.java -@Component -public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { - - private ApplicationContext applicationContext; - - private List listeners; - - public DataChangedEventDispatcher(final ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - @Override - @SuppressWarnings("unchecked") - public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - case APP_AUTH: - listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); - break; - case PLUGIN: - listener.onPluginChanged((List) event.getSource(), event.getEventType()); - break; - case RULE: - listener.onRuleChanged((List) event.getSource(), event.getEventType()); - break; - case SELECTOR: - listener.onSelectorChanged((List) event.getSource(), event.getEventType()); - break; - case META_DATA: - listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); - break; - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } - } - - @Override - public void afterPropertiesSet() { - Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); - this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); - } -} -``` - -### 2.5.1 Listener injection - -You can see that the DataChangedEventDispatcher implements the InitializingBean interface, overrides the afterPropertiesSet method, and uses @ Component when Spring starts. This override method is called after the container is loaded. In the After PropertiesSet method, all the beans of the DataChangedListener type are obtained and placed in the class property listeners. - -So the question is, when are these listeners injected into the container? - -First look at the definition of the Data ChangedListener interface: - -```java -/** - * Event listener, used to send notification of event changes, - * used to support HTTP, websocket, zookeeper and other event notifications. - * - * @author huangxiaofeng - * @author xiaoyu - */ -public interface DataChangedListener { - - /** - * invoke this method when AppAuth was received. - * - * @param changed the changed - * @param eventType the event type - */ - default void onAppAuthChanged(List changed, DataEventTypeEnum eventType) { - } - - /** - * invoke this method when Plugin was received. - * - * @param changed the changed - * @param eventType the event type - */ - default void onPluginChanged(List changed, DataEventTypeEnum eventType) { - } - - /** - * invoke this method when Selector was received. - * - * @param changed the changed - * @param eventType the event type - */ - default void onSelectorChanged(List changed, DataEventTypeEnum eventType) { - } - - /** - * On meta data changed. - * - * @param changed the changed - * @param eventType the event type - */ - default void onMetaDataChanged(List changed, DataEventTypeEnum eventType) { - - } - - /** - * invoke this method when Rule was received. - * - * @param changed the changed - * @param eventType the event type - */ - default void onRuleChanged(List changed, DataEventTypeEnum eventType) { - } - -} -``` - -It can be seen that there are five methods defined in the interface, which respectively deal with the corresponding processing methods when the data changes of appAuth, plugin, selector, metaData and rule are monitored. - -Its inheritance relationship: - -![ DataChangedListener ](/assets/img/blog3/DataChangedListener.png) - -Because the websocket is used by default, the listener here corresponds to the Web socketData ChangedListener, Alt + F7, and the place where this class is instantiated is the following configuration class: - -```java -// DataSyncConfiguration.java -@Configuration -public class DataSyncConfiguration { - - /** - * http long polling. - */ - @Configuration - @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true") - @EnableConfigurationProperties(HttpSyncProperties.class) - static class HttpLongPollingListener { - @Bean - @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class) - public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) { - return new HttpLongPollingDataChangedListener(httpSyncProperties); - } - } - - /** - * The type Zookeeper listener. - */ - @Configuration - @ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url") - @Import(ZookeeperConfiguration.class) - static class ZookeeperListener { - @Bean - @ConditionalOnMissingBean(ZookeeperDataChangedListener.class) - public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) { - return new ZookeeperDataChangedListener(zkClient); - } - @Bean - @ConditionalOnMissingBean(ZookeeperDataInit.class) - public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { - return new ZookeeperDataInit(zkClient, syncDataService); - } - } - - /** - * The type Nacos listener. - */ - @Configuration - @ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url") - @Import(NacosConfiguration.class) - static class NacosListener { - @Bean - @ConditionalOnMissingBean(NacosDataChangedListener.class) - public DataChangedListener nacosDataChangedListener(final ConfigService configService) { - return new NacosDataChangedListener(configService); - } - } - - /** - * The WebsocketListener(default strategy). - */ - @Configuration - @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true) - @EnableConfigurationProperties(WebsocketSyncProperties.class) - static class WebsocketListener { - @Bean - @ConditionalOnMissingBean(WebsocketDataChangedListener.class) - public DataChangedListener websocketDataChangedListener() { - return new WebsocketDataChangedListener(); - } - @Bean - @ConditionalOnMissingBean(WebsocketCollector.class) - public WebsocketCollector websocketCollector() { - return new WebsocketCollector(); - } - @Bean - @ConditionalOnMissingBean(ServerEndpointExporter.class) - public ServerEndpointExporter serverEndpointExporter() { - return new ServerEndpointExporter(); - } - } -} -``` - -There are four data synchronization strategies: HTTP long polling, zookeeper, nacos, and websocket (default strategy). - -See the web socket annotation @ ConditionalOnProperty (name = "soul. Sync. Web socket. Enabled", having Value = "true", Match (IfMissing = true), find the following configuration in the configuration file: - -```yaml -soul: - sync: - websocket: - enabled: true -``` - -This is where the truth comes out. - -If you do not want to use the default synchronization policy of the web socket, you can write the corresponding configuration in the configuration file. - -### 2.5.2 Listening event processing logic - -In order to prevent you from turning back and looking at it, which is inconvenient, I will post the processing logic code here: - -```java -// DataChangedEventDispatcher.java -@Override - @SuppressWarnings("unchecked") - public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - case APP_AUTH: - listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); - break; - case PLUGIN: - listener.onPluginChanged((List) event.getSource(), event.getEventType()); - break; - case RULE: - listener.onRuleChanged((List) event.getSource(), event.getEventType()); - break; - case SELECTOR: - listener.onSelectorChanged((List) event.getSource(), event.getEventType()); - break; - case META_DATA: - listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); - break; - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } - } -``` - -All listeners are traversed here. For the current web socket, there is only one listener, and it is not known when the other multiple cases will appear. It is doubtful here, and we will come back to add (//TODO) when we encounter related cases later. - -Different logics are used according to the type of the published event. The types here correspond to the methods defined in the DataChangedListener interface. - -The listener here is an instance of the Web socketData ChangedListener, which will enter the corresponding method in the class: - -```java -// WebsocketDataChangedListener.java -public class WebsocketDataChangedListener implements DataChangedListener { - - @Override - public void onPluginChanged(final List pluginDataList, final DataEventTypeEnum eventType) { - WebsocketData websocketData = - new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); - } - - @Override - public void onSelectorChanged(final List selectorDataList, final DataEventTypeEnum eventType) { - WebsocketData websocketData = - new WebsocketData<>(ConfigGroupEnum.SELECTOR.name(), eventType.name(), selectorDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); - } - - @Override - public void onRuleChanged(final List ruleDataList, final DataEventTypeEnum eventType) { - WebsocketData configData = - new WebsocketData<>(ConfigGroupEnum.RULE.name(), eventType.name(), ruleDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); - } - - @Override - public void onAppAuthChanged(final List appAuthDataList, final DataEventTypeEnum eventType) { - WebsocketData configData = - new WebsocketData<>(ConfigGroupEnum.APP_AUTH.name(), eventType.name(), appAuthDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); - } - - @Override - public void onMetaDataChanged(final List metaDataList, final DataEventTypeEnum eventType) { - WebsocketData configData = - new WebsocketData<>(ConfigGroupEnum.META_DATA.name(), eventType.name(), metaDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); - } - -} -``` - -As you can see in the code, the data is encapsulated as Web socketData and sent using the WebsocketController. Send method. - -## 2.6 Synchronize data to soul-bootstrap - -```java -// WebsocketCollector.java -@Slf4j -@ServerEndpoint("/websocket") -public class WebsocketCollector { - - private static final Set SESSION_SET = new CopyOnWriteArraySet<>(); - - private static final String SESSION_KEY = "sessionKey"; - - /** - * On open. - * - * @param session the session - */ - @OnOpen - public void onOpen(final Session session) { - log.info("websocket on open successful...."); - SESSION_SET.add(session); - } - - /** - * On message. - * - * @param message the message - * @param session the session - */ - @OnMessage - public void onMessage(final String message, final Session session) { - if (message.equals(DataEventTypeEnum.MYSELF.name())) { - try { - ThreadLocalUtil.put(SESSION_KEY, session); - SpringBeanUtils.getInstance().getBean(SyncDataService.class).syncAll(DataEventTypeEnum.MYSELF); - } finally { - ThreadLocalUtil.clear(); - } - } - } - - /** - * On close. - * - * @param session the session - */ - @OnClose - public void onClose(final Session session) { - SESSION_SET.remove(session); - ThreadLocalUtil.clear(); - } - - /** - * On error. - * - * @param session the session - * @param error the error - */ - @OnError - public void onError(final Session session, final Throwable error) { - SESSION_SET.remove(session); - ThreadLocalUtil.clear(); - log.error("websocket collection error: ", error); - } - - /** - * Send. - * - * @param message the message - * @param type the type - */ - public static void send(final String message, final DataEventTypeEnum type) { - if (StringUtils.isNotBlank(message)) { - if (DataEventTypeEnum.MYSELF == type) { - try { - Session session = (Session) ThreadLocalUtil.get(SESSION_KEY); - if (session != null) { - session.getBasicRemote().sendText(message); - } - } catch (IOException e) { - log.error("websocket send result is exception: ", e); - } - return; - } - for (Session session : SESSION_SET) { - try { - session.getBasicRemote().sendText(message); - } catch (IOException e) { - log.error("websocket send result is exception: ", e); - } - } - } - } -} -``` - -The Web socketController uses the @ ServerEndpoint ( "/web socket") annotation, opens a web socket service interface, and waits for a connection. - -After the soul-bootstrap is started, the web socket will be connected, and the onOpen method will be triggered to store the Session of this connection information in the Set set of the SESSION \_ SET. - -In the send method, it will first determine whether the DataEventTypeEnum type is MYSELF. This type can be traced back to 2.3-2.4. This time it is UPDATE. As for when it is MYSELF, it needs to be added later. It is doubtful here (//TODO). - -The following for loop iterates through all the web socket connection sessions to send the change data. - -At this point, the default web socket synchronization data strategy is clear. - -> Zhu Ming - -## Data synchronization between background and gateway (Web socket) - -### How to establish Web socket? In the background? - -![05](/assets/img/blog1/05.png) Data SyncConfiguration: As the configuration factory of Spring Bean, various listeners can be constructed according to the configuration information. Including HTTP long polling mode, Zookeeper mode, Nacos mode, Web socket method. - -```java -@Configuration -public class DataSyncConfiguration { - - // In the configuration of the soul-admin project, use soul.sync.websocket.enabled to enable or disable WebSocket. - @Configuration - @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true) - @EnableConfigurationProperties(WebsocketSyncProperties.class) - static class WebsocketListener { - - @Bean - @ConditionalOnMissingBean(WebsocketCollector.class) - public WebsocketCollector websocketCollector() { - return new WebsocketCollector(); - } - } -} -``` - -Web socketListener: As `DataSyncConfiguration` the internal class of, it is responsible for the initialization of the web socket listener. Web socket Collector: It monitors the websocket connection and receives information. Maintain all session sessions connected to the background, and provide `send()` methods to notify session information. - -### How does the gateway set up a Web socket? - -![06](/assets/img/blog1/06.png) - -Web socket SyncData Configuration: As the configuration factory of Spring Bean, it is the gateway's entrance to build Websocket communication. (An independent startup project `soul-spring-boot-starter-sync-data-websocket` is provided for the gateway to choose freely.) - -```java -@Configuration -@ConditionalOnClass(WebsocketSyncDataService.class) -@ConditionalOnProperty(prefix = "soul.sync.websocket", name = "urls") -@Slf4j -public class WebsocketSyncDataConfiguration { - - // Collect all subscribers registered as Beans, such as PluginDataSubscriber, MetaDataSubscriber, AuthDataSubscriber - @Bean - public SyncDataService websocketSyncDataService(final ObjectProvider websocketConfig, final ObjectProvider pluginSubscriber, final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { - log.info("you use websocket sync soul data......."); - return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new), pluginSubscriber.getIfAvailable(), metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); - } - - // In the configuration of the soul-bootstrap project, use soul.sync.websocket to configure the backend path for establishing connections - @Bean - @ConfigurationProperties(prefix = "soul.sync.websocket") - public WebsocketConfig websocketConfig() { - return new WebsocketConfig(); - } -} -``` - -Web socket SyncData Service: Get all the registered beans `WebsocketConfig` and the various `DataSubscriber` subscribers, and build an implemented `WebsocketClient` `SoulWebsocketClient` list - -SoulWeb socket Client: `Websocket` The communication class monitors the websocket connection and receives information. After receiving the information from the background, it will notify each subscriber. - -```java -public final class SoulWebsocketClient extends WebSocketClient { - - private final WebsocketDataHandler websocketDataHandler; - - private void handleResult(final String result) { - WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class); - ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType()); - // Determine the event type of data changes based on the incoming information, such as refresh, update, delete, etc. - String eventType = websocketData.getEventType(); - String json = GsonUtils.getInstance().toJson(websocketData.getData()); - websocketDataHandler.executor(groupEnum, json, eventType); - } -} -``` - -Web socketData Handler: Construct the data processing classes of various implementations `AbstractDataHandler` and cache them during initialization. - -```java -public class WebsocketDataHandler { - - // Cache all DataHandler data change handling classes - private static final EnumMap ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class); - - public WebsocketDataHandler(final PluginDataSubscriber pluginDataSubscriber, - final List metaDataSubscribers, - final List authDataSubscribers) { - ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataHandler(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AuthDataHandler(authDataSubscribers)); - ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataHandler(metaDataSubscribers)); - } - - public void executor(final ConfigGroupEnum type, final String json, final String eventType) { - // Call the corresponding DataHandler data processing class based on the data change event type - ENUM_MAP.get(type).handle(json, eventType); - } -} -``` - -### Gateway data change call chain - -After the entry class `SoulWebsocketClient` that implements Websocket communication receives the background communication, the `executor()` method called `WebsocketDataHandler` matches the information type, and calls the corresponding `DataHandler` `handler()` to process the information. - -![07](/assets/img/blog1/07.png) - -AbstractDataHandler: The implementation `handler()` method calls the corresponding event abstract method according to the type of the event (such as refresh, update, create, delete, etc.). - -```java -public abstract class AbstractDataHandler implements DataHandler { - - // Distribute to respective methods based on the data event type (eventType), these methods are implemented by subclasses since different types of metadata handlers have different ways of processing - @Override - public void handle(final String json, final String eventType) { - List dataList = convert(json); - if (CollectionUtils.isNotEmpty(dataList)) { - DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType); - switch (eventTypeEnum) { - case REFRESH: - case MYSELF: - doRefresh(dataList); - break; - case UPDATE: - case CREATE: - doUpdate(dataList); - break; - case DELETE: - doDelete(dataList); - break; - default: - break; - } - } - } -} -``` - -XXX DataHandler: This refers to each implementation class `AbstractDataHandler` of (such as `PluginDataHandler`), whose main function is to call its subscriber. - -Different `DataHandler` calls have different subscription methods: - -- Notification of plug-in metadata changes `PluginDataHandler` is called `onSubscribe()` -- The notification selector is `SelectorDataHandler` called `onSelectorSubscribe()` to change the metadata -- Notification rule metadata change `RuleDataHandler` is invoked `onRuleSubscribe()` - -```java -@RequiredArgsConstructor -public class PluginDataHandler extends AbstractDataHandler { - - private final PluginDataSubscriber pluginDataSubscriber; - - @Override - protected void doUpdate(final List dataList) { - // Call onSubscribe() of the subscriber, sending the PluginData data object - dataList.forEach(pluginDataSubscriber::onSubscribe); - } - - // ... -} -``` - -CommonPluginData Subscriber: The `onSubscribe()` method of the subscriber notifies all classes injected as beans `PluginDataHandler` (not to be confused with the previous class of the same name, which is `soul-plugin-base` the interface under. Its implementation classes are in the respective pluggable plug-in packages. - -![ image-20210122172333111 ](/assets/img/blog1/image-20210122172333111.png) - -```java -public class CommonPluginDataSubscriber implements PluginDataSubscriber { - - // Collect and cache all registered data handlers, such as DividePluginDataHandler under the HTTP plugin divide - private final Map handlerMap; - - // Called for plugin metadata changes - @Override - public void onSubscribe(final PluginData pluginData) { - BaseDataCache.getInstance().cachePluginData(pluginData); - Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData)); - } - - // Called for selector metadata changes - @Override - public void onSelectorSubscribe(final SelectorData selectorData) { - BaseDataCache.getInstance().cacheSelectData(selectorData); - Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData)); - } - - // Called for rule metadata changes - @Override - public void onRuleSubscribe(final RuleData ruleData) { - BaseDataCache.getInstance().cacheRuleData(ruleData); - Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData)); - } -} -``` - -### TIPS - -There are two classes PluginDataHandler with the same name under the whole project. One `soul-sync-data-websocket` of them is under the project, which is used to notify the plug-in metadata change, and the other is under the `soul-plugin-base` project, which is used to define the metadata update of each type of plug-in. - -To summarize the naming meaning of these two classes, ** `soul-sync-data-websocket` the "plugin" in the lower class name means that the type of metadata is a plug-in class, and `soul-plugin-base` the "plugin" in the lower class name means that the subclass that inherits it comes from each pluggable plug-in. Such as divide, dubbo plugins, etc. ** +--- +title: Soul Gateway Learns WebSocket Data Synchronization Analysis +author: fanjinpeng,zhuming +date: 2021-01-22 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +> Fan Jinpeng + +# 1.Previously on + +In Part 4, we analyzed that after the HTTP user service system accesses the Soul gateway, it will call the registration interface of soul-admin, register all the interface information that needs to be proxied by the gateway to soul-admin, and finally, it will connect through the web socket. Synchronize the interface information received by soul-admin to Soul Gateway (soul-bootstrap). Today, we will continue to analyze how the data is synchronized to soul-bootstrap. + +If you don't know the process, you can go out and turn left to see the fourth article. + +# 2.Soul-admin and soul-bootstrap data synchronization + +In order to verify the data synchronization process, there is no need to start the business system. You can just start the soul-admin and soul-bootstrap systems. You can open or close the plug-in on the page to see how this process is implemented. + +Link to the official website of data synchronization strategy + +## 2.1 Start 2 systems + +They are all started by default according to the project, and no configuration files need to be modified. + +## 2.2. Page operation search interface + +Start the divide plug-in here, F12, and see which interface soul-admin will be called in the foreground. + +![ open_divide_plugin ](/assets/img/blog3/open_divide_plugin.png) + +You can see that the foreground sends a PUT request to the background: http://localhost:9095/plugin/5. + +## 2.3 Background interface + +Search for this interface in the project + +```java +// PluginController.java +@RestController +@RequestMapping("/plugin") +public class PluginController { + +... + + /** + * update plugin. + * + * @param id primary key. + * @param pluginDTO plugin. + * @return {@linkplain SoulAdminResult} + */ + @PutMapping("/{id}") + public SoulAdminResult updatePlugin(@PathVariable("id") final String id, @RequestBody final PluginDTO pluginDTO) { + Objects.requireNonNull(pluginDTO); + pluginDTO.setId(id); + final String result = pluginService.createOrUpdate(pluginDTO); + if (StringUtils.isNoneBlank(result)) { + return SoulAdminResult.error(result); + } + return SoulAdminResult.success(SoulResultMessage.UPDATE_SUCCESS); + } + +... + +} +``` + +Into the implementation class. + +```java +// PluginServiceImpl.java +/** + * create or update plugin. + * + * @param pluginDTO {@linkplain PluginDTO} + * @return rows + */ + @Override + @Transactional(rollbackFor = Exception.class) + public String createOrUpdate(final PluginDTO pluginDTO) { + final String msg = checkData(pluginDTO); + if (StringUtils.isNoneBlank(msg)) { + return msg; + } + PluginDO pluginDO = PluginDO.buildPluginDO(pluginDTO); + DataEventTypeEnum eventType = DataEventTypeEnum.CREATE; + if (StringUtils.isBlank(pluginDTO.getId())) { + pluginMapper.insertSelective(pluginDO); + } else { + eventType = DataEventTypeEnum.UPDATE; + pluginMapper.updateSelective(pluginDO); + } + + // publish change event. + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType, + Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO)))); + return StringUtils.EMPTY; + } +``` + +It can be seen here that the first half is to operate the database and persist the relevant information; the second half is to publish an event. + +## 2.4 Publish the event + +The event published here is encapsulated by DataChangedEvent, and there is an enumeration in it. There are many types here: + +```java +/** + * configuration group. + * + * @author huangxiaofeng + */ +public enum ConfigGroupEnum { + + APP_AUTH, + + PLUGIN, + + RULE, + + SELECTOR, + + META_DATA; + +... + +} +``` + +Seeing these types, if you still have an impression of the fourth article, you can see that the types of events sent at that time were SELECTOR and RULE, and now it is PLUGIN. Although the types are different, it does not affect us to continue to analyze the logic behind. Let's continue. + +Another eventType is also an enumeration. There are five types: DELETE, CREATE, UPDATE, REFRESH, and MYSELF. In this case, it is UPDATE. + +```java +/** + * The enum Data event type. + * + * @author xiaoyu + */ +public enum DataEventTypeEnum { + /** + * delete event. + */ + DELETE, + /** + * insert event. + */ + CREATE, + /** + * update event. + */ + UPDATE, + /** + * REFRESH data event type enum. + */ + REFRESH, + /** + * Myself data event type enum. + */ + MYSELF; + +... + +} +``` + +## 2.5 Listen for events + +Locate the code that listens for events: + +```java +// DataChangedEventDispatcher.java +@Component +public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { + + private ApplicationContext applicationContext; + + private List listeners; + + public DataChangedEventDispatcher(final ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + @SuppressWarnings("unchecked") + public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + case APP_AUTH: + listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); + break; + case PLUGIN: + listener.onPluginChanged((List) event.getSource(), event.getEventType()); + break; + case RULE: + listener.onRuleChanged((List) event.getSource(), event.getEventType()); + break; + case SELECTOR: + listener.onSelectorChanged((List) event.getSource(), event.getEventType()); + break; + case META_DATA: + listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); + break; + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } + } + + @Override + public void afterPropertiesSet() { + Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); + this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); + } +} +``` + +### 2.5.1 Listener injection + +You can see that the DataChangedEventDispatcher implements the InitializingBean interface, overrides the afterPropertiesSet method, and uses @ Component when Spring starts. This override method is called after the container is loaded. In the After PropertiesSet method, all the beans of the DataChangedListener type are obtained and placed in the class property listeners. + +So the question is, when are these listeners injected into the container? + +First look at the definition of the Data ChangedListener interface: + +```java +/** + * Event listener, used to send notification of event changes, + * used to support HTTP, websocket, zookeeper and other event notifications. + * + * @author huangxiaofeng + * @author xiaoyu + */ +public interface DataChangedListener { + + /** + * invoke this method when AppAuth was received. + * + * @param changed the changed + * @param eventType the event type + */ + default void onAppAuthChanged(List changed, DataEventTypeEnum eventType) { + } + + /** + * invoke this method when Plugin was received. + * + * @param changed the changed + * @param eventType the event type + */ + default void onPluginChanged(List changed, DataEventTypeEnum eventType) { + } + + /** + * invoke this method when Selector was received. + * + * @param changed the changed + * @param eventType the event type + */ + default void onSelectorChanged(List changed, DataEventTypeEnum eventType) { + } + + /** + * On meta data changed. + * + * @param changed the changed + * @param eventType the event type + */ + default void onMetaDataChanged(List changed, DataEventTypeEnum eventType) { + + } + + /** + * invoke this method when Rule was received. + * + * @param changed the changed + * @param eventType the event type + */ + default void onRuleChanged(List changed, DataEventTypeEnum eventType) { + } + +} +``` + +It can be seen that there are five methods defined in the interface, which respectively deal with the corresponding processing methods when the data changes of appAuth, plugin, selector, metaData and rule are monitored. + +Its inheritance relationship: + +![ DataChangedListener ](/assets/img/blog3/DataChangedListener.png) + +Because the websocket is used by default, the listener here corresponds to the Web socketData ChangedListener, Alt + F7, and the place where this class is instantiated is the following configuration class: + +```java +// DataSyncConfiguration.java +@Configuration +public class DataSyncConfiguration { + + /** + * http long polling. + */ + @Configuration + @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true") + @EnableConfigurationProperties(HttpSyncProperties.class) + static class HttpLongPollingListener { + @Bean + @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class) + public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) { + return new HttpLongPollingDataChangedListener(httpSyncProperties); + } + } + + /** + * The type Zookeeper listener. + */ + @Configuration + @ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url") + @Import(ZookeeperConfiguration.class) + static class ZookeeperListener { + @Bean + @ConditionalOnMissingBean(ZookeeperDataChangedListener.class) + public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) { + return new ZookeeperDataChangedListener(zkClient); + } + @Bean + @ConditionalOnMissingBean(ZookeeperDataInit.class) + public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { + return new ZookeeperDataInit(zkClient, syncDataService); + } + } + + /** + * The type Nacos listener. + */ + @Configuration + @ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url") + @Import(NacosConfiguration.class) + static class NacosListener { + @Bean + @ConditionalOnMissingBean(NacosDataChangedListener.class) + public DataChangedListener nacosDataChangedListener(final ConfigService configService) { + return new NacosDataChangedListener(configService); + } + } + + /** + * The WebsocketListener(default strategy). + */ + @Configuration + @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true) + @EnableConfigurationProperties(WebsocketSyncProperties.class) + static class WebsocketListener { + @Bean + @ConditionalOnMissingBean(WebsocketDataChangedListener.class) + public DataChangedListener websocketDataChangedListener() { + return new WebsocketDataChangedListener(); + } + @Bean + @ConditionalOnMissingBean(WebsocketCollector.class) + public WebsocketCollector websocketCollector() { + return new WebsocketCollector(); + } + @Bean + @ConditionalOnMissingBean(ServerEndpointExporter.class) + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + } +} +``` + +There are four data synchronization strategies: HTTP long polling, zookeeper, nacos, and websocket (default strategy). + +See the web socket annotation @ ConditionalOnProperty (name = "soul. Sync. Web socket. Enabled", having Value = "true", Match (IfMissing = true), find the following configuration in the configuration file: + +```yaml +soul: + sync: + websocket: + enabled: true +``` + +This is where the truth comes out. + +If you do not want to use the default synchronization policy of the web socket, you can write the corresponding configuration in the configuration file. + +### 2.5.2 Listening event processing logic + +In order to prevent you from turning back and looking at it, which is inconvenient, I will post the processing logic code here: + +```java +// DataChangedEventDispatcher.java +@Override + @SuppressWarnings("unchecked") + public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + case APP_AUTH: + listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); + break; + case PLUGIN: + listener.onPluginChanged((List) event.getSource(), event.getEventType()); + break; + case RULE: + listener.onRuleChanged((List) event.getSource(), event.getEventType()); + break; + case SELECTOR: + listener.onSelectorChanged((List) event.getSource(), event.getEventType()); + break; + case META_DATA: + listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); + break; + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } + } +``` + +All listeners are traversed here. For the current web socket, there is only one listener, and it is not known when the other multiple cases will appear. It is doubtful here, and we will come back to add (//TODO) when we encounter related cases later. + +Different logics are used according to the type of the published event. The types here correspond to the methods defined in the DataChangedListener interface. + +The listener here is an instance of the Web socketData ChangedListener, which will enter the corresponding method in the class: + +```java +// WebsocketDataChangedListener.java +public class WebsocketDataChangedListener implements DataChangedListener { + + @Override + public void onPluginChanged(final List pluginDataList, final DataEventTypeEnum eventType) { + WebsocketData websocketData = + new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); + } + + @Override + public void onSelectorChanged(final List selectorDataList, final DataEventTypeEnum eventType) { + WebsocketData websocketData = + new WebsocketData<>(ConfigGroupEnum.SELECTOR.name(), eventType.name(), selectorDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); + } + + @Override + public void onRuleChanged(final List ruleDataList, final DataEventTypeEnum eventType) { + WebsocketData configData = + new WebsocketData<>(ConfigGroupEnum.RULE.name(), eventType.name(), ruleDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); + } + + @Override + public void onAppAuthChanged(final List appAuthDataList, final DataEventTypeEnum eventType) { + WebsocketData configData = + new WebsocketData<>(ConfigGroupEnum.APP_AUTH.name(), eventType.name(), appAuthDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); + } + + @Override + public void onMetaDataChanged(final List metaDataList, final DataEventTypeEnum eventType) { + WebsocketData configData = + new WebsocketData<>(ConfigGroupEnum.META_DATA.name(), eventType.name(), metaDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); + } + +} +``` + +As you can see in the code, the data is encapsulated as Web socketData and sent using the WebsocketController. Send method. + +## 2.6 Synchronize data to soul-bootstrap + +```java +// WebsocketCollector.java +@Slf4j +@ServerEndpoint("/websocket") +public class WebsocketCollector { + + private static final Set SESSION_SET = new CopyOnWriteArraySet<>(); + + private static final String SESSION_KEY = "sessionKey"; + + /** + * On open. + * + * @param session the session + */ + @OnOpen + public void onOpen(final Session session) { + log.info("websocket on open successful...."); + SESSION_SET.add(session); + } + + /** + * On message. + * + * @param message the message + * @param session the session + */ + @OnMessage + public void onMessage(final String message, final Session session) { + if (message.equals(DataEventTypeEnum.MYSELF.name())) { + try { + ThreadLocalUtil.put(SESSION_KEY, session); + SpringBeanUtils.getInstance().getBean(SyncDataService.class).syncAll(DataEventTypeEnum.MYSELF); + } finally { + ThreadLocalUtil.clear(); + } + } + } + + /** + * On close. + * + * @param session the session + */ + @OnClose + public void onClose(final Session session) { + SESSION_SET.remove(session); + ThreadLocalUtil.clear(); + } + + /** + * On error. + * + * @param session the session + * @param error the error + */ + @OnError + public void onError(final Session session, final Throwable error) { + SESSION_SET.remove(session); + ThreadLocalUtil.clear(); + log.error("websocket collection error: ", error); + } + + /** + * Send. + * + * @param message the message + * @param type the type + */ + public static void send(final String message, final DataEventTypeEnum type) { + if (StringUtils.isNotBlank(message)) { + if (DataEventTypeEnum.MYSELF == type) { + try { + Session session = (Session) ThreadLocalUtil.get(SESSION_KEY); + if (session != null) { + session.getBasicRemote().sendText(message); + } + } catch (IOException e) { + log.error("websocket send result is exception: ", e); + } + return; + } + for (Session session : SESSION_SET) { + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + log.error("websocket send result is exception: ", e); + } + } + } + } +} +``` + +The Web socketController uses the @ ServerEndpoint ( "/web socket") annotation, opens a web socket service interface, and waits for a connection. + +After the soul-bootstrap is started, the web socket will be connected, and the onOpen method will be triggered to store the Session of this connection information in the Set set of the SESSION \_ SET. + +In the send method, it will first determine whether the DataEventTypeEnum type is MYSELF. This type can be traced back to 2.3-2.4. This time it is UPDATE. As for when it is MYSELF, it needs to be added later. It is doubtful here (//TODO). + +The following for loop iterates through all the web socket connection sessions to send the change data. + +At this point, the default web socket synchronization data strategy is clear. + +> Zhu Ming + +## Data synchronization between background and gateway (Web socket) + +### How to establish Web socket? In the background? + +![05](/assets/img/blog1/05.png) Data SyncConfiguration: As the configuration factory of Spring Bean, various listeners can be constructed according to the configuration information. Including HTTP long polling mode, Zookeeper mode, Nacos mode, Web socket method. + +```java +@Configuration +public class DataSyncConfiguration { + + // In the configuration of the soul-admin project, use soul.sync.websocket.enabled to enable or disable WebSocket. + @Configuration + @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true) + @EnableConfigurationProperties(WebsocketSyncProperties.class) + static class WebsocketListener { + + @Bean + @ConditionalOnMissingBean(WebsocketCollector.class) + public WebsocketCollector websocketCollector() { + return new WebsocketCollector(); + } + } +} +``` + +Web socketListener: As `DataSyncConfiguration` the internal class of, it is responsible for the initialization of the web socket listener. Web socket Collector: It monitors the websocket connection and receives information. Maintain all session sessions connected to the background, and provide `send()` methods to notify session information. + +### How does the gateway set up a Web socket? + +![06](/assets/img/blog1/06.png) + +Web socket SyncData Configuration: As the configuration factory of Spring Bean, it is the gateway's entrance to build Websocket communication. (An independent startup project `soul-spring-boot-starter-sync-data-websocket` is provided for the gateway to choose freely.) + +```java +@Configuration +@ConditionalOnClass(WebsocketSyncDataService.class) +@ConditionalOnProperty(prefix = "soul.sync.websocket", name = "urls") +@Slf4j +public class WebsocketSyncDataConfiguration { + + // Collect all subscribers registered as Beans, such as PluginDataSubscriber, MetaDataSubscriber, AuthDataSubscriber + @Bean + public SyncDataService websocketSyncDataService(final ObjectProvider websocketConfig, final ObjectProvider pluginSubscriber, final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { + log.info("you use websocket sync soul data......."); + return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new), pluginSubscriber.getIfAvailable(), metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); + } + + // In the configuration of the soul-bootstrap project, use soul.sync.websocket to configure the backend path for establishing connections + @Bean + @ConfigurationProperties(prefix = "soul.sync.websocket") + public WebsocketConfig websocketConfig() { + return new WebsocketConfig(); + } +} +``` + +Web socket SyncData Service: Get all the registered beans `WebsocketConfig` and the various `DataSubscriber` subscribers, and build an implemented `WebsocketClient` `SoulWebsocketClient` list + +SoulWeb socket Client: `Websocket` The communication class monitors the websocket connection and receives information. After receiving the information from the background, it will notify each subscriber. + +```java +public final class SoulWebsocketClient extends WebSocketClient { + + private final WebsocketDataHandler websocketDataHandler; + + private void handleResult(final String result) { + WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class); + ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType()); + // Determine the event type of data changes based on the incoming information, such as refresh, update, delete, etc. + String eventType = websocketData.getEventType(); + String json = GsonUtils.getInstance().toJson(websocketData.getData()); + websocketDataHandler.executor(groupEnum, json, eventType); + } +} +``` + +Web socketData Handler: Construct the data processing classes of various implementations `AbstractDataHandler` and cache them during initialization. + +```java +public class WebsocketDataHandler { + + // Cache all DataHandler data change handling classes + private static final EnumMap ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class); + + public WebsocketDataHandler(final PluginDataSubscriber pluginDataSubscriber, + final List metaDataSubscribers, + final List authDataSubscribers) { + ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataHandler(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AuthDataHandler(authDataSubscribers)); + ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataHandler(metaDataSubscribers)); + } + + public void executor(final ConfigGroupEnum type, final String json, final String eventType) { + // Call the corresponding DataHandler data processing class based on the data change event type + ENUM_MAP.get(type).handle(json, eventType); + } +} +``` + +### Gateway data change call chain + +After the entry class `SoulWebsocketClient` that implements Websocket communication receives the background communication, the `executor()` method called `WebsocketDataHandler` matches the information type, and calls the corresponding `DataHandler` `handler()` to process the information. + +![07](/assets/img/blog1/07.png) + +AbstractDataHandler: The implementation `handler()` method calls the corresponding event abstract method according to the type of the event (such as refresh, update, create, delete, etc.). + +```java +public abstract class AbstractDataHandler implements DataHandler { + + // Distribute to respective methods based on the data event type (eventType), these methods are implemented by subclasses since different types of metadata handlers have different ways of processing + @Override + public void handle(final String json, final String eventType) { + List dataList = convert(json); + if (CollectionUtils.isNotEmpty(dataList)) { + DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType); + switch (eventTypeEnum) { + case REFRESH: + case MYSELF: + doRefresh(dataList); + break; + case UPDATE: + case CREATE: + doUpdate(dataList); + break; + case DELETE: + doDelete(dataList); + break; + default: + break; + } + } + } +} +``` + +XXX DataHandler: This refers to each implementation class `AbstractDataHandler` of (such as `PluginDataHandler`), whose main function is to call its subscriber. + +Different `DataHandler` calls have different subscription methods: + +- Notification of plug-in metadata changes `PluginDataHandler` is called `onSubscribe()` +- The notification selector is `SelectorDataHandler` called `onSelectorSubscribe()` to change the metadata +- Notification rule metadata change `RuleDataHandler` is invoked `onRuleSubscribe()` + +```java +@RequiredArgsConstructor +public class PluginDataHandler extends AbstractDataHandler { + + private final PluginDataSubscriber pluginDataSubscriber; + + @Override + protected void doUpdate(final List dataList) { + // Call onSubscribe() of the subscriber, sending the PluginData data object + dataList.forEach(pluginDataSubscriber::onSubscribe); + } + + // ... +} +``` + +CommonPluginData Subscriber: The `onSubscribe()` method of the subscriber notifies all classes injected as beans `PluginDataHandler` (not to be confused with the previous class of the same name, which is `soul-plugin-base` the interface under. Its implementation classes are in the respective pluggable plug-in packages. + +![ image-20210122172333111 ](/assets/img/blog1/image-20210122172333111.png) + +```java +public class CommonPluginDataSubscriber implements PluginDataSubscriber { + + // Collect and cache all registered data handlers, such as DividePluginDataHandler under the HTTP plugin divide + private final Map handlerMap; + + // Called for plugin metadata changes + @Override + public void onSubscribe(final PluginData pluginData) { + BaseDataCache.getInstance().cachePluginData(pluginData); + Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData)); + } + + // Called for selector metadata changes + @Override + public void onSelectorSubscribe(final SelectorData selectorData) { + BaseDataCache.getInstance().cacheSelectData(selectorData); + Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData)); + } + + // Called for rule metadata changes + @Override + public void onRuleSubscribe(final RuleData ruleData) { + BaseDataCache.getInstance().cacheRuleData(ruleData); + Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData)); + } +} +``` + +### TIPS + +There are two classes PluginDataHandler with the same name under the whole project. One `soul-sync-data-websocket` of them is under the project, which is used to notify the plug-in metadata change, and the other is under the `soul-plugin-base` project, which is used to define the metadata update of each type of plug-in. + +To summarize the naming meaning of these two classes, ** `soul-sync-data-websocket` the "plugin" in the lower class name means that the type of metadata is a plug-in class, and `soul-plugin-base` the "plugin" in the lower class name means that the subclass that inherits it comes from each pluggable plug-in. Such as divide, dubbo plugins, etc. ** diff --git a/src/blog/soul_source_learning_11_SPI.md b/src/blog/soul_source_learning_11_SPI.md index d136bc29da..9882f9127d 100644 --- a/src/blog/soul_source_learning_11_SPI.md +++ b/src/blog/soul_source_learning_11_SPI.md @@ -1,828 +1,828 @@ ---- -title: Soul Gateway Learning SPI -author: zhuming -date: 2021-01-30 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -# Use of SPI in SOUL - -When analyzing the load balancing strategy of the divide plug-in, I saw a line of code: - -```java -DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); -``` - -At that time, it was easy to skip its implementation, and its function was easy to analyze, calling a method that looked like a tool class, passing in a cluster of multiple nodes, and returning a node. This is a load balancer.. - -But there are a lot of details, the most important of which is the use of the SPI to select specific implementation classes. Take a look at the code for this method: - -```java -public class LoadBalanceUtils { - - public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) { - // 调用自定义的 SPI 得到一个子类 - LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm); - return loadBalance.select(upstreamList, ip); - } -} -``` - -The latter is to call the `select()` specific subclass method, according to the different implementation of the subclass, will eventually show a variety of forms. The current subclass implementations are: - -- HashLoadBalance -- RandomLoadBalance -- RoundRobinLoadBalance - -The key is `ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);` this line of work. - -Before we look at it, let's take a look at the SPI mechanism provided by Java. - -## Java SPI - -There is such a definition _<<高可用可伸缩微服务架构>> 第 3 章 Apache Dubbo 框架的原理与实现_ in. - -> The full name of SPI is Service Provider Interface, which is a built-in service provider discovery function of JDK and a dynamic replacement discovery mechanism. For example, to dynamically add an implementation to an interface at runtime, you only need to add an implementation. - -There is also a very vivid brain map in the book, which shows the use of SPI: - -![08](/assets/img/blog1/08.png) - -That is to say, in the implementation of our code, there is no need to write a Factory, use MAP to wrap some subclasses, and the final return type is the parent interface. You only need to define the resource file and specify the parent interface and its subclasses in the file, and then you can get all the defined subclass objects by setting them: - -```java -ServiceLoader loaders = ServiceLoader.load(Interface.class) -for(Interface interface : loaders){ - System.out.println(interface.toString()); -} -``` - -Compared with the ordinary factory pattern, this method is definitely more in line with the principle of opening and closing, adding a new subclass without modifying the factory method, but editing the resource file. - -### Start with a Demo - -According to the specification of SPI, I built a demo to see the specific implementation effect. - -![ image-20210129095623013 ](/assets/img/blog1/image-20210129095623013.png) - -![ image-20210129095703911 ](/assets/img/blog1/image-20210129095703911.png) - -A `run()` method is defined in Animal, and a subclass implements it. - -```java -public interface Animal { - void run(); -} - -public class Dog implements Animal { - @Override - public void run() { - System.out.println("狗在跑"); - } -} - -public class Horse implements Animal { - @Override - public void run() { - System.out.println("马在跑"); - } -} -``` - -Use the loading class of SPI to get the execution result of the subclass: - -```java -private static void test() { - final ServiceLoader load = ServiceLoader.load(Animal.class); - - for (Animal animal : load) { - System.out.println(animal); - animal.run(); - } -} -``` - -![ image-20210129103047851 ](/assets/img/blog1/image-20210129103047851.png) - -After the call, we get the implementation classes previously written in the resource file and successfully invoke their respective `run()` methods. - -At this point, I have a question **, does each call `ServiceLoader.load(Animal.class)` return the same object? ** If it is, I guess it is loaded into the cache at startup, if not, it may be using reflection at the bottom, and each call has a certain consumption. Let's look at the following experiment: - -```java -public static void main(String[] args) { - for (int i = 0; i < 2; i++) { - test(); - System.out.println("----------"); - } -} - -private static void test() { - final ServiceLoader load = ServiceLoader.load(Animal.class); - for (Animal animal : load) { - System.out.println(animal); - animal.run(); - } -} -``` - -![ image-20210129103451844 ](/assets/img/blog1/image-20210129103451844.png) - -The objects in the two calls are different, which makes me worry about its performance, so let's analyze its code first and see how to implement it. - -### Implementation of SPI - -To find `java.util,ServiceLoaders` this class, the most striking thing is the directory where we placed the resource files according to the specifications before. - -```java -public final class ServiceLoader implements Iterable { - - private static final String PREFIX = "META-INF/services/"; -} -``` - -When the debug `PREFIX` attribute is called, it is found that `ServiceLoader.load` the lazy loading method is actually used, and the actual return class is not found when it is called, but when it is traversed. - -Its lazy loading is implemented in the following code: - -```java -public final class ServiceLoader implements Iterable { - - public static ServiceLoader load(Class service) { - // 获取当前的类加载器 (我们自己的通常是弟中弟 AppClassLoader ) - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return ServiceLoader.load(service, cl); - } - - public static ServiceLoader load(Class service, ClassLoader loader) { - // 调用构造器初始化对象 (说明每次调用都使用新的 ServiceLoader 对象) - return new ServiceLoader<>(service, loader); - } - - private ServiceLoader(Class svc, ClassLoader cl) { - service = Objects.requireNonNull(svc, "Service interface cannot be null"); - loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; - acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; - // 上面都是将信息放入对象实例属性中, 这行才是关键调用 - reload(); - } - - public void reload() { - providers.clear(); - // 创建懒加载迭代器, 传入关键的接口 Class 以及加载器 - lookupIterator = new LazyIterator(service, loader); - } -} - -``` - -After the call `ServiceLoader.load`, the key thing is not done, just pass the interface class and loader to LazyIterator, the implementation class of the iterator. - -Seeing this, we can guess that when the object returned by the real iteration call is called, the iterator must be required to complete the search and initialization of the implementation class, while the parameter passing is Class information and loader, and the initialization of the implementation class will obviously be reflection. - -Take a look at how LazyIterator is implemented, starting with where it will be called `hasNext()` in the first place: - -```java -private class LazyIterator implements Iterator { - - public boolean hasNext() { - if (acc == null) { - return hasNextService(); - } else { - // ... - } - } - - private boolean hasNextService() { - if (nextName != null) { - return true; - } - if (configs == null) { - try { - String fullName = PREFIX + service.getName(); - if (loader == null) - configs = ClassLoader.getSystemResources(fullName); - else - // 加载资源文件 - configs = loader.getResources(fullName); - } catch (IOException x) { - fail(service, "Error locating configuration files", x); - } - } - while ((pending == null) || !pending.hasNext()) { - if (!configs.hasMoreElements()) { - return false; - } - // 解析出资源文件中写入的实现类类名 - pending = parse(service, configs.nextElement()); - } - // 获取一个类名 - nextName = pending.next(); - return true; - } -} -``` - -![ image-20210129111231212 ](/assets/img/blog1/image-20210129111231212.png) - -`hasNext()` The call can get the name of the class in our resource, write it to the instance property `nextName`, and return it `true` so that the iterator can make `next()` the call. - -```java -public S next() { - if (acc == null) { - return nextService(); - } else { - // ... - } -} - -private S nextService() { - if (!hasNextService()) throw new NoSuchElementException(); - String cn = nextName; - nextName = null; - Class c = null; - try { - // 反射得到 Class 对象 - c = Class.forName(cn, false, loader); - } catch (ClassNotFoundException x) { - fail(service, "Provider " + cn + " not found"); - } - if (!service.isAssignableFrom(c)) { - fail(service, "Provider " + cn + " not a subtype"); - } - try { - // 初始化对象, 并判断是否与接口符合 - S p = service.cast(c.newInstance()); - // 将初始化的对象放入hash缓存 (关键步骤) - providers.put(cn, p); - return p; - } catch (Throwable x) { - fail(service, "Provider " + cn + " could not be instantiated", x); - } - throw new Error(); // This cannot happen -} -``` - -Here we understand that after initialization, the object will be put into the cache, and the key is the interface class. There will be no reflection consumption in the second call. - -So why do we produce different object instances in the way we test before? The reason is that each call `ServiceLoader.load()` produces a new `ServiceLoader` object. We will improve the test method: - -```java -public static void main(String[] args) { - // 复用 ServiceLoaders - final ServiceLoader load = ServiceLoader.load(Animal.class); - for (int i = 0; i < 2; i++) { - test(load); - System.out.println("----------"); - } -} - -private static void test(ServiceLoader load) { - for (Animal animal : load) { - System.out.println(animal); - animal.run(); - } -} -``` - -![ image-20210129113307494 ](/assets/img/blog1/image-20210129113307494.png) - -### Java SPI Thinking - -There are a lot of details that we haven't described in the Java SPI, but that's the main process. Our two previous questions about how to implement and performance can also be answered: - -1. How to implement: Read the resource file through the IO stream, load the corresponding path by reflection and generate a Class object, and put it into the cache after initialization -2. Performance: The first iteration call will have a reflection call, but when used multiple times, as long as the same ServiceLoader object is used, multiple reflections can be avoided, because the objects in the cache will be reused directly. - -At this point, I have a very confused place, before I thought it was very similar to the factory method, but it has an advantage over it, because after adding a subclass, you only need to change the resource file without changing the factory class. - -But when I tried to use Java SPI to implement it, I found that it could not achieve this effect. An important reason is ** The individual implementation classes in the resource file are not differentiated ** that I could not filter out the implementation class that I needed to cache in `ServiceLoaders`. - -So where is its usage scenario? - -## JDBC SPI Usage - -According to the information, the most critical pluggable driver design in JDBC is implemented by SPI. - -### Mysql driver package SPI - -In each database connection package, the implementation of JDBC mode needs to implement its Driver interface. The practical one is the SPI mode. Let's take a look. - -![ image-20210130202512831 ](/assets/img/blog1/image-20210130202512831.png) - -So how do the JDBC-related classes in the JDK implement this? The key class is DriverManager - -```java -public class DriverManager { - - static { - loadInitialDrivers(); - } - - private static void loadInitialDrivers() { - // ... - - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - - // 这里就是 SPI 的实现, 迭代时实际会 Class.forName() 初始化实现类 - ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class); - Iterator driversIterator = loadedDrivers.iterator(); - try{ - while(driversIterator.hasNext()) { - driversIterator.next(); - } - } catch(Throwable t) { - // Do nothing - } - return null; - } - }); - - // ... - } -} -``` - -If the static method of DriverManager is called in the code, the above code will be triggered, and what does the initialization of the ** Its function is to initialize all the Driver implementation classes in the SPI resource file. ** implementation class do? Keep looking `com.mysql.jdbc.Driver` - -```java -public class Driver extends NonRegisteringDriver implements java.sql.Driver { - static { - try { - // 调用 DriverManager 的注册方法, 将此 Driver 实现类注册到 JDBC 的 Driver 管理器中 - java.sql.DriverManager.registerDriver(new Driver()); - } catch (SQLException E) { - throw new RuntimeException("Can't register driver!"); - } - } -} -``` - -The registration method of DriverManager is very simple, that is, the input parameters are put into static variables as a global cache. - -```java -public class DriverManager { - // 缓存 Driver 实现类 - private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>(); - - public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { - registerDriver(driver, null); - } - - public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { - if(driver != null) { - // 注册到变量中 - registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); - } else { - throw new NullPointerException(); - } - } -} -``` - -### Filter Driver: Contract is greater than configuration - -In normal use, we will get the connection directly `DriverManager.getConnection(url, user, passwd)`, but there is a question here. We have registered multiple drivers in DriverManager. Why can we determine a unique Driver here? - -To find the `getConnection()` DriverManager first: - -```java -public static Connection getConnection(String url, String user, String password) throws SQLException { - // ... - return (getConnection(url, info, Reflection.getCallerClass())); -} - -private static Connection getConnection( - String url, java.util.Properties info, Class caller) throws SQLException { - - // ... - - for(DriverInfo aDriver : registeredDrivers) { - // isDriverAllowed() 仅是通过 Class.forName() 初始化, 没有甄别作用 - if(isDriverAllowed(aDriver.driver, callerCL)) { - try { - // 最关键的点在这行, 筛选工作其实在实现类自身的 connect() 方法中, 会根据传入的 url 筛选 - Connection con = aDriver.driver.connect(url, info); - if (con != null) { - return (con); - } - } catch (SQLException ex) { - } - } else { - } - - } - - // ... -} -``` - -See how filtering is implemented in the all-important Mysql Driver (which inherits from NonRegisteringDriver) - -```java -public class NonRegisteringDriver implements java.sql.Driver { - private static final String URL_PREFIX = "jdbc:mysql://"; - private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://"; - private static final String MXJ_URL_PREFIX = "jdbc:mysql:mxj://"; - public static final String LOADBALANCE_URL_PREFIX = "jdbc:mysql:loadbalance://"; - - public java.sql.Connection connect(String url, Properties info) throws SQLException { - // ... - // parseURL() 会匹配 url 是否符合其所在 Driver 的连接方式 - // 这里就是采用"约定大于配置"的思想, 通过匹配路径头做筛选 - if ((props = parseURL(url, info)) == null) { - return null; - } - - // ... - } - - public Properties parseURL(String url, Properties defaults) throws java.sql.SQLException { - // ... - // 如果 url 不匹配此 Driver 的路径则返回null, 最外层会继续尝试下个 Driver - if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX) - && !StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { - return null; - } - - // ... - } -} -``` - -### Summary MySQL & JDBC - -See here, I think you already understand the implementation of SPI in MySQL & JDBC. Summarize a few points. - -- The DriverManager in JDBC loads the SPI resource file and `java.sql.Driver` initializes all the implementation classes. -- In fact, when the class is initialized, it will create its own object and inject it into DriverManager for unified management. -- The DriverManager filters the managed Drivers by the Driver implementation class itself, which is only responsible for traversing and taking out the available Drivers -- The Driver implementation class determines whether it should return itself by passing in the database URL header. If not, return `null`.. JDBC's DriverManager receives the `null` call that will continue with the next Driver implementation class. -- The MySql driver actual selection scheme is path header matching, which is one of - -### JDBC Demo - -After writing these analyses, let's look at how to implement a simple demo. - -Let's share the way I wrote it before. - -```java -static { - try { - // 反射, 该类加载时会在静态块中, 向 DriverManager 注册 Driver - Class.forName("com.mysql.jdbc.Driver"); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } -} - -public static void main(String[] args) { - try ( - final Connection conn = DriverManager.getConnection(url, user, passwd); - final Statement stmt = conn.createStatement(); - final ResultSet rs = stmt.executeQuery("select count(1) from test") - ) { - while (rs.next()) { - int count = rs.getInt("count(1)"); - System.out.println(count); - } - } catch (Exception e) { - e.printStackTrace(); - } -} -``` - -Although this can be used, don't you think there is extra code? Look at my new way of writing. - -```java -public static void main(String[] args) throws ClassNotFoundException { - try ( - final Connection conn = DriverManager.getConnection(url, user, passwd); - final Statement stmt = conn.createStatement(); - final ResultSet rs = stmt.executeQuery("select count(1) from test") - ) { - while (rs.next()) { - int count = rs.getInt("count(1)"); - System.out.println(count); - } - } catch (Exception e) { - e.printStackTrace(); - } -} -``` - -Only these simple codes are needed. `DriverManager.getConnection()` When called, the DriverManager will automatically load the implementation class in the SPI, and we do not need to `Class.forName()` manually call `java.mysql.Driver` the initialization. - -** See here I think you still understand the most important role of SPI. There is no need to explicitly write out the implementation class corresponding to the interface ** - -So we also have a problem in "Java SPI Thinking" that has been solved. ** How do you distinguish the implementation classes to be used in the SPI? Let the implementation class decide for itself, and the outer call simply iterates over all. ** - -## SOUL SPI implementation - -We have a thorough understanding of the use of SPI in Java, while the SPI in Soul is designed by ourselves, using the design idea of SPI in Dubbo. You can see the associated annotation on the `org.dromara.soul.spi.SPI` annotation class. - -```java -/** - * SPI Extend the processing. - * All spi system reference the apache implementation of - * https://github.com/apache/dubbo/blob/master/dubbo-common/src/main/java/org/apache/dubbo/common/extension. - */ -``` - -### Java SPI bug - -When analyzing the use of Java SPI in the last two modules, some shortcomings were found: - -1. If the ServiceLoader is used improperly ** Does not properly utilize its caching mechanism **, it will cause the class object to be reflected and the instance object to be initialized every time the concrete implementation class is obtained. Not to mention that the performance is over, the object obtained every time is different, which may cause program problems. -2. That is to say, every time you look for a specific implementation class, you have to iterate over it. Although the use of fewer subclasses has no effect, this way is still silly. In addition, referring to the implementation of JDBC in MySQL driver, we also need to design a more complex filtering mechanism. - -So how does the implementation of Soul SPI solve these two problems? The key lies in the next two sub-modules. - -- Optimized Extension Loader -- Enhanced getJoin () - -### Optimized Extension Loader - -Let's first look at the overall picture of the SPI implementation project, which is `soul-spi`: - -![ image-20210130214402997 ](/assets/img/blog1/image-20210130214402997.png) - -The core class is the Extension Loader, which can be said to be the Soul version of the ServiceLoader. It also defines the path location of the SPI resource file. - -```java -public final class ExtensionLoader { - private static final String SOUL_DIRECTORY = "META-INF/soul/"; -} -``` - -By examining the callers of its methods, we find the entry method. - -```java -public final class ExtensionLoader { - - private static final Map, ExtensionLoader> LOADERS = new ConcurrentHashMap<>(); - - public static ExtensionLoader getExtensionLoader(final Class clazz) { - // ... - - // 根据加载类对象取出缓存中数据, 如果没有则新建 ExtensionLoader 对象并放入缓存 - ExtensionLoader extensionLoader = (ExtensionLoader) LOADERS.get(clazz); - if (extensionLoader != null) { - return extensionLoader; - } - LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz)); - return (ExtensionLoader) LOADERS.get(clazz); - } -} -``` - -This method acts like a ServiceLoader `load()` method and returns a ServiceLoader object. - -It's just that the implementation in Soul changes the way it caches the Extension Loader object so that - -### Enhanced search getJoin () - -Let's look at the `getJoin()` Extension Loader method, which I understand as ** Better implementation of ServiceLoader Iterator Edition **. It also does two things that the ServiceLoader iteration did: - -- Initialize the implementation class in the SPI - -- Cache the implementation class-> as a Map collection of the form Key-Value - -Based on the K-V cache mode, it also made a transformation that I was most looking forward to: - -- The direct matching of time complexity `O(1)` to realize class mode - -### Multi-tier cache - -The reason ExtensionLoader can do this enhanced search without iterating over everything each time is that it relies on three different types of caching. - -These three caches are divided into two layers, each of which has different purposes. The overview is as follows: - -```java -// 一层缓存 -private final Map> cachedInstances = new ConcurrentHashMap<>(); - -// 二层缓存之一 -private final Holder>> cachedClasses = new Holder<>(); - -// 二层缓存之一 -private final Map, Object> joinInstances = new ConcurrentHashMap<>(); -``` - -#### Tier 1 cache: cachedInstances - -The first is the first-tier cache, which is the first thing we come into contact with when searching for the specific implementation class of the interface. If we hit it, we can directly get the object of the implementation class. - -```java -private final Map> cachedInstances = new ConcurrentHashMap<>(); -``` - -It `key` is actually the information we configured in the Soul SPI resource file, such as the resource file of the load balancing implementation class of the Divide plug-in. - -![ image-20210130230250748 ](/assets/img/blog1/image-20210130230250748.png) - -And it `value` 's the Holder object, which holds the object of the implementation class. When called `getJoin()`, pass in an identity (such as random) to get the implementation class object. - -```java -public T getJoin(final String name) { - // ... - Holder objectHolder = cachedInstances.get(name); - Object value = objectHolder.getValue(); - // ... - return (T) value; -} -``` - -#### Layer 2 Cache: cachedClasses - -It `cachedClasses` stores the mapping between the identity (random) and the class object - -```java -private final Holder>> cachedClasses = new Holder<>(); -``` - -How is the `cachedClasses` cached information populated? Is directly triggered to retrieve the SPI resource file and then parse it into a `cachedClasses` cache. The specific method is in `loadResources()` - -```java -private void loadResources(final Map> classes, final URL url) throws IOException { - Properties properties = new Properties(); - // 解析资源文件 - properties.load(inputStream); - properties.forEach((name, classPath) -> { - // 读出 K-V 结构并组装成 classes, 外层调用会包装到 cachedClasses - loadClass(classes, name, classPath); - }); -} -``` - -#### Second-tier cache: joinInstances - -The `joinInstances` cache holds the mapping of class objects to object instances. - -```java -private final Map, Object> joinInstances = new ConcurrentHashMap<>(); -``` - -This layer of cache will get the class object of the corresponding identifier (random) with the help of the second layer of cache, and cache it into itself through the initialization instance of the class object. The corresponding implementation method is as follow - -```java -private T createExtension(final String name) { - Class aClass = getExtensionClasses().get(name); - Object o = joinInstances.get(aClass); - if (o == null) { - joinInstances.putIfAbsent(aClass, aClass.newInstance()); - } - return (T) o; -} -``` - -#### Cache summary - -When the implementation class of an interface is loaded through the Extension Loader, the flow chart of the cache call is as follows: - -![09](/assets/img/blog1/09.png) - -### Detailed source code analysis (can be skipped) - -```java -// name can be understood as an identifier used to distinguish a specific implementation class in the SPI file. -public T getJoin(final String name) { - // ... - // cachedInstances caches all Holder objects. The value property of the Holder object holds the concrete implementation class. - // I understand cachedInstances as the first-level cache. If it hits, it directly returns the desired class. - Holder objectHolder = cachedInstances.get(name); - if (objectHolder == null) { - cachedInstances.putIfAbsent(name, new Holder<>()); - objectHolder = cachedInstances.get(name); - } - Object value = objectHolder.getValue(); - // Double-checked locking: if not hit, call createExtension() - if (value == null) { - synchronized (cachedInstances) { - value = objectHolder.getValue(); - if (value == null) { - value = createExtension(name); - objectHolder.setValue(value); - } - } - } - return (T) value; -} -``` - -```java -private T createExtension(final String name) { - // Critical code, searching for the class object corresponding to the identifier. - Class aClass = getExtensionClasses().get(name); - if (aClass == null) { - throw new IllegalArgumentException("name is error"); - } - // joinInstances can be understood as the second-level cache, where K-V maps class objects to their initialized instances. - Object o = joinInstances.get(aClass); - if (o == null) { - try { - joinInstances.putIfAbsent(aClass, aClass.newInstance()); - o = joinInstances.get(aClass); - } catch (InstantiationException | IllegalAccessException e) { - // ... - } - } - return (T) o; -} -``` - -```java -public Map> getExtensionClasses() { - // cachedClasses is the third-level cache, storing the mapping of identifiers to class objects. - Map> classes = cachedClasses.getValue(); - if (classes == null) { - synchronized (cachedClasses) { - classes = cachedClasses.getValue(); - if (classes == null) { - // Construct the classes cache, with K-V structure as identifier-class object. - classes = loadExtensionClass(); - cachedClasses.setValue(classes); - } - } - } - return classes; -} -``` - -```java -private Map> loadExtensionClass() { - // Get the SPI annotation of the interface. - SPI annotation = clazz.getAnnotation(SPI.class); - if (annotation != null) { - String value = annotation.value(); - if (StringUtils.isNotBlank(value)) { - cachedDefaultName = value; - } - } - // Construct the classes cache, with K-V structure as identifier-class object. - Map> classes = new HashMap<>(16); - loadDirectory(classes); - return classes; -} -``` - -```java -private void loadDirectory(final Map> classes) { - String fileName = SOUL_DIRECTORY + clazz.getName(); - try { - ClassLoader classLoader = ExtensionLoader.class.getClassLoader(); - // Read the SPI resource file. - Enumeration urls = classLoader != null ? classLoader.getResources(fileName) - : ClassLoader.getSystemResources(fileName); - if (urls != null) { - while (urls.hasMoreElements()) { - URL url = urls.nextElement(); - // Construct the classes cache, with K-V structure as identifier-class object. - loadResources(classes, url); - } - } - } -} -``` - -```java -private void loadResources(final Map> classes, final URL url) throws IOException { - try (InputStream inputStream = url.openStream()) { - Properties properties = new Properties(); - properties.load(inputStream); - // Parse the resource file into K-V structure. - properties.forEach((k, v) -> { - String name = (String) k; - String classPath = (String) v; - if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(classPath)) { - try { - // Load the class path into classes cache, along with identifier and class path. - loadClass(classes, name, classPath); - } catch (ClassNotFoundException e) { - throw new IllegalStateException("load extension resources error", e); - } - } - }); - } -} -``` - -```java -private void loadClass(final Map> classes, - final String name, final String classPath) throws ClassNotFoundException { - // Reflect the class path from the resource file into a class object. - Class subClass = Class.forName(classPath); - // Get the Join annotation of the implementation class. - Join annotation = subClass.getAnnotation(Join.class); - Class oldClass = classes.get(name); - if (oldClass == null) { - // Put it into the classes cache as K-V, with identifier as the key and class object as the value. - classes.put(name, subClass); - } -} -``` +--- +title: Soul Gateway Learning SPI +author: zhuming +date: 2021-01-30 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +# Use of SPI in SOUL + +When analyzing the load balancing strategy of the divide plug-in, I saw a line of code: + +```java +DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); +``` + +At that time, it was easy to skip its implementation, and its function was easy to analyze, calling a method that looked like a tool class, passing in a cluster of multiple nodes, and returning a node. This is a load balancer.. + +But there are a lot of details, the most important of which is the use of the SPI to select specific implementation classes. Take a look at the code for this method: + +```java +public class LoadBalanceUtils { + + public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) { + // 调用自定义的 SPI 得到一个子类 + LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm); + return loadBalance.select(upstreamList, ip); + } +} +``` + +The latter is to call the `select()` specific subclass method, according to the different implementation of the subclass, will eventually show a variety of forms. The current subclass implementations are: + +- HashLoadBalance +- RandomLoadBalance +- RoundRobinLoadBalance + +The key is `ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);` this line of work. + +Before we look at it, let's take a look at the SPI mechanism provided by Java. + +## Java SPI + +There is such a definition _<<高可用可伸缩微服务架构>> 第 3 章 Apache Dubbo 框架的原理与实现_ in. + +> The full name of SPI is Service Provider Interface, which is a built-in service provider discovery function of JDK and a dynamic replacement discovery mechanism. For example, to dynamically add an implementation to an interface at runtime, you only need to add an implementation. + +There is also a very vivid brain map in the book, which shows the use of SPI: + +![08](/assets/img/blog1/08.png) + +That is to say, in the implementation of our code, there is no need to write a Factory, use MAP to wrap some subclasses, and the final return type is the parent interface. You only need to define the resource file and specify the parent interface and its subclasses in the file, and then you can get all the defined subclass objects by setting them: + +```java +ServiceLoader loaders = ServiceLoader.load(Interface.class) +for(Interface interface : loaders){ + System.out.println(interface.toString()); +} +``` + +Compared with the ordinary factory pattern, this method is definitely more in line with the principle of opening and closing, adding a new subclass without modifying the factory method, but editing the resource file. + +### Start with a Demo + +According to the specification of SPI, I built a demo to see the specific implementation effect. + +![ image-20210129095623013 ](/assets/img/blog1/image-20210129095623013.png) + +![ image-20210129095703911 ](/assets/img/blog1/image-20210129095703911.png) + +A `run()` method is defined in Animal, and a subclass implements it. + +```java +public interface Animal { + void run(); +} + +public class Dog implements Animal { + @Override + public void run() { + System.out.println("狗在跑"); + } +} + +public class Horse implements Animal { + @Override + public void run() { + System.out.println("马在跑"); + } +} +``` + +Use the loading class of SPI to get the execution result of the subclass: + +```java +private static void test() { + final ServiceLoader load = ServiceLoader.load(Animal.class); + + for (Animal animal : load) { + System.out.println(animal); + animal.run(); + } +} +``` + +![ image-20210129103047851 ](/assets/img/blog1/image-20210129103047851.png) + +After the call, we get the implementation classes previously written in the resource file and successfully invoke their respective `run()` methods. + +At this point, I have a question **, does each call `ServiceLoader.load(Animal.class)` return the same object? ** If it is, I guess it is loaded into the cache at startup, if not, it may be using reflection at the bottom, and each call has a certain consumption. Let's look at the following experiment: + +```java +public static void main(String[] args) { + for (int i = 0; i < 2; i++) { + test(); + System.out.println("----------"); + } +} + +private static void test() { + final ServiceLoader load = ServiceLoader.load(Animal.class); + for (Animal animal : load) { + System.out.println(animal); + animal.run(); + } +} +``` + +![ image-20210129103451844 ](/assets/img/blog1/image-20210129103451844.png) + +The objects in the two calls are different, which makes me worry about its performance, so let's analyze its code first and see how to implement it. + +### Implementation of SPI + +To find `java.util,ServiceLoaders` this class, the most striking thing is the directory where we placed the resource files according to the specifications before. + +```java +public final class ServiceLoader implements Iterable { + + private static final String PREFIX = "META-INF/services/"; +} +``` + +When the debug `PREFIX` attribute is called, it is found that `ServiceLoader.load` the lazy loading method is actually used, and the actual return class is not found when it is called, but when it is traversed. + +Its lazy loading is implemented in the following code: + +```java +public final class ServiceLoader implements Iterable { + + public static ServiceLoader load(Class service) { + // 获取当前的类加载器 (我们自己的通常是弟中弟 AppClassLoader ) + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return ServiceLoader.load(service, cl); + } + + public static ServiceLoader load(Class service, ClassLoader loader) { + // 调用构造器初始化对象 (说明每次调用都使用新的 ServiceLoader 对象) + return new ServiceLoader<>(service, loader); + } + + private ServiceLoader(Class svc, ClassLoader cl) { + service = Objects.requireNonNull(svc, "Service interface cannot be null"); + loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; + acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; + // 上面都是将信息放入对象实例属性中, 这行才是关键调用 + reload(); + } + + public void reload() { + providers.clear(); + // 创建懒加载迭代器, 传入关键的接口 Class 以及加载器 + lookupIterator = new LazyIterator(service, loader); + } +} + +``` + +After the call `ServiceLoader.load`, the key thing is not done, just pass the interface class and loader to LazyIterator, the implementation class of the iterator. + +Seeing this, we can guess that when the object returned by the real iteration call is called, the iterator must be required to complete the search and initialization of the implementation class, while the parameter passing is Class information and loader, and the initialization of the implementation class will obviously be reflection. + +Take a look at how LazyIterator is implemented, starting with where it will be called `hasNext()` in the first place: + +```java +private class LazyIterator implements Iterator { + + public boolean hasNext() { + if (acc == null) { + return hasNextService(); + } else { + // ... + } + } + + private boolean hasNextService() { + if (nextName != null) { + return true; + } + if (configs == null) { + try { + String fullName = PREFIX + service.getName(); + if (loader == null) + configs = ClassLoader.getSystemResources(fullName); + else + // 加载资源文件 + configs = loader.getResources(fullName); + } catch (IOException x) { + fail(service, "Error locating configuration files", x); + } + } + while ((pending == null) || !pending.hasNext()) { + if (!configs.hasMoreElements()) { + return false; + } + // 解析出资源文件中写入的实现类类名 + pending = parse(service, configs.nextElement()); + } + // 获取一个类名 + nextName = pending.next(); + return true; + } +} +``` + +![ image-20210129111231212 ](/assets/img/blog1/image-20210129111231212.png) + +`hasNext()` The call can get the name of the class in our resource, write it to the instance property `nextName`, and return it `true` so that the iterator can make `next()` the call. + +```java +public S next() { + if (acc == null) { + return nextService(); + } else { + // ... + } +} + +private S nextService() { + if (!hasNextService()) throw new NoSuchElementException(); + String cn = nextName; + nextName = null; + Class c = null; + try { + // 反射得到 Class 对象 + c = Class.forName(cn, false, loader); + } catch (ClassNotFoundException x) { + fail(service, "Provider " + cn + " not found"); + } + if (!service.isAssignableFrom(c)) { + fail(service, "Provider " + cn + " not a subtype"); + } + try { + // 初始化对象, 并判断是否与接口符合 + S p = service.cast(c.newInstance()); + // 将初始化的对象放入hash缓存 (关键步骤) + providers.put(cn, p); + return p; + } catch (Throwable x) { + fail(service, "Provider " + cn + " could not be instantiated", x); + } + throw new Error(); // This cannot happen +} +``` + +Here we understand that after initialization, the object will be put into the cache, and the key is the interface class. There will be no reflection consumption in the second call. + +So why do we produce different object instances in the way we test before? The reason is that each call `ServiceLoader.load()` produces a new `ServiceLoader` object. We will improve the test method: + +```java +public static void main(String[] args) { + // 复用 ServiceLoaders + final ServiceLoader load = ServiceLoader.load(Animal.class); + for (int i = 0; i < 2; i++) { + test(load); + System.out.println("----------"); + } +} + +private static void test(ServiceLoader load) { + for (Animal animal : load) { + System.out.println(animal); + animal.run(); + } +} +``` + +![ image-20210129113307494 ](/assets/img/blog1/image-20210129113307494.png) + +### Java SPI Thinking + +There are a lot of details that we haven't described in the Java SPI, but that's the main process. Our two previous questions about how to implement and performance can also be answered: + +1. How to implement: Read the resource file through the IO stream, load the corresponding path by reflection and generate a Class object, and put it into the cache after initialization +2. Performance: The first iteration call will have a reflection call, but when used multiple times, as long as the same ServiceLoader object is used, multiple reflections can be avoided, because the objects in the cache will be reused directly. + +At this point, I have a very confused place, before I thought it was very similar to the factory method, but it has an advantage over it, because after adding a subclass, you only need to change the resource file without changing the factory class. + +But when I tried to use Java SPI to implement it, I found that it could not achieve this effect. An important reason is ** The individual implementation classes in the resource file are not differentiated ** that I could not filter out the implementation class that I needed to cache in `ServiceLoaders`. + +So where is its usage scenario? + +## JDBC SPI Usage + +According to the information, the most critical pluggable driver design in JDBC is implemented by SPI. + +### Mysql driver package SPI + +In each database connection package, the implementation of JDBC mode needs to implement its Driver interface. The practical one is the SPI mode. Let's take a look. + +![ image-20210130202512831 ](/assets/img/blog1/image-20210130202512831.png) + +So how do the JDBC-related classes in the JDK implement this? The key class is DriverManager + +```java +public class DriverManager { + + static { + loadInitialDrivers(); + } + + private static void loadInitialDrivers() { + // ... + + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + + // 这里就是 SPI 的实现, 迭代时实际会 Class.forName() 初始化实现类 + ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class); + Iterator driversIterator = loadedDrivers.iterator(); + try{ + while(driversIterator.hasNext()) { + driversIterator.next(); + } + } catch(Throwable t) { + // Do nothing + } + return null; + } + }); + + // ... + } +} +``` + +If the static method of DriverManager is called in the code, the above code will be triggered, and what does the initialization of the ** Its function is to initialize all the Driver implementation classes in the SPI resource file. ** implementation class do? Keep looking `com.mysql.jdbc.Driver` + +```java +public class Driver extends NonRegisteringDriver implements java.sql.Driver { + static { + try { + // 调用 DriverManager 的注册方法, 将此 Driver 实现类注册到 JDBC 的 Driver 管理器中 + java.sql.DriverManager.registerDriver(new Driver()); + } catch (SQLException E) { + throw new RuntimeException("Can't register driver!"); + } + } +} +``` + +The registration method of DriverManager is very simple, that is, the input parameters are put into static variables as a global cache. + +```java +public class DriverManager { + // 缓存 Driver 实现类 + private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>(); + + public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { + registerDriver(driver, null); + } + + public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { + if(driver != null) { + // 注册到变量中 + registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); + } else { + throw new NullPointerException(); + } + } +} +``` + +### Filter Driver: Contract is greater than configuration + +In normal use, we will get the connection directly `DriverManager.getConnection(url, user, passwd)`, but there is a question here. We have registered multiple drivers in DriverManager. Why can we determine a unique Driver here? + +To find the `getConnection()` DriverManager first: + +```java +public static Connection getConnection(String url, String user, String password) throws SQLException { + // ... + return (getConnection(url, info, Reflection.getCallerClass())); +} + +private static Connection getConnection( + String url, java.util.Properties info, Class caller) throws SQLException { + + // ... + + for(DriverInfo aDriver : registeredDrivers) { + // isDriverAllowed() 仅是通过 Class.forName() 初始化, 没有甄别作用 + if(isDriverAllowed(aDriver.driver, callerCL)) { + try { + // 最关键的点在这行, 筛选工作其实在实现类自身的 connect() 方法中, 会根据传入的 url 筛选 + Connection con = aDriver.driver.connect(url, info); + if (con != null) { + return (con); + } + } catch (SQLException ex) { + } + } else { + } + + } + + // ... +} +``` + +See how filtering is implemented in the all-important Mysql Driver (which inherits from NonRegisteringDriver) + +```java +public class NonRegisteringDriver implements java.sql.Driver { + private static final String URL_PREFIX = "jdbc:mysql://"; + private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://"; + private static final String MXJ_URL_PREFIX = "jdbc:mysql:mxj://"; + public static final String LOADBALANCE_URL_PREFIX = "jdbc:mysql:loadbalance://"; + + public java.sql.Connection connect(String url, Properties info) throws SQLException { + // ... + // parseURL() 会匹配 url 是否符合其所在 Driver 的连接方式 + // 这里就是采用"约定大于配置"的思想, 通过匹配路径头做筛选 + if ((props = parseURL(url, info)) == null) { + return null; + } + + // ... + } + + public Properties parseURL(String url, Properties defaults) throws java.sql.SQLException { + // ... + // 如果 url 不匹配此 Driver 的路径则返回null, 最外层会继续尝试下个 Driver + if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX) + && !StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { + return null; + } + + // ... + } +} +``` + +### Summary MySQL & JDBC + +See here, I think you already understand the implementation of SPI in MySQL & JDBC. Summarize a few points. + +- The DriverManager in JDBC loads the SPI resource file and `java.sql.Driver` initializes all the implementation classes. +- In fact, when the class is initialized, it will create its own object and inject it into DriverManager for unified management. +- The DriverManager filters the managed Drivers by the Driver implementation class itself, which is only responsible for traversing and taking out the available Drivers +- The Driver implementation class determines whether it should return itself by passing in the database URL header. If not, return `null`.. JDBC's DriverManager receives the `null` call that will continue with the next Driver implementation class. +- The MySql driver actual selection scheme is path header matching, which is one of + +### JDBC Demo + +After writing these analyses, let's look at how to implement a simple demo. + +Let's share the way I wrote it before. + +```java +static { + try { + // 反射, 该类加载时会在静态块中, 向 DriverManager 注册 Driver + Class.forName("com.mysql.jdbc.Driver"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } +} + +public static void main(String[] args) { + try ( + final Connection conn = DriverManager.getConnection(url, user, passwd); + final Statement stmt = conn.createStatement(); + final ResultSet rs = stmt.executeQuery("select count(1) from test") + ) { + while (rs.next()) { + int count = rs.getInt("count(1)"); + System.out.println(count); + } + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +Although this can be used, don't you think there is extra code? Look at my new way of writing. + +```java +public static void main(String[] args) throws ClassNotFoundException { + try ( + final Connection conn = DriverManager.getConnection(url, user, passwd); + final Statement stmt = conn.createStatement(); + final ResultSet rs = stmt.executeQuery("select count(1) from test") + ) { + while (rs.next()) { + int count = rs.getInt("count(1)"); + System.out.println(count); + } + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +Only these simple codes are needed. `DriverManager.getConnection()` When called, the DriverManager will automatically load the implementation class in the SPI, and we do not need to `Class.forName()` manually call `java.mysql.Driver` the initialization. + +** See here I think you still understand the most important role of SPI. There is no need to explicitly write out the implementation class corresponding to the interface ** + +So we also have a problem in "Java SPI Thinking" that has been solved. ** How do you distinguish the implementation classes to be used in the SPI? Let the implementation class decide for itself, and the outer call simply iterates over all. ** + +## SOUL SPI implementation + +We have a thorough understanding of the use of SPI in Java, while the SPI in Soul is designed by ourselves, using the design idea of SPI in Dubbo. You can see the associated annotation on the `org.dromara.soul.spi.SPI` annotation class. + +```java +/** + * SPI Extend the processing. + * All spi system reference the apache implementation of + * https://github.com/apache/dubbo/blob/master/dubbo-common/src/main/java/org/apache/dubbo/common/extension. + */ +``` + +### Java SPI bug + +When analyzing the use of Java SPI in the last two modules, some shortcomings were found: + +1. If the ServiceLoader is used improperly ** Does not properly utilize its caching mechanism **, it will cause the class object to be reflected and the instance object to be initialized every time the concrete implementation class is obtained. Not to mention that the performance is over, the object obtained every time is different, which may cause program problems. +2. That is to say, every time you look for a specific implementation class, you have to iterate over it. Although the use of fewer subclasses has no effect, this way is still silly. In addition, referring to the implementation of JDBC in MySQL driver, we also need to design a more complex filtering mechanism. + +So how does the implementation of Soul SPI solve these two problems? The key lies in the next two sub-modules. + +- Optimized Extension Loader +- Enhanced getJoin () + +### Optimized Extension Loader + +Let's first look at the overall picture of the SPI implementation project, which is `soul-spi`: + +![ image-20210130214402997 ](/assets/img/blog1/image-20210130214402997.png) + +The core class is the Extension Loader, which can be said to be the Soul version of the ServiceLoader. It also defines the path location of the SPI resource file. + +```java +public final class ExtensionLoader { + private static final String SOUL_DIRECTORY = "META-INF/soul/"; +} +``` + +By examining the callers of its methods, we find the entry method. + +```java +public final class ExtensionLoader { + + private static final Map, ExtensionLoader> LOADERS = new ConcurrentHashMap<>(); + + public static ExtensionLoader getExtensionLoader(final Class clazz) { + // ... + + // 根据加载类对象取出缓存中数据, 如果没有则新建 ExtensionLoader 对象并放入缓存 + ExtensionLoader extensionLoader = (ExtensionLoader) LOADERS.get(clazz); + if (extensionLoader != null) { + return extensionLoader; + } + LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz)); + return (ExtensionLoader) LOADERS.get(clazz); + } +} +``` + +This method acts like a ServiceLoader `load()` method and returns a ServiceLoader object. + +It's just that the implementation in Soul changes the way it caches the Extension Loader object so that + +### Enhanced search getJoin () + +Let's look at the `getJoin()` Extension Loader method, which I understand as ** Better implementation of ServiceLoader Iterator Edition **. It also does two things that the ServiceLoader iteration did: + +- Initialize the implementation class in the SPI + +- Cache the implementation class-> as a Map collection of the form Key-Value + +Based on the K-V cache mode, it also made a transformation that I was most looking forward to: + +- The direct matching of time complexity `O(1)` to realize class mode + +### Multi-tier cache + +The reason ExtensionLoader can do this enhanced search without iterating over everything each time is that it relies on three different types of caching. + +These three caches are divided into two layers, each of which has different purposes. The overview is as follows: + +```java +// 一层缓存 +private final Map> cachedInstances = new ConcurrentHashMap<>(); + +// 二层缓存之一 +private final Holder>> cachedClasses = new Holder<>(); + +// 二层缓存之一 +private final Map, Object> joinInstances = new ConcurrentHashMap<>(); +``` + +#### Tier 1 cache: cachedInstances + +The first is the first-tier cache, which is the first thing we come into contact with when searching for the specific implementation class of the interface. If we hit it, we can directly get the object of the implementation class. + +```java +private final Map> cachedInstances = new ConcurrentHashMap<>(); +``` + +It `key` is actually the information we configured in the Soul SPI resource file, such as the resource file of the load balancing implementation class of the Divide plug-in. + +![ image-20210130230250748 ](/assets/img/blog1/image-20210130230250748.png) + +And it `value` 's the Holder object, which holds the object of the implementation class. When called `getJoin()`, pass in an identity (such as random) to get the implementation class object. + +```java +public T getJoin(final String name) { + // ... + Holder objectHolder = cachedInstances.get(name); + Object value = objectHolder.getValue(); + // ... + return (T) value; +} +``` + +#### Layer 2 Cache: cachedClasses + +It `cachedClasses` stores the mapping between the identity (random) and the class object + +```java +private final Holder>> cachedClasses = new Holder<>(); +``` + +How is the `cachedClasses` cached information populated? Is directly triggered to retrieve the SPI resource file and then parse it into a `cachedClasses` cache. The specific method is in `loadResources()` + +```java +private void loadResources(final Map> classes, final URL url) throws IOException { + Properties properties = new Properties(); + // 解析资源文件 + properties.load(inputStream); + properties.forEach((name, classPath) -> { + // 读出 K-V 结构并组装成 classes, 外层调用会包装到 cachedClasses + loadClass(classes, name, classPath); + }); +} +``` + +#### Second-tier cache: joinInstances + +The `joinInstances` cache holds the mapping of class objects to object instances. + +```java +private final Map, Object> joinInstances = new ConcurrentHashMap<>(); +``` + +This layer of cache will get the class object of the corresponding identifier (random) with the help of the second layer of cache, and cache it into itself through the initialization instance of the class object. The corresponding implementation method is as follow + +```java +private T createExtension(final String name) { + Class aClass = getExtensionClasses().get(name); + Object o = joinInstances.get(aClass); + if (o == null) { + joinInstances.putIfAbsent(aClass, aClass.newInstance()); + } + return (T) o; +} +``` + +#### Cache summary + +When the implementation class of an interface is loaded through the Extension Loader, the flow chart of the cache call is as follows: + +![09](/assets/img/blog1/09.png) + +### Detailed source code analysis (can be skipped) + +```java +// name can be understood as an identifier used to distinguish a specific implementation class in the SPI file. +public T getJoin(final String name) { + // ... + // cachedInstances caches all Holder objects. The value property of the Holder object holds the concrete implementation class. + // I understand cachedInstances as the first-level cache. If it hits, it directly returns the desired class. + Holder objectHolder = cachedInstances.get(name); + if (objectHolder == null) { + cachedInstances.putIfAbsent(name, new Holder<>()); + objectHolder = cachedInstances.get(name); + } + Object value = objectHolder.getValue(); + // Double-checked locking: if not hit, call createExtension() + if (value == null) { + synchronized (cachedInstances) { + value = objectHolder.getValue(); + if (value == null) { + value = createExtension(name); + objectHolder.setValue(value); + } + } + } + return (T) value; +} +``` + +```java +private T createExtension(final String name) { + // Critical code, searching for the class object corresponding to the identifier. + Class aClass = getExtensionClasses().get(name); + if (aClass == null) { + throw new IllegalArgumentException("name is error"); + } + // joinInstances can be understood as the second-level cache, where K-V maps class objects to their initialized instances. + Object o = joinInstances.get(aClass); + if (o == null) { + try { + joinInstances.putIfAbsent(aClass, aClass.newInstance()); + o = joinInstances.get(aClass); + } catch (InstantiationException | IllegalAccessException e) { + // ... + } + } + return (T) o; +} +``` + +```java +public Map> getExtensionClasses() { + // cachedClasses is the third-level cache, storing the mapping of identifiers to class objects. + Map> classes = cachedClasses.getValue(); + if (classes == null) { + synchronized (cachedClasses) { + classes = cachedClasses.getValue(); + if (classes == null) { + // Construct the classes cache, with K-V structure as identifier-class object. + classes = loadExtensionClass(); + cachedClasses.setValue(classes); + } + } + } + return classes; +} +``` + +```java +private Map> loadExtensionClass() { + // Get the SPI annotation of the interface. + SPI annotation = clazz.getAnnotation(SPI.class); + if (annotation != null) { + String value = annotation.value(); + if (StringUtils.isNotBlank(value)) { + cachedDefaultName = value; + } + } + // Construct the classes cache, with K-V structure as identifier-class object. + Map> classes = new HashMap<>(16); + loadDirectory(classes); + return classes; +} +``` + +```java +private void loadDirectory(final Map> classes) { + String fileName = SOUL_DIRECTORY + clazz.getName(); + try { + ClassLoader classLoader = ExtensionLoader.class.getClassLoader(); + // Read the SPI resource file. + Enumeration urls = classLoader != null ? classLoader.getResources(fileName) + : ClassLoader.getSystemResources(fileName); + if (urls != null) { + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + // Construct the classes cache, with K-V structure as identifier-class object. + loadResources(classes, url); + } + } + } +} +``` + +```java +private void loadResources(final Map> classes, final URL url) throws IOException { + try (InputStream inputStream = url.openStream()) { + Properties properties = new Properties(); + properties.load(inputStream); + // Parse the resource file into K-V structure. + properties.forEach((k, v) -> { + String name = (String) k; + String classPath = (String) v; + if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(classPath)) { + try { + // Load the class path into classes cache, along with identifier and class path. + loadClass(classes, name, classPath); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("load extension resources error", e); + } + } + }); + } +} +``` + +```java +private void loadClass(final Map> classes, + final String name, final String classPath) throws ClassNotFoundException { + // Reflect the class path from the resource file into a class object. + Class subClass = Class.forName(classPath); + // Get the Join annotation of the implementation class. + Join annotation = subClass.getAnnotation(Join.class); + Class oldClass = classes.get(name); + if (oldClass == null) { + // Put it into the classes cache as K-V, with identifier as the key and class object as the value. + classes.put(name, subClass); + } +} +``` diff --git a/src/blog/soul_source_learning_12_sign.md b/src/blog/soul_source_learning_12_sign.md index 48693eb49b..28f7a8cd13 100644 --- a/src/blog/soul_source_learning_12_sign.md +++ b/src/blog/soul_source_learning_12_sign.md @@ -1,125 +1,125 @@ ---- -title: Soul Gateway Learning Sign Plugin -author: tangtian -date: 2021-01-29 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: Blog ---- - -## Introduction - -The sign plug-in is used to sign and authenticate requests. - -## AK/SK Introduction - -AK/SK (Access Key ID/Secret Access Key) is the access key, including the access key ID (AK) and the secret access key (SK), which is mainly used to authenticate and authenticate the user's calling behavior. - -## Plugin usage-take (/dubbo/findAll) as an example - -### Support added `sign` in SoulBootstrap's POM. XML films - -```xml - - - org.dromara - soul-spring-boot-starter-plugin-sign - ${last.version} - - -``` - -### Add appKey, secretKey - -![image.png](/assets/img/blog4/01.png)![image.png](/assets/img/blog4/02.png)![image.png](/assets/img/blog4/03.png)![image.png](/assets/img/blog4/04.png) - -## Configure Selectors and Regulators - -Add Selector ![image.png](/assets/img/blog4/05.png) Add Ruler ![image.png](/assets/img/blog4/06.png) - -### Add the service of obtaining authentication - -Add an external access method to your service - -```java - @GetMapping("/authUrl") - public String authUrl() { - Map map = Maps.newHashMapWithExpectedSize(2); - //Convert timestamp to string form of milliseconds: String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()) - String timetamp = String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()) ; - System.out.println(timetamp); - map.put("timestamp",timetamp); //The value should be in the form of a string representing milliseconds - map.put("path", "/dubbo/findAll"); - map.put("version", "1.0.0"); - List storedKeys = Arrays.stream(map.keySet() - .toArray(new String[]{})) - .sorted(Comparator.naturalOrder()) - .collect(Collectors.toList()); - final String sign = storedKeys.stream() - .map(key -> String.join("", key, map.get(key))) - .collect(Collectors.joining()).trim() - .concat("D19CF79F647A465AB9C5C66F430CAD28");//SECRETkey - return DigestUtils.md5DigestAsHex(sign.getBytes()).toUpperCase(); - } - -``` - -The following should be noted ![image.png](/assets/img/blog4/07.png) - -### Adding authentication header information in the gateway - -![image.png](/assets/img/blog4/08.png) - -### Demo of the requested result - -Passed return ![image.png](/assets/img/blog4/09.png) 5min timeout return ![image.png](/assets/img/blog4/10.png) appKey filling error return ![image.png](/assets/img/blog4/11.png) signature error return ![image.png](/assets/img/blog4/12.png) disable sign plug-in return ![image.png](/assets/img/blog4/13.png) - -## Implementation Analysis of sign Plug-in - -### Pair in Java - -Simply speaking, pair stores a pair of key values, while map can store multiple pairs of key values. SignPlugin plug-in calls signVerify method in DefaultSignService to judge whether sign plug-in is available. If yes, obtain the soul Context stored in global plug-in and call verify method - -```java -if (signData != null && signData.getEnabled()) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - return verify(soulContext, exchange); -} -``` - -In the verify method, determine whether the request header information is correct. If not, throw the log. Error ( "sign parameters are incomplete, { }", soulContext) exception - -```java -if (StringUtils.isBlank(soulContext.getAppKey()) - || StringUtils.isBlank(soulContext.getSign()) - || StringUtils.isBlank(soulContext.getTimestamp())) { - log.error("sign parameters are incomplete,{}", soulContext); - return Pair.of(Boolean.FALSE, Constants.SIGN_PARAMS_ERROR); -} -``` - -Judge whether the request time is timeout - -```java - if (between > delay) { - return Pair.of(Boolean.FALSE, String.format(SoulResultEnum.SING_TIME_IS_TIMEOUT.getMsg(), delay)); - } -``` - -Continue to call the sign method without timeout to get the authentication data, which is configured in soulAdmin - -```java -AppAuthData appAuthData = SignAuthDataCache.getInstance().obtainAuthData(soulContext.getAppKey()); -``` - -The appAuthData data will be judged later. If the data is wrong, the acquired parameters will not be re-signed to judge whether the incoming data is the same as the re-signed data. - -```java -String sigKey = SignUtils.generateSign(appAuthData.getAppSecret(), buildParamsMap(soulContext)); -``` - -If all the verification is passed, the authentication access request is completed. +--- +title: Soul Gateway Learning Sign Plugin +author: tangtian +date: 2021-01-29 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: Blog +--- + +## Introduction + +The sign plug-in is used to sign and authenticate requests. + +## AK/SK Introduction + +AK/SK (Access Key ID/Secret Access Key) is the access key, including the access key ID (AK) and the secret access key (SK), which is mainly used to authenticate and authenticate the user's calling behavior. + +## Plugin usage-take (/dubbo/findAll) as an example + +### Support added `sign` in SoulBootstrap's POM. XML films + +```xml + + + org.dromara + soul-spring-boot-starter-plugin-sign + ${last.version} + + +``` + +### Add appKey, secretKey + +![image.png](/assets/img/blog4/01.png)![image.png](/assets/img/blog4/02.png)![image.png](/assets/img/blog4/03.png)![image.png](/assets/img/blog4/04.png) + +## Configure Selectors and Regulators + +Add Selector ![image.png](/assets/img/blog4/05.png) Add Ruler ![image.png](/assets/img/blog4/06.png) + +### Add the service of obtaining authentication + +Add an external access method to your service + +```java + @GetMapping("/authUrl") + public String authUrl() { + Map map = Maps.newHashMapWithExpectedSize(2); + //Convert timestamp to string form of milliseconds: String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()) + String timetamp = String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()) ; + System.out.println(timetamp); + map.put("timestamp",timetamp); //The value should be in the form of a string representing milliseconds + map.put("path", "/dubbo/findAll"); + map.put("version", "1.0.0"); + List storedKeys = Arrays.stream(map.keySet() + .toArray(new String[]{})) + .sorted(Comparator.naturalOrder()) + .collect(Collectors.toList()); + final String sign = storedKeys.stream() + .map(key -> String.join("", key, map.get(key))) + .collect(Collectors.joining()).trim() + .concat("D19CF79F647A465AB9C5C66F430CAD28");//SECRETkey + return DigestUtils.md5DigestAsHex(sign.getBytes()).toUpperCase(); + } + +``` + +The following should be noted ![image.png](/assets/img/blog4/07.png) + +### Adding authentication header information in the gateway + +![image.png](/assets/img/blog4/08.png) + +### Demo of the requested result + +Passed return ![image.png](/assets/img/blog4/09.png) 5min timeout return ![image.png](/assets/img/blog4/10.png) appKey filling error return ![image.png](/assets/img/blog4/11.png) signature error return ![image.png](/assets/img/blog4/12.png) disable sign plug-in return ![image.png](/assets/img/blog4/13.png) + +## Implementation Analysis of sign Plug-in + +### Pair in Java + +Simply speaking, pair stores a pair of key values, while map can store multiple pairs of key values. SignPlugin plug-in calls signVerify method in DefaultSignService to judge whether sign plug-in is available. If yes, obtain the soul Context stored in global plug-in and call verify method + +```java +if (signData != null && signData.getEnabled()) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + return verify(soulContext, exchange); +} +``` + +In the verify method, determine whether the request header information is correct. If not, throw the log. Error ( "sign parameters are incomplete, { }", soulContext) exception + +```java +if (StringUtils.isBlank(soulContext.getAppKey()) + || StringUtils.isBlank(soulContext.getSign()) + || StringUtils.isBlank(soulContext.getTimestamp())) { + log.error("sign parameters are incomplete,{}", soulContext); + return Pair.of(Boolean.FALSE, Constants.SIGN_PARAMS_ERROR); +} +``` + +Judge whether the request time is timeout + +```java + if (between > delay) { + return Pair.of(Boolean.FALSE, String.format(SoulResultEnum.SING_TIME_IS_TIMEOUT.getMsg(), delay)); + } +``` + +Continue to call the sign method without timeout to get the authentication data, which is configured in soulAdmin + +```java +AppAuthData appAuthData = SignAuthDataCache.getInstance().obtainAuthData(soulContext.getAppKey()); +``` + +The appAuthData data will be judged later. If the data is wrong, the acquired parameters will not be re-signed to judge whether the incoming data is the same as the re-signed data. + +```java +String sigKey = SignUtils.generateSign(appAuthData.getAppSecret(), buildParamsMap(soulContext)); +``` + +If all the verification is passed, the authentication access request is completed. diff --git a/src/blog/soul_source_learning_13_zookeeper_01.md b/src/blog/soul_source_learning_13_zookeeper_01.md index 01ff0af984..deb5f85e1d 100644 --- a/src/blog/soul_source_learning_13_zookeeper_01.md +++ b/src/blog/soul_source_learning_13_zookeeper_01.md @@ -1,284 +1,284 @@ ---- -title: Soul Gateway Learns Zookeeper Data Synchronization 01 -author: liquan -date: 2021-01-20 -tag: - - Soul -cover: '/assets/img/architecture/soul-framework.png' -head: - - - meta - - name: Blog ---- - -#### Start soul-admin and soul-bootstrap, and use zookeeper to synchronize data to the gateway - -###### I. Configuration environment - -1. For soul-admin service configuration, restart the service. - -soul-admin/src/main/resources/application.yml - -```yaml -soul: - sync: - zookeeper: - url: localhost:2181 - sessionTimeout: 5000 - connectionTimeout: 2000 -``` - -2. Soul-bootstrap gateway service configuration needs to be restarted - -soul-bootstrap/pom.xml - -```xml - - - org.dromara - soul-spring-boot-starter-sync-data-zookeeper - ${project.version} - -``` - -soul-bootstrap/src/main/resources/application-local.yml - -```yaml -soul: - sync: - zookeeper: - url: localhost:2181 - sessionTimeout: 5000 - connectionTimeout: 2000 -``` - -###### II. Startup Service - -1. Start zookeeper - -``` -zookeeper ./bin/zkServer.sh start -/usr/bin/java -ZooKeeper JMX enabled by default -Using config: /Documents/soft/zookeeper/bin/../conf/zoo.cfg -Starting zookeeper ... STARTED -``` - -2. The soul-admin gateway background service is started. After the service is started, you can see the ZooKeeper request call. - -``` - -2021-01-20 17:34:48.752 INFO 64500 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Starting ZkClient event thread. -2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:zookeeper.version=3.5.6-c11b7e26bc554b8523dc929761dd28808913f091, built on 10/08/2019 20:18 GMT -2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:host.name=10.7.254.31 -2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.version=1.8.0_261 -2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.vendor=Oracle Corporation -...... -2021-01-20 17:34:48.806 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown error) -2021-01-20 17:34:48.826 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Socket connection established, initiating session, client: /0:0:0:0:0:0:0:1:58214, server: localhost/0:0:0:0:0:0:0:1:2181 -2021-01-20 17:34:48.857 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x1000b5e22f50001, negotiated timeout = 5000 -2021-01-20 17:34:48.861 INFO 64500 --- [ain-EventThread] org.I0Itec.zkclient.ZkClient : zookeeper state changed (SyncConnected) -``` - -3. The soul-bootstrap gateway service is started. After the service is started, you can see the ZooKeeper request call. - -``` -2021-01-20 17:35:58.996 INFO 64583 --- [ main] s.b.s.d.z.ZookeeperSyncDataConfiguration : you use zookeeper sync soul data....... -2021-01-20 17:35:59.003 INFO 64583 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Starting ZkClient event thread. -...... - -2021-01-20 17:35:59.012 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:user.home=/Users/liquan -2021-01-20 17:35:59.012 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:os.memory.total=310MB -2021-01-20 17:35:59.018 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Initiating client connection, connectString=localhost:2181 sessionTimeout=5000 watcher=org.I0Itec.zkclient.ZkClient@114a5e0 -2021-01-20 17:35:59.121 INFO 64583 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x1000b5e22f50002, negotiated timeout = 5000 -2021-01-20 17:35:59.126 INFO 64583 --- [ain-EventThread] org.I0Itec.zkclient.ZkClient : zookeeper state changed (SyncConnected) -``` - -4. View the registration information ![在这里插入图片描述](/assets/img/blog5/zk1.png) of soul gateway synchronization on zookeeper - -###### III. Analysis of data synchronization principle of Soul gateway Zookeeper - -After the soul-admin is started, the zkclient. ZkClient org.I0Itec. Is seen in the console, which is used as an entry point for tracing and debugging. - -1. Function of ZookeeperConfiguration: Register zkClient to the Spring container. - -```java -// EnableConfigurationProperties Purpose: Enables classes annotated with @ConfigurationProperties. If a configuration class is only annotated with @ConfigurationProperties and not with @Component, then the bean converted from the properties configuration file cannot be obtained in the IOC container. @EnableConfigurationProperties is equivalent to injecting classes that use @ConfigurationProperties. -// @ConditionalOnMissingBean If the specified class is missing in the container, perform injection. Opposite of @ConditionalOnBean. -/** - * ZookeeperConfiguration . - * @author xiaoyu(Myth) - */ -@EnableConfigurationProperties(ZookeeperProperties.class) -public class ZookeeperConfiguration { - /** - * register zkClient in spring ioc. - * - * @param zookeeperProp the zookeeper configuration - * @return ZkClient {@linkplain ZkClient} - */ - @Bean - @ConditionalOnMissingBean(ZkClient.class) - public ZkClient zkClient(final ZookeeperProperties zookeeperProp) { - return new ZkClient(zookeeperProp.getUrl(), zookeeperProp.getSessionTimeout(), zookeeperProp.getConnectionTimeout()); - } -} -``` - -After the soul-admin is started, it will read the zookeeper configuration information and inject zkClient into the container to establish a connection with the zookeeper. ![pic](/assets/img/blog5/zk2.png) - -![pic](/assets/img/blog5/zk3.png) - -2. The afterPropertiesSet method of DataChangedEventDispatcher will be called in the call stack of the instantiated ZkClient. - -The org. Dromara. Soul. Admin. Listener. DataChangedEventDispatcher acts as an event router, which forwards changed events to each ConfigEventListener. - -This class implements the InitializingBean. During the DataChangedEventDispatcher initialization, the afterPropertiesSet method is executed. - -The After PropertiesSet method looks for beans in the container whose type is the DataChangedListener. Class. - -```java -@Component -public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { - private ApplicationContext applicationContext; - private List listeners; - public DataChangedEventDispatcher(final ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } -@Override -@SuppressWarnings("unchecked") -public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - case APP_AUTH: - listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); - break; - ....... - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } - ...... - @Override - public void afterPropertiesSet() { - Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); - this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); - } -} -``` - -3. The execution of the afterPropertiesSet method will find the instantiation of DataChangedListener. Class related classes. - -Role of org. Dromara. Soul. Admin. Config. Data SyncConfiguration: Data synchronization configuration class. - -ZookeeperData ChangedListener Data Change Listener, which is used to listen to metadata changes and then synchronize to zookeeper. - -ZookeeperDataInit zookeeper data initialization. Function: Synchronize initialization data to zookeeper. - -```java -/** - * The type Zookeeper listener. - */ -@Configuration -@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url") -@Import(ZookeeperConfiguration.class) -static class ZookeeperListener { - /** - * Config event listener data changed listener. - * @param zkClient the zk client - * @return the data changed listener - */ - @Bean - @ConditionalOnMissingBean(ZookeeperDataChangedListener.class) - public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) { - return new ZookeeperDataChangedListener(zkClient); - } - /** - * Zookeeper data init zookeeper data init - * @param zkClient the zk client - * @param syncDataService the sync data service - * @return the zookeeper data init - */ - @Bean - @ConditionalOnMissingBean(ZookeeperDataInit.class) - public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { - return new ZookeeperDataInit(zkClient, syncDataService); - } -} -``` - -4. Role of org. Dromara. Soul. Admin. Listener. Zookeeper. ZookeeperDataInit: responsible for synchronizing initialization data to zookeeper. This class implements the CommandLine Runner. - -CommandLine Runner: Function: SpringBoot will traverse all entity classes that implement CommandLineRunner and execute the run method after the project is started. If it needs to be executed in a certain order, Then you need to use a @ Order annotation on the entity class (or implement the Order interface) to indicate the order. - -The run method calls the syncData Service. SyncAll method. - -```java -public class ZookeeperDataInit implements CommandLineRunner { - private final ZkClient zkClient; - private final SyncDataService syncDataService; - /** - * Instantiates a new Zookeeper data init. - * @param zkClient the zk client - * @param syncDataService the sync data service - */ - public ZookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { - this.zkClient = zkClient; - this.syncDataService = syncDataService; - } - @Override - public void run(final String... args) { - String pluginPath = ZkPathConstants.PLUGIN_PARENT; - String authPath = ZkPathConstants.APP_AUTH_PARENT; - String metaDataPath = ZkPathConstants.META_DATA; - if (!zkClient.exists(pluginPath) && !zkClient.exists(authPath) && !zkClient.exists(metaDataPath)) { - syncDataService.syncAll(DataEventTypeEnum.REFRESH); - } - } -} -``` - -5、org.dromara.soul.admin.service.sync.SyncDataServiceImpl - -The syncAll method invokes the event publisher to publish an event of DataEventTypeEnum. REFRESH. - -```java -/** - * The type sync data service. - * @author xiaoyu(Myth) - */ -@Service("syncDataService") -public class SyncDataServiceImpl implements SyncDataService { - // Publishes events, informing all listeners related to this event. - private final ApplicationEventPublisher eventPublisher; - ...... - @Override - public boolean syncAll(final DataEventTypeEnum type) { - appAuthService.syncData(); - List pluginDataList = pluginService.listAll(); - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList)); - List selectorDataList = selectorService.listAll(); - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList)); - List ruleDataList = ruleService.listAll(); - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList)); - metaDataService.syncData(); - return true; - } - ...... -} -``` - -![pic](/assets/img/blog5//zk4.png) - -6. After the event is published, the onApplicationEvent method of the org. Dromara. Soul. Admin. Listener. DataChangedEventDispatcher class will listen to the change of the event and traverse all listeners to synchronize the data. The listener implementation class here is ZookeeperData ChangedListener, which synchronizes data to zookeeper through zkClient according to the corresponding event type. - -![pic](/assets/img/blog5//zk5.png) - -7. Soul-admin initialization to data to zookeeper mind map - -![pic](/assets/img/blog5//zk6.png) - -###### IV. Summary - -When the soul-admin is started, it will synchronize the gateway data rule, metaData, selector, plugin, etc. To the zookeeper. The data change will publish the DataChangedEvent event, and the listening event will synchronize the data to the zookeeper. - -[ Soul gateway data synchronization principle ](https://dromara.org/projects/soul/data-sync/) +--- +title: Soul Gateway Learns Zookeeper Data Synchronization 01 +author: liquan +date: 2021-01-20 +tag: + - Soul +cover: '/assets/img/architecture/soul-framework.png' +head: + - - meta + - name: Blog +--- + +#### Start soul-admin and soul-bootstrap, and use zookeeper to synchronize data to the gateway + +###### I. Configuration environment + +1. For soul-admin service configuration, restart the service. + +soul-admin/src/main/resources/application.yml + +```yaml +soul: + sync: + zookeeper: + url: localhost:2181 + sessionTimeout: 5000 + connectionTimeout: 2000 +``` + +2. Soul-bootstrap gateway service configuration needs to be restarted + +soul-bootstrap/pom.xml + +```xml + + + org.dromara + soul-spring-boot-starter-sync-data-zookeeper + ${project.version} + +``` + +soul-bootstrap/src/main/resources/application-local.yml + +```yaml +soul: + sync: + zookeeper: + url: localhost:2181 + sessionTimeout: 5000 + connectionTimeout: 2000 +``` + +###### II. Startup Service + +1. Start zookeeper + +``` +zookeeper ./bin/zkServer.sh start +/usr/bin/java +ZooKeeper JMX enabled by default +Using config: /Documents/soft/zookeeper/bin/../conf/zoo.cfg +Starting zookeeper ... STARTED +``` + +2. The soul-admin gateway background service is started. After the service is started, you can see the ZooKeeper request call. + +``` + +2021-01-20 17:34:48.752 INFO 64500 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Starting ZkClient event thread. +2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:zookeeper.version=3.5.6-c11b7e26bc554b8523dc929761dd28808913f091, built on 10/08/2019 20:18 GMT +2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:host.name=10.7.254.31 +2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.version=1.8.0_261 +2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.vendor=Oracle Corporation +...... +2021-01-20 17:34:48.806 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown error) +2021-01-20 17:34:48.826 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Socket connection established, initiating session, client: /0:0:0:0:0:0:0:1:58214, server: localhost/0:0:0:0:0:0:0:1:2181 +2021-01-20 17:34:48.857 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x1000b5e22f50001, negotiated timeout = 5000 +2021-01-20 17:34:48.861 INFO 64500 --- [ain-EventThread] org.I0Itec.zkclient.ZkClient : zookeeper state changed (SyncConnected) +``` + +3. The soul-bootstrap gateway service is started. After the service is started, you can see the ZooKeeper request call. + +``` +2021-01-20 17:35:58.996 INFO 64583 --- [ main] s.b.s.d.z.ZookeeperSyncDataConfiguration : you use zookeeper sync soul data....... +2021-01-20 17:35:59.003 INFO 64583 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Starting ZkClient event thread. +...... + +2021-01-20 17:35:59.012 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:user.home=/Users/liquan +2021-01-20 17:35:59.012 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:os.memory.total=310MB +2021-01-20 17:35:59.018 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Initiating client connection, connectString=localhost:2181 sessionTimeout=5000 watcher=org.I0Itec.zkclient.ZkClient@114a5e0 +2021-01-20 17:35:59.121 INFO 64583 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x1000b5e22f50002, negotiated timeout = 5000 +2021-01-20 17:35:59.126 INFO 64583 --- [ain-EventThread] org.I0Itec.zkclient.ZkClient : zookeeper state changed (SyncConnected) +``` + +4. View the registration information ![在这里插入图片描述](/assets/img/blog5/zk1.png) of soul gateway synchronization on zookeeper + +###### III. Analysis of data synchronization principle of Soul gateway Zookeeper + +After the soul-admin is started, the zkclient. ZkClient org.I0Itec. Is seen in the console, which is used as an entry point for tracing and debugging. + +1. Function of ZookeeperConfiguration: Register zkClient to the Spring container. + +```java +// EnableConfigurationProperties Purpose: Enables classes annotated with @ConfigurationProperties. If a configuration class is only annotated with @ConfigurationProperties and not with @Component, then the bean converted from the properties configuration file cannot be obtained in the IOC container. @EnableConfigurationProperties is equivalent to injecting classes that use @ConfigurationProperties. +// @ConditionalOnMissingBean If the specified class is missing in the container, perform injection. Opposite of @ConditionalOnBean. +/** + * ZookeeperConfiguration . + * @author xiaoyu(Myth) + */ +@EnableConfigurationProperties(ZookeeperProperties.class) +public class ZookeeperConfiguration { + /** + * register zkClient in spring ioc. + * + * @param zookeeperProp the zookeeper configuration + * @return ZkClient {@linkplain ZkClient} + */ + @Bean + @ConditionalOnMissingBean(ZkClient.class) + public ZkClient zkClient(final ZookeeperProperties zookeeperProp) { + return new ZkClient(zookeeperProp.getUrl(), zookeeperProp.getSessionTimeout(), zookeeperProp.getConnectionTimeout()); + } +} +``` + +After the soul-admin is started, it will read the zookeeper configuration information and inject zkClient into the container to establish a connection with the zookeeper. ![pic](/assets/img/blog5/zk2.png) + +![pic](/assets/img/blog5/zk3.png) + +2. The afterPropertiesSet method of DataChangedEventDispatcher will be called in the call stack of the instantiated ZkClient. + +The org. Dromara. Soul. Admin. Listener. DataChangedEventDispatcher acts as an event router, which forwards changed events to each ConfigEventListener. + +This class implements the InitializingBean. During the DataChangedEventDispatcher initialization, the afterPropertiesSet method is executed. + +The After PropertiesSet method looks for beans in the container whose type is the DataChangedListener. Class. + +```java +@Component +public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { + private ApplicationContext applicationContext; + private List listeners; + public DataChangedEventDispatcher(final ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } +@Override +@SuppressWarnings("unchecked") +public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + case APP_AUTH: + listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); + break; + ....... + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } + ...... + @Override + public void afterPropertiesSet() { + Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); + this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); + } +} +``` + +3. The execution of the afterPropertiesSet method will find the instantiation of DataChangedListener. Class related classes. + +Role of org. Dromara. Soul. Admin. Config. Data SyncConfiguration: Data synchronization configuration class. + +ZookeeperData ChangedListener Data Change Listener, which is used to listen to metadata changes and then synchronize to zookeeper. + +ZookeeperDataInit zookeeper data initialization. Function: Synchronize initialization data to zookeeper. + +```java +/** + * The type Zookeeper listener. + */ +@Configuration +@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url") +@Import(ZookeeperConfiguration.class) +static class ZookeeperListener { + /** + * Config event listener data changed listener. + * @param zkClient the zk client + * @return the data changed listener + */ + @Bean + @ConditionalOnMissingBean(ZookeeperDataChangedListener.class) + public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) { + return new ZookeeperDataChangedListener(zkClient); + } + /** + * Zookeeper data init zookeeper data init + * @param zkClient the zk client + * @param syncDataService the sync data service + * @return the zookeeper data init + */ + @Bean + @ConditionalOnMissingBean(ZookeeperDataInit.class) + public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { + return new ZookeeperDataInit(zkClient, syncDataService); + } +} +``` + +4. Role of org. Dromara. Soul. Admin. Listener. Zookeeper. ZookeeperDataInit: responsible for synchronizing initialization data to zookeeper. This class implements the CommandLine Runner. + +CommandLine Runner: Function: SpringBoot will traverse all entity classes that implement CommandLineRunner and execute the run method after the project is started. If it needs to be executed in a certain order, Then you need to use a @ Order annotation on the entity class (or implement the Order interface) to indicate the order. + +The run method calls the syncData Service. SyncAll method. + +```java +public class ZookeeperDataInit implements CommandLineRunner { + private final ZkClient zkClient; + private final SyncDataService syncDataService; + /** + * Instantiates a new Zookeeper data init. + * @param zkClient the zk client + * @param syncDataService the sync data service + */ + public ZookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { + this.zkClient = zkClient; + this.syncDataService = syncDataService; + } + @Override + public void run(final String... args) { + String pluginPath = ZkPathConstants.PLUGIN_PARENT; + String authPath = ZkPathConstants.APP_AUTH_PARENT; + String metaDataPath = ZkPathConstants.META_DATA; + if (!zkClient.exists(pluginPath) && !zkClient.exists(authPath) && !zkClient.exists(metaDataPath)) { + syncDataService.syncAll(DataEventTypeEnum.REFRESH); + } + } +} +``` + +5、org.dromara.soul.admin.service.sync.SyncDataServiceImpl + +The syncAll method invokes the event publisher to publish an event of DataEventTypeEnum. REFRESH. + +```java +/** + * The type sync data service. + * @author xiaoyu(Myth) + */ +@Service("syncDataService") +public class SyncDataServiceImpl implements SyncDataService { + // Publishes events, informing all listeners related to this event. + private final ApplicationEventPublisher eventPublisher; + ...... + @Override + public boolean syncAll(final DataEventTypeEnum type) { + appAuthService.syncData(); + List pluginDataList = pluginService.listAll(); + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList)); + List selectorDataList = selectorService.listAll(); + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList)); + List ruleDataList = ruleService.listAll(); + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList)); + metaDataService.syncData(); + return true; + } + ...... +} +``` + +![pic](/assets/img/blog5//zk4.png) + +6. After the event is published, the onApplicationEvent method of the org. Dromara. Soul. Admin. Listener. DataChangedEventDispatcher class will listen to the change of the event and traverse all listeners to synchronize the data. The listener implementation class here is ZookeeperData ChangedListener, which synchronizes data to zookeeper through zkClient according to the corresponding event type. + +![pic](/assets/img/blog5//zk5.png) + +7. Soul-admin initialization to data to zookeeper mind map + +![pic](/assets/img/blog5//zk6.png) + +###### IV. Summary + +When the soul-admin is started, it will synchronize the gateway data rule, metaData, selector, plugin, etc. To the zookeeper. The data change will publish the DataChangedEvent event, and the listening event will synchronize the data to the zookeeper. + +[ Soul gateway data synchronization principle ](https://dromara.org/projects/soul/data-sync/) diff --git a/src/blog/soul_source_learning_13_zookeeper_02.md b/src/blog/soul_source_learning_13_zookeeper_02.md index 2a50cf2c04..8a6f8f27e3 100644 --- a/src/blog/soul_source_learning_13_zookeeper_02.md +++ b/src/blog/soul_source_learning_13_zookeeper_02.md @@ -1,197 +1,197 @@ ---- -title: Soul Gateway Learns Zookeeper Data Synchronization 02 -author: liquan -date: 2021-01-21 -tag: - - Soul -cover: '/assets/img/architecture/soul-framework.png' -head: - - - meta - - name: Blog ---- - -#### Start the admin? And Gateway. Admin operation, using zookeeper to synchronize data to the gateway - -The soul-admin [previous article](https://dromara.org/blog/soul_source_learning_13_zookeeper_01) startup process is the entry. After analyzing the soul-admin startup, the gateway data rule, metaData, selector, plugin, etc. Will be synchronized to the zookeeper. - -The data change will publish the DataChangedEvent event, and the listening event will synchronize the data to the zookeeper. This article follows the previous article to continue to track the source code and analyze the principle of zookeeper synchronizing data to the gateway: - -- Soul-admin changes the gateway data and tracks the data synchronization process. -- How soul-bootstrap gets zookeeper data and how it senses changes in gateway data. - -###### 1. Soul-admin changes the gateway data and tracks the data synchronization process - -1. Try to change the status of the divide plug-in in the background of the gateway, debug tracking. - -![pic](/assets/img/blog5/zk7.png) - -2. After the plug-in is updated, a Data ChangedEvent event will be issued. - -![pic](/assets/img/blog5/zk8.png) - -3. Org. Dromara. Soul. Admin. Listener. DataChangedEventDispatcher -- > onApplicationEvent () is responsible for listening to events. - -![pic](/assets/img/blog5/zk9.png) - -4. The org. Dromara. Soul. Admin. Listener. Zookeeper. ZookeeperData ChangedListener is responsible for synchronizing data to zookeeper. - -![pic](/assets/img/blog5/zk10.png) - -###### 2. How does soul-bootstrap get zookeeper data and how does it perceive changes in gateway data. - -1. Soul-bootstrap dependency - -```xml - - org.dromara - soul-spring-boot-starter-sync-data-zookeeper - ${project.version} - -``` - -2. Soul-bootstrap will automatically inject org. Dromara. Soul. Spring. Boot. Sync. Data. Zookeeper. ZookeeperSyncData Configuration after startup. - -Inject ZkClient into the container by reading the Zookeeper configuration. - -The SyncData Service injects the data synchronization service bean into the container and obtains the ZkClient (zookeeper client) from the Spring container. Plugin Subscriber, metaSubscribers, authSubscribers. - -```java -public class ZookeeperSyncDataConfiguration { - /** - * Sync data service sync data service. - * @param zkClient the zk client - * @param pluginSubscriber the plugin subscriber - * @param metaSubscribers the meta subscribers - * @param authSubscribers the auth subscribers - * @return the sync data service - */ - @Bean - public SyncDataService syncDataService(final ObjectProvider zkClient, final ObjectProvider pluginSubscriber, - final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { - log.info("you use zookeeper sync soul data......."); - return new ZookeeperSyncDataService(zkClient.getIfAvailable(), pluginSubscriber.getIfAvailable(), - metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); - } - /** - * register zkClient in spring ioc. - * @param zookeeperConfig the zookeeper configuration - * @return ZkClient {@linkplain ZkClient} - */ - @Bean - public ZkClient zkClient(final ZookeeperConfig zookeeperConfig) { - return new ZkClient(zookeeperConfig.getUrl(), zookeeperConfig.getSessionTimeout(), zookeeperConfig.getConnectionTimeout()); - } -} -``` - -3. Initialize the org. Dromara. Soul. Sync. Data. Zookeeper. ZookeeperSyncData Service, that is, after the soul-bootstrap is started, the data will be obtained from the zookeeper and synchronized to the memory. - -- watcherData()--> watcherAll() --> watcherPlugin() --> cachePluginData()。 -- The zkClient. SubscribeData Changes () monitors the modification and deletion of the contents of the current node and its child nodes. - -```java -public class ZookeeperSyncDataService implements SyncDataService, AutoCloseable { - private final ZkClient zkClient; - private final PluginDataSubscriber pluginDataSubscriber; - private final List metaDataSubscribers; - private final List authDataSubscribers; - /** - * Instantiates a new Zookeeper cache manager. - * @param zkClient the zk client - * @param pluginDataSubscriber the plugin data subscriber - * @param metaDataSubscribers the meta data subscribers - * @param authDataSubscribers the auth data subscribers - */ - public ZookeeperSyncDataService(final ZkClient zkClient, final PluginDataSubscriber pluginDataSubscriber, - final List metaDataSubscribers, final List authDataSubscribers) { - this.zkClient = zkClient; - this.pluginDataSubscriber = pluginDataSubscriber; - this.metaDataSubscribers = metaDataSubscribers; - this.authDataSubscribers = authDataSubscribers; - watcherData(); - watchAppAuth(); - watchMetaData(); - } - ...... - private void watcherData() { - final String pluginParent = ZkPathConstants.PLUGIN_PARENT; - List pluginZKs = zkClientGetChildren(pluginParent); - for (String pluginName : pluginZKs) { - watcherAll(pluginName); - } - zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> { - if (CollectionUtils.isNotEmpty(currentChildren)) { - for (String pluginName : currentChildren) { - watcherAll(pluginName); - } - } - }); - } - ...... - private void watcherPlugin(final String pluginName) { - String pluginPath = ZkPathConstants.buildPluginPath(pluginName); - if (!zkClient.exists(pluginPath)) { - zkClient.createPersistent(pluginPath, true); - } - cachePluginData(zkClient.readData(pluginPath)); - subscribePluginDataChanges(pluginPath, pluginName); - } -} -``` - -4. The debug process - -![pic](/assets/img/blog5/zk11.png) - -###### 3. How does soul-bootstrap sense changes in gateway data - -1. Put a breakpoint on the org. Dromara. Soul. Sync. Data. Zookeeper. ZookeeperSyncData ServicecacheRuleData method, update the plug-in rule, and observe whether the breakpoint is entered. - -```java -private void cacheRuleData(final RuleData ruleData) { - Optional.ofNullable(ruleData) - .ifPresent(data -> Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.onRuleSubscribe(data))); -} -``` - -2. Soul-admin operates in the background to change the divide plug-in rules. First, soul-admin will publish the event and listen to the event to update the data synchronously to zookeeper. - -![pic](/assets/img/blog5/zk12.png) - -3. Soul-bootstrap has indeed received an update of the plug-in data. According to the "zookeeper synchronization principle" introduced by Soul's official website, it mainly relies on the watch mechanism of zookeeper. - -Org. Dromara. Soul. Sync. Data. Zookeeper. ZookeeperSyncData Service: - -The zkClient. SubscribeData Changes () monitors the modification and deletion of the contents of the current node and its child nodes. - -```java -zkClient.subscribeChildChanges(groupParentPath, (parentPath, currentChildren) -> { - if (CollectionUtils.isNotEmpty(currentChildren)) { - List addSubscribePath = addSubscribePath(childrenList, currentChildren); - // Get the newly added node data and subscribe to that node - addSubscribePath.stream().map(addPath -> { - String realPath = buildRealPath(parentPath, addPath); - cacheRuleData(zkClient.readData(realPath)); - return realPath; - }).forEach(this::subscribeRuleDataChanges); - } -}); -private void subscribeRuleDataChanges(final String path) { - zkClient.subscribeDataChanges(path, new IZkDataListener() { - @Override - public void handleDataChange(final String dataPath, final Object data) { - cacheRuleData((RuleData) data); - } - @Override - public void handleDataDeleted(final String dataPath) { - unCacheRuleData(dataPath); - } - }); -} -``` - -![pic](/assets/img/blog5/zk13.png) - -###### IV. Summary - -![pic](/assets/img/blog5/zk14.png) +--- +title: Soul Gateway Learns Zookeeper Data Synchronization 02 +author: liquan +date: 2021-01-21 +tag: + - Soul +cover: '/assets/img/architecture/soul-framework.png' +head: + - - meta + - name: Blog +--- + +#### Start the admin? And Gateway. Admin operation, using zookeeper to synchronize data to the gateway + +The soul-admin [previous article](https://dromara.org/blog/soul_source_learning_13_zookeeper_01) startup process is the entry. After analyzing the soul-admin startup, the gateway data rule, metaData, selector, plugin, etc. Will be synchronized to the zookeeper. + +The data change will publish the DataChangedEvent event, and the listening event will synchronize the data to the zookeeper. This article follows the previous article to continue to track the source code and analyze the principle of zookeeper synchronizing data to the gateway: + +- Soul-admin changes the gateway data and tracks the data synchronization process. +- How soul-bootstrap gets zookeeper data and how it senses changes in gateway data. + +###### 1. Soul-admin changes the gateway data and tracks the data synchronization process + +1. Try to change the status of the divide plug-in in the background of the gateway, debug tracking. + +![pic](/assets/img/blog5/zk7.png) + +2. After the plug-in is updated, a Data ChangedEvent event will be issued. + +![pic](/assets/img/blog5/zk8.png) + +3. Org. Dromara. Soul. Admin. Listener. DataChangedEventDispatcher -- > onApplicationEvent () is responsible for listening to events. + +![pic](/assets/img/blog5/zk9.png) + +4. The org. Dromara. Soul. Admin. Listener. Zookeeper. ZookeeperData ChangedListener is responsible for synchronizing data to zookeeper. + +![pic](/assets/img/blog5/zk10.png) + +###### 2. How does soul-bootstrap get zookeeper data and how does it perceive changes in gateway data. + +1. Soul-bootstrap dependency + +```xml + + org.dromara + soul-spring-boot-starter-sync-data-zookeeper + ${project.version} + +``` + +2. Soul-bootstrap will automatically inject org. Dromara. Soul. Spring. Boot. Sync. Data. Zookeeper. ZookeeperSyncData Configuration after startup. + +Inject ZkClient into the container by reading the Zookeeper configuration. + +The SyncData Service injects the data synchronization service bean into the container and obtains the ZkClient (zookeeper client) from the Spring container. Plugin Subscriber, metaSubscribers, authSubscribers. + +```java +public class ZookeeperSyncDataConfiguration { + /** + * Sync data service sync data service. + * @param zkClient the zk client + * @param pluginSubscriber the plugin subscriber + * @param metaSubscribers the meta subscribers + * @param authSubscribers the auth subscribers + * @return the sync data service + */ + @Bean + public SyncDataService syncDataService(final ObjectProvider zkClient, final ObjectProvider pluginSubscriber, + final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { + log.info("you use zookeeper sync soul data......."); + return new ZookeeperSyncDataService(zkClient.getIfAvailable(), pluginSubscriber.getIfAvailable(), + metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); + } + /** + * register zkClient in spring ioc. + * @param zookeeperConfig the zookeeper configuration + * @return ZkClient {@linkplain ZkClient} + */ + @Bean + public ZkClient zkClient(final ZookeeperConfig zookeeperConfig) { + return new ZkClient(zookeeperConfig.getUrl(), zookeeperConfig.getSessionTimeout(), zookeeperConfig.getConnectionTimeout()); + } +} +``` + +3. Initialize the org. Dromara. Soul. Sync. Data. Zookeeper. ZookeeperSyncData Service, that is, after the soul-bootstrap is started, the data will be obtained from the zookeeper and synchronized to the memory. + +- watcherData()--> watcherAll() --> watcherPlugin() --> cachePluginData()。 +- The zkClient. SubscribeData Changes () monitors the modification and deletion of the contents of the current node and its child nodes. + +```java +public class ZookeeperSyncDataService implements SyncDataService, AutoCloseable { + private final ZkClient zkClient; + private final PluginDataSubscriber pluginDataSubscriber; + private final List metaDataSubscribers; + private final List authDataSubscribers; + /** + * Instantiates a new Zookeeper cache manager. + * @param zkClient the zk client + * @param pluginDataSubscriber the plugin data subscriber + * @param metaDataSubscribers the meta data subscribers + * @param authDataSubscribers the auth data subscribers + */ + public ZookeeperSyncDataService(final ZkClient zkClient, final PluginDataSubscriber pluginDataSubscriber, + final List metaDataSubscribers, final List authDataSubscribers) { + this.zkClient = zkClient; + this.pluginDataSubscriber = pluginDataSubscriber; + this.metaDataSubscribers = metaDataSubscribers; + this.authDataSubscribers = authDataSubscribers; + watcherData(); + watchAppAuth(); + watchMetaData(); + } + ...... + private void watcherData() { + final String pluginParent = ZkPathConstants.PLUGIN_PARENT; + List pluginZKs = zkClientGetChildren(pluginParent); + for (String pluginName : pluginZKs) { + watcherAll(pluginName); + } + zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> { + if (CollectionUtils.isNotEmpty(currentChildren)) { + for (String pluginName : currentChildren) { + watcherAll(pluginName); + } + } + }); + } + ...... + private void watcherPlugin(final String pluginName) { + String pluginPath = ZkPathConstants.buildPluginPath(pluginName); + if (!zkClient.exists(pluginPath)) { + zkClient.createPersistent(pluginPath, true); + } + cachePluginData(zkClient.readData(pluginPath)); + subscribePluginDataChanges(pluginPath, pluginName); + } +} +``` + +4. The debug process + +![pic](/assets/img/blog5/zk11.png) + +###### 3. How does soul-bootstrap sense changes in gateway data + +1. Put a breakpoint on the org. Dromara. Soul. Sync. Data. Zookeeper. ZookeeperSyncData ServicecacheRuleData method, update the plug-in rule, and observe whether the breakpoint is entered. + +```java +private void cacheRuleData(final RuleData ruleData) { + Optional.ofNullable(ruleData) + .ifPresent(data -> Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.onRuleSubscribe(data))); +} +``` + +2. Soul-admin operates in the background to change the divide plug-in rules. First, soul-admin will publish the event and listen to the event to update the data synchronously to zookeeper. + +![pic](/assets/img/blog5/zk12.png) + +3. Soul-bootstrap has indeed received an update of the plug-in data. According to the "zookeeper synchronization principle" introduced by Soul's official website, it mainly relies on the watch mechanism of zookeeper. + +Org. Dromara. Soul. Sync. Data. Zookeeper. ZookeeperSyncData Service: + +The zkClient. SubscribeData Changes () monitors the modification and deletion of the contents of the current node and its child nodes. + +```java +zkClient.subscribeChildChanges(groupParentPath, (parentPath, currentChildren) -> { + if (CollectionUtils.isNotEmpty(currentChildren)) { + List addSubscribePath = addSubscribePath(childrenList, currentChildren); + // Get the newly added node data and subscribe to that node + addSubscribePath.stream().map(addPath -> { + String realPath = buildRealPath(parentPath, addPath); + cacheRuleData(zkClient.readData(realPath)); + return realPath; + }).forEach(this::subscribeRuleDataChanges); + } +}); +private void subscribeRuleDataChanges(final String path) { + zkClient.subscribeDataChanges(path, new IZkDataListener() { + @Override + public void handleDataChange(final String dataPath, final Object data) { + cacheRuleData((RuleData) data); + } + @Override + public void handleDataDeleted(final String dataPath) { + unCacheRuleData(dataPath); + } + }); +} +``` + +![pic](/assets/img/blog5/zk13.png) + +###### IV. Summary + +![pic](/assets/img/blog5/zk14.png) diff --git a/src/blog/soul_source_learning_14_nacos.md b/src/blog/soul_source_learning_14_nacos.md index 34c786e620..8d5979ba9a 100644 --- a/src/blog/soul_source_learning_14_nacos.md +++ b/src/blog/soul_source_learning_14_nacos.md @@ -1,355 +1,355 @@ ---- -title: Soul Gateway Learns Nacos Data Synchronization -author: liquan -date: 2021-01-26 -tag: - - Soul -cover: '/assets/img/blog5/ns15.png' -head: - - - meta - - name: Blog ---- - -This article analyzes the principle of Nacos data synchronization. - -1. Configure the environment first - -- soul-adminsoul-admin/src/main/resources/application.yml - -```yaml -soul: - sync: - nacos: - url: localhost:8848 - namespace: 1c10d748-af86-43b9-8265-75f487d20c6c - # acm: - # enabled: false - # endpoint: acm.aliyun.com - # namespace: - # accessKey: - # secretKey: -``` - -Soul-admin/POM. XML, where the default configuration is available - -```xml - - com.alibaba.nacos - nacos-client - ${nacos-client.version} - -``` - -- soul-bootstrapsoul-bootstrap/src/main/resources/application-local.yml - -```yaml -soul: - sync: - nacos: - url: localhost:8848 - namespace: 1c10d748-af86-43b9-8265-75f487d20c6c -# acm: -# enabled: false -# endpoint: acm.aliyun.com -# namespace: -# accessKey: -# secretKey: -``` - -Soul-bootstrap/POM. XML. The following configuration is not available by default and needs to be added manually - -```xml - - org.dromara - soul-spring-boot-starter-sync-data-nacos - ${project.version} - -``` - -- Start the service - -``` -1、Start nacos -2、Start soul-admin -3、Start soul-bootstrap -``` - -2. The above looks quite smooth. This process has encountered a pit. The soul-bootstrap cannot be started and the null pointer exception is reported. The following is a detailed record. First of all, soul-admin will not actively synchronize the gateway data to nacos after it is started. It needs to be synchronized manually, which is not mentioned on the official website. This problem has been bothering me for a long time. Finally, I saw other students in the group encounter the same problem and refer to their articles to solve it. Here is a record of the solution process. - -1) Soul-bootstrap encountered the following error, NullPointerException, when starting. - -When the soul-bootstrap is started, it will go to nacos to get the gateway data, see the breakpoint below, and get the empty data. - -``` -Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. -2021-01-25 16:49:06.052 ERROR 5273 --- [ main] o.s.boot.SpringApplication : Application run failed -org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosSyncDataService' defined in class path resource [org/dromara/soul/springboot/starter/sync/data/nacos/NacosSyncDataConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is java.lang.NullPointerException -...... - at org.dromara.soul.bootstrap.SoulBootstrapApplication.main(SoulBootstrapApplication.java:37) [classes/:na] -Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is java.lang.NullPointerException - at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] - at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] - ... 19 common frames omitted -Caused by: java.lang.NullPointerException: null - at org.dromara.soul.sync.data.nacos.handler.NacosCacheHandler.updateMetaDataMap(NacosCacheHandler.java:128) ~[classes/:na] - at org.dromara.soul.sync.data.nacos.handler.NacosCacheHandler.watcherData(NacosCacheHandler.java:167) ~[classes/:na] - at org.dromara.soul.sync.data.nacos.NacosSyncDataService.start(NacosSyncDataService.java:59) ~[classes/:na] - at org.dromara.soul.sync.data.nacos.NacosSyncDataService.(NacosSyncDataService.java:49) ~[classes/:na] - at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration.nacosSyncDataService(NacosSyncDataConfiguration.java:66) ~[classes/:na] - at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7.CGLIB$nacosSyncDataService$0() ~[classes/:na] - at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7$$FastClassBySpringCGLIB$$3830e886.invoke() ~[classes/:na] - at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE] - at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE] - at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7.nacosSyncDataService() ~[classes/:na] -...... - at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] - ... 20 common frames omitted -``` - -![pic](/assets/img/blog5/ns1.png) - -2. Go to nacos to check whether there is gateway data. According to the configured "namespace: 1c10d748-af86-43b9-8265-75f487d20c6c", there is nothing. - -![pic](/assets/img/blog5/ns2.png) - -![pic](/assets/img/blog5/ns3.png) - -3. Try to go to soul-admin for manual synchronization, but nacos cannot see the data. You must manually create the namespace "1c10d748-af86-43b9-8265-75f487d20c6c", as shown in the following figure. - -![pic](/assets/img/blog5/ns4.png) - -4. Go to soul-admin to manually synchronize the data, and you will see the configuration information of the gateway on nacos. At this time, soul-bootstrap still cannot be started, because there is still a lack of metadata information. Only dubbo and spring cloud services have metadata, while HTTP does not have metadata, so we have to start the dubbo service. Then synchronize the metadata in soul-admin. - -![pic](/assets/img/blog5/ns5.png) - -Soul-admin Click Synchronize Data to synchronize the metadata to nacos - -![pic](/assets/img/blog5/ns6.png) - -Soul-admin Click Synchronize Data to synchronize the authentication data to nacos - -![pic](/assets/img/blog5/ns7.png) - -At this point, nacos has seen all the gateway data - -![pic](/assets/img/blog5/ns8.png) - -5. Start the soul-bootstrap again, and finally start it successfully. - -``` -2021-01-25 17:56:54.798 INFO 10051 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[monitor] [org.dromara.soul.plugin.monitor.MonitorPlugin] -2021-01-25 17:56:54.798 INFO 10051 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[response] [org.dromara.soul.plugin.httpclient.response.WebClientResponsePlugin] -2021-01-25 17:56:54.990 INFO 10051 --- [ main] d.s.s.s.s.d.n.NacosSyncDataConfiguration : you use nacos sync soul data....... -2021-01-25 17:56:58.890 INFO 10051 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' -2021-01-25 17:56:59.758 INFO 10051 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195 -2021-01-25 17:56:59.764 INFO 10051 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 8.401 seconds (JVM running for 9.95) -``` - -6. Summary: - -After configuration, I feel that using nacos to synchronize data is not very friendly. There are many pitfalls in the configuration process. First of all, soul-admin will not actively synchronize gateway data to nacos, and manual synchronization is required. Soul-bootstrap must rely on all of the gateway configuration data soul. Plugin, soul. Selector, soul. Selector, soul. Meta, and soul. Auth. If the gateway only proxies the HTTP service (no metadata), the soul-bootstrap cannot be started. There is no detailed explanation on the official website, which is not very friendly to Xiaobai. - -We know that soul-admin will not automatically synchronize data to nacos after startup, and manual operation is required. - -Let's analyze the data synchronization process of soul-admin, nacos, and soul-bootstrap. - -##### How does soul-admin synchronize gateway data? - -1. After the plug-in information is updated, a DataChangedEvent event will be released. - -```java -/** - * create or update plugin - * @param pluginDTO {@linkplain PluginDTO} - * @return rows - */ -@Override -@Transactional(rollbackFor = Exception.class) -public String createOrUpdate(final PluginDTO pluginDTO) { - ...... - // publish change event. - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType, - Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO)))); - return StringUtils.EMPTY; -} -``` - -2. The listening event processing class DataChangedEventDispatcher is responsible for calling the specific listening implementation class to process the DataChangedEvent event. The specific implementation class here is NacosData ChangedListener. - -> org.dromara.soul.admin.listener.DataChangedEventDispatcher - -> After the DataChangedEventDispatcher is initialized, it will execute afterPropertiesSet () to obtain all beans with DataChangedListener. Class in the container - -```java - -@Override -public void afterPropertiesSet() { - Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); - this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); -} -``` - -> > After the DataChangedEventDispatcher monitors the change event, it will execute onApplicationEvent to traverse all listening classes to process the listening event. Here is NacosDataChangedListener, as shown in the following debug figure. - -```java - -@Override -@SuppressWarnings("unchecked") -public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - ...... - case RULE: - listener.onRuleChanged((List) event.getSource(), event.getEventType()); - break; - ...... - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } -} -``` - -![pic](/assets/img/blog5/ns9.png) - -3. NacosData ChangedListener will execute onRuleChanged, and updateRuleMap will first synchronize the gateway data to the memory, and then synchronize to nacos through publishConfig. - -> org.dromara.soul.admin.listener.nacos.NacosDataChangedListener - -```java -// Execute the listening event -@Override -public void onRuleChanged(final List changed, final DataEventTypeEnum eventType) { - updateRuleMap(getConfig(RULE_DATA_ID)); - switch (eventType) { - ...... - default: - changed.forEach(rule -> { - List ls = RULE_MAP - .getOrDefault(rule.getSelectorId(), new ArrayList<>()) - .stream() - .filter(s -> !s.getId().equals(rule.getSelectorId())) - .sorted(RULE_DATA_COMPARATOR) - .collect(Collectors.toList()); - ls.add(rule); - RULE_MAP.put(rule.getSelectorId(), ls); - }); - break; - } - publishConfig(RULE_DATA_ID, RULE_MAP); -} -// Synchronize to memory -private void updateRuleMap(final String configInfo) { - JsonObject jo = GsonUtils.getInstance().fromJson(configInfo, JsonObject.class); - Set set = new HashSet<>(RULE_MAP.keySet()); - ...... - RULE_MAP.keySet().removeAll(set); -} -// Synchronize to Nacos -@SneakyThrows -private void publishConfig(final String dataId, final Object data) { - configService.publishConfig(dataId, GROUP, GsonUtils.getInstance().toJson(data)); -} -``` - -![pic](/assets/img/blog5/ns10.png) - -4. DataChangedEventDispatcher and NacosData ChangedListener Class Inheritance Relationship - -![pic](/assets/img/blog5/ns11.png) - -![pic](/assets/img/blog5/ns12.png) - -5. Summary - -> 1. For example, soul-admin updates the gateway data and publishes a DataChangedEvent event, and the eventPublisher. PublishEvent (new DataChangedEvent ()). -> 2. The DataChange dEventDispatcher -- > onApplicationEvent () method listens to events and determines that the listening class is NacosData ChangedListener. -> 3. NacosData ChangedListener -- > onRuleChanged () handles the event -> 4. Synchronize to memory update RuleMap (RULE _ DATA _ ID) -> 5. Sync to nacos publishConfig (RULE _ DATA _ ID, RULE \_ MAP) - -##### How does soul-bootstrap synchronize gateway data? - -1. Soul-bootstrap adds nacos dependency soul-spring-boot-starter-sync-data-nacos, which will automatically inject NacosSyncDataConfiguration after the service is started. - -> org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration -> -> > The NacosSyncData Service is responsible for reading and synchronizing the nacos gateway data - -```java -@Configuration -@ConditionalOnClass(NacosSyncDataService.class) -@ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url") -@Slf4j -public class NacosSyncDataConfiguration { - // Inject the Nacos data synchronization service - @Bean - public SyncDataService nacosSyncDataService(final ObjectProvider configService, final ObjectProvider pluginSubscriber, - final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { - log.info("you use nacos sync soul data......."); - return new NacosSyncDataService(configService.getIfAvailable(), pluginSubscriber.getIfAvailable(), - metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); - } - // Inject Nacos client configuration service - @Bean - public ConfigService nacosConfigService(final NacosConfig nacosConfig) throws Exception { - Properties properties = new Properties(); - ...... - return NacosFactory.createConfigService(properties); - } - // Inject Nacos configuration - @Bean - @ConfigurationProperties(prefix = "soul.sync.nacos") - public NacosConfig nacosConfig() { - return new NacosConfig(); - } -} -``` - -2、org.dromara.soul.sync.data.nacos.NacosSyncDataService - -> Initialization executes start -> -> > The watcherData is responsible for listening to the nacos gateway data -> > -> > > UpdatePluginMap synchronize gateway data to memory - -```java -public void start() { - ...... - watcherData(RULE_DATA_ID, this::updateRuleMap); - ...... -} -@SneakyThrows -private String getConfigAndSignListener(final String dataId, final Listener listener) { - return configService.getConfigAndSignListener(dataId, GROUP, 6000, listener); -} -protected void watcherData(final String dataId, final OnChange oc) { - Listener listener = new Listener() { - @Override - public void receiveConfigInfo(final String configInfo) { - oc.change(configInfo); - } - ...... - }; - oc.change(getConfigAndSignListener(dataId, listener)); - LISTENERS.getOrDefault(dataId, new ArrayList<>()).add(listener); -} -``` - -![pic](/assets/img/blog5/ns13.png) - -3. NacosSyncData Service Class Diagram - -![pic](/assets/img/blog5/ns14.png) - -4. Summary - -> 1. Soul-bootstrap starts to automatically inject NacosSyncDataConfiguration into the container -> 2. The NacosSyncDataConfiguration class will inject the NacosSyncData Service into the container. -> 3. NacosSyncData Service -- > start () -- > watcherData () listens to nacos and synchronizes gateway data to memory -> 4、watcherData() --> updatePluginMap() - -##### Sum up - -![pic](/assets/img/blog5/ns15.png) +--- +title: Soul Gateway Learns Nacos Data Synchronization +author: liquan +date: 2021-01-26 +tag: + - Soul +cover: '/assets/img/blog5/ns15.png' +head: + - - meta + - name: Blog +--- + +This article analyzes the principle of Nacos data synchronization. + +1. Configure the environment first + +- soul-adminsoul-admin/src/main/resources/application.yml + +```yaml +soul: + sync: + nacos: + url: localhost:8848 + namespace: 1c10d748-af86-43b9-8265-75f487d20c6c + # acm: + # enabled: false + # endpoint: acm.aliyun.com + # namespace: + # accessKey: + # secretKey: +``` + +Soul-admin/POM. XML, where the default configuration is available + +```xml + + com.alibaba.nacos + nacos-client + ${nacos-client.version} + +``` + +- soul-bootstrapsoul-bootstrap/src/main/resources/application-local.yml + +```yaml +soul: + sync: + nacos: + url: localhost:8848 + namespace: 1c10d748-af86-43b9-8265-75f487d20c6c +# acm: +# enabled: false +# endpoint: acm.aliyun.com +# namespace: +# accessKey: +# secretKey: +``` + +Soul-bootstrap/POM. XML. The following configuration is not available by default and needs to be added manually + +```xml + + org.dromara + soul-spring-boot-starter-sync-data-nacos + ${project.version} + +``` + +- Start the service + +``` +1、Start nacos +2、Start soul-admin +3、Start soul-bootstrap +``` + +2. The above looks quite smooth. This process has encountered a pit. The soul-bootstrap cannot be started and the null pointer exception is reported. The following is a detailed record. First of all, soul-admin will not actively synchronize the gateway data to nacos after it is started. It needs to be synchronized manually, which is not mentioned on the official website. This problem has been bothering me for a long time. Finally, I saw other students in the group encounter the same problem and refer to their articles to solve it. Here is a record of the solution process. + +1) Soul-bootstrap encountered the following error, NullPointerException, when starting. + +When the soul-bootstrap is started, it will go to nacos to get the gateway data, see the breakpoint below, and get the empty data. + +``` +Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. +2021-01-25 16:49:06.052 ERROR 5273 --- [ main] o.s.boot.SpringApplication : Application run failed +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosSyncDataService' defined in class path resource [org/dromara/soul/springboot/starter/sync/data/nacos/NacosSyncDataConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is java.lang.NullPointerException +...... + at org.dromara.soul.bootstrap.SoulBootstrapApplication.main(SoulBootstrapApplication.java:37) [classes/:na] +Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is java.lang.NullPointerException + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] + ... 19 common frames omitted +Caused by: java.lang.NullPointerException: null + at org.dromara.soul.sync.data.nacos.handler.NacosCacheHandler.updateMetaDataMap(NacosCacheHandler.java:128) ~[classes/:na] + at org.dromara.soul.sync.data.nacos.handler.NacosCacheHandler.watcherData(NacosCacheHandler.java:167) ~[classes/:na] + at org.dromara.soul.sync.data.nacos.NacosSyncDataService.start(NacosSyncDataService.java:59) ~[classes/:na] + at org.dromara.soul.sync.data.nacos.NacosSyncDataService.(NacosSyncDataService.java:49) ~[classes/:na] + at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration.nacosSyncDataService(NacosSyncDataConfiguration.java:66) ~[classes/:na] + at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7.CGLIB$nacosSyncDataService$0() ~[classes/:na] + at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7$$FastClassBySpringCGLIB$$3830e886.invoke() ~[classes/:na] + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE] + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE] + at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7.nacosSyncDataService() ~[classes/:na] +...... + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] + ... 20 common frames omitted +``` + +![pic](/assets/img/blog5/ns1.png) + +2. Go to nacos to check whether there is gateway data. According to the configured "namespace: 1c10d748-af86-43b9-8265-75f487d20c6c", there is nothing. + +![pic](/assets/img/blog5/ns2.png) + +![pic](/assets/img/blog5/ns3.png) + +3. Try to go to soul-admin for manual synchronization, but nacos cannot see the data. You must manually create the namespace "1c10d748-af86-43b9-8265-75f487d20c6c", as shown in the following figure. + +![pic](/assets/img/blog5/ns4.png) + +4. Go to soul-admin to manually synchronize the data, and you will see the configuration information of the gateway on nacos. At this time, soul-bootstrap still cannot be started, because there is still a lack of metadata information. Only dubbo and spring cloud services have metadata, while HTTP does not have metadata, so we have to start the dubbo service. Then synchronize the metadata in soul-admin. + +![pic](/assets/img/blog5/ns5.png) + +Soul-admin Click Synchronize Data to synchronize the metadata to nacos + +![pic](/assets/img/blog5/ns6.png) + +Soul-admin Click Synchronize Data to synchronize the authentication data to nacos + +![pic](/assets/img/blog5/ns7.png) + +At this point, nacos has seen all the gateway data + +![pic](/assets/img/blog5/ns8.png) + +5. Start the soul-bootstrap again, and finally start it successfully. + +``` +2021-01-25 17:56:54.798 INFO 10051 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[monitor] [org.dromara.soul.plugin.monitor.MonitorPlugin] +2021-01-25 17:56:54.798 INFO 10051 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[response] [org.dromara.soul.plugin.httpclient.response.WebClientResponsePlugin] +2021-01-25 17:56:54.990 INFO 10051 --- [ main] d.s.s.s.s.d.n.NacosSyncDataConfiguration : you use nacos sync soul data....... +2021-01-25 17:56:58.890 INFO 10051 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' +2021-01-25 17:56:59.758 INFO 10051 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195 +2021-01-25 17:56:59.764 INFO 10051 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 8.401 seconds (JVM running for 9.95) +``` + +6. Summary: + +After configuration, I feel that using nacos to synchronize data is not very friendly. There are many pitfalls in the configuration process. First of all, soul-admin will not actively synchronize gateway data to nacos, and manual synchronization is required. Soul-bootstrap must rely on all of the gateway configuration data soul. Plugin, soul. Selector, soul. Selector, soul. Meta, and soul. Auth. If the gateway only proxies the HTTP service (no metadata), the soul-bootstrap cannot be started. There is no detailed explanation on the official website, which is not very friendly to Xiaobai. + +We know that soul-admin will not automatically synchronize data to nacos after startup, and manual operation is required. + +Let's analyze the data synchronization process of soul-admin, nacos, and soul-bootstrap. + +##### How does soul-admin synchronize gateway data? + +1. After the plug-in information is updated, a DataChangedEvent event will be released. + +```java +/** + * create or update plugin + * @param pluginDTO {@linkplain PluginDTO} + * @return rows + */ +@Override +@Transactional(rollbackFor = Exception.class) +public String createOrUpdate(final PluginDTO pluginDTO) { + ...... + // publish change event. + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType, + Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO)))); + return StringUtils.EMPTY; +} +``` + +2. The listening event processing class DataChangedEventDispatcher is responsible for calling the specific listening implementation class to process the DataChangedEvent event. The specific implementation class here is NacosData ChangedListener. + +> org.dromara.soul.admin.listener.DataChangedEventDispatcher + +> After the DataChangedEventDispatcher is initialized, it will execute afterPropertiesSet () to obtain all beans with DataChangedListener. Class in the container + +```java + +@Override +public void afterPropertiesSet() { + Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); + this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); +} +``` + +> > After the DataChangedEventDispatcher monitors the change event, it will execute onApplicationEvent to traverse all listening classes to process the listening event. Here is NacosDataChangedListener, as shown in the following debug figure. + +```java + +@Override +@SuppressWarnings("unchecked") +public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + ...... + case RULE: + listener.onRuleChanged((List) event.getSource(), event.getEventType()); + break; + ...... + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } +} +``` + +![pic](/assets/img/blog5/ns9.png) + +3. NacosData ChangedListener will execute onRuleChanged, and updateRuleMap will first synchronize the gateway data to the memory, and then synchronize to nacos through publishConfig. + +> org.dromara.soul.admin.listener.nacos.NacosDataChangedListener + +```java +// Execute the listening event +@Override +public void onRuleChanged(final List changed, final DataEventTypeEnum eventType) { + updateRuleMap(getConfig(RULE_DATA_ID)); + switch (eventType) { + ...... + default: + changed.forEach(rule -> { + List ls = RULE_MAP + .getOrDefault(rule.getSelectorId(), new ArrayList<>()) + .stream() + .filter(s -> !s.getId().equals(rule.getSelectorId())) + .sorted(RULE_DATA_COMPARATOR) + .collect(Collectors.toList()); + ls.add(rule); + RULE_MAP.put(rule.getSelectorId(), ls); + }); + break; + } + publishConfig(RULE_DATA_ID, RULE_MAP); +} +// Synchronize to memory +private void updateRuleMap(final String configInfo) { + JsonObject jo = GsonUtils.getInstance().fromJson(configInfo, JsonObject.class); + Set set = new HashSet<>(RULE_MAP.keySet()); + ...... + RULE_MAP.keySet().removeAll(set); +} +// Synchronize to Nacos +@SneakyThrows +private void publishConfig(final String dataId, final Object data) { + configService.publishConfig(dataId, GROUP, GsonUtils.getInstance().toJson(data)); +} +``` + +![pic](/assets/img/blog5/ns10.png) + +4. DataChangedEventDispatcher and NacosData ChangedListener Class Inheritance Relationship + +![pic](/assets/img/blog5/ns11.png) + +![pic](/assets/img/blog5/ns12.png) + +5. Summary + +> 1. For example, soul-admin updates the gateway data and publishes a DataChangedEvent event, and the eventPublisher. PublishEvent (new DataChangedEvent ()). +> 2. The DataChange dEventDispatcher -- > onApplicationEvent () method listens to events and determines that the listening class is NacosData ChangedListener. +> 3. NacosData ChangedListener -- > onRuleChanged () handles the event +> 4. Synchronize to memory update RuleMap (RULE _ DATA _ ID) +> 5. Sync to nacos publishConfig (RULE _ DATA _ ID, RULE \_ MAP) + +##### How does soul-bootstrap synchronize gateway data? + +1. Soul-bootstrap adds nacos dependency soul-spring-boot-starter-sync-data-nacos, which will automatically inject NacosSyncDataConfiguration after the service is started. + +> org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration +> +> > The NacosSyncData Service is responsible for reading and synchronizing the nacos gateway data + +```java +@Configuration +@ConditionalOnClass(NacosSyncDataService.class) +@ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url") +@Slf4j +public class NacosSyncDataConfiguration { + // Inject the Nacos data synchronization service + @Bean + public SyncDataService nacosSyncDataService(final ObjectProvider configService, final ObjectProvider pluginSubscriber, + final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { + log.info("you use nacos sync soul data......."); + return new NacosSyncDataService(configService.getIfAvailable(), pluginSubscriber.getIfAvailable(), + metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); + } + // Inject Nacos client configuration service + @Bean + public ConfigService nacosConfigService(final NacosConfig nacosConfig) throws Exception { + Properties properties = new Properties(); + ...... + return NacosFactory.createConfigService(properties); + } + // Inject Nacos configuration + @Bean + @ConfigurationProperties(prefix = "soul.sync.nacos") + public NacosConfig nacosConfig() { + return new NacosConfig(); + } +} +``` + +2、org.dromara.soul.sync.data.nacos.NacosSyncDataService + +> Initialization executes start +> +> > The watcherData is responsible for listening to the nacos gateway data +> > +> > > UpdatePluginMap synchronize gateway data to memory + +```java +public void start() { + ...... + watcherData(RULE_DATA_ID, this::updateRuleMap); + ...... +} +@SneakyThrows +private String getConfigAndSignListener(final String dataId, final Listener listener) { + return configService.getConfigAndSignListener(dataId, GROUP, 6000, listener); +} +protected void watcherData(final String dataId, final OnChange oc) { + Listener listener = new Listener() { + @Override + public void receiveConfigInfo(final String configInfo) { + oc.change(configInfo); + } + ...... + }; + oc.change(getConfigAndSignListener(dataId, listener)); + LISTENERS.getOrDefault(dataId, new ArrayList<>()).add(listener); +} +``` + +![pic](/assets/img/blog5/ns13.png) + +3. NacosSyncData Service Class Diagram + +![pic](/assets/img/blog5/ns14.png) + +4. Summary + +> 1. Soul-bootstrap starts to automatically inject NacosSyncDataConfiguration into the container +> 2. The NacosSyncDataConfiguration class will inject the NacosSyncData Service into the container. +> 3. NacosSyncData Service -- > start () -- > watcherData () listens to nacos and synchronizes gateway data to memory +> 4、watcherData() --> updatePluginMap() + +##### Sum up + +![pic](/assets/img/blog5/ns15.png) diff --git a/src/blog/soul_source_learning_15_plugin_chain.md b/src/blog/soul_source_learning_15_plugin_chain.md index 41d2f6909d..420ef3482e 100644 --- a/src/blog/soul_source_learning_15_plugin_chain.md +++ b/src/blog/soul_source_learning_15_plugin_chain.md @@ -1,18 +1,18 @@ ---- -title: Soul Gateway Learning Plugin Chain Implementation -author: shenxiangjun -date: 2021-01-21 -tag: - - Soul -cover: '/assets/img/blog6/mirco.png' -head: - - - meta - - name: Blog ---- - -### I. Introduction - -** Plugins are the soul of Soul. ** - -Soul uses the idea of plug-in design to realize the hot plug of plug-ins, and it is very easy to expand. Built-in rich plug-in support, authentication, current limiting, fuse, firewall and so on. -待补,文章内部有报错 +--- +title: Soul Gateway Learning Plugin Chain Implementation +author: shenxiangjun +date: 2021-01-21 +tag: + - Soul +cover: '/assets/img/blog6/mirco.png' +head: + - - meta + - name: Blog +--- + +### I. Introduction + +** Plugins are the soul of Soul. ** + +Soul uses the idea of plug-in design to realize the hot plug of plug-ins, and it is very easy to expand. Built-in rich plug-in support, authentication, current limiting, fuse, firewall and so on. +待补,文章内部有报错 diff --git a/src/blog/soul_source_learning_16_divide_sxj.md b/src/blog/soul_source_learning_16_divide_sxj.md index 8a3a64b9c6..e7a60ad44b 100644 --- a/src/blog/soul_source_learning_16_divide_sxj.md +++ b/src/blog/soul_source_learning_16_divide_sxj.md @@ -1,326 +1,326 @@ ---- -title: Soul Gateway Learning Divide Plugin Source Code Interpretation -author: shenxiangjun -tag: - - Soul -date: 2021-02-01 -cover: /assets/img/activite/soul-xmind.png -head: - - - meta - - name: Blog ---- - -## Plug-in overview - -** Plug-in positioning ** - -The divide plug-in is an HTTP forward proxy plug-in, and all HTTP requests are load balanced by the plug-in (the specific load balancing policy is specified in the rule). - -** Effective time ** - -When the rpcType of the request header is HTTP and the plug-in is enabled, it will match the rules according to the request parameters, and finally be handed over to the downstream plug-in for responsive proxy invocation. - -## Plug-in processing flow - -1. First, review the general process of the request processing plug-in (AbstractSoulPlugin # execute): - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - // Get plugin data - String pluginName = named(); - final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); - if (pluginData != null && pluginData.getEnabled()) { - // Obtain selector data - final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); - ... - // Match selector - final SelectorData selectorData = matchSelector(exchange, selectors); - ... - // Obtain rule data - final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); - ... - // Match rule - RuleData rule; - if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { - //get last - rule = rules.get(rules.size() - 1); - } else { - rule = matchRule(exchange, rules); - } - ... - // Execute custom processing - return doExecute(exchange, chain, selectorData, rule); - } - // Continue executing plugin chain processing - return chain.execute(exchange); -} -``` - -The AbstractSoulPlugin first matches the corresponding selector and rule, and then executes the custom processing of the plug-in if the match is passed. - -2. Take a look at the custom processing flow of the divide plug-in (DividePlugin # doExecute): - -```java -protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - ... - // Prepare rule handling object (internally holds: load balancing algorithm name, retry count, and timeout) - final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); - // Get the list of available services for the selector - final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); - ... - // Select the specific service instance IP to be distributed (load balancing) - final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); - DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); - ... - //Set HTTP URL, timeout, and retry count - String domain = buildDomain(divideUpstream); - String realURL = buildRealURL(domain, soulContext, exchange); - exchange.getAttributes().put(Constants.HTTP_URL, realURL); - exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); - exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); - // Continue executing downstream of the plugin chain - return chain.execute(exchange); -} -``` - -The DividePlugin first obtains the list of available services corresponding to the selector, then performs load balancing to select the target server instance IP to be distributed, and finally sets the final URL, timeout time, and retry times for the downstream of the plug-in chain to process. - -** Notice ** - -The divide plug-in itself is only responsible for selecting the server instance to be distributed according to the selector, rules, and load balancing strategy, and does not directly initiate an HTTP request to the back-end service. - -## Host probe - -As mentioned above, divide needs to obtain the list of services. Take a look at the obtained implementation (UpstreamCacheManager # findUpstreamListBySelectorId): - -```java -public List findUpstreamListBySelectorId(final String selectorId) { - return UPSTREAM_MAP_TEMP.get(selectorId); -} -``` - -The list of surviving services is obtained internally through the UPSTREAM _ MAP _ TEMP. - -Two hash tables are maintained within the Upstream Cache Manager: - -- UPSTREAM_MAP: - - The full service hash table is responsible for storing full upstream service information, where key is the selector ID, and value is the list of services using the same selector. - -- UPSTREAM_MAP_TEMP: - - The temporary service hash table is responsible for storing the upstream service information of the activity, the key is the selector ID, and the value is the service list using the same selector. - -In the previous chapters, we mentioned that the submit method updates the UPSTREAM _ MAP and UPSTREAM _ MAP _ TEMP at the same time during data synchronization, but how to maintain the UPSTREAM _ MAP \_ TEMP when the subsequent service is offline? Everything has to start with IP exploration. - -#### 3.1 Opportunity of exploration - -The time to explore is from the initialization of the Upstream Cache Manager: - -```java -private UpstreamCacheManager() { - // Health check switch check - boolean check = Boolean.parseBoolean(System.getProperty("soul.upstream.check", "false")); - if (check) { - // Start scheduled health check task - new ScheduledThreadPoolExecutor(1, SoulThreadFactory.create("scheduled-upstream-task", false)) - .scheduleWithFixedDelay(this::scheduled, - 30, Integer.parseInt(System.getProperty("soul.upstream.scheduledTime", "30")), TimeUnit.SECONDS); - } -} -``` - -When the Upstream Cache Manager is initialized, if the probe switch is turned on, the timed probe task will be created. Here, it is executed once every 30 seconds by default. - -There are two configuration parameters involved here: - -- Soul. Upstream. Check detection switch: default value is true, and if set to false, it means no detection -- Soul. Upstream. ScheduledTime detection interval, 10 seconds by default - -#### 3.2. Exploration mission - -1. Next, let's look at the implementation of the probe task (Upstream Cache Manager # scheduled): - -```java -private void scheduled() { - if (UPSTREAM_MAP.size() > 0) { - UPSTREAM_MAP.forEach((k, v) -> { - // Perform health check - List result = check(v); - if (result.size() > 0) { - UPSTREAM_MAP_TEMP.put(k, result); - } else { - UPSTREAM_MAP_TEMP.remove(k); - } - }); - } -} -``` - -The task is responsible for traversing and registering the full service hash table one by one and checking the service activity: - -- If the survival number is greater than 0, the survival service hash table is updated -- Otherwise, removing the corresponding content of the survival service hash table - -2. Continue to see the service list liveness check process (Upstream Cache Manager # check): - -```java -private List check(final List upstreamList) { - List resultList = Lists.newArrayListWithCapacity(upstreamList.size()); - for (DivideUpstream divideUpstream : upstreamList) { - // Check service liveness - final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl()); - if (pass) { - // Update service status - if (!divideUpstream.isStatus()) { - divideUpstream.setTimestamp(System.currentTimeMillis()); - divideUpstream.setStatus(true); - ... - } - // Record surviving services - resultList.add(divideUpstream); - } else { - // Update service status - divideUpstream.setStatus(false); - ... - } - } - return resultList; -} -``` - -It is responsible for traversing the service list, checking the activity of each service according to the URL and registering the surviving services. - -#### 3.3 Activity check - -1. Service liveness check implementation (Upstream CheckUtils # checkUrl): - -```java -public static boolean checkUrl(final String url) { - ... - // Check if the URL is in IP + port format - if (checkIP(url)) { - // Process IP and port - String[] hostPort; - if (url.startsWith(HTTP)) { - final String[] http = StringUtils.split(url, "\\/\\/"); - hostPort = StringUtils.split(http[1], Constants.COLONS); - } else { - hostPort = StringUtils.split(url, Constants.COLONS); - } - // Test if the host can be connected - return isHostConnector(hostPort[0], Integer.parseInt(hostPort[1])); - } else { - // Test if the host is reachable - return isHostReachable(url); - } -} -``` - -Check if the URL is in IP + port format: - -- If it is in IP + port format, test whether the host can be connected -- Otherwise, test whether the host is reachable - -2. Test whether the host is connectable (Upstream CheckUtils # isHostConnector): - -```java -private static boolean isHostConnector(final String host, final int port) { - try (Socket socket = new Socket()) { - socket.connect(new InetSocketAddress(host, port)); - } catch (IOException e) { - return false; - } - return true; -} -``` - -Test IP connectivity through socket connection. - -3. Test whether the host is reachable (UpstreamCheckUtils # isHostReachable): - -```java -private static boolean isHostReachable(final String host) { - try { - return InetAddress.getByName(host).isReachable(1000); - } catch (IOException ignored) { - } - return false; -} -``` - -Non IP + port format URL Try to use domain name format to test if the host is reachable. - -On the whole, the server information that the divide plug-in gets from the cache comes from data synchronization and is updated regularly and actively by the probe task. - -## Load balancing - -As mentioned above, divide selects the service IP for final distribution through the load balancing algorithm. Let's take a look at the implementation of load balancing (LoadBalan ceUtils # selector): - -```java -public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) { - LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm); - return loadBalance.select(upstreamList, ip); -} -``` - -Internally, the Extension Loader is used to implement the SPI mechanism, and then the corresponding load balancing algorithm is loaded through the algorithm name to execute the load balancing calculation and finally distribute to the service IP. - -The soul gateway supports three load balancing strategies by default - -- HASH (needs to be calculated, and there may be imbalance) -- RANDOM (simplest and fastest, almost average under a large number of requests) -- ROUND \_ ROBIN (need to record the status, which has a certain impact, and there is not much difference in the results between random and polling under large data volume) - -The default is RANDOM random algorithm, and the algorithm processing is as follows (RandomLoadBalance # doSelect): - -```java -public DivideUpstream doSelect(final List upstreamList, final String ip) { - int totalWeight = calculateTotalWeight(upstreamList); - boolean sameWeight = isAllUpStreamSameWeight(upstreamList); - // If weights are inconsistent, randomize based on total weight - if (totalWeight > 0 && !sameWeight) { - return random(totalWeight, upstreamList); - } - // Randomize based on the number of services - return random(upstreamList); -} -``` - -Judging whether the weights of the services in the service list are consistent: - -- If the weights are not consistent, they will be randomized according to the total weight -- Otherwise, random by number of services - -Randomize details by total weight (RandomLoadBalance # random): - -```java -private DivideUpstream random(final int totalWeight, final List upstreamList) { - // Generate a random number based on the total weight - int offset = RANDOM.nextInt(totalWeight); - // Determine which segment the random value falls into - for (DivideUpstream divideUpstream : upstreamList) { - offset -= getWeight(divideUpstream); - if (offset < 0) { - return divideUpstream; - } - } - return upstreamList.get(0); -} -``` - -## Sum up - -Processing flow of divide plug-in: - -- Get a list of available services - - - The list of services originally came from `soul-admin` data synchronization - - By default, the list of available services is actively updated every 30 seconds. - -- Load balancing - - Load balancing algorithm for loading target by extension loader - - Execute a specific balancing strategy - - Returns a final selection of service information -- Set the URL information of the final service -- To be processed downstream of the plug-in chain +--- +title: Soul Gateway Learning Divide Plugin Source Code Interpretation +author: shenxiangjun +tag: + - Soul +date: 2021-02-01 +cover: /assets/img/activite/soul-xmind.png +head: + - - meta + - name: Blog +--- + +## Plug-in overview + +** Plug-in positioning ** + +The divide plug-in is an HTTP forward proxy plug-in, and all HTTP requests are load balanced by the plug-in (the specific load balancing policy is specified in the rule). + +** Effective time ** + +When the rpcType of the request header is HTTP and the plug-in is enabled, it will match the rules according to the request parameters, and finally be handed over to the downstream plug-in for responsive proxy invocation. + +## Plug-in processing flow + +1. First, review the general process of the request processing plug-in (AbstractSoulPlugin # execute): + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + // Get plugin data + String pluginName = named(); + final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); + if (pluginData != null && pluginData.getEnabled()) { + // Obtain selector data + final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); + ... + // Match selector + final SelectorData selectorData = matchSelector(exchange, selectors); + ... + // Obtain rule data + final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); + ... + // Match rule + RuleData rule; + if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { + //get last + rule = rules.get(rules.size() - 1); + } else { + rule = matchRule(exchange, rules); + } + ... + // Execute custom processing + return doExecute(exchange, chain, selectorData, rule); + } + // Continue executing plugin chain processing + return chain.execute(exchange); +} +``` + +The AbstractSoulPlugin first matches the corresponding selector and rule, and then executes the custom processing of the plug-in if the match is passed. + +2. Take a look at the custom processing flow of the divide plug-in (DividePlugin # doExecute): + +```java +protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + ... + // Prepare rule handling object (internally holds: load balancing algorithm name, retry count, and timeout) + final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); + // Get the list of available services for the selector + final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); + ... + // Select the specific service instance IP to be distributed (load balancing) + final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); + DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); + ... + //Set HTTP URL, timeout, and retry count + String domain = buildDomain(divideUpstream); + String realURL = buildRealURL(domain, soulContext, exchange); + exchange.getAttributes().put(Constants.HTTP_URL, realURL); + exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); + exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); + // Continue executing downstream of the plugin chain + return chain.execute(exchange); +} +``` + +The DividePlugin first obtains the list of available services corresponding to the selector, then performs load balancing to select the target server instance IP to be distributed, and finally sets the final URL, timeout time, and retry times for the downstream of the plug-in chain to process. + +** Notice ** + +The divide plug-in itself is only responsible for selecting the server instance to be distributed according to the selector, rules, and load balancing strategy, and does not directly initiate an HTTP request to the back-end service. + +## Host probe + +As mentioned above, divide needs to obtain the list of services. Take a look at the obtained implementation (UpstreamCacheManager # findUpstreamListBySelectorId): + +```java +public List findUpstreamListBySelectorId(final String selectorId) { + return UPSTREAM_MAP_TEMP.get(selectorId); +} +``` + +The list of surviving services is obtained internally through the UPSTREAM _ MAP _ TEMP. + +Two hash tables are maintained within the Upstream Cache Manager: + +- UPSTREAM_MAP: + + The full service hash table is responsible for storing full upstream service information, where key is the selector ID, and value is the list of services using the same selector. + +- UPSTREAM_MAP_TEMP: + + The temporary service hash table is responsible for storing the upstream service information of the activity, the key is the selector ID, and the value is the service list using the same selector. + +In the previous chapters, we mentioned that the submit method updates the UPSTREAM _ MAP and UPSTREAM _ MAP _ TEMP at the same time during data synchronization, but how to maintain the UPSTREAM _ MAP \_ TEMP when the subsequent service is offline? Everything has to start with IP exploration. + +#### 3.1 Opportunity of exploration + +The time to explore is from the initialization of the Upstream Cache Manager: + +```java +private UpstreamCacheManager() { + // Health check switch check + boolean check = Boolean.parseBoolean(System.getProperty("soul.upstream.check", "false")); + if (check) { + // Start scheduled health check task + new ScheduledThreadPoolExecutor(1, SoulThreadFactory.create("scheduled-upstream-task", false)) + .scheduleWithFixedDelay(this::scheduled, + 30, Integer.parseInt(System.getProperty("soul.upstream.scheduledTime", "30")), TimeUnit.SECONDS); + } +} +``` + +When the Upstream Cache Manager is initialized, if the probe switch is turned on, the timed probe task will be created. Here, it is executed once every 30 seconds by default. + +There are two configuration parameters involved here: + +- Soul. Upstream. Check detection switch: default value is true, and if set to false, it means no detection +- Soul. Upstream. ScheduledTime detection interval, 10 seconds by default + +#### 3.2. Exploration mission + +1. Next, let's look at the implementation of the probe task (Upstream Cache Manager # scheduled): + +```java +private void scheduled() { + if (UPSTREAM_MAP.size() > 0) { + UPSTREAM_MAP.forEach((k, v) -> { + // Perform health check + List result = check(v); + if (result.size() > 0) { + UPSTREAM_MAP_TEMP.put(k, result); + } else { + UPSTREAM_MAP_TEMP.remove(k); + } + }); + } +} +``` + +The task is responsible for traversing and registering the full service hash table one by one and checking the service activity: + +- If the survival number is greater than 0, the survival service hash table is updated +- Otherwise, removing the corresponding content of the survival service hash table + +2. Continue to see the service list liveness check process (Upstream Cache Manager # check): + +```java +private List check(final List upstreamList) { + List resultList = Lists.newArrayListWithCapacity(upstreamList.size()); + for (DivideUpstream divideUpstream : upstreamList) { + // Check service liveness + final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl()); + if (pass) { + // Update service status + if (!divideUpstream.isStatus()) { + divideUpstream.setTimestamp(System.currentTimeMillis()); + divideUpstream.setStatus(true); + ... + } + // Record surviving services + resultList.add(divideUpstream); + } else { + // Update service status + divideUpstream.setStatus(false); + ... + } + } + return resultList; +} +``` + +It is responsible for traversing the service list, checking the activity of each service according to the URL and registering the surviving services. + +#### 3.3 Activity check + +1. Service liveness check implementation (Upstream CheckUtils # checkUrl): + +```java +public static boolean checkUrl(final String url) { + ... + // Check if the URL is in IP + port format + if (checkIP(url)) { + // Process IP and port + String[] hostPort; + if (url.startsWith(HTTP)) { + final String[] http = StringUtils.split(url, "\\/\\/"); + hostPort = StringUtils.split(http[1], Constants.COLONS); + } else { + hostPort = StringUtils.split(url, Constants.COLONS); + } + // Test if the host can be connected + return isHostConnector(hostPort[0], Integer.parseInt(hostPort[1])); + } else { + // Test if the host is reachable + return isHostReachable(url); + } +} +``` + +Check if the URL is in IP + port format: + +- If it is in IP + port format, test whether the host can be connected +- Otherwise, test whether the host is reachable + +2. Test whether the host is connectable (Upstream CheckUtils # isHostConnector): + +```java +private static boolean isHostConnector(final String host, final int port) { + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(host, port)); + } catch (IOException e) { + return false; + } + return true; +} +``` + +Test IP connectivity through socket connection. + +3. Test whether the host is reachable (UpstreamCheckUtils # isHostReachable): + +```java +private static boolean isHostReachable(final String host) { + try { + return InetAddress.getByName(host).isReachable(1000); + } catch (IOException ignored) { + } + return false; +} +``` + +Non IP + port format URL Try to use domain name format to test if the host is reachable. + +On the whole, the server information that the divide plug-in gets from the cache comes from data synchronization and is updated regularly and actively by the probe task. + +## Load balancing + +As mentioned above, divide selects the service IP for final distribution through the load balancing algorithm. Let's take a look at the implementation of load balancing (LoadBalan ceUtils # selector): + +```java +public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) { + LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm); + return loadBalance.select(upstreamList, ip); +} +``` + +Internally, the Extension Loader is used to implement the SPI mechanism, and then the corresponding load balancing algorithm is loaded through the algorithm name to execute the load balancing calculation and finally distribute to the service IP. + +The soul gateway supports three load balancing strategies by default + +- HASH (needs to be calculated, and there may be imbalance) +- RANDOM (simplest and fastest, almost average under a large number of requests) +- ROUND \_ ROBIN (need to record the status, which has a certain impact, and there is not much difference in the results between random and polling under large data volume) + +The default is RANDOM random algorithm, and the algorithm processing is as follows (RandomLoadBalance # doSelect): + +```java +public DivideUpstream doSelect(final List upstreamList, final String ip) { + int totalWeight = calculateTotalWeight(upstreamList); + boolean sameWeight = isAllUpStreamSameWeight(upstreamList); + // If weights are inconsistent, randomize based on total weight + if (totalWeight > 0 && !sameWeight) { + return random(totalWeight, upstreamList); + } + // Randomize based on the number of services + return random(upstreamList); +} +``` + +Judging whether the weights of the services in the service list are consistent: + +- If the weights are not consistent, they will be randomized according to the total weight +- Otherwise, random by number of services + +Randomize details by total weight (RandomLoadBalance # random): + +```java +private DivideUpstream random(final int totalWeight, final List upstreamList) { + // Generate a random number based on the total weight + int offset = RANDOM.nextInt(totalWeight); + // Determine which segment the random value falls into + for (DivideUpstream divideUpstream : upstreamList) { + offset -= getWeight(divideUpstream); + if (offset < 0) { + return divideUpstream; + } + } + return upstreamList.get(0); +} +``` + +## Sum up + +Processing flow of divide plug-in: + +- Get a list of available services + + - The list of services originally came from `soul-admin` data synchronization + - By default, the list of available services is actively updated every 30 seconds. + +- Load balancing + - Load balancing algorithm for loading target by extension loader + - Execute a specific balancing strategy + - Returns a final selection of service information +- Set the URL information of the final service +- To be processed downstream of the plug-in chain diff --git a/src/blog/soul_source_learning_17_http.md b/src/blog/soul_source_learning_17_http.md index 2c0a69d86a..b941f85dea 100644 --- a/src/blog/soul_source_learning_17_http.md +++ b/src/blog/soul_source_learning_17_http.md @@ -1,405 +1,405 @@ ---- -title: Soul Gateway Learning Http Request Adventure -author: baiyu -date: 2021-01-26 -tag: - - Soul -cover: https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d89a3b45058846be94aa0b8935ec1868~tplv-k3u1fbpfcp-watermark.image -head: - - - meta - - name: Blog ---- - -# Review - -In the Soul Request Processing Overview article, we learned that Soul handles requests in ** Excute of Default SoulPluginChain ** a library, where it executes a plug-in chain pattern to complete the request processing. - -We have generally combed the injected ** plugins ** plug-ins, but even so, we still can't see the whole picture. For this reason, we have specially combed the classes involved in the soul plug-ins. The overall results are shown in the following figure. - -![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d9c8e69429e4cb1bcc5bd54ad4f6112~tplv-k3u1fbpfcp-watermark.image) - -As you can see in the teasing article, the core classes are ** SoulPlugin、PluginEnum、PluginDataHandler、MetaDataSubscriber **. In the teasing request related article, we only need to focus on the SoulPlugin and PluginEnum classes at present. - -Now that we have some understanding of the SoulPlugin class, what is the main purpose of the PluginEnum enumeration class? - -PluginEnum: An enumeration class for plug-ins - -| Property | Action | -| -------- | ---------------------------------------------------------------------- | -| code | The smaller the order of plug-in execution, the earlier the execution. | -| role | The role has not found a physical reference address at this time | -| name | Plug-in name | - -In fact, it is not difficult to find that the current ** Plugins for Default SoulPluginChain ** plug-in has a fixed order of execution, so where is the order of execution of this plug-in defined? - -Finally, it can be traced back to the ** SoulConfiguration ** class. - -```java - public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { - // ... - final List soulPlugins = pluginList.stream() - .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); - return new SoulWebHandler(soulPlugins); - } -``` - -Sort out the related references of the entire PluginEnum class, and sort out the following table. It is not difficult to see the order relationship ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d89a3b45058846be94aa0b8935ec1868~tplv-k3u1fbpfcp-watermark.image) between plug-ins. - -| Level | Action | -| ------------- | ------------------------------------------------------------------------------------ | -| Level 1 | Only GlobalPlugin Global Plugin | -| Level 2 to 8 | It can be understood as a pre-processing plug-in before the request is initiated | -| Level 9 to 11 | It can be understood as different call processing for the way of the caller. | -| Level 12 | Only MonitorPlugin monitor plug-in | -| Level 13 | It is a response-related plug-in for processing the results returned by each caller. | - -In the previous review, we have already understood the general process of soul processing requests. - -- 1.GloBalPlugin performs global initialization -- 2.Some plug-ins process the request according to rules such as authentication, current limiting, and fusing -- 3.Select the calling mode suitable for you to assemble the parameters and initiate the call. -- 4.Monitor -- 5.Process the result of the call - -# Request process sorting - -> The following demo code screenshot is from the HTTP demo under soul-examples, and the interface address called is the http://127.0.0.1:9195/http/test/findByUserId?userId=10. - -Bury the point in the ** Excute of Default SoulPluginChain ** method to see what classes an HTTP request call goes through? - -```java -public Mono execute(final ServerWebExchange exchange) { - return Mono.defer(() -> { - if (this.index < plugins.size()) { - SoulPlugin plugin = plugins.get(this.index++); - Boolean skip = plugin.skip(exchange); - if (skip) { - System.out.println("Skipped plugin: "+plugin.getClass().getName().replace("org.dromara.soul.plugin.","")); - return this.execute(exchange); - } - System.out.println("Not skipped plugin: "+plugin.getClass().getName().replace("org.dromara.soul.plugin.","")); - return plugin.execute(exchange, this); - } - return Mono.empty(); - }); - } -``` - -The unskipped plug-ins for the final output are as follows: - -Plug-ins that are not skipped are global. Global Plugin.
-Plug-ins that are not skipped are sign. SignPlugin.
-Plug-ins that are not skipped are WAF. WafPlugin.
-Plug-ins that are not skipped are ratelimiter. Rate LimiterPlugin.
-Plug-ins that are not skipped are hystrix. Hystrix Plugin.
-The plug-in that was not skipped was resilience4j.Resilience4JPlugin
-Plug-ins that are not skipped are divide. DividePlugin.
-Plug-ins that are not skipped are HTTP client. Web ClientPlugin.
-Plug-ins that are not skipped are Alibaba. Dubbo. Param. Body ParamPlugin.
-Plug-ins that are not skipped are monitor. MonitorPlugin.
-Plug-ins that are not skipped are HTTP client. Response. Web ClientResponsePlugin.
- -> Here is a little puzzle, why this Alibaba. Dubbo. Param. BodyParamPlugin plug-in will be executed, temporarily ignored, and tracked later. - -We found that the general flow of the plug-in executed by a gateway call for an HTTP request was consistent with our guess.
-For now, let's just pick the key points, namely ** GlobalPlugin、DividePlugin、WebClientPlugin、WebClientResponsePlugin **. - -Initiate a Debug call to track the actions of each of the four plug-ins in turn. - -## Global Plugin SoulContext Object Wrapper - -The plug-in's excute method for GlobalPlugin looks like this - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - final ServerHttpRequest request = exchange.getRequest(); - final HttpHeaders headers = request.getHeaders(); - final String upgrade = headers.getFirst("Upgrade"); - SoulContext soulContext; - if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) { - soulContext = builder.build(exchange); - } else { - final MultiValueMap queryParams = request.getQueryParams(); - soulContext = transformMap(queryParams); - } - exchange.getAttributes().put(Constants.CONTEXT, soulContext); - return chain.execute(exchange); - } -``` - -It is not difficult to see that the main purpose of the excute method of GlobalPlugin is to encapsulate an ** The SoulContext object ** exchange object and put it into the exchange (the exchange object is a shared object on the entire plug-in chain, and after a plug-in is executed, it is passed to the next plug-in. I understand it as a ThreadLocal-like object. - -So what are the properties of the SoulContext object? - -| property | Meaning | -| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -| module | Each RPCType has a different value for the preceding address of the gateway call when HTTP is called. | -| method | Method name after cutting (when RpcType is HTTP) | -| rpcType | RPC call types include Http, dubbo, sofa, and so on | -| httpMethod | Http calls currently only support get, post, | -| sign | Currently, we do not know the specific function of the authentication related attributes, which may be related to the SignPlugin plug-in. | -| timestamp | Timestamp | -| appKey | Currently, we do not know the specific function of the authentication related attributes, which may be related to the SignPlugin plug-in. | -| path | Path refers to the full path of the call to the soul Gateway (when RpcType is HTTP) | -| contextPath | Consistent with module value (when RPCType is HTTP) | -| realUrl | Consistent with the value of method (when RpcType is HTTP) | -| dubboParams | Parameters for dubbo? | -| startDateTime | The start time is suspected to be combined with the monitoring plug-in and the statistical indicator module. | - -After executing the Global Plugin, the final encapsulated SoulContext object looks like this. ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d34d9e900a1e4448b8f15302db36a5bb~tplv-k3u1fbpfcp-watermark.image) - -The parameter encapsulation of SoulContext of other RPCTypes can be traced by viewing the ** DefaultSoulContext Builder build ** method. Since this article mainly traces HTTP calls, it is not redundant to discuss here. - -## DividePlugin Routing Plugin - -After the GlobalPlugin plug-in is executed, it is finally packaged into one ** The SoulContext object ** and placed in ** ServerWebExchange ** for use by the downstream call chain. - -Next, let's take a look ** Divide Plugin ** at what kind of role it plays in the whole chain call process? - -### AbstractSoulPlugin - -By tracing back to the source code ** The DividePlugin plug-in inherits from the AbstractSoulPlugin class, which implements the SoulPlugin interface **. - -So ** AbstractSoulPlugin ** what extensions have been made? Let's tease out the methods of this class. - -| method | Action | -| -------------------- | ------------------------------------------------------------------------------------------------------------------- | -| excute | Implemented in the SoulPlugin interface, plays a role ** The role of the template approach ** in AbstractSoulPlugin | -| doexcute | Implemented ** Abstract method ** by various subclasses | -| matchSelector | Match selector | -| filterSelector | Filter selector | -| matchRule | Matching rules | -| filterRule | Filter rules | -| handleSelectorIsNull | Handle null selector case | -| handleRuleIsNull | Handle null rule case | -| selectorLog | Selector log printing | -| ruleLog | Rule log printing | - -Look at ** excute ** the specific function of the method. - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - String pluginName = named(); - // Obtain corresponding plugin - final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); - // Check if the plugin is enabled - if (pluginData != null && pluginData.getEnabled()) { - // Obtain all selectors under the plugin - final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); - if (CollectionUtils.isEmpty(selectors)) { - return handleSelectorIsNull(pluginName, exchange, chain); - } - // Match selector - final SelectorData selectorData = matchSelector(exchange, selectors); - if (Objects.isNull(selectorData)) { - return handleSelectorIsNull(pluginName, exchange, chain); - } - // Log selector - selectorLog(selectorData, pluginName); - final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); - if (CollectionUtils.isEmpty(rules)) { - return handleRuleIsNull(pluginName, exchange, chain); - } - RuleData rule; - if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { - rule = rules.get(rules.size() - 1); - } else { - // Match rule - rule = matchRule(exchange, rules); - } - if (Objects.isNull(rule)) { - return handleRu![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f523f655f0014d288b7a4502cc6a08d1~tplv-k3u1fbpfcp-watermark.image)leIsNull(pluginName, exchange, chain); - } - // Log rule - ruleLog(rule, pluginName); - // Execute subclass-specific implementation - return doExecute(exchange, chain, selectorData, rule); - } - return chain.execute(exchange); - } -``` - -The final flow chart is as follows: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1ec8bd02d6546c79a96d67535049aae~tplv-k3u1fbpfcp-watermark.image) - -PS: In the above flow chart, there is no specific method-level processing. - -However, there are still several points that need to be explained: - -- 1.The plug-in data, selector data, and rule data are all obtained from ** BaseDataCache **. This class is the class that will be affected in the data synchronization process. -- 2.Selector type. When the SpringMvc project is used to register an interface, an isFull option will be set to true to represent the global proxy. In the global proxy mode, only one selector \ rule (referring to all interfaces of the proxy) will be registered, so the corresponding processing here is rule. Size () -1. -- 3.For the selection of selector and rule, the actual processing is much more complicated. Considering that it is to introduce the general logic of a request process, it will not be elaborated here. If you are interested, you can check it ** Match Strategy, AbstractMatchStrategy and their related implementation classes ** (a separate article will be explained later). The corresponding page here is as follows: ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f624b13f205a44e29b2799718433e0c9~tplv-k3u1fbpfcp-watermark.image)![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f590c2cf336442f08a52b864c81d41a8~tplv-k3u1fbpfcp-watermark.image) - -To sort out ** Exeute method of AbstractSoulPlugin ** the function, after the guidance of the above flow chart, we already know that the function of this method is to select the plug-in -- > select the selector -- > select the rule, and finally hand over to the method of the ** doexcute ** subclass. - -Next, let's take a ** The doexcute of DividePlugin ** look at what the method does. - -### DividePlugin - -```java -protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - // Obtain rule handling data - final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); - // Obtain injected addresses under this selector - final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); - if (CollectionUtils.isEmpty(upstreamList)) { - log.error("divide upstream configuration error: {}", rule.toString()); - Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); - // Choose an address based on the load balancing strategy corresponding to the rule - DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); - if (Objects.isNull(divideUpstream)) { - log.error("divide has no upstream"); - Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // set the http url - String domain = buildDomain(divideUpstream); - // Assemble the real calling address - String realURL = buildRealURL(domain, soulContext, exchange); - exchange.getAttributes().put(Constants.HTTP_URL, realURL); - // Set timeout and retry count - exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); - exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); - return chain.execute(exchange); - } -``` - -After the above code is sorted out, the general logic is as follows: - -- 1.Obtain the registration address corresponding to the selector, and the corresponding page data is as follows ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bea039b5c98040ee80433f785dac85aa~tplv-k3u1fbpfcp-watermark.image) -- 2.Obtain the load balancing policy according to the handle field of the rule, and select the real call address (** LoadBalanceUtils **), retry times and timeout time. The corresponding page data is as follows. ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07db1f8f76fc43b2aec61ee0f9ca4c05~tplv-k3u1fbpfcp-watermark.image) -- 3.The real call address, timeout, and number of retries are passed to ** ServerWebExchange ** for use by the downstream call chain. Demo of debug: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0aa5f7d95f2942169b33029f074f1712~tplv-k3u1fbpfcp-watermark.image) PS: We don't see where the parameters are in the above theme logic? Where is this parameter encapsulated? The answer ** In the build RealURL method ** is obtained from ** exchange ** the context. - -## Web ClientPlugin Http request calling plug-in - -Next, let's look at how Soul initiates the request call. - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - // Obtain the real address - String urlPath = exchange.getAttribute(Constants.HTTP_URL); - if (StringUtils.isEmpty(urlPath)) { - Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // Obtain the timeout period - long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L); - // Obtain the retry count - int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0); - log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes); - HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue()); - WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath); - return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain); - } -``` - -In the web Client ** excute ** method, three things are done - -- 1.Take out the properties you put into exchange from the Divide plugin. ** Real address of the call, timeout, number of retries **. -- 2.Encapsulates an ** RequestBodySpec ** object (something that doesn't recognize responsive programming) -- 3.A ** handleRequestBody ** method was called - -Know ** handleRequestBody ** the method first - -```java -private Mono handleRequestBody(final WebClient.RequestBodySpec requestBodySpec, - final ServerWebExchange exchange, - final long timeout, - final int retryTimes, - final SoulPluginChain chain) { - return requestBodySpec.headers(httpHeaders -> { - httpHeaders.addAll(exchange.getRequest().getHeaders()); - httpHeaders.remove(HttpHeaders.HOST); - }) - .contentType(buildMediaType(exchange)) - .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody())) - .exchange() - // Log on failure - .doOnError(e -> log.error(e.getMessage())) - // Set timeout - .timeout(Duration.ofMillis(timeout)) - // Set request retry strategy - .retryWhen(Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException) - .retryMax(retryTimes) - .backoff(Backoff.exponential(Duration.ofMillis(200), Duration.ofSeconds(20), 2, true))) - // Handle after request completes - .flatMap(e -> doNext(e, exchange, chain)); - - } -``` - -In this method, it can be generally understood as - -- The request header from exchange is placed in the request header for this call. -- Set the contentType -- Set the timeout -- Set the failure response -- Set the retry scenario and retry times -- Processing of final results. Need to see another one ** The doNext method ** in the process - -The general logic is to determine whether the request is successful or not, and put the result of the request into exchange for the downstream plug-in to process. - -```java -private Mono doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) { - if (res.statusCode().is2xxSuccessful()) { - exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); - } else { - exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.ERROR.getName()); - } - exchange.getAttributes().put(Constants.CLIENT_RESPONSE_ATTR, res); - return chain.execute(exchange); - } -``` - -PS: Although we don't understand responsive programming, it doesn't affect us to read the code. - -## Web ClientResponsePlugin Http Result Processing Plug-in - -The excute method of this implementation has no core logic, which is to judge the status code of the request and return different data formats to the front end according to the status code. - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - return chain.execute(exchange).then(Mono.defer(() -> { - ServerHttpResponse response = exchange.getResponse(); - ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR); - if (Objects.isNull(clientResponse) - || response.getStatusCode() == HttpStatus.BAD_GATEWAY - || response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) { - Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { - Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - response.setStatusCode(clientResponse.statusCode()); - response.getCookies().putAll(clientResponse.cookies()); - response.getHeaders().putAll(clientResponse.headers().asHttpHeaders()); - return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())); - })); - } -``` - -# Sum up - -At this point, an Http request call based on the Soul gateway is largely over. - -Combing HTTP request call flow - -- Global Plugin encapsulates the Soul Context object -- The front plug-in handles operations such as fusing and current-limiting authentication. -- The Divide plug-in selects the real address of the corresponding call, the number of retries, and the timeout period. -- The Web Client plug-in makes the actual Http call -- The Web ClientResponse plug-in processes the corresponding result and returns to the foreground. - -Based on the general flow of Http calls, we can roughly guess that the flow based on other RPC calls is to replace the plug-in that initiates the request and the plug-in that returns the result processing. - -In the above, we also mentioned the selection ** LoadBalanceUtils ** of routing rules, selectors and the processing ** MatchStrategy ** of rules. - -After that, a new chapter will be opened to unveil the mystery of RPC generalization call, routing, selector and rule matching step by step. +--- +title: Soul Gateway Learning Http Request Adventure +author: baiyu +date: 2021-01-26 +tag: + - Soul +cover: https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d89a3b45058846be94aa0b8935ec1868~tplv-k3u1fbpfcp-watermark.image +head: + - - meta + - name: Blog +--- + +# Review + +In the Soul Request Processing Overview article, we learned that Soul handles requests in ** Excute of Default SoulPluginChain ** a library, where it executes a plug-in chain pattern to complete the request processing. + +We have generally combed the injected ** plugins ** plug-ins, but even so, we still can't see the whole picture. For this reason, we have specially combed the classes involved in the soul plug-ins. The overall results are shown in the following figure. + +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d9c8e69429e4cb1bcc5bd54ad4f6112~tplv-k3u1fbpfcp-watermark.image) + +As you can see in the teasing article, the core classes are ** SoulPlugin、PluginEnum、PluginDataHandler、MetaDataSubscriber **. In the teasing request related article, we only need to focus on the SoulPlugin and PluginEnum classes at present. + +Now that we have some understanding of the SoulPlugin class, what is the main purpose of the PluginEnum enumeration class? + +PluginEnum: An enumeration class for plug-ins + +| Property | Action | +| -------- | ---------------------------------------------------------------------- | +| code | The smaller the order of plug-in execution, the earlier the execution. | +| role | The role has not found a physical reference address at this time | +| name | Plug-in name | + +In fact, it is not difficult to find that the current ** Plugins for Default SoulPluginChain ** plug-in has a fixed order of execution, so where is the order of execution of this plug-in defined? + +Finally, it can be traced back to the ** SoulConfiguration ** class. + +```java + public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { + // ... + final List soulPlugins = pluginList.stream() + .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); + return new SoulWebHandler(soulPlugins); + } +``` + +Sort out the related references of the entire PluginEnum class, and sort out the following table. It is not difficult to see the order relationship ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d89a3b45058846be94aa0b8935ec1868~tplv-k3u1fbpfcp-watermark.image) between plug-ins. + +| Level | Action | +| ------------- | ------------------------------------------------------------------------------------ | +| Level 1 | Only GlobalPlugin Global Plugin | +| Level 2 to 8 | It can be understood as a pre-processing plug-in before the request is initiated | +| Level 9 to 11 | It can be understood as different call processing for the way of the caller. | +| Level 12 | Only MonitorPlugin monitor plug-in | +| Level 13 | It is a response-related plug-in for processing the results returned by each caller. | + +In the previous review, we have already understood the general process of soul processing requests. + +- 1.GloBalPlugin performs global initialization +- 2.Some plug-ins process the request according to rules such as authentication, current limiting, and fusing +- 3.Select the calling mode suitable for you to assemble the parameters and initiate the call. +- 4.Monitor +- 5.Process the result of the call + +# Request process sorting + +> The following demo code screenshot is from the HTTP demo under soul-examples, and the interface address called is the http://127.0.0.1:9195/http/test/findByUserId?userId=10. + +Bury the point in the ** Excute of Default SoulPluginChain ** method to see what classes an HTTP request call goes through? + +```java +public Mono execute(final ServerWebExchange exchange) { + return Mono.defer(() -> { + if (this.index < plugins.size()) { + SoulPlugin plugin = plugins.get(this.index++); + Boolean skip = plugin.skip(exchange); + if (skip) { + System.out.println("Skipped plugin: "+plugin.getClass().getName().replace("org.dromara.soul.plugin.","")); + return this.execute(exchange); + } + System.out.println("Not skipped plugin: "+plugin.getClass().getName().replace("org.dromara.soul.plugin.","")); + return plugin.execute(exchange, this); + } + return Mono.empty(); + }); + } +``` + +The unskipped plug-ins for the final output are as follows: + +Plug-ins that are not skipped are global. Global Plugin.
+Plug-ins that are not skipped are sign. SignPlugin.
+Plug-ins that are not skipped are WAF. WafPlugin.
+Plug-ins that are not skipped are ratelimiter. Rate LimiterPlugin.
+Plug-ins that are not skipped are hystrix. Hystrix Plugin.
+The plug-in that was not skipped was resilience4j.Resilience4JPlugin
+Plug-ins that are not skipped are divide. DividePlugin.
+Plug-ins that are not skipped are HTTP client. Web ClientPlugin.
+Plug-ins that are not skipped are Alibaba. Dubbo. Param. Body ParamPlugin.
+Plug-ins that are not skipped are monitor. MonitorPlugin.
+Plug-ins that are not skipped are HTTP client. Response. Web ClientResponsePlugin.
+ +> Here is a little puzzle, why this Alibaba. Dubbo. Param. BodyParamPlugin plug-in will be executed, temporarily ignored, and tracked later. + +We found that the general flow of the plug-in executed by a gateway call for an HTTP request was consistent with our guess.
+For now, let's just pick the key points, namely ** GlobalPlugin、DividePlugin、WebClientPlugin、WebClientResponsePlugin **. + +Initiate a Debug call to track the actions of each of the four plug-ins in turn. + +## Global Plugin SoulContext Object Wrapper + +The plug-in's excute method for GlobalPlugin looks like this + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + final ServerHttpRequest request = exchange.getRequest(); + final HttpHeaders headers = request.getHeaders(); + final String upgrade = headers.getFirst("Upgrade"); + SoulContext soulContext; + if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) { + soulContext = builder.build(exchange); + } else { + final MultiValueMap queryParams = request.getQueryParams(); + soulContext = transformMap(queryParams); + } + exchange.getAttributes().put(Constants.CONTEXT, soulContext); + return chain.execute(exchange); + } +``` + +It is not difficult to see that the main purpose of the excute method of GlobalPlugin is to encapsulate an ** The SoulContext object ** exchange object and put it into the exchange (the exchange object is a shared object on the entire plug-in chain, and after a plug-in is executed, it is passed to the next plug-in. I understand it as a ThreadLocal-like object. + +So what are the properties of the SoulContext object? + +| property | Meaning | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| module | Each RPCType has a different value for the preceding address of the gateway call when HTTP is called. | +| method | Method name after cutting (when RpcType is HTTP) | +| rpcType | RPC call types include Http, dubbo, sofa, and so on | +| httpMethod | Http calls currently only support get, post, | +| sign | Currently, we do not know the specific function of the authentication related attributes, which may be related to the SignPlugin plug-in. | +| timestamp | Timestamp | +| appKey | Currently, we do not know the specific function of the authentication related attributes, which may be related to the SignPlugin plug-in. | +| path | Path refers to the full path of the call to the soul Gateway (when RpcType is HTTP) | +| contextPath | Consistent with module value (when RPCType is HTTP) | +| realUrl | Consistent with the value of method (when RpcType is HTTP) | +| dubboParams | Parameters for dubbo? | +| startDateTime | The start time is suspected to be combined with the monitoring plug-in and the statistical indicator module. | + +After executing the Global Plugin, the final encapsulated SoulContext object looks like this. ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d34d9e900a1e4448b8f15302db36a5bb~tplv-k3u1fbpfcp-watermark.image) + +The parameter encapsulation of SoulContext of other RPCTypes can be traced by viewing the ** DefaultSoulContext Builder build ** method. Since this article mainly traces HTTP calls, it is not redundant to discuss here. + +## DividePlugin Routing Plugin + +After the GlobalPlugin plug-in is executed, it is finally packaged into one ** The SoulContext object ** and placed in ** ServerWebExchange ** for use by the downstream call chain. + +Next, let's take a look ** Divide Plugin ** at what kind of role it plays in the whole chain call process? + +### AbstractSoulPlugin + +By tracing back to the source code ** The DividePlugin plug-in inherits from the AbstractSoulPlugin class, which implements the SoulPlugin interface **. + +So ** AbstractSoulPlugin ** what extensions have been made? Let's tease out the methods of this class. + +| method | Action | +| -------------------- | ------------------------------------------------------------------------------------------------------------------- | +| excute | Implemented in the SoulPlugin interface, plays a role ** The role of the template approach ** in AbstractSoulPlugin | +| doexcute | Implemented ** Abstract method ** by various subclasses | +| matchSelector | Match selector | +| filterSelector | Filter selector | +| matchRule | Matching rules | +| filterRule | Filter rules | +| handleSelectorIsNull | Handle null selector case | +| handleRuleIsNull | Handle null rule case | +| selectorLog | Selector log printing | +| ruleLog | Rule log printing | + +Look at ** excute ** the specific function of the method. + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + String pluginName = named(); + // Obtain corresponding plugin + final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); + // Check if the plugin is enabled + if (pluginData != null && pluginData.getEnabled()) { + // Obtain all selectors under the plugin + final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); + if (CollectionUtils.isEmpty(selectors)) { + return handleSelectorIsNull(pluginName, exchange, chain); + } + // Match selector + final SelectorData selectorData = matchSelector(exchange, selectors); + if (Objects.isNull(selectorData)) { + return handleSelectorIsNull(pluginName, exchange, chain); + } + // Log selector + selectorLog(selectorData, pluginName); + final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); + if (CollectionUtils.isEmpty(rules)) { + return handleRuleIsNull(pluginName, exchange, chain); + } + RuleData rule; + if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { + rule = rules.get(rules.size() - 1); + } else { + // Match rule + rule = matchRule(exchange, rules); + } + if (Objects.isNull(rule)) { + return handleRu![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f523f655f0014d288b7a4502cc6a08d1~tplv-k3u1fbpfcp-watermark.image)leIsNull(pluginName, exchange, chain); + } + // Log rule + ruleLog(rule, pluginName); + // Execute subclass-specific implementation + return doExecute(exchange, chain, selectorData, rule); + } + return chain.execute(exchange); + } +``` + +The final flow chart is as follows: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1ec8bd02d6546c79a96d67535049aae~tplv-k3u1fbpfcp-watermark.image) + +PS: In the above flow chart, there is no specific method-level processing. + +However, there are still several points that need to be explained: + +- 1.The plug-in data, selector data, and rule data are all obtained from ** BaseDataCache **. This class is the class that will be affected in the data synchronization process. +- 2.Selector type. When the SpringMvc project is used to register an interface, an isFull option will be set to true to represent the global proxy. In the global proxy mode, only one selector \ rule (referring to all interfaces of the proxy) will be registered, so the corresponding processing here is rule. Size () -1. +- 3.For the selection of selector and rule, the actual processing is much more complicated. Considering that it is to introduce the general logic of a request process, it will not be elaborated here. If you are interested, you can check it ** Match Strategy, AbstractMatchStrategy and their related implementation classes ** (a separate article will be explained later). The corresponding page here is as follows: ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f624b13f205a44e29b2799718433e0c9~tplv-k3u1fbpfcp-watermark.image)![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f590c2cf336442f08a52b864c81d41a8~tplv-k3u1fbpfcp-watermark.image) + +To sort out ** Exeute method of AbstractSoulPlugin ** the function, after the guidance of the above flow chart, we already know that the function of this method is to select the plug-in -- > select the selector -- > select the rule, and finally hand over to the method of the ** doexcute ** subclass. + +Next, let's take a ** The doexcute of DividePlugin ** look at what the method does. + +### DividePlugin + +```java +protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + // Obtain rule handling data + final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); + // Obtain injected addresses under this selector + final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); + if (CollectionUtils.isEmpty(upstreamList)) { + log.error("divide upstream configuration error: {}", rule.toString()); + Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); + // Choose an address based on the load balancing strategy corresponding to the rule + DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); + if (Objects.isNull(divideUpstream)) { + log.error("divide has no upstream"); + Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // set the http url + String domain = buildDomain(divideUpstream); + // Assemble the real calling address + String realURL = buildRealURL(domain, soulContext, exchange); + exchange.getAttributes().put(Constants.HTTP_URL, realURL); + // Set timeout and retry count + exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); + exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); + return chain.execute(exchange); + } +``` + +After the above code is sorted out, the general logic is as follows: + +- 1.Obtain the registration address corresponding to the selector, and the corresponding page data is as follows ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bea039b5c98040ee80433f785dac85aa~tplv-k3u1fbpfcp-watermark.image) +- 2.Obtain the load balancing policy according to the handle field of the rule, and select the real call address (** LoadBalanceUtils **), retry times and timeout time. The corresponding page data is as follows. ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07db1f8f76fc43b2aec61ee0f9ca4c05~tplv-k3u1fbpfcp-watermark.image) +- 3.The real call address, timeout, and number of retries are passed to ** ServerWebExchange ** for use by the downstream call chain. Demo of debug: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0aa5f7d95f2942169b33029f074f1712~tplv-k3u1fbpfcp-watermark.image) PS: We don't see where the parameters are in the above theme logic? Where is this parameter encapsulated? The answer ** In the build RealURL method ** is obtained from ** exchange ** the context. + +## Web ClientPlugin Http request calling plug-in + +Next, let's look at how Soul initiates the request call. + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + // Obtain the real address + String urlPath = exchange.getAttribute(Constants.HTTP_URL); + if (StringUtils.isEmpty(urlPath)) { + Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // Obtain the timeout period + long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L); + // Obtain the retry count + int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0); + log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes); + HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue()); + WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath); + return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain); + } +``` + +In the web Client ** excute ** method, three things are done + +- 1.Take out the properties you put into exchange from the Divide plugin. ** Real address of the call, timeout, number of retries **. +- 2.Encapsulates an ** RequestBodySpec ** object (something that doesn't recognize responsive programming) +- 3.A ** handleRequestBody ** method was called + +Know ** handleRequestBody ** the method first + +```java +private Mono handleRequestBody(final WebClient.RequestBodySpec requestBodySpec, + final ServerWebExchange exchange, + final long timeout, + final int retryTimes, + final SoulPluginChain chain) { + return requestBodySpec.headers(httpHeaders -> { + httpHeaders.addAll(exchange.getRequest().getHeaders()); + httpHeaders.remove(HttpHeaders.HOST); + }) + .contentType(buildMediaType(exchange)) + .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody())) + .exchange() + // Log on failure + .doOnError(e -> log.error(e.getMessage())) + // Set timeout + .timeout(Duration.ofMillis(timeout)) + // Set request retry strategy + .retryWhen(Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException) + .retryMax(retryTimes) + .backoff(Backoff.exponential(Duration.ofMillis(200), Duration.ofSeconds(20), 2, true))) + // Handle after request completes + .flatMap(e -> doNext(e, exchange, chain)); + + } +``` + +In this method, it can be generally understood as + +- The request header from exchange is placed in the request header for this call. +- Set the contentType +- Set the timeout +- Set the failure response +- Set the retry scenario and retry times +- Processing of final results. Need to see another one ** The doNext method ** in the process + +The general logic is to determine whether the request is successful or not, and put the result of the request into exchange for the downstream plug-in to process. + +```java +private Mono doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) { + if (res.statusCode().is2xxSuccessful()) { + exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); + } else { + exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.ERROR.getName()); + } + exchange.getAttributes().put(Constants.CLIENT_RESPONSE_ATTR, res); + return chain.execute(exchange); + } +``` + +PS: Although we don't understand responsive programming, it doesn't affect us to read the code. + +## Web ClientResponsePlugin Http Result Processing Plug-in + +The excute method of this implementation has no core logic, which is to judge the status code of the request and return different data formats to the front end according to the status code. + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + return chain.execute(exchange).then(Mono.defer(() -> { + ServerHttpResponse response = exchange.getResponse(); + ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR); + if (Objects.isNull(clientResponse) + || response.getStatusCode() == HttpStatus.BAD_GATEWAY + || response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) { + Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { + Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + response.setStatusCode(clientResponse.statusCode()); + response.getCookies().putAll(clientResponse.cookies()); + response.getHeaders().putAll(clientResponse.headers().asHttpHeaders()); + return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())); + })); + } +``` + +# Sum up + +At this point, an Http request call based on the Soul gateway is largely over. + +Combing HTTP request call flow + +- Global Plugin encapsulates the Soul Context object +- The front plug-in handles operations such as fusing and current-limiting authentication. +- The Divide plug-in selects the real address of the corresponding call, the number of retries, and the timeout period. +- The Web Client plug-in makes the actual Http call +- The Web ClientResponse plug-in processes the corresponding result and returns to the foreground. + +Based on the general flow of Http calls, we can roughly guess that the flow based on other RPC calls is to replace the plug-in that initiates the request and the plug-in that returns the result processing. + +In the above, we also mentioned the selection ** LoadBalanceUtils ** of routing rules, selectors and the processing ** MatchStrategy ** of rules. + +After that, a new chapter will be opened to unveil the mystery of RPC generalization call, routing, selector and rule matching step by step. diff --git a/src/blog/soul_source_learning_18_ratelimiter.md b/src/blog/soul_source_learning_18_ratelimiter.md index 61cd520d3f..2a92660ff7 100644 --- a/src/blog/soul_source_learning_18_ratelimiter.md +++ b/src/blog/soul_source_learning_18_ratelimiter.md @@ -1,237 +1,237 @@ ---- -title: Soul Gateway Learning RateLimiter Plugin -author: baiyu -date: 2021-01-30 -tag: - - Soul -cover: https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9434447ebc674f58b65c26b65f855181~tplv-k3u1fbpfcp-watermark.image -head: - - - meta - - name: Blog ---- - -## Review - -In the previous article on HTTP requests, the processing flow of Soul plug-ins was generally combed, and the specific functions of DividePlugin, GlobalPlugin, WebClientPlugin and WebCilent ResponsePlugin plug-ins were also learned. In the process of sorting out, it is found that there ** Order of precedence ** are Soul plug-ins, and many pre-plug-in operations have been done before the DividePlugin plug-in, which includes the topics ** Rate LimiterPlugin ** we analyzed in this chapter (one of them). - -## Learn to use - -### Read the official documents to have a general understanding of it. - -The rate Limiter plug-in - -Through the reading of official documents, we know the ** RateLimiterPlugin ** two core points ** Speed, capacity **. - -The following explanation comes from the official document. - -- Capacity: is the maximum number of requests a user is allowed to execute in one second. This is the number of tokens the token bucket can hold. -- Rate: This is how many requests per second you allow the user to execute and any requests that are dropped. This is the fill rate of the token bucket. - -It can be seen that ** RateLimiterPlugin ** the core of current limiting lies in ** Token bucket algorithm ** its implementation. - -PS: There are four common implementations ** Token bucket algorithm ** of the current limiting algorithm, ** Funnel algorithm **, ** Counter (fixed window) algorithm **, ** Sliding window algorithm **. See the corresponding blog introduction for details. - -### Initial use - -#### Enable the corresponding plug-in - -At the Soul gateway ** System Management-Plug-in Management **, change the status to the enabled status. Note that redis related configuration needs to be filled in here. The Soul token bucket is based on redis. - -Why is Soul's token bucket algorithm based on redis? - -In the case of cluster deployment, the token bucket algorithm of a single machine can not meet the current limiting function in the cluster state. - -![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e25dd524c294b4f9c227e3f2127757f~tplv-k3u1fbpfcp-watermark.image) - -#### Add current limit selectors, rules, - -At the Soul Gateway ** List of plug-ins **, select rate \_ limiter to add the rule and selector configuration. If you don't know how to add it, you can read the matching logic of the selector \ rule first. ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9cbbc63ed6214aeda8c70f8e34d7c19c~tplv-k3u1fbpfcp-watermark.image)![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/25e67268dd5e4aa9a081a51963a03da8~tplv-k3u1fbpfcp-watermark.image) The capacity and rate added here are both 1, mainly to verify whether the plug-in is enabled. - -#### Interface corresponding access - -Call *http://127.0.0.1:9195/http/test/findByUserId?userId=10* to access. When the rate is higher than 1, the following interface returns the result, which means the plug-in is successfully used. - -```json -{ - "code": 429, - "message": "You have been restricted, please try again later!", - "data": null -} -``` - -### Source Code Reading Read the source code with questions - -#### How to ensure that the redis configuration takes effect immediately after the page is modified, and the corresponding redis connection in the background is changed immediately. - -The answer is that natural data synchronization is closely related. - -When modifying the configuration of the plug-in, an event notification of plug-in data change is also issued. When combing the overall process of Soul Gateway synchronization data, it has been known that the modified plug-in data not only changes the data in the JVM cache, but also distributes the corresponding plug-in. As shown in the following figure ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9434447ebc674f58b65c26b65f855181~tplv-k3u1fbpfcp-watermark.image), for ** RateLimiterPlugin ** the interface that is mainly implemented ** handlePlugin **, what exactly does this corresponding implementation do? - -The specific method is as follows ** Rate LimiterPluginData Handler handlerPlugin **. - -```java -public void handlerPlugin(final PluginData pluginData) { - if (Objects.nonNull(pluginData) && pluginData.getEnabled()) { - // Load rate limiting plugin configuration - RateLimiterConfig rateLimiterConfig = GsonUtils.getInstance().fromJson(pluginData.getConfig(), RateLimiterConfig.class); - // Check if Redis connection value needs to be reloaded - if (Objects.isNull(Singleton.INST.get(ReactiveRedisTemplate.class)) - || Objects.isNull(Singleton.INST.get(RateLimiterConfig.class)) - || !rateLimiterConfig.equals(Singleton.INST.get(RateLimiterConfig.class))) { - LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(rateLimiterConfig); - lettuceConnectionFactory.afterPropertiesSet(); - RedisSerializer serializer = new StringRedisSerializer(); - RedisSerializationContext serializationContext = - RedisSerializationContext.newSerializationContext().key(serializer).value(serializer).hashKey(serializer).hashValue(serializer).build(); - ReactiveRedisTemplate reactiveRedisTemplate = new ReactiveRedisTemplate<>(lettuceConnectionFactory, serializationContext); - Singleton.INST.single(ReactiveRedisTemplate.class, reactiveRedisTemplate); - Singleton.INST.single(RateLimiterConfig.class, rateLimiterConfig); - } - } - } -``` - -There are several key points in the above code: - -In the above code, the configuration of the current limiting plug-in and the corresponding redisTemplate instance are put into the corresponding map of the Singleton. INST. - -When the plug-in data is received, judging whether a redis connection instance and a current limiting configuration instance exist, judging whether the current current limiting configuration instance is consistent with the transmitted current limiting instance, if not, considering that the configuration is changed, and re-initializing the current limiting instance and the connection pool instance to be put into the map of the Singleton. INST, Hot deployment of changes to the redis configuration is thus guaranteed. - -The code in the if judgment is encapsulated into a corresponding redis connection pool based on SpringDataRedis. - -The PS: Singleton. INST is a singleton pattern implemented by enumeration. - -### How is the current limiting plug-in implemented at the bottom? - -#### Debug call chain - -** RateLimiterPlugin ** Because of the need to limit the flow of specific rules, it is still implemented ** AbstractSoulPlugin **, and the methods and functions that have been ** An excute of AbstractSoulPlugin ** combed before are still used, so I will not repeat the explanation here. You can watch the Http call process . Deepen the impression of this class. - -The focus of this section is to see what specific ** doexcute ** methods have done. - -```java - protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final String handle = rule.getHandle(); - final RateLimiterHandle limiterHandle = GsonUtils.getInstance().fromJson(handle, RateLimiterHandle.class); - return redisRateLimiter.isAllowed(rule.getId(), limiterHandle.getReplenishRate(), limiterHandle.getBurstCapacity()) - .flatMap(response -> { - if (!response.isAllowed()) { - // Return error message with 429 error code - exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); - Object error = SoulResultWrap.error(SoulResultEnum.TOO_MANY_REQUESTS.getCode(), SoulResultEnum.TOO_MANY_REQUESTS.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - return chain.execute(exchange); - }); - } -``` - -In the above code, it can be seen that whether the token is successfully obtained is judged by ** redisRateLimiter.isAllowed **. The method is as follows - -```java - public Mono isAllowed(final String id, final double replenishRate, final double burstCapacity) { - if (!this.initialized.get()) { - throw new IllegalStateException("RedisRateLimiter is not initialized"); - } - // Get the Redis key - List keys = getKeys(id); - //The parameters required for encapsulating the execution of a Lua script. The first parameter is the rate, the second parameter is the capacity, the third parameter is the current 10-digit timestamp, and the fourth parameter is a fixed value of 1, which represents the number of tokens requested. - List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", Instant.now().getEpochSecond() + "", "1"); - //Execute the Lua script - Flux> resultFlux = Singleton.INST.get(ReactiveRedisTemplate.class).execute(this.script, keys, scriptArgs); - return resultFlux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))) - .reduce(new ArrayList(), (longs, l) -> { - longs.addAll(l); - return longs; - }).map(results -> { - //'allowed' indicates the execution result (1 for success) - boolean allowed = results.get(0) == 1L; - Long tokensLeft = results.get(1); - RateLimiterResponse rateLimiterResponse = new RateLimiterResponse(allowed, tokensLeft); - log.info("RateLimiter response:{}", rateLimiterResponse.toString()); - return rateLimiterResponse; - }).doOnError(throwable -> log.error("Error determining if user allowed from redis:{}", throwable.getMessage())); - } -``` - -#### Method get Keys (ID) - -This method is to obtain the keys that redis needs to operate. Two types of keys are obtained in the following format: - -![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55ce72f4e044405fbd3b1461905072f2~tplv-k3u1fbpfcp-watermark.image) - -The particularly long number in the middle is ** Rule ID **, because the smallest granularity of the current limit is the rule. - -The first timestamp record I ** Timestamp of the last call ** - -The second token records that ** The number of tokens remaining after the last call was completed ** - -#### execute(this.script, keys, scriptArgs) - -Executing the Lua script keys passes the return value of getKeys (ID), and scriptArgs passes the required parameters - -By reading the above code, we know that the specific implementation of the current limit rule is handed over to the specific Lua script. - -PS: It needs to be reminded here that the current limiting algorithm is a token bucket algorithm. There are two general implementations of the token bucket algorithm. One is that a thread continuously generates tokens. When a request comes in, it first obtains tokens from the corresponding queue. However, this token generation method will consume a lot of performance when the threshold is set to be particularly large. Therefore, there is a second token bucket algorithm, The number of tokens is calculated in real time as they are acquired, and soul is based on the second implementation. - -#### Analysis of Lua Current Limiting Algorithm - -```lua --- Key to store the remaining token count for the current rule -local tokens_key = KEYS[1] --- Key for the timestamp of the last call for the current rule -local timestamp_key = KEYS[2] - --- Rate -local rate = tonumber(ARGV[1]) --- Capacity -local capacity = tonumber(ARGV[2]) --- Current timestamp -local now = tonumber(ARGV[3]) --- Value is 1 -local requested = tonumber(ARGV[4]) --- Calculate fill time by dividing capacity by rate -local fill_time = capacity/rate --- Calculate TTL by rounding down fill time * 2 -local ttl = math.floor(fill_time*2) - --- Get the current token count -local last_tokens = tonumber(redis.call("get", tokens_key)) -if last_tokens == nil then --- Set token count to the configured capacity if it's not present - last_tokens = capacity -end --- Get the timestamp of the last call -local last_refreshed = tonumber(redis.call("get", timestamp_key)) -if last_refreshed == nil then - last_refreshed = 0 -end --- Calculate the time difference between the last call and the current call -local delta = math.max(0, now-last_refreshed) --- Calculate the current remaining token count -local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) --- Check if there are enough tokens (at least 1) available -local allowed = filled_tokens >= requested -local new_tokens = filled_tokens -local allowed_num = 0 -if allowed then - -- Consume one token - new_tokens = filled_tokens - requested - allowed_num = 1 -end - --- Use setex to set the key's TTL and new value -redis.call("setex", tokens_key, ttl, new_tokens) -redis.call("setex", timestamp_key, ttl, now) - -return { allowed_num, new_tokens } -``` - -It is recommended to understand the role of Lua ** KEYS ARGS ** and the understanding of keys [1] and argv [1] in redis Lua. - -The overall logic of the Lua code is still very clear, and I can't explain it in detail here. The code comments have been completed. - -I have two doubts here. - -- Is the calculation of the ** ttl ** parameter multiplied by 2 for fear that it is not an integer? , so the \ \* 2 take the minimum operation? -- Is last _ tokens + (\* Rate), where the delta parameter is the subtraction of two ten-bit timestamps, but the rate is generated in seconds, shouldn't it be last _ tokens + ( (delta/1000)? \* deltarate) the core code for the calculation of ** filled_tokens ** parameters? +--- +title: Soul Gateway Learning RateLimiter Plugin +author: baiyu +date: 2021-01-30 +tag: + - Soul +cover: https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9434447ebc674f58b65c26b65f855181~tplv-k3u1fbpfcp-watermark.image +head: + - - meta + - name: Blog +--- + +## Review + +In the previous article on HTTP requests, the processing flow of Soul plug-ins was generally combed, and the specific functions of DividePlugin, GlobalPlugin, WebClientPlugin and WebCilent ResponsePlugin plug-ins were also learned. In the process of sorting out, it is found that there ** Order of precedence ** are Soul plug-ins, and many pre-plug-in operations have been done before the DividePlugin plug-in, which includes the topics ** Rate LimiterPlugin ** we analyzed in this chapter (one of them). + +## Learn to use + +### Read the official documents to have a general understanding of it. + +The rate Limiter plug-in + +Through the reading of official documents, we know the ** RateLimiterPlugin ** two core points ** Speed, capacity **. + +The following explanation comes from the official document. + +- Capacity: is the maximum number of requests a user is allowed to execute in one second. This is the number of tokens the token bucket can hold. +- Rate: This is how many requests per second you allow the user to execute and any requests that are dropped. This is the fill rate of the token bucket. + +It can be seen that ** RateLimiterPlugin ** the core of current limiting lies in ** Token bucket algorithm ** its implementation. + +PS: There are four common implementations ** Token bucket algorithm ** of the current limiting algorithm, ** Funnel algorithm **, ** Counter (fixed window) algorithm **, ** Sliding window algorithm **. See the corresponding blog introduction for details. + +### Initial use + +#### Enable the corresponding plug-in + +At the Soul gateway ** System Management-Plug-in Management **, change the status to the enabled status. Note that redis related configuration needs to be filled in here. The Soul token bucket is based on redis. + +Why is Soul's token bucket algorithm based on redis? + +In the case of cluster deployment, the token bucket algorithm of a single machine can not meet the current limiting function in the cluster state. + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e25dd524c294b4f9c227e3f2127757f~tplv-k3u1fbpfcp-watermark.image) + +#### Add current limit selectors, rules, + +At the Soul Gateway ** List of plug-ins **, select rate \_ limiter to add the rule and selector configuration. If you don't know how to add it, you can read the matching logic of the selector \ rule first. ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9cbbc63ed6214aeda8c70f8e34d7c19c~tplv-k3u1fbpfcp-watermark.image)![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/25e67268dd5e4aa9a081a51963a03da8~tplv-k3u1fbpfcp-watermark.image) The capacity and rate added here are both 1, mainly to verify whether the plug-in is enabled. + +#### Interface corresponding access + +Call *http://127.0.0.1:9195/http/test/findByUserId?userId=10* to access. When the rate is higher than 1, the following interface returns the result, which means the plug-in is successfully used. + +```json +{ + "code": 429, + "message": "You have been restricted, please try again later!", + "data": null +} +``` + +### Source Code Reading Read the source code with questions + +#### How to ensure that the redis configuration takes effect immediately after the page is modified, and the corresponding redis connection in the background is changed immediately. + +The answer is that natural data synchronization is closely related. + +When modifying the configuration of the plug-in, an event notification of plug-in data change is also issued. When combing the overall process of Soul Gateway synchronization data, it has been known that the modified plug-in data not only changes the data in the JVM cache, but also distributes the corresponding plug-in. As shown in the following figure ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9434447ebc674f58b65c26b65f855181~tplv-k3u1fbpfcp-watermark.image), for ** RateLimiterPlugin ** the interface that is mainly implemented ** handlePlugin **, what exactly does this corresponding implementation do? + +The specific method is as follows ** Rate LimiterPluginData Handler handlerPlugin **. + +```java +public void handlerPlugin(final PluginData pluginData) { + if (Objects.nonNull(pluginData) && pluginData.getEnabled()) { + // Load rate limiting plugin configuration + RateLimiterConfig rateLimiterConfig = GsonUtils.getInstance().fromJson(pluginData.getConfig(), RateLimiterConfig.class); + // Check if Redis connection value needs to be reloaded + if (Objects.isNull(Singleton.INST.get(ReactiveRedisTemplate.class)) + || Objects.isNull(Singleton.INST.get(RateLimiterConfig.class)) + || !rateLimiterConfig.equals(Singleton.INST.get(RateLimiterConfig.class))) { + LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(rateLimiterConfig); + lettuceConnectionFactory.afterPropertiesSet(); + RedisSerializer serializer = new StringRedisSerializer(); + RedisSerializationContext serializationContext = + RedisSerializationContext.newSerializationContext().key(serializer).value(serializer).hashKey(serializer).hashValue(serializer).build(); + ReactiveRedisTemplate reactiveRedisTemplate = new ReactiveRedisTemplate<>(lettuceConnectionFactory, serializationContext); + Singleton.INST.single(ReactiveRedisTemplate.class, reactiveRedisTemplate); + Singleton.INST.single(RateLimiterConfig.class, rateLimiterConfig); + } + } + } +``` + +There are several key points in the above code: + +In the above code, the configuration of the current limiting plug-in and the corresponding redisTemplate instance are put into the corresponding map of the Singleton. INST. + +When the plug-in data is received, judging whether a redis connection instance and a current limiting configuration instance exist, judging whether the current current limiting configuration instance is consistent with the transmitted current limiting instance, if not, considering that the configuration is changed, and re-initializing the current limiting instance and the connection pool instance to be put into the map of the Singleton. INST, Hot deployment of changes to the redis configuration is thus guaranteed. + +The code in the if judgment is encapsulated into a corresponding redis connection pool based on SpringDataRedis. + +The PS: Singleton. INST is a singleton pattern implemented by enumeration. + +### How is the current limiting plug-in implemented at the bottom? + +#### Debug call chain + +** RateLimiterPlugin ** Because of the need to limit the flow of specific rules, it is still implemented ** AbstractSoulPlugin **, and the methods and functions that have been ** An excute of AbstractSoulPlugin ** combed before are still used, so I will not repeat the explanation here. You can watch the Http call process . Deepen the impression of this class. + +The focus of this section is to see what specific ** doexcute ** methods have done. + +```java + protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final String handle = rule.getHandle(); + final RateLimiterHandle limiterHandle = GsonUtils.getInstance().fromJson(handle, RateLimiterHandle.class); + return redisRateLimiter.isAllowed(rule.getId(), limiterHandle.getReplenishRate(), limiterHandle.getBurstCapacity()) + .flatMap(response -> { + if (!response.isAllowed()) { + // Return error message with 429 error code + exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); + Object error = SoulResultWrap.error(SoulResultEnum.TOO_MANY_REQUESTS.getCode(), SoulResultEnum.TOO_MANY_REQUESTS.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + return chain.execute(exchange); + }); + } +``` + +In the above code, it can be seen that whether the token is successfully obtained is judged by ** redisRateLimiter.isAllowed **. The method is as follows + +```java + public Mono isAllowed(final String id, final double replenishRate, final double burstCapacity) { + if (!this.initialized.get()) { + throw new IllegalStateException("RedisRateLimiter is not initialized"); + } + // Get the Redis key + List keys = getKeys(id); + //The parameters required for encapsulating the execution of a Lua script. The first parameter is the rate, the second parameter is the capacity, the third parameter is the current 10-digit timestamp, and the fourth parameter is a fixed value of 1, which represents the number of tokens requested. + List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", Instant.now().getEpochSecond() + "", "1"); + //Execute the Lua script + Flux> resultFlux = Singleton.INST.get(ReactiveRedisTemplate.class).execute(this.script, keys, scriptArgs); + return resultFlux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))) + .reduce(new ArrayList(), (longs, l) -> { + longs.addAll(l); + return longs; + }).map(results -> { + //'allowed' indicates the execution result (1 for success) + boolean allowed = results.get(0) == 1L; + Long tokensLeft = results.get(1); + RateLimiterResponse rateLimiterResponse = new RateLimiterResponse(allowed, tokensLeft); + log.info("RateLimiter response:{}", rateLimiterResponse.toString()); + return rateLimiterResponse; + }).doOnError(throwable -> log.error("Error determining if user allowed from redis:{}", throwable.getMessage())); + } +``` + +#### Method get Keys (ID) + +This method is to obtain the keys that redis needs to operate. Two types of keys are obtained in the following format: + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55ce72f4e044405fbd3b1461905072f2~tplv-k3u1fbpfcp-watermark.image) + +The particularly long number in the middle is ** Rule ID **, because the smallest granularity of the current limit is the rule. + +The first timestamp record I ** Timestamp of the last call ** + +The second token records that ** The number of tokens remaining after the last call was completed ** + +#### execute(this.script, keys, scriptArgs) + +Executing the Lua script keys passes the return value of getKeys (ID), and scriptArgs passes the required parameters + +By reading the above code, we know that the specific implementation of the current limit rule is handed over to the specific Lua script. + +PS: It needs to be reminded here that the current limiting algorithm is a token bucket algorithm. There are two general implementations of the token bucket algorithm. One is that a thread continuously generates tokens. When a request comes in, it first obtains tokens from the corresponding queue. However, this token generation method will consume a lot of performance when the threshold is set to be particularly large. Therefore, there is a second token bucket algorithm, The number of tokens is calculated in real time as they are acquired, and soul is based on the second implementation. + +#### Analysis of Lua Current Limiting Algorithm + +```lua +-- Key to store the remaining token count for the current rule +local tokens_key = KEYS[1] +-- Key for the timestamp of the last call for the current rule +local timestamp_key = KEYS[2] + +-- Rate +local rate = tonumber(ARGV[1]) +-- Capacity +local capacity = tonumber(ARGV[2]) +-- Current timestamp +local now = tonumber(ARGV[3]) +-- Value is 1 +local requested = tonumber(ARGV[4]) +-- Calculate fill time by dividing capacity by rate +local fill_time = capacity/rate +-- Calculate TTL by rounding down fill time * 2 +local ttl = math.floor(fill_time*2) + +-- Get the current token count +local last_tokens = tonumber(redis.call("get", tokens_key)) +if last_tokens == nil then +-- Set token count to the configured capacity if it's not present + last_tokens = capacity +end +-- Get the timestamp of the last call +local last_refreshed = tonumber(redis.call("get", timestamp_key)) +if last_refreshed == nil then + last_refreshed = 0 +end +-- Calculate the time difference between the last call and the current call +local delta = math.max(0, now-last_refreshed) +-- Calculate the current remaining token count +local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) +-- Check if there are enough tokens (at least 1) available +local allowed = filled_tokens >= requested +local new_tokens = filled_tokens +local allowed_num = 0 +if allowed then + -- Consume one token + new_tokens = filled_tokens - requested + allowed_num = 1 +end + +-- Use setex to set the key's TTL and new value +redis.call("setex", tokens_key, ttl, new_tokens) +redis.call("setex", timestamp_key, ttl, now) + +return { allowed_num, new_tokens } +``` + +It is recommended to understand the role of Lua ** KEYS ARGS ** and the understanding of keys [1] and argv [1] in redis Lua. + +The overall logic of the Lua code is still very clear, and I can't explain it in detail here. The code comments have been completed. + +I have two doubts here. + +- Is the calculation of the ** ttl ** parameter multiplied by 2 for fear that it is not an integer? , so the \ \* 2 take the minimum operation? +- Is last _ tokens + (\* Rate), where the delta parameter is the subtraction of two ten-bit timestamps, but the rate is generated in seconds, shouldn't it be last _ tokens + ( (delta/1000)? \* deltarate) the core code for the calculation of ** filled_tokens ** parameters? diff --git a/src/blog/soul_source_learning_19_redirect.md b/src/blog/soul_source_learning_19_redirect.md index 45f8162738..2dab7ef6cd 100644 --- a/src/blog/soul_source_learning_19_redirect.md +++ b/src/blog/soul_source_learning_19_redirect.md @@ -1,133 +1,133 @@ ---- -title: Soul Gateway Learning Redirect Plugin -author: axing -date: 2021-03-16 -tag: - - Soul -cover: /assets/img/blog6/01.jpg -head: - - - meta - - name: Blog ---- - -# Introduction - -When the `Soul` gateway makes a proxy call to the target service, it can use `redirect` the plug-in to redirect the request. There are two scenarios: one is to `redirectUrl` configure it as a third-party URL address and directly use `308` it to forward and jump, and the other is `redirectUrl` to forward the configuration beginning with `/` to the gateway itself. - -## Plug-in configuration - -- In `soul-admin` – > Plug-in Management – > `redirect`, set to on. -- Dependencies added `redirect` `maven` in `soul-bootstrap` the project's `pom.xml` files. -- Set the selector rule in the `soul- admin` background. Only the matching request will be forwarded and redirected. Please see for details: [Selector and Rule Configuration](https://dromara.org/zh/projects/soul/selector-and-rule). - -## Maven dependency - -Add the plug-in dependency in the `soul-bootstrap` project `pom.xml` file. - -```xml - - org.dromara - soul-spring-boot-starter-plugin-redirect - ${last.version} - -``` - -## Scenes - -> As the name implies, `redirect` a plug-in is `uri` a redirection and redirection of. - -### Redirect - -- When we `Rule` configure the custom path, it should be a reachable service path. -- When the request is matched, the service jump will be performed `308` according to the user-defined path `Soul gateway`. - -![Redirect configuration](https://dromara.org/img/soul/plugin/redirect/redirect-01.png) - -### Gateway self-interface forwarding - -- When the matching rules are met, the service internally forwards using `DispatcherHandler` the internal interface. -- To realize the gateway's own interface forwarding, we need to start with the prefix in the configuration path `/`. The specific configuration is shown in the following figure. - -![Self-interface forwarding](https://dromara.org/img/soul/plugin/redirect/redirect-02.png) - -## Source Code Parsing - -Before parsing `redirect` the redirect source code, it's important to understand that the Soul Gateway is based on the SpringBoot WebFlux implementation, where `WebFlux` requests are handled by default `DispatcherHandler` if nothing is configured by default. This is the responsive `MVC` processing core. Take a look at the initialization: - -```java -protected void initStrategies(ApplicationContext context) { - Map mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); - ArrayList mappings = new ArrayList(mappingBeans.values()); - AnnotationAwareOrderComparator.sort(mappings); - // handlerMapping related - this.handlerMappings = Collections.unmodifiableList(mappings); - Map adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); - // handlerAdapter related - this.handlerAdapters = new ArrayList(adapterBeans.values()); - AnnotationAwareOrderComparator.sort(this.handlerAdapters); - Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false); - // resultHandler related - this.resultHandlers = new ArrayList(beans.values()); - AnnotationAwareOrderComparator.sort(this.resultHandlers); -} -``` - -After that, we are familiar with the `MVC` core processing `DispatcherHandler#handle` method. - -```java -public Mono handle(ServerWebExchange exchange) { - return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { - return mapping.getHandler(exchange); - }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { - return this.invokeHandler(exchange, handler); - }).flatMap((result) -> { - return this.handleResult(exchange, result); - }); -} -``` - -To figure out how to handle it by default `DispatcherHandler`, let's talk about Soul Gateway, `SoulWebHandler` which implements the `WebHandler` interface. And then `BeanName` replace that previously `DispatcherHandler` registered default proces `handler` with the declaration `webHandler`. - -```java -@Bean("webHandler") -public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { - List pluginList = plugins.getIfAvailable(Collections::emptyList); - List soulPlugins = pluginList.stream() - .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); - soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); - return new SoulWebHandler(soulPlugins); -} -``` - -So far, we understand that the default request has been `SoulWebHandler#handle` processed. What if we need to forward it to the gateway itself `MVC`? The following is `DispatcherHandler` injected during initialization `RedirectPlugin`, and then `DispatcherHandler` distributed according to the specific request. The specific core code is as follows: - -```java -@Override -protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, - final SelectorData selector, final RuleData rule) { - final String handle = rule.getHandle(); - final RedirectHandle redirectHandle = GsonUtils.getInstance().fromJson(handle, RedirectHandle.class); - if (Objects.isNull(redirectHandle) || StringUtils.isBlank(redirectHandle.getRedirectURI())) { - log.error("uri redirect rule can not configuration: {}", handle); - return chain.execute(exchange); - } - // Handle self-forwarding paths starting with "/" - if (redirectHandle.getRedirectURI().startsWith(ROOT_PATH_PREFIX)) { - ServerHttpRequest request = exchange.getRequest().mutate() - .uri(Objects.requireNonNull(UriUtils.createUri(redirectHandle.getRedirectURI()))).build(); - ServerWebExchange mutated = exchange.mutate().request(request).build(); - return dispatcherHandler.handle(mutated); - } else { - // Perform a 308 redirect - ServerHttpResponse response = exchange.getResponse(); - response.setStatusCode(HttpStatus.PERMANENT_REDIRECT); - response.getHeaders().add(HttpHeaders.LOCATION, redirectHandle.getRedirectURI()); - return response.setComplete(); - } -} -``` - -### Reference link: - -- [ Design of Spring Web Flux and Analysis of Its Operation Principle ](https://learnku.com/articles/30263#replies) -- [ Spring Web Flux operation principle ](https://www.processon.com/view/link/5d0763ede4b039f39f3b5a8a) +--- +title: Soul Gateway Learning Redirect Plugin +author: axing +date: 2021-03-16 +tag: + - Soul +cover: /assets/img/blog6/01.jpg +head: + - - meta + - name: Blog +--- + +# Introduction + +When the `Soul` gateway makes a proxy call to the target service, it can use `redirect` the plug-in to redirect the request. There are two scenarios: one is to `redirectUrl` configure it as a third-party URL address and directly use `308` it to forward and jump, and the other is `redirectUrl` to forward the configuration beginning with `/` to the gateway itself. + +## Plug-in configuration + +- In `soul-admin` – > Plug-in Management – > `redirect`, set to on. +- Dependencies added `redirect` `maven` in `soul-bootstrap` the project's `pom.xml` files. +- Set the selector rule in the `soul- admin` background. Only the matching request will be forwarded and redirected. Please see for details: [Selector and Rule Configuration](https://dromara.org/zh/projects/soul/selector-and-rule). + +## Maven dependency + +Add the plug-in dependency in the `soul-bootstrap` project `pom.xml` file. + +```xml + + org.dromara + soul-spring-boot-starter-plugin-redirect + ${last.version} + +``` + +## Scenes + +> As the name implies, `redirect` a plug-in is `uri` a redirection and redirection of. + +### Redirect + +- When we `Rule` configure the custom path, it should be a reachable service path. +- When the request is matched, the service jump will be performed `308` according to the user-defined path `Soul gateway`. + +![Redirect configuration](https://dromara.org/img/soul/plugin/redirect/redirect-01.png) + +### Gateway self-interface forwarding + +- When the matching rules are met, the service internally forwards using `DispatcherHandler` the internal interface. +- To realize the gateway's own interface forwarding, we need to start with the prefix in the configuration path `/`. The specific configuration is shown in the following figure. + +![Self-interface forwarding](https://dromara.org/img/soul/plugin/redirect/redirect-02.png) + +## Source Code Parsing + +Before parsing `redirect` the redirect source code, it's important to understand that the Soul Gateway is based on the SpringBoot WebFlux implementation, where `WebFlux` requests are handled by default `DispatcherHandler` if nothing is configured by default. This is the responsive `MVC` processing core. Take a look at the initialization: + +```java +protected void initStrategies(ApplicationContext context) { + Map mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); + ArrayList mappings = new ArrayList(mappingBeans.values()); + AnnotationAwareOrderComparator.sort(mappings); + // handlerMapping related + this.handlerMappings = Collections.unmodifiableList(mappings); + Map adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); + // handlerAdapter related + this.handlerAdapters = new ArrayList(adapterBeans.values()); + AnnotationAwareOrderComparator.sort(this.handlerAdapters); + Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false); + // resultHandler related + this.resultHandlers = new ArrayList(beans.values()); + AnnotationAwareOrderComparator.sort(this.resultHandlers); +} +``` + +After that, we are familiar with the `MVC` core processing `DispatcherHandler#handle` method. + +```java +public Mono handle(ServerWebExchange exchange) { + return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { + return mapping.getHandler(exchange); + }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { + return this.invokeHandler(exchange, handler); + }).flatMap((result) -> { + return this.handleResult(exchange, result); + }); +} +``` + +To figure out how to handle it by default `DispatcherHandler`, let's talk about Soul Gateway, `SoulWebHandler` which implements the `WebHandler` interface. And then `BeanName` replace that previously `DispatcherHandler` registered default proces `handler` with the declaration `webHandler`. + +```java +@Bean("webHandler") +public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { + List pluginList = plugins.getIfAvailable(Collections::emptyList); + List soulPlugins = pluginList.stream() + .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); + soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); + return new SoulWebHandler(soulPlugins); +} +``` + +So far, we understand that the default request has been `SoulWebHandler#handle` processed. What if we need to forward it to the gateway itself `MVC`? The following is `DispatcherHandler` injected during initialization `RedirectPlugin`, and then `DispatcherHandler` distributed according to the specific request. The specific core code is as follows: + +```java +@Override +protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, + final SelectorData selector, final RuleData rule) { + final String handle = rule.getHandle(); + final RedirectHandle redirectHandle = GsonUtils.getInstance().fromJson(handle, RedirectHandle.class); + if (Objects.isNull(redirectHandle) || StringUtils.isBlank(redirectHandle.getRedirectURI())) { + log.error("uri redirect rule can not configuration: {}", handle); + return chain.execute(exchange); + } + // Handle self-forwarding paths starting with "/" + if (redirectHandle.getRedirectURI().startsWith(ROOT_PATH_PREFIX)) { + ServerHttpRequest request = exchange.getRequest().mutate() + .uri(Objects.requireNonNull(UriUtils.createUri(redirectHandle.getRedirectURI()))).build(); + ServerWebExchange mutated = exchange.mutate().request(request).build(); + return dispatcherHandler.handle(mutated); + } else { + // Perform a 308 redirect + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.PERMANENT_REDIRECT); + response.getHeaders().add(HttpHeaders.LOCATION, redirectHandle.getRedirectURI()); + return response.setComplete(); + } +} +``` + +### Reference link: + +- [ Design of Spring Web Flux and Analysis of Its Operation Principle ](https://learnku.com/articles/30263#replies) +- [ Spring Web Flux operation principle ](https://www.processon.com/view/link/5d0763ede4b039f39f3b5a8a) diff --git a/src/blog/soul_source_learning_20_sentinel.md b/src/blog/soul_source_learning_20_sentinel.md index 07fc4b114f..898976e7a3 100644 --- a/src/blog/soul_source_learning_20_sentinel.md +++ b/src/blog/soul_source_learning_20_sentinel.md @@ -1,250 +1,250 @@ ---- -title: Soul Gateway Learning Sentinel Plugin -author: luoxiaolong -date: 2021-03-19 -tag: - - Soul -cover: /assets/img/blog6/02.jpg -head: - - - meta - - name: Blog ---- - -# Overview - -Fuse and flow control are very necessary functions in the service gateway. The soul uses different mature components to implement this part of the function, and users can choose according to their preferences. This article will introduce how to use the Sentinel component of Ali to realize the fuse and flow control functions in soul. This article will first introduce the scenario and significance of fuse and flow control. It then describes how to configure the use of the sentinel plug-in for flow control and fusing on the soul. Finally, it briefly analyzes how soul uses the Sentinel component from the source code level. - -# Fuse and flow control - -## Scene description - -As the entrance of the traffic, the service gateway has the responsibility to protect the subsequent services. The following two scenarios that are seriously harmful to services are often encountered in production, and they are also issues that business gateways must pay attention to. One situation is that during large-scale promotions such as Double 11 or Double 12, the request volume of the interface is several times higher than usual. If the capacity is not well evaluated, this surge of requests can easily lead to the complete unavailability of the entire service. This kind of downtime is often caused not by loopholes in business logic, but by too many requests and insufficient resources. Another situation is that there are some core services in the whole service system, and multiple business processes depend on this service. However, all services have unstable processing or service damage, resulting in long request processing time or frequent exceptions. Excluding the case of business BUG, it may be a sudden and very random block. Generally, it will be automatically repaired if the request volume is slowed down, but if it is not protected, there will be a domino effect that causes the entire service to be unavailable. This scenario is slightly different from the first scenario, in which the actual traffic does have an unmanageable peak, while the second scenario mainly considers the chain reaction caused by the inevitable and unpredictable jitter of the service itself. - -## Flow control - -For the first scenario, our usual practice is to carry out flow control. The core idea is that the service gateway ensures that the number of requests to the back is the amount that the service can bear. The redundant requests are directly rejected or added to the waiting queue to ensure that the service will not be suspended, and most of the requests can still be processed normally. When considering the strategy of flow control, we should mainly consider the following questions: - -1. By what angle is the flow controlled? -2. What is the threshold? -3. What is the flow control strategy? - -For the first problem, the normal idea is to monitor traffic through QPS, that is, flow control when the number of requests per second exceeds a certain limit. But in fact, there is another way to monitor traffic from the number of concurrency. This control scenario is also very meaningful. For example, when the downstream application causes service instability and response delay increase for some reason, it means that the throughput of the gateway decreases and more threads are occupied. In extreme cases, it even leads to the exhaustion of the thread pool. In a sense, flow control through concurrency can protect the gateway service itself to a certain extent. For the second question, the threshold is easy to understand, which is the boundary of triggering flow control. If we consider from QPS, it is how many times per second to start flow control. If we consider from the number of concurrency, it is how many times the number of threads requesting context exceeds. For the third problem, we generally have the following three solutions: - -1. Reject directly. This strategy is well understood as rejecting the service directly when the QPS is above the threshold without transmitting the request to a subsequent service. -2. This strategy is aimed at the scenario that when the system is in a low water level for a long time, there may be a sudden increase in flow, and directly pulling the system to a high water level may instantly crush the system. The way to start preheating is to slowly increase the threshold, gradually increase the threshold within a certain period of time until it reaches the setting, and give the cold system a preheating time to avoid the cold system being crushed. Requests that exceed the threshold are also rejected. -3. Queuing at a constant speed. The core idea of this strategy is to let requests pass at fixed intervals. When a request comes, if the time interval between the current request and the last passed request is not less than a preset value, the current request is passed; Otherwise, the expected passing time of the current request is calculated. If the expected passing time of the request is less than the timeout time preset by the rule, the request will wait until the preset time comes (queuing for processing). If the expected passage time exceeds the maximum queuing time, the request will be rejected directly. - -## Fuse - -For the second scenario, the usual way to deal with it is to set the service fuse. Simply put, when a service we detect is abnormal, we will not access it so as not to cause more pressure on it by more requests. After a period of time, if the service is detected to be restored, the traffic will be sent back. We first need to determine whether the service is unstable or jittery. Then think about what we should do if we find a service that is shaking. How to judge whether the service is back to normal. We can generally judge whether the service is unstable in the following three ways. - -1. Slow call proportion: when the number of requests in the unit statistical time is greater than the set minimum number of requests, and the request exceeding the maximum tolerance time is greater than the threshold, it is judged that the service is abnormal, and the fuse is triggered; -2. Abnormal proportion: when the proportion of abnormal requests in the unit statistical time is greater than the threshold, we determine that the service is abnormal and trigger the fuse; -3. Abnormal number: when the number of abnormal requests in unit time reaches the threshold, it is determined that the service is abnormal, and the fuse is triggered; - -When we judge that the service is abnormal through the above three indicators and fuse the service, we can choose to report an error directly for the request within a certain period of time (within the fuse duration), without blocking the upstream service, and let the requester decide how to deal with it. Or directly trigger service degradation. Service degradation can be roughly understood as requesting a simplified version of this business, which omits many non-core processes and only ultimately ensures that the process is processed (ultimately consistent). Like a real circuit breaker, a service circuit breaker will automatically recover. Generally, after the fuse is triggered, the service is in the fuse state for a period of time and does not provide services, and then it enters the half-open state. If the following small number of requests do not report errors and the response time is reasonable, the service is restored. If it is still abnormal, it continues to fuse. - -# Sentinel plugin in soul - -Sentinel is Alibaba's open source traffic control component for distributed service architecture, which mainly takes traffic as the starting point to help you ensure the stability of microservices from multiple dimensions such as traffic control, fuse degradation, and system adaptive protection. Soul, as an excellent open source gateway in China, integrates Sentinel into its own system as a plug-in, so that users can use the flow control and service fuse functions provided by Sentinel through simple configuration. The following is a brief description of how to configure the use of the sentinel plug-in in soul. - -First, log in to the soul management platform and configure the plug-in in the "plug-in list" -- > "sentinel". The configuration of "Selector" is not the focus of this article and will not be introduced. Click "Add Rules" to make specific settings, as shown in the following figure. - -![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-02.png) - -In this configuration page, "Name", "Matching Mode", "Condition", "Log Printing", and "Whether to Enable" and "Execution Order" are general configurations of the soul plug-in and will not be described here. What we need to focus on is the configuration items in "processing". These configuration items can be divided into two groups. The first four options are about fuse configuration, and the last four options are about flow control configuration. In soul, we can set the flow control and fuse policy for a group of requests at the same time. Next, we will focus on how to use each configuration item. - -## Fuse - -First of all, let's look at the configuration related to the fuse. It has four configuration items: "fuse threshold", "whether to open the fuse", "fuse window size" and the unnamed service exception judgment method. Fuse switch indicates whether to open the fuse (1 open \ 0 not open). The fuse window size refers to the number of seconds after triggering the fuse to enter the half-open state. In the half-open state, if the request is normal, it will enter the normal state. If the request is still abnormal, it will continue to fuse. The fusing judgment mode and fusing threshold need to be combined. In soul, three service exception determination methods of sentinel are used. They are: - -1. Slow call percentage. In this mode, the threshold is the number of milliseconds that are determined to be slow calls. The ratio of slow call is 1 by default and cannot be changed, that is, the fuse will be triggered if the threshold is exceeded within the unit statistical time. This mode is the default mode for sentinel. -2. Exception proportion, in this mode, the threshold refers to the upper limit of the proportion of exception requests in the unit statistical time, and a number of [0.0, 1.0] needs to be filled in, indicating 0% -100% -3. Exception number policy. In this mode, the threshold refers to the upper limit of the number of exception requests per unit of statistical time. - -It should be noted that soul uses the default parameters of sentinel for the unit statistics duration (statIntervalMs) and the minimum number of fusing requests (minRequestAmount). One second and five times respectively. The unit duration specifies the exception judgment, with 1 second as the statistical range, and the next second starts counting again. The minimum number of requests means that if the number of requests is less than 5 in 1 second, the fuse will not be triggered even if the threshold is reached. - -![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-03.png) - -The configuration as shown in the figure above means that the fuse configuration is turned on. If 5 requests of this service are abnormal within 1 second, the fuse will be turned on for 10 seconds. After 10 seconds, the service will enter the half-open state. If the requests are normal, the service will become normal. If the requests are still abnormal, the fuse will continue to be turned on. If the service is requested during the fuse period, the soul gateway will directly return the request error, and the protection back-end service will not receive the request again. - -## Flow control - -There are five configurations related to flow control, which are "flow control effect", "current limit threshold", "flow control switch" and "current limit threshold type" from top to bottom and from left to right. The first is the type of throttling. We can choose "QPS" or "Number of concurrent threads". This parameter specifies from which angle we set the threshold of throttling. The threshold is the upper limit of the QPS or the number of threads, and the throttling policy is initiated when this threshold is reached. The specific current limiting strategy is configured in "flow control effect". In the flow control strategy, we can select "direct rejection", "warm up", "uniform queuing" and "warm up + uniform queuing". Direct rejection is better understood, that is, after the number of QPS or threads reaches the threshold, redundant requests are returned directly with errors. Warm-up means that the threshold gradually increases to the specified threshold within 10 seconds, that is, the threshold for the first 2-3 seconds is lower than the set threshold, but the threshold is gradually increased and reaches the specified threshold after 10 seconds, so that the system can have a warm-up process. If the request exceeds the threshold, the soul gateway will report an error and return directly. The uniform queue mode will strictly control the time interval of each request. If the flow control type is QPS and the threshold is 10, soul will control to transmit one request to the back-end service every 100ms. The extra requests will enter the waiting queue first, and each request will wait for 500ms at most. If the estimated waiting time of the request exceeds 500ms, it will report an error and return directly. It should be noted that if the flow limit type is the number of concurrent threads, then the flow control effect can only be "direct rejection". As shown in the figure below, this configuration indicates that the soul gateway will ensure that the QPS of this service does not exceed 10, and redundant requests will report errors directly. - -![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-04.png) - -It should be noted that the Sentinel component runs independently in each gateway of the soul. If the gateway is a cluster, the amount actually transmitted to the following services needs to be multiplied by the number of soul gateway services during flow control. That is, if our soul gateway deploys three nodes, all requests are evenly distributed to each node through nginx. The flow control configured for one interface is 10 qps, so the actual QPS to be processed by the backward service is 10 \ \* 3. Fuse also needs to consider this situation, only when a service on three nodes triggers a fuse, then the service will not receive any more requests. - -# Sentinel Plugin Source Code Reading - -The source code of Sentinel plug-in in soul mainly includes three parts, "Sentinel Rule Handle" is responsible for processing the processing logic when the Sentinel rule is synchronized from the management node, and "Sentine lPlugin" the processing logic of the plug-in ". "SentinelFallback Handler" for the processing logic that triggered the flow control or fuse. ". Let me take a look at them one by one. First is "Sentine lRuleHandle" ", the source code is as follows: - -```java -public class SentinelRuleHandle implements PluginDataHandler { - - @Override - public void handlerRule(final RuleData ruleData) { - // Process new sentinel configuration - SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(ruleData.getHandle(), SentinelHandle.class); - sentinelHandle.checkData(sentinelHandle); - // Get all existing flow control configurations and delete configurations with the same resourceName as the new configuration - List flowRules = FlowRuleManager.getRules() - .stream() - .filter(r -> !r.getResource().equals(getResourceName(ruleData))) - .collect(Collectors.toList()); - if (sentinelHandle.getFlowRuleEnable() == Constants.SENTINEL_ENABLE_FLOW_RULE) { - // If flow control is enabled - // Set sentinel flow control rules based on the configuration - FlowRule rule = new FlowRule(getResourceName(ruleData)); - // Set threshold - rule.setCount(sentinelHandle.getFlowRuleCount()); - // Flow control mode: QPS or thread - rule.setGrade(sentinelHandle.getFlowRuleGrade()); - // Flow control behavior: 0. default(reject directly), 1. warm up, 2. rate limiter, 3. warm up + rate limiter - rule.setControlBehavior(sentinelHandle.getFlowRuleControlBehavior()); - flowRules.add(rule); - } - // Update all flow control configurations - FlowRuleManager.loadRules(flowRules); - // Get all existing circuit breaker configurations and delete configurations with the same resourceName as the new configuration - List degradeRules = DegradeRuleManager.getRules() - .stream() - .filter(r -> !r.getResource().equals(getResourceName(ruleData))) - .collect(Collectors.toList()); - if (sentinelHandle.getDegradeRuleEnable() == Constants.SENTINEL_ENABLE_DEGRADE_RULE) { - // If circuit breaker is enabled - // Set sentinel circuit breaker rules based on the configuration - DegradeRule rule = new DegradeRule(getResourceName(ruleData)); - // Set circuit breaker threshold - rule.setCount(sentinelHandle.getDegradeRuleCount()); - // Basis for circuit breaker judgment 0: average RT, 1: exception ratio, 2: exception count - rule.setGrade(sentinelHandle.getDegradeRuleGrade()); - // Circuit breaker time window - rule.setTimeWindow(sentinelHandle.getDegradeRuleTimeWindow()); - degradeRules.add(rule); - } - // Update all circuit breaker configurations - DegradeRuleManager.loadRules(degradeRules); - } - - @Override - public void removeRule(final RuleData ruleData) { - // Remove specified rule - FlowRuleManager.loadRules(FlowRuleManager.getRules() - .stream() - .filter(r -> !r.getResource().equals(getResourceName(ruleData))) - .collect(Collectors.toList())); - DegradeRuleManager.loadRules(DegradeRuleManager.getRules() - .stream() - .filter(r -> !r.getResource().equals(getResourceName(ruleData))) - .collect(Collectors.toList())); - } - - @Override - public String pluginNamed() { - return PluginEnum.SENTINEL.getName(); - } - - /** - * return sentinel resource name. - * - * @param ruleData ruleData - * @return string string - */ - public static String getResourceName(final RuleData ruleData) { - return ruleData.getSelectorId() + "_" + ruleData.getName(); - } - -} -``` - -The Sentine lPlugin "of the plug-in execution logic code is as follow - -```java -public class SentinelPlugin extends AbstractSoulPlugin { - // Handler for exception handling - private final SentinelFallbackHandler sentinelFallbackHandler; - - public SentinelPlugin(final SentinelFallbackHandler sentinelFallbackHandler) { - this.sentinelFallbackHandler = sentinelFallbackHandler; - } - - @Override - protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - // Generate sentinel resource name from plugin configuration, which corresponds to one flow control or circuit breaker strategy - String resourceName = SentinelRuleHandle.getResourceName(rule); - // Verify sentinel plugin's configuration information - SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), SentinelHandle.class); - sentinelHandle.checkData(sentinelHandle); - // Introduce Sentinel's official Transformer and delegate the request to Sentinel for handling - return chain.execute(exchange).transform(new SentinelReactorTransformer<>(resourceName)) - .doOnSuccess(v -> { - HttpStatus status = exchange.getResponse().getStatusCode(); - if (status == null || !status.is2xxSuccessful()) { - exchange.getResponse().setStatusCode(null); - throw new SentinelFallbackException(status == null ? HttpStatus.INTERNAL_SERVER_ERROR : status); - } - }) - //If Sentinel triggers flow control or circuit breaker and causes an error, call sentinelFallbackHandler to return an error message - .onErrorResume(throwable -> sentinelFallbackHandler.fallback(exchange, UriUtils.createUri(sentinelHandle.getFallbackUri()), throwable)); - } - // Plugin name: sentinel - @Override - public String named() { - return PluginEnum.SENTINEL.getName(); - } - // Order: 45 - @Override - public int getOrder() { - return PluginEnum.SENTINEL.getCode(); - } - - public static class SentinelFallbackException extends HttpStatusCodeException { - public SentinelFallbackException(final HttpStatus statusCode) { - super(statusCode); - } - } -} -``` - -Exception handling is Sentine lFallbackHandler ". In the soul, whether it is the processing of the request after the fuse or the request under flow control, the soul will directly return an error - -```java -public class SentinelFallbackHandler implements FallbackHandler { - - @Override - public Mono generateError(final ServerWebExchange exchange, final Throwable throwable) { - Object error; - - if (throwable instanceof DegradeException) { - // Circuit breaker triggered - // Set HTTP status to 500 - exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); - // Set request body - error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); - } else if (throwable instanceof FlowException) { - // Flow control error, indicating that the client should retry - // Set HTTP status to 429 - exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); - // Set request body - error = SoulResultWrap.error(SoulResultEnum.TOO_MANY_REQUESTS.getCode(), SoulResultEnum.TOO_MANY_REQUESTS.getMsg(), null); - } else if (throwable instanceof BlockException) { - // Parent class of FlowException, indicating that the service is blocked - // Set HTTP status to 429 - exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); - // Set request body - error = SoulResultWrap.error(SoulResultEnum.SENTINEL_BLOCK_ERROR.getCode(), SoulResultEnum.SENTINEL_BLOCK_ERROR.getMsg(), null); - } else { - return Mono.error(throwable); - } - return WebFluxResultUtils.result(exchange, error); - } -} -``` - -# Sum up - -The soul gateway encapsulates an excellent flow control component, sentinel, which provides users with easy-to-use flow control and fuse functions. It should be noted that when soul uses sentinel, some parameters are configured by default. If there is a need to modify, you need to adjust the source code by yourself. Secondly, the soul gateway can be deployed in a distributed manner, but distributed flow control is not used when sentinel is used, and the flow control of each soul gateway node for the same resource is independent but identical. +--- +title: Soul Gateway Learning Sentinel Plugin +author: luoxiaolong +date: 2021-03-19 +tag: + - Soul +cover: /assets/img/blog6/02.jpg +head: + - - meta + - name: Blog +--- + +# Overview + +Fuse and flow control are very necessary functions in the service gateway. The soul uses different mature components to implement this part of the function, and users can choose according to their preferences. This article will introduce how to use the Sentinel component of Ali to realize the fuse and flow control functions in soul. This article will first introduce the scenario and significance of fuse and flow control. It then describes how to configure the use of the sentinel plug-in for flow control and fusing on the soul. Finally, it briefly analyzes how soul uses the Sentinel component from the source code level. + +# Fuse and flow control + +## Scene description + +As the entrance of the traffic, the service gateway has the responsibility to protect the subsequent services. The following two scenarios that are seriously harmful to services are often encountered in production, and they are also issues that business gateways must pay attention to. One situation is that during large-scale promotions such as Double 11 or Double 12, the request volume of the interface is several times higher than usual. If the capacity is not well evaluated, this surge of requests can easily lead to the complete unavailability of the entire service. This kind of downtime is often caused not by loopholes in business logic, but by too many requests and insufficient resources. Another situation is that there are some core services in the whole service system, and multiple business processes depend on this service. However, all services have unstable processing or service damage, resulting in long request processing time or frequent exceptions. Excluding the case of business BUG, it may be a sudden and very random block. Generally, it will be automatically repaired if the request volume is slowed down, but if it is not protected, there will be a domino effect that causes the entire service to be unavailable. This scenario is slightly different from the first scenario, in which the actual traffic does have an unmanageable peak, while the second scenario mainly considers the chain reaction caused by the inevitable and unpredictable jitter of the service itself. + +## Flow control + +For the first scenario, our usual practice is to carry out flow control. The core idea is that the service gateway ensures that the number of requests to the back is the amount that the service can bear. The redundant requests are directly rejected or added to the waiting queue to ensure that the service will not be suspended, and most of the requests can still be processed normally. When considering the strategy of flow control, we should mainly consider the following questions: + +1. By what angle is the flow controlled? +2. What is the threshold? +3. What is the flow control strategy? + +For the first problem, the normal idea is to monitor traffic through QPS, that is, flow control when the number of requests per second exceeds a certain limit. But in fact, there is another way to monitor traffic from the number of concurrency. This control scenario is also very meaningful. For example, when the downstream application causes service instability and response delay increase for some reason, it means that the throughput of the gateway decreases and more threads are occupied. In extreme cases, it even leads to the exhaustion of the thread pool. In a sense, flow control through concurrency can protect the gateway service itself to a certain extent. For the second question, the threshold is easy to understand, which is the boundary of triggering flow control. If we consider from QPS, it is how many times per second to start flow control. If we consider from the number of concurrency, it is how many times the number of threads requesting context exceeds. For the third problem, we generally have the following three solutions: + +1. Reject directly. This strategy is well understood as rejecting the service directly when the QPS is above the threshold without transmitting the request to a subsequent service. +2. This strategy is aimed at the scenario that when the system is in a low water level for a long time, there may be a sudden increase in flow, and directly pulling the system to a high water level may instantly crush the system. The way to start preheating is to slowly increase the threshold, gradually increase the threshold within a certain period of time until it reaches the setting, and give the cold system a preheating time to avoid the cold system being crushed. Requests that exceed the threshold are also rejected. +3. Queuing at a constant speed. The core idea of this strategy is to let requests pass at fixed intervals. When a request comes, if the time interval between the current request and the last passed request is not less than a preset value, the current request is passed; Otherwise, the expected passing time of the current request is calculated. If the expected passing time of the request is less than the timeout time preset by the rule, the request will wait until the preset time comes (queuing for processing). If the expected passage time exceeds the maximum queuing time, the request will be rejected directly. + +## Fuse + +For the second scenario, the usual way to deal with it is to set the service fuse. Simply put, when a service we detect is abnormal, we will not access it so as not to cause more pressure on it by more requests. After a period of time, if the service is detected to be restored, the traffic will be sent back. We first need to determine whether the service is unstable or jittery. Then think about what we should do if we find a service that is shaking. How to judge whether the service is back to normal. We can generally judge whether the service is unstable in the following three ways. + +1. Slow call proportion: when the number of requests in the unit statistical time is greater than the set minimum number of requests, and the request exceeding the maximum tolerance time is greater than the threshold, it is judged that the service is abnormal, and the fuse is triggered; +2. Abnormal proportion: when the proportion of abnormal requests in the unit statistical time is greater than the threshold, we determine that the service is abnormal and trigger the fuse; +3. Abnormal number: when the number of abnormal requests in unit time reaches the threshold, it is determined that the service is abnormal, and the fuse is triggered; + +When we judge that the service is abnormal through the above three indicators and fuse the service, we can choose to report an error directly for the request within a certain period of time (within the fuse duration), without blocking the upstream service, and let the requester decide how to deal with it. Or directly trigger service degradation. Service degradation can be roughly understood as requesting a simplified version of this business, which omits many non-core processes and only ultimately ensures that the process is processed (ultimately consistent). Like a real circuit breaker, a service circuit breaker will automatically recover. Generally, after the fuse is triggered, the service is in the fuse state for a period of time and does not provide services, and then it enters the half-open state. If the following small number of requests do not report errors and the response time is reasonable, the service is restored. If it is still abnormal, it continues to fuse. + +# Sentinel plugin in soul + +Sentinel is Alibaba's open source traffic control component for distributed service architecture, which mainly takes traffic as the starting point to help you ensure the stability of microservices from multiple dimensions such as traffic control, fuse degradation, and system adaptive protection. Soul, as an excellent open source gateway in China, integrates Sentinel into its own system as a plug-in, so that users can use the flow control and service fuse functions provided by Sentinel through simple configuration. The following is a brief description of how to configure the use of the sentinel plug-in in soul. + +First, log in to the soul management platform and configure the plug-in in the "plug-in list" -- > "sentinel". The configuration of "Selector" is not the focus of this article and will not be introduced. Click "Add Rules" to make specific settings, as shown in the following figure. + +![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-02.png) + +In this configuration page, "Name", "Matching Mode", "Condition", "Log Printing", and "Whether to Enable" and "Execution Order" are general configurations of the soul plug-in and will not be described here. What we need to focus on is the configuration items in "processing". These configuration items can be divided into two groups. The first four options are about fuse configuration, and the last four options are about flow control configuration. In soul, we can set the flow control and fuse policy for a group of requests at the same time. Next, we will focus on how to use each configuration item. + +## Fuse + +First of all, let's look at the configuration related to the fuse. It has four configuration items: "fuse threshold", "whether to open the fuse", "fuse window size" and the unnamed service exception judgment method. Fuse switch indicates whether to open the fuse (1 open \ 0 not open). The fuse window size refers to the number of seconds after triggering the fuse to enter the half-open state. In the half-open state, if the request is normal, it will enter the normal state. If the request is still abnormal, it will continue to fuse. The fusing judgment mode and fusing threshold need to be combined. In soul, three service exception determination methods of sentinel are used. They are: + +1. Slow call percentage. In this mode, the threshold is the number of milliseconds that are determined to be slow calls. The ratio of slow call is 1 by default and cannot be changed, that is, the fuse will be triggered if the threshold is exceeded within the unit statistical time. This mode is the default mode for sentinel. +2. Exception proportion, in this mode, the threshold refers to the upper limit of the proportion of exception requests in the unit statistical time, and a number of [0.0, 1.0] needs to be filled in, indicating 0% -100% +3. Exception number policy. In this mode, the threshold refers to the upper limit of the number of exception requests per unit of statistical time. + +It should be noted that soul uses the default parameters of sentinel for the unit statistics duration (statIntervalMs) and the minimum number of fusing requests (minRequestAmount). One second and five times respectively. The unit duration specifies the exception judgment, with 1 second as the statistical range, and the next second starts counting again. The minimum number of requests means that if the number of requests is less than 5 in 1 second, the fuse will not be triggered even if the threshold is reached. + +![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-03.png) + +The configuration as shown in the figure above means that the fuse configuration is turned on. If 5 requests of this service are abnormal within 1 second, the fuse will be turned on for 10 seconds. After 10 seconds, the service will enter the half-open state. If the requests are normal, the service will become normal. If the requests are still abnormal, the fuse will continue to be turned on. If the service is requested during the fuse period, the soul gateway will directly return the request error, and the protection back-end service will not receive the request again. + +## Flow control + +There are five configurations related to flow control, which are "flow control effect", "current limit threshold", "flow control switch" and "current limit threshold type" from top to bottom and from left to right. The first is the type of throttling. We can choose "QPS" or "Number of concurrent threads". This parameter specifies from which angle we set the threshold of throttling. The threshold is the upper limit of the QPS or the number of threads, and the throttling policy is initiated when this threshold is reached. The specific current limiting strategy is configured in "flow control effect". In the flow control strategy, we can select "direct rejection", "warm up", "uniform queuing" and "warm up + uniform queuing". Direct rejection is better understood, that is, after the number of QPS or threads reaches the threshold, redundant requests are returned directly with errors. Warm-up means that the threshold gradually increases to the specified threshold within 10 seconds, that is, the threshold for the first 2-3 seconds is lower than the set threshold, but the threshold is gradually increased and reaches the specified threshold after 10 seconds, so that the system can have a warm-up process. If the request exceeds the threshold, the soul gateway will report an error and return directly. The uniform queue mode will strictly control the time interval of each request. If the flow control type is QPS and the threshold is 10, soul will control to transmit one request to the back-end service every 100ms. The extra requests will enter the waiting queue first, and each request will wait for 500ms at most. If the estimated waiting time of the request exceeds 500ms, it will report an error and return directly. It should be noted that if the flow limit type is the number of concurrent threads, then the flow control effect can only be "direct rejection". As shown in the figure below, this configuration indicates that the soul gateway will ensure that the QPS of this service does not exceed 10, and redundant requests will report errors directly. + +![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-04.png) + +It should be noted that the Sentinel component runs independently in each gateway of the soul. If the gateway is a cluster, the amount actually transmitted to the following services needs to be multiplied by the number of soul gateway services during flow control. That is, if our soul gateway deploys three nodes, all requests are evenly distributed to each node through nginx. The flow control configured for one interface is 10 qps, so the actual QPS to be processed by the backward service is 10 \ \* 3. Fuse also needs to consider this situation, only when a service on three nodes triggers a fuse, then the service will not receive any more requests. + +# Sentinel Plugin Source Code Reading + +The source code of Sentinel plug-in in soul mainly includes three parts, "Sentinel Rule Handle" is responsible for processing the processing logic when the Sentinel rule is synchronized from the management node, and "Sentine lPlugin" the processing logic of the plug-in ". "SentinelFallback Handler" for the processing logic that triggered the flow control or fuse. ". Let me take a look at them one by one. First is "Sentine lRuleHandle" ", the source code is as follows: + +```java +public class SentinelRuleHandle implements PluginDataHandler { + + @Override + public void handlerRule(final RuleData ruleData) { + // Process new sentinel configuration + SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(ruleData.getHandle(), SentinelHandle.class); + sentinelHandle.checkData(sentinelHandle); + // Get all existing flow control configurations and delete configurations with the same resourceName as the new configuration + List flowRules = FlowRuleManager.getRules() + .stream() + .filter(r -> !r.getResource().equals(getResourceName(ruleData))) + .collect(Collectors.toList()); + if (sentinelHandle.getFlowRuleEnable() == Constants.SENTINEL_ENABLE_FLOW_RULE) { + // If flow control is enabled + // Set sentinel flow control rules based on the configuration + FlowRule rule = new FlowRule(getResourceName(ruleData)); + // Set threshold + rule.setCount(sentinelHandle.getFlowRuleCount()); + // Flow control mode: QPS or thread + rule.setGrade(sentinelHandle.getFlowRuleGrade()); + // Flow control behavior: 0. default(reject directly), 1. warm up, 2. rate limiter, 3. warm up + rate limiter + rule.setControlBehavior(sentinelHandle.getFlowRuleControlBehavior()); + flowRules.add(rule); + } + // Update all flow control configurations + FlowRuleManager.loadRules(flowRules); + // Get all existing circuit breaker configurations and delete configurations with the same resourceName as the new configuration + List degradeRules = DegradeRuleManager.getRules() + .stream() + .filter(r -> !r.getResource().equals(getResourceName(ruleData))) + .collect(Collectors.toList()); + if (sentinelHandle.getDegradeRuleEnable() == Constants.SENTINEL_ENABLE_DEGRADE_RULE) { + // If circuit breaker is enabled + // Set sentinel circuit breaker rules based on the configuration + DegradeRule rule = new DegradeRule(getResourceName(ruleData)); + // Set circuit breaker threshold + rule.setCount(sentinelHandle.getDegradeRuleCount()); + // Basis for circuit breaker judgment 0: average RT, 1: exception ratio, 2: exception count + rule.setGrade(sentinelHandle.getDegradeRuleGrade()); + // Circuit breaker time window + rule.setTimeWindow(sentinelHandle.getDegradeRuleTimeWindow()); + degradeRules.add(rule); + } + // Update all circuit breaker configurations + DegradeRuleManager.loadRules(degradeRules); + } + + @Override + public void removeRule(final RuleData ruleData) { + // Remove specified rule + FlowRuleManager.loadRules(FlowRuleManager.getRules() + .stream() + .filter(r -> !r.getResource().equals(getResourceName(ruleData))) + .collect(Collectors.toList())); + DegradeRuleManager.loadRules(DegradeRuleManager.getRules() + .stream() + .filter(r -> !r.getResource().equals(getResourceName(ruleData))) + .collect(Collectors.toList())); + } + + @Override + public String pluginNamed() { + return PluginEnum.SENTINEL.getName(); + } + + /** + * return sentinel resource name. + * + * @param ruleData ruleData + * @return string string + */ + public static String getResourceName(final RuleData ruleData) { + return ruleData.getSelectorId() + "_" + ruleData.getName(); + } + +} +``` + +The Sentine lPlugin "of the plug-in execution logic code is as follow + +```java +public class SentinelPlugin extends AbstractSoulPlugin { + // Handler for exception handling + private final SentinelFallbackHandler sentinelFallbackHandler; + + public SentinelPlugin(final SentinelFallbackHandler sentinelFallbackHandler) { + this.sentinelFallbackHandler = sentinelFallbackHandler; + } + + @Override + protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + // Generate sentinel resource name from plugin configuration, which corresponds to one flow control or circuit breaker strategy + String resourceName = SentinelRuleHandle.getResourceName(rule); + // Verify sentinel plugin's configuration information + SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), SentinelHandle.class); + sentinelHandle.checkData(sentinelHandle); + // Introduce Sentinel's official Transformer and delegate the request to Sentinel for handling + return chain.execute(exchange).transform(new SentinelReactorTransformer<>(resourceName)) + .doOnSuccess(v -> { + HttpStatus status = exchange.getResponse().getStatusCode(); + if (status == null || !status.is2xxSuccessful()) { + exchange.getResponse().setStatusCode(null); + throw new SentinelFallbackException(status == null ? HttpStatus.INTERNAL_SERVER_ERROR : status); + } + }) + //If Sentinel triggers flow control or circuit breaker and causes an error, call sentinelFallbackHandler to return an error message + .onErrorResume(throwable -> sentinelFallbackHandler.fallback(exchange, UriUtils.createUri(sentinelHandle.getFallbackUri()), throwable)); + } + // Plugin name: sentinel + @Override + public String named() { + return PluginEnum.SENTINEL.getName(); + } + // Order: 45 + @Override + public int getOrder() { + return PluginEnum.SENTINEL.getCode(); + } + + public static class SentinelFallbackException extends HttpStatusCodeException { + public SentinelFallbackException(final HttpStatus statusCode) { + super(statusCode); + } + } +} +``` + +Exception handling is Sentine lFallbackHandler ". In the soul, whether it is the processing of the request after the fuse or the request under flow control, the soul will directly return an error + +```java +public class SentinelFallbackHandler implements FallbackHandler { + + @Override + public Mono generateError(final ServerWebExchange exchange, final Throwable throwable) { + Object error; + + if (throwable instanceof DegradeException) { + // Circuit breaker triggered + // Set HTTP status to 500 + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + // Set request body + error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); + } else if (throwable instanceof FlowException) { + // Flow control error, indicating that the client should retry + // Set HTTP status to 429 + exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); + // Set request body + error = SoulResultWrap.error(SoulResultEnum.TOO_MANY_REQUESTS.getCode(), SoulResultEnum.TOO_MANY_REQUESTS.getMsg(), null); + } else if (throwable instanceof BlockException) { + // Parent class of FlowException, indicating that the service is blocked + // Set HTTP status to 429 + exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); + // Set request body + error = SoulResultWrap.error(SoulResultEnum.SENTINEL_BLOCK_ERROR.getCode(), SoulResultEnum.SENTINEL_BLOCK_ERROR.getMsg(), null); + } else { + return Mono.error(throwable); + } + return WebFluxResultUtils.result(exchange, error); + } +} +``` + +# Sum up + +The soul gateway encapsulates an excellent flow control component, sentinel, which provides users with easy-to-use flow control and fuse functions. It should be noted that when soul uses sentinel, some parameters are configured by default. If there is a need to modify, you need to adjust the source code by yourself. Secondly, the soul gateway can be deployed in a distributed manner, but distributed flow control is not used when sentinel is used, and the flow control of each soul gateway node for the same resource is independent but identical. diff --git a/src/blog/soul_source_learning_21_resilience4j.md b/src/blog/soul_source_learning_21_resilience4j.md index 9fb7733555..4e5101edfd 100644 --- a/src/blog/soul_source_learning_21_resilience4j.md +++ b/src/blog/soul_source_learning_21_resilience4j.md @@ -1,338 +1,338 @@ ---- -title: Soul Gateway Learning Resilience4j Plugin -author: yanbing -tag: - - Soul -date: 2021-03-22 -cover: /assets/img/blog6/03.jpg -head: - - - meta - - name: Blog ---- - -## Aim - -- What is Resilience4J? -- Resilience 4j experience with soul - - Current-limiting - - Fuse -- Interpretation of Resilience4J Plug-in Source Code - -## What is Resilience4j? - -- Resilience4J is the recommended fault tolerance scheme of Spring Cloud Gateway. It is a lightweight fault tolerance library. -- It borrows from Hystrix and uses JDK8 functional programming, namely lambda expressions. -- In contrast, Netflix Hystrix has a compilation dependency on Archaius. Resilience4j You don't need to reference all the dependencies. You can reference the relevant modules according to the functions you need. Hystrix will not be updated. Spring offers an alternative to Netflix Hystrix, namely Resilence4J -- Resilience4J provides a range of usability features that enhance microservices: - - - Circuit Breaker - - Rate Limiter - - Isolation based on semaphore - - Cache - - Time limiter - - Request to restart Retry - -- Official Dependency Package - -```Java - - io.github.resilience4j - resilience4j-circuitbreaker - ${resilience.version} - -``` - -## Resilience 4j experience with soul - -- First, open Resilience4j ![](https://img-blog.csdnimg.cn/20210321112151395.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODY5MjQz,size_16,color_FFFFFF,t_70) in the soul-admin console plug-in management. - -- Add dependency in soul gateway - -```Java - - org.dromara - soul-spring-boot-starter-plugin-ratelimiter - ${project.version} - -``` - -- Start three services, a soul-admin, a soul-bootstrap, and a soul-examples-http - -- Find Resilience4j in the plug-in list on the soul-admin console and customize the configuration, as shown in the following figure. ![](https://img-blog.csdnimg.cn/20210321112202189.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODY5MjQz,size_16,color_FFFFFF,t_70) - -- [ Introduction to the configuration of soul official website ](https://dromara.org/zh/projects/soul/resilience4j-plugin/) - -``` -* Resilience4j Processing in Detail: - - * timeoutDurationRate:Timeout for waiting to acquire tokens, in milliseconds, default value: 5000. - - * limitRefreshPeriod:Time interval for refreshing tokens, in milliseconds, default value: 500. - - * limitForPeriod:Number of tokens refreshed each time, default value: 50. - - * circuitEnable:Whether to enable circuit breaker, 0: off, 1: on, default value: 0. - - * timeoutDuration:Timeout for circuit breaker, in milliseconds, default value: 30000. - - * fallbackUri:URI for fallback handling. - - * slidingWindowSize:Size of the sliding window, default value: 100. - - * slidingWindowType:Type of the sliding window, 0: based on count, 1: based on time, default value: 0. - - * minimumNumberOfCalls:Minimum number of requests to trigger circuit breaker, circuit breaker statistics will be calculated only if this threshold is exceeded, default value: 100. - - * waitIntervalFunctionInOpenState:Duration for which the circuit breaker remains open, in milliseconds, default value: 10. - - * permittedNumberOfCallsInHalfOpenState:Size of the circular buffer in the half-open state, circuit breaker calculation will be performed only if this number is reached, default value: 10. - - * failureRateThreshold:Percentage of error rate, circuit breaker will only open if this threshold is reached, default value: 50. - - * automaticTransitionFromOpenToHalfOpenEnabled:Whether to automatically transition from open state to half-open state, true: yes, false: no, default value: false. -``` - -### Current-limiting - -- The parameter configuration is checked as follows. If the parameter value is less than the default value, the default value will be directly assigned, so it is convenient to directly modify the configuration of the source code for testing the effect: the number of tokens refreshed each time is 2, the time interval for refreshing tokens is 1 s, and the timeout time is 1 s. - -```java - /** - * check filed default value. - * - * @param resilience4JHandle {@linkplain Resilience4JHandle} - * @return {@linkplain Resilience4JHandle} - */ - public Resilience4JHandle checkData(final Resilience4JHandle resilience4JHandle) { - resilience4JHandle.setTimeoutDurationRate(Math.max(resilience4JHandle.getTimeoutDurationRate(), Constants.TIMEOUT_DURATION_RATE)); - //resilience4JHandle.setLimitRefreshPeriod(Math.max(resilience4JHandle.getLimitRefreshPeriod(), Constants.LIMIT_REFRESH_PERIOD)); - //resilience4JHandle.setLimitForPeriod(Math.max(resilience4JHandle.getLimitForPeriod(), Constants.LIMIT_FOR_PERIOD)); - // Set the number of tokens refreshed each time to 2, and the time interval for refreshing tokens to 1 second - resilience4JHandle.setLimitRefreshPeriod(1000); - resilience4JHandle.setLimitForPeriod(2); - resilience4JHandle.setTimeoutDuration(1000); - resilience4JHandle.setCircuitEnable(Math.max(resilience4JHandle.getCircuitEnable(), Constants.CIRCUIT_ENABLE)); - //resilience4JHandle.setTimeoutDuration(Math.max(resilience4JHandle.getTimeoutDuration(), Constants.TIMEOUT_DURATION)); - resilience4JHandle.setFallbackUri(!"0".equals(resilience4JHandle.getFallbackUri()) ? resilience4JHandle.getFallbackUri() : ""); - resilience4JHandle.setSlidingWindowSize(Math.max(resilience4JHandle.getSlidingWindowSize(), Constants.SLIDING_WINDOW_SIZE)); - resilience4JHandle.setSlidingWindowType(Math.max(resilience4JHandle.getSlidingWindowType(), Constants.SLIDING_WINDOW_TYPE)); - resilience4JHandle.setMinimumNumberOfCalls(Math.max(resilience4JHandle.getMinimumNumberOfCalls(), Constants.MINIMUM_NUMBER_OF_CALLS)); - resilience4JHandle.setWaitIntervalFunctionInOpenState(Math.max(resilience4JHandle.getWaitIntervalFunctionInOpenState(), Constants.WAIT_INTERVAL_FUNCTION_IN_OPEN_STATE)); - resilience4JHandle.setPermittedNumberOfCallsInHalfOpenState(Math.max(resilience4JHandle.getPermittedNumberOfCallsInHalfOpenState(), Constants.PERMITTED_NUMBER_OF_CALLS_IN_HALF_OPEN_STATE)); - resilience4JHandle.setFailureRateThreshold(Math.max(resilience4JHandle.getFailureRateThreshold(), Constants.FAILURE_RATE_THRESHOLD)); - return resilience4JHandle; - } -``` - -- Use SuperBenchmarker tool, 4 threads, execute 10s - -```java -C:\Users\v-yanb07>sb -u http://localhost:9195/http/test/findByUserId?userId=1 -c 4 -N 10 -Starting at 2021-03-14 15:46:28 -[Press C to stop the test] -23 (RPS: 1) ----------------Finished!---------------- -Finished at 2021-03-14 15:46:51 (took 00:00:23.0477097) -24 (RPS: 1) Status 200: 25 - -RPS: 2.2 (requests/second) -Max: 2020ms -Min: 472ms -Avg: 1677ms - - 50% below 1994ms - 60% below 1997ms - 70% below 1999ms - 80% below 1999ms - 90% below 2001ms - 95% below 2019ms - 98% below 2020ms - 99% below 2020ms -99.9% below 2020ms -``` - -- Output log - -```java -2021-03-14 12:16:35.252 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test -2021-03-14 12:16:36.249 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : Current limiting test -2021-03-14 12:16:36.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test -2021-03-14 12:16:37.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test -2021-03-14 12:16:37.250 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : Current limiting test -2021-03-14 12:16:38.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test -2021-03-14 12:16:38.250 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : Current limiting test -2021-03-14 12:16:39.252 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test -2021-03-14 12:16:39.252 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : Current limiting test -``` - -The console log outputs two lines per second to verify that the throttling is in effect - -### Fuse - -- From the configuration information, we know that the fuse is off by default, and we need to open it. -- Soul-examples-http Add sleep time at call interface - -```java - @GetMapping("/findByUserId") - public UserDTO findByUserId(@RequestParam("userId") final String userId) throws Exception{ - UserDTO userDTO = new UserDTO(); - userDTO.setUserId(userId); - userDTO.setUserName("hello world"); - log.info("Current limiting test"); - - int i = RandomUtils.nextInt(1,3); - if(i %2==0){ - //throw new Exception("Exception thrown"); - Thread.currentThread().sleep(2000); - } - return userDTO; - } -``` - -- Resilience4JHandle # checkData Manually set the timeout to 1s - -```java - resilience4JHandle.setTimeoutDuration(1000); -``` - -- The pos interface calls - > http://localhost:9195/http/test/findByUserId?userId=1 - -In case of multiple requests, some requests return normal data, and some requests return the following data, indicating that the timeout fuse is effective. - -```java -{ - "code": 500, - "message": "Internal Server Error", - "data": "404 NOT_FOUND" -} -``` - -## Interpretation of Resilience4J Plug-in Source Code - -The soul gateway Resilience4j plug-in source code uses a [reactive programming](https://developer.ibm.com/zh/languages/java/articles/j-cn-with-reactor-response-encode/) lot of methods. First, you need to understand responsive programming. - -- Resilience4J Plug-in Directory Structure - -``` -└─resilience4j - │ Resilience4JPlugin.java // Plugin processing, core class - │ - ├─build - │ Resilience4JBuilder.java // Build Resilience4JConf object - │ - ├─conf - │ Resilience4JConf.java - │ - ├─executor - │ CombinedExecutor.java // Limiter and circuit breaker executor - │ Executor.java - │ RateLimiterExecutor.java // Limiter executor - │ - ├─factory - │ Resilience4JRegistryFactory.java // Build limiter and circuit breaker objects - │ - └─handler - Resilience4JHandler.java -``` - -- Resilience4JPlugn # doExecuteResilience4JPlugn inherits AbstractSoulPlugin like other soul plug-ins. As long as it is enabled, it will go to the core method doExecute through the chain mechanism. - -```java - @Override - protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - // Get configuration information object - Resilience4JHandle resilience4JHandle = GsonUtils.getGson().fromJson(rule.getHandle(), Resilience4JHandle.class); - // Check configuration information, assign default values if they're less than defaults - resilience4JHandle = resilience4JHandle.checkData(resilience4JHandle); - // circuitEnable configuration: 1 enables circuit breaker component, otherwise use limiter component - if (resilience4JHandle.getCircuitEnable() == 1) { - return combined(exchange, chain, rule); - } - - return rateLimiter(exchange, chain, rule); - } -``` - -- Current Limiting Resilience4JPlugin # rateLimiter - -```java - private Mono rateLimiter(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) { - return ratelimiterExecutor.run( - // chain.execute(exchange) calls subsequent plugins - chain.execute(exchange), fallback(ratelimiterExecutor, exchange, null), Resilience4JBuilder.build(rule)) - .onErrorResume(throwable -> ratelimiterExecutor.withoutFallback(exchange, throwable)) - - - // Called by ratelimiterExecutor.run - @Override -public Mono run(final Mono toRun, final Function> fallback, final Resilience4JConf conf) { - // Limiter component - RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(conf.getId(), conf.getRateLimiterConfig()); - // Limiting execution - Mono to = toRun.transformDeferred(RateLimiterOperator.of(rateLimiter)); - if (fallback != null) { - // Execute fallback - return to.onErrorResume(fallback); - } - return to; -} - - - // to.onErrorResume(fallback); - default Mono fallback(ServerWebExchange exchange, String uri, Throwable t) { - if (StringUtils.isBlank(uri)) { - return withoutFallback(exchange, t); - } - DispatcherHandler dispatcherHandler = SpringBeanUtils.getInstance().getBean(DispatcherHandler.class); - ServerHttpRequest request = exchange.getRequest().mutate().uri(Objects.requireNonNull(UriUtils.createUri(uri))).build(); - ServerWebExchange mutated = exchange.mutate().request(request).build(); - // Execute the fallback - return dispatcherHandler.handle(mutated); -} -``` - -- Fuse Resilience 4JPlugin # combined - -```java - private Mono combined(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) { - Resilience4JConf conf = Resilience4JBuilder.build(rule); - return combinedExecutor.run( - chain.execute(exchange).doOnSuccess(v -> { - HttpStatus status = exchange.getResponse().getStatusCode(); - if (status == null || !status.is2xxSuccessful()) { - exchange.getResponse().setStatusCode(null); - throw new CircuitBreakerStatusCodeException(status == null ? HttpStatus.INTERNAL_SERVER_ERROR : status); - } - }), fallback(combinedExecutor, exchange, conf.getFallBackUri()), conf); - } - - - // Executed in combinedExecutor#run - public Mono run(final Mono run, final Function> fallback, final Resilience4JConf resilience4JConf) { - RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(resilience4JConf.getId(), resilience4JConf.getRateLimiterConfig()); - CircuitBreaker circuitBreaker = Resilience4JRegistryFactory.circuitBreaker(resilience4JConf.getId(), resilience4JConf.getCircuitBreakerConfig()); - //Circuit breaker operation - Mono to = run.transformDeferred(CircuitBreakerOperator.of(circuitBreaker)) - // Limiting operation - .transformDeferred(RateLimiterOperator.of(rateLimiter)) - // Set timeout - .timeout(resilience4JConf.getTimeLimiterConfig().getTimeoutDuration()) - // Throw timeout exception if timeout occurs - .doOnError(TimeoutException.class, t -> circuitBreaker.onError( - resilience4JConf.getTimeLimiterConfig().getTimeoutDuration().toMillis(), - TimeUnit.MILLISECONDS, - t)); - if (fallback != null) { - to = to.onErrorResume(fallback); - } - return to; - } -``` - -## Sum up - -- The soul gateway provides current limiting and fusing, and the fusing is off by default -- If the parameter value is less than the default value, the default value will be used directly +--- +title: Soul Gateway Learning Resilience4j Plugin +author: yanbing +tag: + - Soul +date: 2021-03-22 +cover: /assets/img/blog6/03.jpg +head: + - - meta + - name: Blog +--- + +## Aim + +- What is Resilience4J? +- Resilience 4j experience with soul + - Current-limiting + - Fuse +- Interpretation of Resilience4J Plug-in Source Code + +## What is Resilience4j? + +- Resilience4J is the recommended fault tolerance scheme of Spring Cloud Gateway. It is a lightweight fault tolerance library. +- It borrows from Hystrix and uses JDK8 functional programming, namely lambda expressions. +- In contrast, Netflix Hystrix has a compilation dependency on Archaius. Resilience4j You don't need to reference all the dependencies. You can reference the relevant modules according to the functions you need. Hystrix will not be updated. Spring offers an alternative to Netflix Hystrix, namely Resilence4J +- Resilience4J provides a range of usability features that enhance microservices: + + - Circuit Breaker + - Rate Limiter + - Isolation based on semaphore + - Cache + - Time limiter + - Request to restart Retry + +- Official Dependency Package + +```Java + + io.github.resilience4j + resilience4j-circuitbreaker + ${resilience.version} + +``` + +## Resilience 4j experience with soul + +- First, open Resilience4j ![](https://img-blog.csdnimg.cn/20210321112151395.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODY5MjQz,size_16,color_FFFFFF,t_70) in the soul-admin console plug-in management. + +- Add dependency in soul gateway + +```Java + + org.dromara + soul-spring-boot-starter-plugin-ratelimiter + ${project.version} + +``` + +- Start three services, a soul-admin, a soul-bootstrap, and a soul-examples-http + +- Find Resilience4j in the plug-in list on the soul-admin console and customize the configuration, as shown in the following figure. ![](https://img-blog.csdnimg.cn/20210321112202189.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODY5MjQz,size_16,color_FFFFFF,t_70) + +- [ Introduction to the configuration of soul official website ](https://dromara.org/zh/projects/soul/resilience4j-plugin/) + +``` +* Resilience4j Processing in Detail: + + * timeoutDurationRate:Timeout for waiting to acquire tokens, in milliseconds, default value: 5000. + + * limitRefreshPeriod:Time interval for refreshing tokens, in milliseconds, default value: 500. + + * limitForPeriod:Number of tokens refreshed each time, default value: 50. + + * circuitEnable:Whether to enable circuit breaker, 0: off, 1: on, default value: 0. + + * timeoutDuration:Timeout for circuit breaker, in milliseconds, default value: 30000. + + * fallbackUri:URI for fallback handling. + + * slidingWindowSize:Size of the sliding window, default value: 100. + + * slidingWindowType:Type of the sliding window, 0: based on count, 1: based on time, default value: 0. + + * minimumNumberOfCalls:Minimum number of requests to trigger circuit breaker, circuit breaker statistics will be calculated only if this threshold is exceeded, default value: 100. + + * waitIntervalFunctionInOpenState:Duration for which the circuit breaker remains open, in milliseconds, default value: 10. + + * permittedNumberOfCallsInHalfOpenState:Size of the circular buffer in the half-open state, circuit breaker calculation will be performed only if this number is reached, default value: 10. + + * failureRateThreshold:Percentage of error rate, circuit breaker will only open if this threshold is reached, default value: 50. + + * automaticTransitionFromOpenToHalfOpenEnabled:Whether to automatically transition from open state to half-open state, true: yes, false: no, default value: false. +``` + +### Current-limiting + +- The parameter configuration is checked as follows. If the parameter value is less than the default value, the default value will be directly assigned, so it is convenient to directly modify the configuration of the source code for testing the effect: the number of tokens refreshed each time is 2, the time interval for refreshing tokens is 1 s, and the timeout time is 1 s. + +```java + /** + * check filed default value. + * + * @param resilience4JHandle {@linkplain Resilience4JHandle} + * @return {@linkplain Resilience4JHandle} + */ + public Resilience4JHandle checkData(final Resilience4JHandle resilience4JHandle) { + resilience4JHandle.setTimeoutDurationRate(Math.max(resilience4JHandle.getTimeoutDurationRate(), Constants.TIMEOUT_DURATION_RATE)); + //resilience4JHandle.setLimitRefreshPeriod(Math.max(resilience4JHandle.getLimitRefreshPeriod(), Constants.LIMIT_REFRESH_PERIOD)); + //resilience4JHandle.setLimitForPeriod(Math.max(resilience4JHandle.getLimitForPeriod(), Constants.LIMIT_FOR_PERIOD)); + // Set the number of tokens refreshed each time to 2, and the time interval for refreshing tokens to 1 second + resilience4JHandle.setLimitRefreshPeriod(1000); + resilience4JHandle.setLimitForPeriod(2); + resilience4JHandle.setTimeoutDuration(1000); + resilience4JHandle.setCircuitEnable(Math.max(resilience4JHandle.getCircuitEnable(), Constants.CIRCUIT_ENABLE)); + //resilience4JHandle.setTimeoutDuration(Math.max(resilience4JHandle.getTimeoutDuration(), Constants.TIMEOUT_DURATION)); + resilience4JHandle.setFallbackUri(!"0".equals(resilience4JHandle.getFallbackUri()) ? resilience4JHandle.getFallbackUri() : ""); + resilience4JHandle.setSlidingWindowSize(Math.max(resilience4JHandle.getSlidingWindowSize(), Constants.SLIDING_WINDOW_SIZE)); + resilience4JHandle.setSlidingWindowType(Math.max(resilience4JHandle.getSlidingWindowType(), Constants.SLIDING_WINDOW_TYPE)); + resilience4JHandle.setMinimumNumberOfCalls(Math.max(resilience4JHandle.getMinimumNumberOfCalls(), Constants.MINIMUM_NUMBER_OF_CALLS)); + resilience4JHandle.setWaitIntervalFunctionInOpenState(Math.max(resilience4JHandle.getWaitIntervalFunctionInOpenState(), Constants.WAIT_INTERVAL_FUNCTION_IN_OPEN_STATE)); + resilience4JHandle.setPermittedNumberOfCallsInHalfOpenState(Math.max(resilience4JHandle.getPermittedNumberOfCallsInHalfOpenState(), Constants.PERMITTED_NUMBER_OF_CALLS_IN_HALF_OPEN_STATE)); + resilience4JHandle.setFailureRateThreshold(Math.max(resilience4JHandle.getFailureRateThreshold(), Constants.FAILURE_RATE_THRESHOLD)); + return resilience4JHandle; + } +``` + +- Use SuperBenchmarker tool, 4 threads, execute 10s + +```java +C:\Users\v-yanb07>sb -u http://localhost:9195/http/test/findByUserId?userId=1 -c 4 -N 10 +Starting at 2021-03-14 15:46:28 +[Press C to stop the test] +23 (RPS: 1) +---------------Finished!---------------- +Finished at 2021-03-14 15:46:51 (took 00:00:23.0477097) +24 (RPS: 1) Status 200: 25 + +RPS: 2.2 (requests/second) +Max: 2020ms +Min: 472ms +Avg: 1677ms + + 50% below 1994ms + 60% below 1997ms + 70% below 1999ms + 80% below 1999ms + 90% below 2001ms + 95% below 2019ms + 98% below 2020ms + 99% below 2020ms +99.9% below 2020ms +``` + +- Output log + +```java +2021-03-14 12:16:35.252 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test +2021-03-14 12:16:36.249 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : Current limiting test +2021-03-14 12:16:36.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test +2021-03-14 12:16:37.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test +2021-03-14 12:16:37.250 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : Current limiting test +2021-03-14 12:16:38.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test +2021-03-14 12:16:38.250 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : Current limiting test +2021-03-14 12:16:39.252 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : Current limiting test +2021-03-14 12:16:39.252 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : Current limiting test +``` + +The console log outputs two lines per second to verify that the throttling is in effect + +### Fuse + +- From the configuration information, we know that the fuse is off by default, and we need to open it. +- Soul-examples-http Add sleep time at call interface + +```java + @GetMapping("/findByUserId") + public UserDTO findByUserId(@RequestParam("userId") final String userId) throws Exception{ + UserDTO userDTO = new UserDTO(); + userDTO.setUserId(userId); + userDTO.setUserName("hello world"); + log.info("Current limiting test"); + + int i = RandomUtils.nextInt(1,3); + if(i %2==0){ + //throw new Exception("Exception thrown"); + Thread.currentThread().sleep(2000); + } + return userDTO; + } +``` + +- Resilience4JHandle # checkData Manually set the timeout to 1s + +```java + resilience4JHandle.setTimeoutDuration(1000); +``` + +- The pos interface calls + > http://localhost:9195/http/test/findByUserId?userId=1 + +In case of multiple requests, some requests return normal data, and some requests return the following data, indicating that the timeout fuse is effective. + +```java +{ + "code": 500, + "message": "Internal Server Error", + "data": "404 NOT_FOUND" +} +``` + +## Interpretation of Resilience4J Plug-in Source Code + +The soul gateway Resilience4j plug-in source code uses a [reactive programming](https://developer.ibm.com/zh/languages/java/articles/j-cn-with-reactor-response-encode/) lot of methods. First, you need to understand responsive programming. + +- Resilience4J Plug-in Directory Structure + +``` +└─resilience4j + │ Resilience4JPlugin.java // Plugin processing, core class + │ + ├─build + │ Resilience4JBuilder.java // Build Resilience4JConf object + │ + ├─conf + │ Resilience4JConf.java + │ + ├─executor + │ CombinedExecutor.java // Limiter and circuit breaker executor + │ Executor.java + │ RateLimiterExecutor.java // Limiter executor + │ + ├─factory + │ Resilience4JRegistryFactory.java // Build limiter and circuit breaker objects + │ + └─handler + Resilience4JHandler.java +``` + +- Resilience4JPlugn # doExecuteResilience4JPlugn inherits AbstractSoulPlugin like other soul plug-ins. As long as it is enabled, it will go to the core method doExecute through the chain mechanism. + +```java + @Override + protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + // Get configuration information object + Resilience4JHandle resilience4JHandle = GsonUtils.getGson().fromJson(rule.getHandle(), Resilience4JHandle.class); + // Check configuration information, assign default values if they're less than defaults + resilience4JHandle = resilience4JHandle.checkData(resilience4JHandle); + // circuitEnable configuration: 1 enables circuit breaker component, otherwise use limiter component + if (resilience4JHandle.getCircuitEnable() == 1) { + return combined(exchange, chain, rule); + } + + return rateLimiter(exchange, chain, rule); + } +``` + +- Current Limiting Resilience4JPlugin # rateLimiter + +```java + private Mono rateLimiter(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) { + return ratelimiterExecutor.run( + // chain.execute(exchange) calls subsequent plugins + chain.execute(exchange), fallback(ratelimiterExecutor, exchange, null), Resilience4JBuilder.build(rule)) + .onErrorResume(throwable -> ratelimiterExecutor.withoutFallback(exchange, throwable)) + + + // Called by ratelimiterExecutor.run + @Override +public Mono run(final Mono toRun, final Function> fallback, final Resilience4JConf conf) { + // Limiter component + RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(conf.getId(), conf.getRateLimiterConfig()); + // Limiting execution + Mono to = toRun.transformDeferred(RateLimiterOperator.of(rateLimiter)); + if (fallback != null) { + // Execute fallback + return to.onErrorResume(fallback); + } + return to; +} + + + // to.onErrorResume(fallback); + default Mono fallback(ServerWebExchange exchange, String uri, Throwable t) { + if (StringUtils.isBlank(uri)) { + return withoutFallback(exchange, t); + } + DispatcherHandler dispatcherHandler = SpringBeanUtils.getInstance().getBean(DispatcherHandler.class); + ServerHttpRequest request = exchange.getRequest().mutate().uri(Objects.requireNonNull(UriUtils.createUri(uri))).build(); + ServerWebExchange mutated = exchange.mutate().request(request).build(); + // Execute the fallback + return dispatcherHandler.handle(mutated); +} +``` + +- Fuse Resilience 4JPlugin # combined + +```java + private Mono combined(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) { + Resilience4JConf conf = Resilience4JBuilder.build(rule); + return combinedExecutor.run( + chain.execute(exchange).doOnSuccess(v -> { + HttpStatus status = exchange.getResponse().getStatusCode(); + if (status == null || !status.is2xxSuccessful()) { + exchange.getResponse().setStatusCode(null); + throw new CircuitBreakerStatusCodeException(status == null ? HttpStatus.INTERNAL_SERVER_ERROR : status); + } + }), fallback(combinedExecutor, exchange, conf.getFallBackUri()), conf); + } + + + // Executed in combinedExecutor#run + public Mono run(final Mono run, final Function> fallback, final Resilience4JConf resilience4JConf) { + RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(resilience4JConf.getId(), resilience4JConf.getRateLimiterConfig()); + CircuitBreaker circuitBreaker = Resilience4JRegistryFactory.circuitBreaker(resilience4JConf.getId(), resilience4JConf.getCircuitBreakerConfig()); + //Circuit breaker operation + Mono to = run.transformDeferred(CircuitBreakerOperator.of(circuitBreaker)) + // Limiting operation + .transformDeferred(RateLimiterOperator.of(rateLimiter)) + // Set timeout + .timeout(resilience4JConf.getTimeLimiterConfig().getTimeoutDuration()) + // Throw timeout exception if timeout occurs + .doOnError(TimeoutException.class, t -> circuitBreaker.onError( + resilience4JConf.getTimeLimiterConfig().getTimeoutDuration().toMillis(), + TimeUnit.MILLISECONDS, + t)); + if (fallback != null) { + to = to.onErrorResume(fallback); + } + return to; + } +``` + +## Sum up + +- The soul gateway provides current limiting and fusing, and the fusing is off by default +- If the parameter value is less than the default value, the default value will be used directly diff --git a/src/blog/soul_source_learning_22_apache_dubbo.md b/src/blog/soul_source_learning_22_apache_dubbo.md index b860bff343..bacd8f3943 100644 --- a/src/blog/soul_source_learning_22_apache_dubbo.md +++ b/src/blog/soul_source_learning_22_apache_dubbo.md @@ -1,501 +1,501 @@ ---- -title: Soul Gateway Learning Apache Dubbo Plugin -author: nuo-promise -date: 2021-03-23 -tag: - - Soul -cover: /assets/img/blog8/08.jpg -head: - - - meta - - name: Blog ---- - -## Aim - -- Introduction to the Apache Dubbo Plugin - - Introduction to metadata -- Apache Dubbo Plugin Configuration - - Bootstrap POM configuration - - Soul-admin Configuration - - Dubbo service POM configuration -- Introduction to Apache Dubbo Generalization Calls - - Using Generalization Calls via the API - - Using generalized calls with spring - - Generalization call implementation flow -- Soul Dubbo Plugin Call Resolution - - ApachDubboPlugin Generalization Call Preparation - - ApacheDubboProxySerivce - - DubboResponsePlugin - - Web FluxResultUtils returns results -- Introduction to Dubbo Generalization Calls -- Sum up -- Reference - -### Introduction to the Apache Dubbo Plugin - -Apache Dubbo is a high-performance and lightweight open source Java service framework, which mainly provides six core capabilities: high-performance RPC invocation for interface agents, intelligent fault tolerance and load balancing, automatic service registration and discovery, high scalability, run-time traffic scheduling, and visual service governance and operation and maintenance. The Dubbo plug-in in the gateway is mainly used to convert `Http requests` to `Dubbo protocol`, and it is also the key for the gateway to implement Dubbo generalization calls. Dubbo plug-ins need to cooperate `metadata` to implement Dubbo calls. - -#### Introduction to metadata - -The function of metadata is to get the real request `path` and `methodName` `parameterTypes` prepare for the generalization call during the protocol conversion. - -![](/assets/img/blog8/01.png) - -- In the database, we have a separate table to store Dubbo meta information. Through the data synchronization scheme, the data of this table will be synchronized to the JVM memory of the gateway -- The table is structured as follows - -```sql -CREATE TABLE IF NOT EXISTS `meta_data` ( -`id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'id', -`app_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Application Name', -`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Path, should be unique', -`path_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Path description', -`rpc_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'RPC type', -`service_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Service Name', -`method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Method Name', -`parameter_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Parameter Types, comma-separated', -`rpc_ext` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'RPC extension information in JSON format', -`date_created` datetime(0) NOT NULL COMMENT 'Creation Time', -`date_updated` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT 'Update Time', -`enabled` tinyint(4) NOT NULL DEFAULT 0 COMMENT 'Enable State', -PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; -``` - -- The `path` field is mainly used to match a piece of data according to your `path` field when requesting the gateway, and then carry out the subsequent processing process. -- `rpc_ext` Field If the proxy interface is a `Dubbo` service interface of type and the field is set `group` `version`, the information will be stored `rpc_ext` in -- Each `Dubbo` interface method will deal with a piece of metadata. Compared with Spring Cloud and HTTP, only one piece of/contextPath/\ \*\* is stored and none is stored respectively. - -### Apache Dubbo Plugin Configuration - -#### Soul-bootstrap POM configuration - -```java - - org.dromara - soul-spring-boot-starter-plugin-apache-dubbo - ${project.version} - - - org.apache.dubbo - dubbo - 2.7.5 - - - org.apache.curator - curator-client - ${curator.version} - - - org.apache.curator - curator-framework - ${curator.version} - - - org.apache.curator - curator-recipes - ${curator.version} - -``` - -#### Soul-admin Configuration - -![](/assets/img/blog8/02.png) - -> Log in to soul-admin background, open the switch of Dubbo configuration option on the plug-in management page, and fill in the connection address of the registry. - -#### Dubbo service POM configuration - -```java - - org.dromara - soul-spring-boot-starter-client-apache-dubbo - ${soul.version} - -``` - -```java -@SoulDubboClient(path = "/insert", desc = "Insert a row of data") -public DubboTest insert(final DubboTest dubboTest) { - dubboTest.setName("hello world Soul Apache Dubbo: " + dubboTest.getName()); - return dubboTest; -} -``` - -> The proxied service uses the `soul-spring-boot-starter-client-apache-dubbo` provided client dependencies and `@SoulDubboClient` annotations to register the interface name, parameter type, and parameter content to the `soul-admin` end at startup, and then the backend `admin` synchronizes the data to the `bootstrap` end. - -### Introduction to Apache Dubbo Generalization Calls - -The generalized interface calling mode is mainly used when the client does not have an API interface and a model class element. All POJOs in the parameters and return values are represented by `Map`. It is usually used for framework integration and can be implemented by calling all services through GenericS. - -#### Using generalized calls through the API (the way the gateway is currently used) - -```java -ReferenceConfig reference = new ReferenceConfig<>(); -reference.setGeneric(true); -reference.setApplication(applicationConfig); -reference.setRegistry(registryConfig); -reference.setInterface(metaData.getServiceName()); -reference.setProtocol("dubbo"); -``` - -> The gateway uses the generic call through API declaration and registration. - -#### Using Generalization Calls with Spring - -```java - -``` - -#### Generalization call implementation flow - -```java -+-------------------------------------------+ +-------------------------------------------+ -| consumer 端 | | provider 端 | -| | | | -| | | | -| | | | -| | | | -| +------------------+ | | +--------------+ | -| |GenericImplFilter | | Invocation | |GenericFilter | | -| +----> | +-------------------------> | | | -| | +------------------+ | | +--------------+ | -| +-----------+ | | | +-----------+ | -| | | | | | | | | -| |Client | | | +--> | Service | | -| | | | | | | | -| +-----------+ | | +-------+---+ | -| | | | | -| ^ +------------------+ | | +--------------+ | | -| | |GenericImplFilter | | | |GenericFilter | <----------+ | -| +-------------+ | <-------------------------+ | | -| +------------------+ | | +--------------+ | -| | | | -| | | | -| | | | -| | | | -+-------------------------------------------+ +-------------------------------------------+ -``` - -> `GenericService` This interface is very similar to Java's reflection call. You only need to provide the name of the method called, the type of the parameter, and the value of the parameter to call the corresponding method directly. -> -> - Generic Filter: responsible for the conversion of provider-side parameters -> - Converts the parameters of the hashMap structure to the corresponding Pojo when called -> - The return result is to convert Pojo to hashMap. -> -> ![](/assets/img/blog8/03.png) -> -> - GenericImpl Filter: It is responsible for the conversion of consumer side parameters and converting Pojo to hashMap interface. -> -> ![](/assets/img/blog8/04.png) - -```java -/** - * Generic service interface - * - * @export - */ -public interface GenericService { - - /** - * Generic invocation - * - * @param method Method name, e.g., findPerson. If there are overloaded methods, include the parameter list, e.g., findPerson(java.lang.String) - * @param parameterTypes Parameter types - * @param args Parameter list - * @return invocation result - * @throws GenericException Exception thrown by the method - */ - Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; - - default CompletableFuture $invokeAsync(String method, String[] parameterTypes, Object[] args) throws GenericException { - Object object = $invoke(method, parameterTypes, args); - if (object instanceof CompletableFuture) { - return (CompletableFuture) object; - } - return CompletableFuture.completedFuture(object); - } - -} -``` - -### Soul Dubbo Plugin Call Resolution - -When a service request is initiated, the method of the `Handle` class is entered `SoulWebHandler` first (as to why it becomes the request entry to query by itself, this article will not explain). The following is the `plugins` plug-in chain call from `DefaultSoulPluginChain` the class. - -```java -@Override - public Mono handle(@NonNull final ServerWebExchange exchange) { - return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler); - } -``` - -```java -@Override -public Mono execute(final ServerWebExchange exchange) { - // Reactive programming - return Mono.defer(() -> { - // Check if the current index is less than the number of plugins - if (this.index < plugins.size()) { - // Get a plugin from plugins one by one - SoulPlugin plugin = plugins.get(this.index++); - // Check if this plugin should be skipped - Boolean skip = plugin.skip(exchange); - if (skip) { - return this.execute(exchange); - } - return plugin.execute(exchange, this); - } - return Mono.empty(); - }); -} -``` - -> This chapter focuses only on Apache Dubbo, so we'll focus on the invocation of the Dubbo plug-in. -> ![](/assets/img/blog8/05.png) -> Through the Debug gateway program, we know that it is actually judged and called one by one according to the above order. Let's focus on `ApacheDubboPlugin` - -#### ApachDubboPlugin Generalization Call Preparation - -```java -@Override - protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - // Get dubbo_params data - String body = exchange.getAttribute(Constants.DUBBO_PARAMS); - // Get attribute value from exchange context - SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - // Get attribute value from exchange metaData - MetaData metaData = exchange.getAttribute(Constants.META_DATA); - // Check if metaData is incorrect, if so, return an error response - if (!checkMetaData(metaData)) { - assert metaData != null; - log.error(" path is :{}, meta data have error.... {}", soulContext.getPath(), metaData.toString()); - exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); - Object error = SoulResultWrap.error(SoulResultEnum.META_DATA_ERROR.getCode(), SoulResultEnum.META_DATA_ERROR.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // Check if parameterTypes and body in metaData are empty, if so, return a body error response - if (StringUtils.isNoneBlank(metaData.getParameterTypes()) && StringUtils.isBlank(body)) { - exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); - Object error = SoulResultWrap.error(SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getCode(), SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // Perform asynchronous call to Dubbo GenericsService with exchange, body, and metaData - final Mono result = dubboProxyService.genericInvoker(body, metaData, exchange); - return result.then(chain.execute(exchange)); - } -``` - -> First, check the parameters required by the generalization call. - -#### ApacheDubboProxyService - -```java -public Mono genericInvoker(final String body, final MetaData metaData, final ServerWebExchange exchange) throws SoulException { - // issue(https://github.com/dromara/soul/issues/471), add dubbo tag route - String dubboTagRouteFromHttpHeaders = exchange.getRequest().getHeaders().getFirst(Constants.DUBBO_TAG_ROUTE); - if (StringUtils.isNotBlank(dubboTagRouteFromHttpHeaders)) { - RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, dubboTagRouteFromHttpHeaders); - } - // Get reference based on metaData path - ReferenceConfig reference = ApplicationConfigCache.getInstance().get(metaData.getPath()); - if (Objects.isNull(reference) || StringUtils.isEmpty(reference.getInterface())) { - ApplicationConfigCache.getInstance().invalidate(metaData.getPath()); - reference = ApplicationConfigCache.getInstance().initRef(metaData); - } - // et the instance of GenericService for generic invocation based on reference - GenericService genericService = reference.get(); - Pair pair; - if (ParamCheckUtils.dubboBodyIsEmpty(body)) { - pair = new ImmutablePair<>(new String[]{}, new Object[]{}); - } else { - // Organize parameter types and values for Dubbo generic invocation based on body and parameterTypes - pair = dubboParamResolveService.buildParameter(body, metaData.getParameterTypes()); - } - // Perform asynchronous call using the default $invokeAsync method of GenericService - CompletableFuture future = genericService.$invokeAsync(metaData.getMethodName(), pair.getLeft(), pair.getRight()); - return Mono.fromFuture(future.thenApply(ret -> { - if (Objects.isNull(ret)) { - ret = Constants.DUBBO_RPC_RESULT_EMPTY; - } - // After successful invocation, copy the result and type to attributes of the exchange - exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, ret); - exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); - return ret; - })).onErrorMap(exception -> exception instanceof GenericException ? new SoulException(((GenericException) exception).getExceptionMessage()) : new SoulException(exception)); -} -``` - -#### DubboResponsePlugin - -```java -@Override -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - return chain.execute(exchange).then(Mono.defer(() -> { - final Object result = exchange.getAttribute(Constants.DUBBO_RPC_RESULT); - if (Objects.isNull(result)) { - Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - Object success = SoulResultWrap.success(SoulResultEnum.SUCCESS.getCode(), SoulResultEnum.SUCCESS.getMsg(), JsonUtils.removeClass(result)); - return WebFluxResultUtils.result(exchange, success); - })); -} -``` - -#### ![](/assets/img/blog8/06.png) - -#### Web FluxResultUtils returns results - -![](/assets/img/blog8/07.png) - -### Introduction to Dubbo Generalization Calls - -Dubbo generalized invocation is mainly divided into two parts, namely, how to use `GenericImplFilter` the consumer side to intercept the generalized invocation, and how to use `GenericFilter` the service provider side to serialize the generalized parameters after intercepting the request and then request the specific service. - -#### How does the service consumer org. Apache. Dubbo. RPC. Filter. GenericImplFilter intercept generalized calls? - -```java -@Activate(group = CommonConstants.CONSUMER, value = GENERIC_KEY, order = 20000) -public class GenericImplFilter implements Filter, Filter.Listener { -@Override - public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { - // ... Omitted non-core code - // Check if it's a generic call - if (isMakingGenericCall(generic, invocation)) { - // Get the generic parameters - Object[] args = (Object[]) invocation.getArguments()[2]; - // If it's nativeJava mode - if (ProtocolUtils.isJavaGenericSerialization(generic)) { - for (Object arg : args) { - if (!(byte[].class == arg.getClass())) { - error(generic, byte[].class.getName(), arg.getClass().getName()); - } - } - // If it's bean mode - } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { - for (Object arg : args) { - if (!(arg instanceof JavaBeanDescriptor)) { - error(generic, JavaBeanDescriptor.class.getName(), arg.getClass().getName()); - } - } - } - - // Set attachment for server-side invocation - invocation.setAttachment( - GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY)); - } - // Perform remote invocation - return invoker.invoke(invocation); - } - private boolean isMakingGenericCall(String generic, Invocation invocation) { - return (invocation.getMethodName().equals($INVOKE) || invocation.getMethodName().equals($INVOKE_ASYNC)) - && invocation.getArguments() != null - && invocation.getArguments().length == 3 - && ProtocolUtils.isGeneric(generic); - } -} -``` - -> GenericImpl Filter implements the interface Filter (I will not introduce the Filter in Dubbo) and then executes the Invoke method. The invoke method mainly does the following things: -> -> - Parameter validation to check whether the call is a generalized call -> - Get the generalization parameters -> - Determine the generalization calling mode: traverse each parameter, and then determine whether the generalization mode of the parameter is native Java or bean in turn -> - Initiates a remote call - -#### The service provider intercepts the generalization request through Generic Filter. - -```java -@Activate(group = CommonConstants.PROVIDER, order = -20000) -public class GenericFilter implements Filter, Filter.Listener { - @Override - public Result invoke(Invoker invoker, Invocation inv) throws RpcException { - // Parameter validation - if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC)) - && inv.getArguments() != null - && inv.getArguments().length == 3 - && !GenericService.class.isAssignableFrom(invoker.getInterface())) { - // Get parameter name, type, and value - String name = ((String) inv.getArguments()[0]).trim(); - String[] types = (String[]) inv.getArguments()[1]; - Object[] args = (Object[]) inv.getArguments()[2]; - try { - // Get the called method using reflection - Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types); - Class[] params = method.getParameterTypes(); - if (args == null) { - args = new Object[params.length]; - } - // Get the generic type used for the generic reference, true or bean or nativejava - String generic = inv.getAttachment(GENERIC_KEY); - if (StringUtils.isBlank(generic)) { - generic = RpcContext.getContext().getAttachment(GENERIC_KEY); - } - // If generic=true, deserialize parameters using the true method - if (StringUtils.isEmpty(generic) - || ProtocolUtils.isDefaultGenericSerialization(generic) - || ProtocolUtils.isGenericReturnRawResult(generic)) { - args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); - // If generic=nativejava, deserialize parameters using the nativejava method - } else if (ProtocolUtils.isJavaGenericSerialization(generic)) { - for (int i = 0; i < args.length; i++) { - if (byte[].class == args[i].getClass()) { - try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) { - args[i] = ExtensionLoader.getExtensionLoader(Serialization.class) - .getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA) - .deserialize(null, is).readObject(); - } catch (Exception e) { - throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e); - } - } else { - throw new RpcException(...); - } - } - // If generic=bean, deserialize parameters using the bean method - } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { - for (int i = 0; i < args.length; i++) { - if (args[i] instanceof JavaBeanDescriptor) { - args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]); - } else { - throw new RpcException(...); - } - } - } ... - // Pass the current request to the next Filter in the FilterChain and return the result - RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), args, inv.getAttachments(), inv.getAttributes()); - rpcInvocation.setInvoker(inv.getInvoker()); - rpcInvocation.setTargetServiceUniqueName(inv.getTargetServiceUniqueName()); - - return invoker.invoke(rpcInvocation); - } catch (NoSuchMethodException e) { - throw new RpcException(e.getMessage(), e); - } catch (ClassNotFoundException e) { - throw new RpcException(e.getMessage(), e); - } - } - // If it's not a generic call, pass the request directly to the next Filter in the FilterChain - return invoker.invoke(inv); - } -} -``` - -> The above is the general process of how the Dubbo service provider intercepts the generalization request and processes it: -> -> - Parameter check to determine whether the request is a generalized call -> - Get the parameter name, parameter type, parameter value, -> - Use reflection to get the method called, and the generalization used `true` or or -> - Deserialize the generalization parameters based on the generalization method -> - Pass the request, including the called method, parameters and context information, to the next Filter in the FilterChain, and return the Result -> - According to the generalization method, deserialize the Result and return it to the service consumer - -### Sum up - -The above is the analysis of how to configure the Dubbo plug-in to the entire call process, and then respectively introduce the details of how the service consumer and service provider intercept the generalized call process and serialize the parameters. I hope it will be helpful to you. - -### Reference - -[https://my.oschina.net/u/4564034/blog/4409382](https://my.oschina.net/u/4564034/blog/4409382) - -[https://qsli.github.io/2018/05/02/dubbo-generic-invoke/](https://qsli.github.io/2018/05/02/dubbo-generic-invoke/) +--- +title: Soul Gateway Learning Apache Dubbo Plugin +author: nuo-promise +date: 2021-03-23 +tag: + - Soul +cover: /assets/img/blog8/08.jpg +head: + - - meta + - name: Blog +--- + +## Aim + +- Introduction to the Apache Dubbo Plugin + - Introduction to metadata +- Apache Dubbo Plugin Configuration + - Bootstrap POM configuration + - Soul-admin Configuration + - Dubbo service POM configuration +- Introduction to Apache Dubbo Generalization Calls + - Using Generalization Calls via the API + - Using generalized calls with spring + - Generalization call implementation flow +- Soul Dubbo Plugin Call Resolution + - ApachDubboPlugin Generalization Call Preparation + - ApacheDubboProxySerivce + - DubboResponsePlugin + - Web FluxResultUtils returns results +- Introduction to Dubbo Generalization Calls +- Sum up +- Reference + +### Introduction to the Apache Dubbo Plugin + +Apache Dubbo is a high-performance and lightweight open source Java service framework, which mainly provides six core capabilities: high-performance RPC invocation for interface agents, intelligent fault tolerance and load balancing, automatic service registration and discovery, high scalability, run-time traffic scheduling, and visual service governance and operation and maintenance. The Dubbo plug-in in the gateway is mainly used to convert `Http requests` to `Dubbo protocol`, and it is also the key for the gateway to implement Dubbo generalization calls. Dubbo plug-ins need to cooperate `metadata` to implement Dubbo calls. + +#### Introduction to metadata + +The function of metadata is to get the real request `path` and `methodName` `parameterTypes` prepare for the generalization call during the protocol conversion. + +![](/assets/img/blog8/01.png) + +- In the database, we have a separate table to store Dubbo meta information. Through the data synchronization scheme, the data of this table will be synchronized to the JVM memory of the gateway +- The table is structured as follows + +```sql +CREATE TABLE IF NOT EXISTS `meta_data` ( +`id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'id', +`app_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Application Name', +`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Path, should be unique', +`path_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Path description', +`rpc_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'RPC type', +`service_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Service Name', +`method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Method Name', +`parameter_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Parameter Types, comma-separated', +`rpc_ext` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'RPC extension information in JSON format', +`date_created` datetime(0) NOT NULL COMMENT 'Creation Time', +`date_updated` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT 'Update Time', +`enabled` tinyint(4) NOT NULL DEFAULT 0 COMMENT 'Enable State', +PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; +``` + +- The `path` field is mainly used to match a piece of data according to your `path` field when requesting the gateway, and then carry out the subsequent processing process. +- `rpc_ext` Field If the proxy interface is a `Dubbo` service interface of type and the field is set `group` `version`, the information will be stored `rpc_ext` in +- Each `Dubbo` interface method will deal with a piece of metadata. Compared with Spring Cloud and HTTP, only one piece of/contextPath/\ \*\* is stored and none is stored respectively. + +### Apache Dubbo Plugin Configuration + +#### Soul-bootstrap POM configuration + +```java + + org.dromara + soul-spring-boot-starter-plugin-apache-dubbo + ${project.version} + + + org.apache.dubbo + dubbo + 2.7.5 + + + org.apache.curator + curator-client + ${curator.version} + + + org.apache.curator + curator-framework + ${curator.version} + + + org.apache.curator + curator-recipes + ${curator.version} + +``` + +#### Soul-admin Configuration + +![](/assets/img/blog8/02.png) + +> Log in to soul-admin background, open the switch of Dubbo configuration option on the plug-in management page, and fill in the connection address of the registry. + +#### Dubbo service POM configuration + +```java + + org.dromara + soul-spring-boot-starter-client-apache-dubbo + ${soul.version} + +``` + +```java +@SoulDubboClient(path = "/insert", desc = "Insert a row of data") +public DubboTest insert(final DubboTest dubboTest) { + dubboTest.setName("hello world Soul Apache Dubbo: " + dubboTest.getName()); + return dubboTest; +} +``` + +> The proxied service uses the `soul-spring-boot-starter-client-apache-dubbo` provided client dependencies and `@SoulDubboClient` annotations to register the interface name, parameter type, and parameter content to the `soul-admin` end at startup, and then the backend `admin` synchronizes the data to the `bootstrap` end. + +### Introduction to Apache Dubbo Generalization Calls + +The generalized interface calling mode is mainly used when the client does not have an API interface and a model class element. All POJOs in the parameters and return values are represented by `Map`. It is usually used for framework integration and can be implemented by calling all services through GenericS. + +#### Using generalized calls through the API (the way the gateway is currently used) + +```java +ReferenceConfig reference = new ReferenceConfig<>(); +reference.setGeneric(true); +reference.setApplication(applicationConfig); +reference.setRegistry(registryConfig); +reference.setInterface(metaData.getServiceName()); +reference.setProtocol("dubbo"); +``` + +> The gateway uses the generic call through API declaration and registration. + +#### Using Generalization Calls with Spring + +```java + +``` + +#### Generalization call implementation flow + +```java ++-------------------------------------------+ +-------------------------------------------+ +| consumer 端 | | provider 端 | +| | | | +| | | | +| | | | +| | | | +| +------------------+ | | +--------------+ | +| |GenericImplFilter | | Invocation | |GenericFilter | | +| +----> | +-------------------------> | | | +| | +------------------+ | | +--------------+ | +| +-----------+ | | | +-----------+ | +| | | | | | | | | +| |Client | | | +--> | Service | | +| | | | | | | | +| +-----------+ | | +-------+---+ | +| | | | | +| ^ +------------------+ | | +--------------+ | | +| | |GenericImplFilter | | | |GenericFilter | <----------+ | +| +-------------+ | <-------------------------+ | | +| +------------------+ | | +--------------+ | +| | | | +| | | | +| | | | +| | | | ++-------------------------------------------+ +-------------------------------------------+ +``` + +> `GenericService` This interface is very similar to Java's reflection call. You only need to provide the name of the method called, the type of the parameter, and the value of the parameter to call the corresponding method directly. +> +> - Generic Filter: responsible for the conversion of provider-side parameters +> - Converts the parameters of the hashMap structure to the corresponding Pojo when called +> - The return result is to convert Pojo to hashMap. +> +> ![](/assets/img/blog8/03.png) +> +> - GenericImpl Filter: It is responsible for the conversion of consumer side parameters and converting Pojo to hashMap interface. +> +> ![](/assets/img/blog8/04.png) + +```java +/** + * Generic service interface + * + * @export + */ +public interface GenericService { + + /** + * Generic invocation + * + * @param method Method name, e.g., findPerson. If there are overloaded methods, include the parameter list, e.g., findPerson(java.lang.String) + * @param parameterTypes Parameter types + * @param args Parameter list + * @return invocation result + * @throws GenericException Exception thrown by the method + */ + Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; + + default CompletableFuture $invokeAsync(String method, String[] parameterTypes, Object[] args) throws GenericException { + Object object = $invoke(method, parameterTypes, args); + if (object instanceof CompletableFuture) { + return (CompletableFuture) object; + } + return CompletableFuture.completedFuture(object); + } + +} +``` + +### Soul Dubbo Plugin Call Resolution + +When a service request is initiated, the method of the `Handle` class is entered `SoulWebHandler` first (as to why it becomes the request entry to query by itself, this article will not explain). The following is the `plugins` plug-in chain call from `DefaultSoulPluginChain` the class. + +```java +@Override + public Mono handle(@NonNull final ServerWebExchange exchange) { + return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler); + } +``` + +```java +@Override +public Mono execute(final ServerWebExchange exchange) { + // Reactive programming + return Mono.defer(() -> { + // Check if the current index is less than the number of plugins + if (this.index < plugins.size()) { + // Get a plugin from plugins one by one + SoulPlugin plugin = plugins.get(this.index++); + // Check if this plugin should be skipped + Boolean skip = plugin.skip(exchange); + if (skip) { + return this.execute(exchange); + } + return plugin.execute(exchange, this); + } + return Mono.empty(); + }); +} +``` + +> This chapter focuses only on Apache Dubbo, so we'll focus on the invocation of the Dubbo plug-in. +> ![](/assets/img/blog8/05.png) +> Through the Debug gateway program, we know that it is actually judged and called one by one according to the above order. Let's focus on `ApacheDubboPlugin` + +#### ApachDubboPlugin Generalization Call Preparation + +```java +@Override + protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + // Get dubbo_params data + String body = exchange.getAttribute(Constants.DUBBO_PARAMS); + // Get attribute value from exchange context + SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + // Get attribute value from exchange metaData + MetaData metaData = exchange.getAttribute(Constants.META_DATA); + // Check if metaData is incorrect, if so, return an error response + if (!checkMetaData(metaData)) { + assert metaData != null; + log.error(" path is :{}, meta data have error.... {}", soulContext.getPath(), metaData.toString()); + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + Object error = SoulResultWrap.error(SoulResultEnum.META_DATA_ERROR.getCode(), SoulResultEnum.META_DATA_ERROR.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // Check if parameterTypes and body in metaData are empty, if so, return a body error response + if (StringUtils.isNoneBlank(metaData.getParameterTypes()) && StringUtils.isBlank(body)) { + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + Object error = SoulResultWrap.error(SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getCode(), SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // Perform asynchronous call to Dubbo GenericsService with exchange, body, and metaData + final Mono result = dubboProxyService.genericInvoker(body, metaData, exchange); + return result.then(chain.execute(exchange)); + } +``` + +> First, check the parameters required by the generalization call. + +#### ApacheDubboProxyService + +```java +public Mono genericInvoker(final String body, final MetaData metaData, final ServerWebExchange exchange) throws SoulException { + // issue(https://github.com/dromara/soul/issues/471), add dubbo tag route + String dubboTagRouteFromHttpHeaders = exchange.getRequest().getHeaders().getFirst(Constants.DUBBO_TAG_ROUTE); + if (StringUtils.isNotBlank(dubboTagRouteFromHttpHeaders)) { + RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, dubboTagRouteFromHttpHeaders); + } + // Get reference based on metaData path + ReferenceConfig reference = ApplicationConfigCache.getInstance().get(metaData.getPath()); + if (Objects.isNull(reference) || StringUtils.isEmpty(reference.getInterface())) { + ApplicationConfigCache.getInstance().invalidate(metaData.getPath()); + reference = ApplicationConfigCache.getInstance().initRef(metaData); + } + // et the instance of GenericService for generic invocation based on reference + GenericService genericService = reference.get(); + Pair pair; + if (ParamCheckUtils.dubboBodyIsEmpty(body)) { + pair = new ImmutablePair<>(new String[]{}, new Object[]{}); + } else { + // Organize parameter types and values for Dubbo generic invocation based on body and parameterTypes + pair = dubboParamResolveService.buildParameter(body, metaData.getParameterTypes()); + } + // Perform asynchronous call using the default $invokeAsync method of GenericService + CompletableFuture future = genericService.$invokeAsync(metaData.getMethodName(), pair.getLeft(), pair.getRight()); + return Mono.fromFuture(future.thenApply(ret -> { + if (Objects.isNull(ret)) { + ret = Constants.DUBBO_RPC_RESULT_EMPTY; + } + // After successful invocation, copy the result and type to attributes of the exchange + exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, ret); + exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); + return ret; + })).onErrorMap(exception -> exception instanceof GenericException ? new SoulException(((GenericException) exception).getExceptionMessage()) : new SoulException(exception)); +} +``` + +#### DubboResponsePlugin + +```java +@Override +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + return chain.execute(exchange).then(Mono.defer(() -> { + final Object result = exchange.getAttribute(Constants.DUBBO_RPC_RESULT); + if (Objects.isNull(result)) { + Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + Object success = SoulResultWrap.success(SoulResultEnum.SUCCESS.getCode(), SoulResultEnum.SUCCESS.getMsg(), JsonUtils.removeClass(result)); + return WebFluxResultUtils.result(exchange, success); + })); +} +``` + +#### ![](/assets/img/blog8/06.png) + +#### Web FluxResultUtils returns results + +![](/assets/img/blog8/07.png) + +### Introduction to Dubbo Generalization Calls + +Dubbo generalized invocation is mainly divided into two parts, namely, how to use `GenericImplFilter` the consumer side to intercept the generalized invocation, and how to use `GenericFilter` the service provider side to serialize the generalized parameters after intercepting the request and then request the specific service. + +#### How does the service consumer org. Apache. Dubbo. RPC. Filter. GenericImplFilter intercept generalized calls? + +```java +@Activate(group = CommonConstants.CONSUMER, value = GENERIC_KEY, order = 20000) +public class GenericImplFilter implements Filter, Filter.Listener { +@Override + public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { + // ... Omitted non-core code + // Check if it's a generic call + if (isMakingGenericCall(generic, invocation)) { + // Get the generic parameters + Object[] args = (Object[]) invocation.getArguments()[2]; + // If it's nativeJava mode + if (ProtocolUtils.isJavaGenericSerialization(generic)) { + for (Object arg : args) { + if (!(byte[].class == arg.getClass())) { + error(generic, byte[].class.getName(), arg.getClass().getName()); + } + } + // If it's bean mode + } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { + for (Object arg : args) { + if (!(arg instanceof JavaBeanDescriptor)) { + error(generic, JavaBeanDescriptor.class.getName(), arg.getClass().getName()); + } + } + } + + // Set attachment for server-side invocation + invocation.setAttachment( + GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY)); + } + // Perform remote invocation + return invoker.invoke(invocation); + } + private boolean isMakingGenericCall(String generic, Invocation invocation) { + return (invocation.getMethodName().equals($INVOKE) || invocation.getMethodName().equals($INVOKE_ASYNC)) + && invocation.getArguments() != null + && invocation.getArguments().length == 3 + && ProtocolUtils.isGeneric(generic); + } +} +``` + +> GenericImpl Filter implements the interface Filter (I will not introduce the Filter in Dubbo) and then executes the Invoke method. The invoke method mainly does the following things: +> +> - Parameter validation to check whether the call is a generalized call +> - Get the generalization parameters +> - Determine the generalization calling mode: traverse each parameter, and then determine whether the generalization mode of the parameter is native Java or bean in turn +> - Initiates a remote call + +#### The service provider intercepts the generalization request through Generic Filter. + +```java +@Activate(group = CommonConstants.PROVIDER, order = -20000) +public class GenericFilter implements Filter, Filter.Listener { + @Override + public Result invoke(Invoker invoker, Invocation inv) throws RpcException { + // Parameter validation + if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC)) + && inv.getArguments() != null + && inv.getArguments().length == 3 + && !GenericService.class.isAssignableFrom(invoker.getInterface())) { + // Get parameter name, type, and value + String name = ((String) inv.getArguments()[0]).trim(); + String[] types = (String[]) inv.getArguments()[1]; + Object[] args = (Object[]) inv.getArguments()[2]; + try { + // Get the called method using reflection + Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types); + Class[] params = method.getParameterTypes(); + if (args == null) { + args = new Object[params.length]; + } + // Get the generic type used for the generic reference, true or bean or nativejava + String generic = inv.getAttachment(GENERIC_KEY); + if (StringUtils.isBlank(generic)) { + generic = RpcContext.getContext().getAttachment(GENERIC_KEY); + } + // If generic=true, deserialize parameters using the true method + if (StringUtils.isEmpty(generic) + || ProtocolUtils.isDefaultGenericSerialization(generic) + || ProtocolUtils.isGenericReturnRawResult(generic)) { + args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); + // If generic=nativejava, deserialize parameters using the nativejava method + } else if (ProtocolUtils.isJavaGenericSerialization(generic)) { + for (int i = 0; i < args.length; i++) { + if (byte[].class == args[i].getClass()) { + try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) { + args[i] = ExtensionLoader.getExtensionLoader(Serialization.class) + .getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA) + .deserialize(null, is).readObject(); + } catch (Exception e) { + throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e); + } + } else { + throw new RpcException(...); + } + } + // If generic=bean, deserialize parameters using the bean method + } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof JavaBeanDescriptor) { + args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]); + } else { + throw new RpcException(...); + } + } + } ... + // Pass the current request to the next Filter in the FilterChain and return the result + RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), args, inv.getAttachments(), inv.getAttributes()); + rpcInvocation.setInvoker(inv.getInvoker()); + rpcInvocation.setTargetServiceUniqueName(inv.getTargetServiceUniqueName()); + + return invoker.invoke(rpcInvocation); + } catch (NoSuchMethodException e) { + throw new RpcException(e.getMessage(), e); + } catch (ClassNotFoundException e) { + throw new RpcException(e.getMessage(), e); + } + } + // If it's not a generic call, pass the request directly to the next Filter in the FilterChain + return invoker.invoke(inv); + } +} +``` + +> The above is the general process of how the Dubbo service provider intercepts the generalization request and processes it: +> +> - Parameter check to determine whether the request is a generalized call +> - Get the parameter name, parameter type, parameter value, +> - Use reflection to get the method called, and the generalization used `true` or or +> - Deserialize the generalization parameters based on the generalization method +> - Pass the request, including the called method, parameters and context information, to the next Filter in the FilterChain, and return the Result +> - According to the generalization method, deserialize the Result and return it to the service consumer + +### Sum up + +The above is the analysis of how to configure the Dubbo plug-in to the entire call process, and then respectively introduce the details of how the service consumer and service provider intercept the generalized call process and serialize the parameters. I hope it will be helpful to you. + +### Reference + +[https://my.oschina.net/u/4564034/blog/4409382](https://my.oschina.net/u/4564034/blog/4409382) + +[https://qsli.github.io/2018/05/02/dubbo-generic-invoke/](https://qsli.github.io/2018/05/02/dubbo-generic-invoke/) diff --git a/src/blog/springboot-forest-deepseek-integration.md b/src/blog/springboot-forest-deepseek-integration.md new file mode 100644 index 0000000000..c43e90ced1 --- /dev/null +++ b/src/blog/springboot-forest-deepseek-integration.md @@ -0,0 +1,342 @@ +--- +title: How to Use Forest to Easily and Quickly Integrate DeepSeek in a SpringBoot Project +author: March 7, 2025 09:58 +date: 2025-03-07 +cover: /assets/img/blog/springboot-forest-deepseek-integration-0.png +head: + - - meta + - name: Blog +--- + +## 1. Environment Requirements + +* JDK 8 / 17 +* SpringBoot 2.x / 3.x +* Forest 1.6.4+ +* Fastjson2 + +### Dependency Configuration + +In addition to basic frameworks like SpringBoot and Lombok, add dependencies for Forest and Fastjson2. + +```xml + + + com.dtflys.forest + forest-spring-boot-starter + 1.6.4 + + + + + com.alibaba + fastjson + 2.0.53 + +``` + +## 2. Apply for a DeepSeek API Key + +Visit the DeepSeek website and navigate to the API Key management page (https://platform.deepseek.com/api_keys) to find your API Key. + +If you don't have a KEY yet, click the `Create API Key` button at the bottom of the page. + +![](/assets/img/blog/springboot-forest-deepseek-integration-0.png) +API Keys Page + +After creation, a dialog will pop up displaying your newly generated API Key string. Copy and save it to a secure location immediately. + +## 3. Configure the Project + +Open the SpringBoot configuration file `application.yml` and add the following code: + +```yaml +# Forest Framework Configuration +forest: + connect-timeout: 10000 # Request connection timeout (ms) + read-timeout: 3600000 # Request data read timeout (ms), longer is better + variables: + apiKey: YOUR_API_KEY # Replace with your obtained API Key + model: deepseek-reasoner # Model supported by DeepSeek, the R1 model +``` + +## 4. Create a Declarative Interface + +Forest supports sending HTTP requests declaratively. The following code defines the DeepSeek API request as a declarative interface. + +```java +public interface DeepSeek { + + @Post( + url = "https://api.deepseek.com/chat/completions", + contentType = "application/json", + headers = "Authorization: Bearer {apiKey}", + data = "{\"messages\":[{\"content\":\"{content}\",\"role\":\"user\"}],\"model\":\"{model}\",\"stream\":true}") + ForestSSE completions(@Var("content") String content); +} +``` + +The meaning of the above code is clear: calling this interface method will send a `POST` request to the URL https://api.deepseek.com/chat/completions. + +Here, `{apiKey}` and `{model}` read the `apiKey` and `model` fields from the configuration file, while `{content}` reads the parameter modified by the `@Var("content")` annotation. The request body data is the JSON string provided in the official documentation, and the desired parameters are concatenated using string template placeholders like `{variable name}`. + +The return type of the interface method is `ForestSSE`, a built-in type provided by the Forest framework primarily used to receive and process SSE (Server-Sent Events) stream messages. + +## 5. Call the Interface + +After creating the declarative interface, you can inject an instance of this interface into the startup class using Spring's `@Resource` annotation. The Forest framework will use the dynamic proxy pattern to automatically generate the corresponding interface proxy class instance and inject it into the class where you need to make the call. + +```java +@Resource +private DeepSeek deepSeek; +``` + +You can then call the interface to send the request and set a Lambda expression to receive and process the returned SSE stream event messages. + +```java +@SpringBootApplication +public class DeepSeekExampleApplication implements CommandLineRunner { + + // DeepSeek declarative interface + @Resource + private DeepSeek deepSeek; + + @Override + public void run(String... args) { + // Call the declarative interface method + deepSeek.completions("Hello, who are you?") + .setOnMessage(event -> { + // Receive and process SSE events + try { + // Get the message data and deserialize it into the DeepSeekResult class + DeepSeekResult result = event.value(DeepSeekResult.class); + // Print the message content from the DeepSeekResult object + System.out.print(result.content()); + } catch (Exception e) { + } + }) + .listen(SSELinesMode.SINGLE_LINE); // Listen for SSE and set to single-line message mode + } + + public static void main(String[] args) { + try { + SpringApplication.run(DeepSeekExampleApplication.class, args); + } catch (Throwable th) { + th.printStackTrace(); + } + } +} +``` + +Here, `DeepSeekResult` is a data class defined according to the returned message format. The specific code is as follows: + +```java +@Data +public class DeepSeekResult { + + private String id; + + private String object; + + private Integer created; + + private String model; + + @JSONField(name = "system_fingerprint") + private String systemFingerprint; + + private List choices; + + // Get the choices[0].delta.content from the message + public String content() { + List choices = getChoices(); + if (CollectionUtil.isNotEmpty(choices)) { + JSONObject chooseJson = choices.get(0); + DeepSeekResultChoice choice = chooseJson.toJavaObject(DeepSeekResultChoice.class); + return choice.getDelta().getContent(); + } + return ""; + } +} +``` + +Other data classes, including `DeepSeekResultChoice`, are similar. The specific code can be found in the code repository address provided at the end of the article. + +## 6. Response Test + +After writing the calling method, we can run the code and see the results. After clicking Run, you can see the console log printing the following content: + +![](/assets/img/blog/springboot-forest-deepseek-integration-1.png) +Test Log + +The upper part of the log, such as `POST https://api.deepseek.com/chat/completions HTTPS [SSE]`, is the Forest request log, which tells you the data and parameters in the outgoing HTTP request. + +The lower part, "Hello! I am an intelligent assistant developed by China's DeepSeek company, DeepSeek-R1..." is naturally DeepSeek's response. + +## 7. Chain of Thought + +The above code example only returns DeepSeek's answer content, not its thinking process, even if the model is `DeepSeek-R1`. To print the chain of thought, the code needs to be modified. + +First, modify the `content()` method in the `DeepSeekResult` class. + +```java +@Data +public class DeepSeekResult { + + private String id; + + private String object; + + private Integer created; + + private String model; + + @JSONField(name = "system_fingerprint") + private String systemFingerprint; + + private List choices; + + // Get the choices[0].delta.reasoning_content + // or choices[0].delta.content from the message + // Identify if it's reasoning content via the DeepSeekContent.isReasoning flag + public DeepSeekContent content() { + List choices = getChoices(); + if (CollectionUtil.isNotEmpty(choices)) { + JSONObject chooseJson = choices.get(0); + DeepSeekResultChoice choice = chooseJson.toJavaObject(DeepSeekResultChoice.class); + String reasoningContent = choice.getDelta().getReasoningContent(); + // Check if reasoningContent exists. If it does, it's chain of thought content; otherwise, it's the pure answer content. + if (StringUtils.isNotEmpty(reasoningContent)) { + return new DeepSeekContent(true, reasoningContent); + } + return new DeepSeekContent(false, choice.getDelta().getContent()); + } + return new DeepSeekContent(); + } +} +``` + +Add the `DeepSeekContent` class. + +```java +@Data +public class DeepSeekContent { + // Whether it is reasoning process content + private boolean reasoning = false; + // The specific content of DeepSeek's response + private String content = ""; + + public DeepSeekContent() { + } + + public DeepSeekContent(boolean reasoning, String content) { + this.reasoning = reasoning; + this.content = content; + } +} +``` + +Finally, modify the interface calling part. + +```java +@SpringBootApplication +public class DeepSeekExampleApplication implements CommandLineRunner { + + // DeepSeek declarative interface + @Resource + private DeepSeek deepSeek; + + @Override + public void run(String... args) { + // Flag: whether it is the first time receiving reasoning content + AtomicBoolean isFirstReasoning = new AtomicBoolean(false); + // Call the declarative interface method + deepSeek.completions("What is 1+1?") + .setOnMessage(event -> { + try { + DeepSeekResult result = event.value(DeepSeekResult.class); + DeepSeekContent content = result.content(); + // Use CAS to check if it's the first time receiving reasoning content + // If yes, print the tag + if (content.isReasoning() && isFirstReasoning.compareAndSet(false, true)) { + System.out.println(""); + System.out.print(content.getContent()); + } else if (!content.isReasoning() && isFirstReasoning.compareAndSet(true, false)) { + // When isFirstReasoning changes from true to false + // it indicates the message is switching from reasoning content to the formal answer content + System.out.print(content.getContent()); + System.out.println("\n\n"); + } else { + // Print normal reasoning or formal answer content + System.out.print(Opt.ofBlankAble(content.getContent()).orElse("")); + } + } catch (Exception e) { + } + }) + .listen(SSELinesMode.SINGLE_LINE); + } + + public static void main(String[] args) { + try { + SpringApplication.run(DeepSeekExampleApplication.class, args); + } catch (Throwable th) { + th.printStackTrace(); + } + } +} +``` + +## 8. Chain of Thought Message Test + +Now you can run the program to test whether the log contains the chain of thought process. + +![](/assets/img/blog/springboot-forest-deepseek-integration-2.png) +Chain of Thought Log.png + +From the log, it can be seen that the program ran normally. The part enclosed between the `` and `` tags is the thinking process explained by DeepSeek. The text after the `` closing tag is its formal answer content. + +## 9. Error Handling + +The case in this article calls the official DeepSeek API. For reasons well known, network errors such as `401` are highly likely to occur when calling the interface. + +To handle such requests, simply add an interceptor. + +```java +// Forest's SSE request interceptor +public class DeepSeekInterceptor implements SSEInterceptor { + + // This method is automatically called when a request response is received + @Override + public ResponseResult onResponse(ForestRequest request, ForestResponse response) { + // Check if the request encountered an error, such as 401, 404, etc. + if (response.isError()) { + // If there is an error, print "The server is busy, please try again later." + System.out.println("The server is busy, please try again later."); + return success(); + } + return proceed(); + } +} +``` + +Then, bind the interceptor to the interface. + +```java +// Bind the interceptor to the entire interface +@BaseRequest(interceptor = DeepSeekInterceptor.class) +public interface DeepSeek { + + @Post( + url = "https://api.deepseek.com/chat/completions", + contentType = "application/json", + headers = "Authorization: Bearer {apiKey}", + data = "{\"messages\":[{\"content\":\"{content}\",\"role\":\"user\"}],\"model\":\"{model}\",\"stream\":true}") + ForestSSE completions(@Var("content") String content); +} +``` + +## 10. Summary + +As can be seen, using Forest's declarative approach to integrate with the DeepSeek API offers many obvious advantages over OkHttp and HttpClient. Besides concise code and ease of implementation, the declarative code is naturally more decoupled. The code in this article naturally achieves decoupling between parameter configuration, HTTP request parameters, and the business logic of interface calls. If you need to change the API Key or model, simply modify the configuration file. If you need to change the HTTP URL or parameters, you can directly modify the declarative interface without affecting the business code that calls the interface. Moreover, it naturally allows unifying the HTTP code for the DeepSeek API into a single interface class for easy management, and the URL, headers, and request body parameters in the request are all clear at a glance. + +**Code Repository Address**: https://gitee.com/dromara/forest/tree/master/forest-examples/example-deepseek \ No newline at end of file diff --git a/src/blog/warm-flow-1.3.0.md b/src/blog/warm-flow-1.3.0.md new file mode 100644 index 0000000000..903c7628e9 --- /dev/null +++ b/src/blog/warm-flow-1.3.0.md @@ -0,0 +1,139 @@ +--- +title: A Workflow Engine with Built-in Process Designer +author: warm-flow +date: 2024-10-23 +cover: /assets/img/blog/warm-flow-1.3.0-0.png +head: + - - meta + - name: Blog +--- + +# A Workflow Engine with a Built-in Process Designer + +**Finally, the exciting version 1.3.0 is here! No more烦恼 (hassles) with integrating a designer. Follow the first four points below to quickly integrate it into your business system. Here’s how to use the designer:** + +## 1. Add Dependency + +```xml + + io.github.minliuhua + warm-flow-plugin-ui-sb-web + 1.3.0 + +``` + +## 2. Backend Path Permissions + +> 1. These two paths need to be permitted, otherwise access will be denied: `/warm-flow-ui/**`, `/warm-flow/**` +> 2. The following is an example configuration for Spring Security: + +```java +@Bean +protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception +{ + return httpSecurity + ....... + // Annotate URLs allowed for anonymous access + .authorizeHttpRequests((requests) -> { + // Backend requests, static resources, accessible anonymously + requests.antMatchers("/warm-flow-ui/**", "/warm-flow/**").permitAll() + // All requests other than the above require authentication + .anyRequest().authenticated(); + }) + ...... + .build(); +} +``` + +## 3. Frontend Loads the Designer + +> 1. The designer page entry address is: `/warm-flow-ui/${definitionId}?disabled=${disabled}` +> 2. The general idea is to proxy the frontend interface (e.g., port 80) to the backend interface (e.g., port 8080) to access this address; everything else remains the same. +> 3. Alternatively, access this address directly through the backend interface, which might require handling CORS issues. +> 4. The following is an Nginx proxy example: + +```nginx +server { + listen 80; + server_name localhost; + + location /warm-flow-ui/ { + proxy_pass http://localhost:8080/warm-flow-ui/; + } +} +``` + +## 4. Integrate Handler Permission Data for the Designer + +> [!IMPORTANT] Implement an interface to provide the designer with data on which users have permissions to handle tasks set on task nodes. + +### 4.1 Handler Permission Selection Modal + +![](/assets/img/blog/warm-flow-1.3.0-0.png) + +### 4.2 Implement the Interface to Fetch Handler Permission Data for the Above Page + +#### 4.2.1 HandlerSelectService Interface + +```java +/** + * Process Designer - Interface for obtaining handler permission setting list + * + * @author warm + */ +public interface HandlerSelectService { + + /** + * Get the list of tabs for handler permission settings, e.g., User, Role, Department, etc. + * @return List of tab names + */ + List getHandlerType(); + + /** + * Get the result list for handler permission settings, e.g., user list, role list, department list, etc. + * @param query Query parameters + * @return Result list + */ + List getHandlerSelect(HandlerQuery query); +} +``` + +## 5. Project Introduction + +> [!IMPORTANT] Warm-Flow is a domestic (Chinese) workflow engine 🎉. Its characteristics are simplicity, lightweight, feature-completeness, and extensibility. It is a workflow engine that can integrate a designer via JAR inclusion. + +1. Simple and Easy to Use: Only 7 tables, less code, quick to learn and integrate. +2. Approval Functions: Support for Approve, Return, Arbitrary Jump, Transfer, Terminate, Countersign, Vote Sign, Delegate, Add/Sign, Mutex and Parallel Gateways. +3. Listeners and Process Variables: Supports four types of listeners to handle different scenarios, flexible and extensible, parameter passing, dynamic permissions. +4. Flowchart: The workflow engine comes with a flowchart and can be used without integrating a process designer. +5. Process Designer: Can be quickly integrated into projects via JAR package, reducing tedious code copying and adaptation. +6. Conditional Expressions: Built-in common and SpEL conditional expressions, and supports custom extensions. +7. Handler Variable Expressions: Built-in expressions in `${handler}` and SpEL formats, meeting different scenarios, flexible and extensible. +8. ORM Framework Extension: Currently supports MyBatis, Mybatis-Plus, Mybatis-Flex, and Jpa. Support for others will be provided by the community later; easy to extend. +9. Database Support: Currently supports MySQL, Oracle, and PostgreSQL. Support for other databases or domestic databases will be added later. +10. Multi-tenancy and Soft Delete: The workflow engine itself maintains implementations for multi-tenancy and soft delete, or you can use the implementation method of the corresponding ORM framework. +11. Supports both Spring and Solon. +12. Compatible with Java 8 and Java 17 (theoretically Java 11 should work too). +13. Official practical project based on Ruoyi-Vue encapsulation provided. + +## 6. Demo Address + +* admin / admin123 + +Demo: http://www.hhzai.top + +## 7. Official Website + +http://warm-flow.cn + +--- + +About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, and scheduling orchestration. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, and is dedicated to providing microservices cloud-native solutions for global users. It aims to allow every participating open-source enthusiast to experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of members, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to interact with me** + +![](/assets/img/blog/warm-flow-1.3.0-1.webp) \ No newline at end of file diff --git a/src/donation/README.md b/src/donation/README.md index 0868fb6a03..57aefcbc5c 100644 --- a/src/donation/README.md +++ b/src/donation/README.md @@ -1,375 +1,376 @@ ---- -title: Donation -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - -## Project Donation - -### Basic Conditions - -Dromara open source community organization has the following basic requirements for donated projects: - -- **Projects must be original and cannot be Fork versions.** -- Projects must be complete application solutions. -- Projects must have good code comments and continuously improved documentation. -- Projects must be hosted on the Gitee platform with Stars exceeding 100. -- Projects must have valid code submission records in the past 2 months. -- **Projects must be approved by the dromara community organization's jury.** -- Preference is given to Gitee recommended projects or those that have obtained GVP. (GVP projects need to contact Gitee official staff to cancel GVP before transferring to the organization, and restore GVP after a successful transfer) -- Priority is given to projects in the fields of container tools, microservice frameworks and tools, distributed transactions, distributed middleware, big data processing, artificial intelligence, IoT, and development/testing/operations related tool chains. - -### Source Repository Impact - -Currently, donated projects are handled by transferring to the dromara organization repository. After the project is transferred, the following impacts will occur: - -- gitee :`https://gitee.com/dromara/repository-name` -- github:`https://github.com/dromara/repository-name` - -If there are absolute path references (images, files, etc.) in the project source code, they need to be changed to relative paths. - -### Repository Permissions - -- The head of the dromara organization will set super admin permissions for the new project leader. -- Project PMC/Committers will be invited to join the dromara organization and set relevant read and write permissions. The project is independently submitted and operated. -- Project PMC/Committers will be assigned emails with the suffix `@dromara.org`. - -### Repository Documentation - -- In principle, you need to contact the person in charge of the dromara official website and update the transfer document at `https://dromara.org`. - -### Version Release - -- In principle, the next version is required to change the package name prefix to `org.dromara` and be published to the MAVEN repository. - -### Organization/Community - -- The head or secretary of the dromara open source organization invites you to join the dromara community group and makes an announcement to all members. -- The dromara open source community's official account releases a welcome article and synchronizes it to the technical community. -- The original repository community group adds the `dromara-xxx` prefix. - -### Donation Project Exit Matters (Preliminary Version) - -The dromara community organization always has a positive, open and tolerant attitude. If the author of the donated project cannot identify with the development philosophy of the organization after joining the dromara organization or wants to transfer to another organization/person, they can apply to the dromara jury for exit. Exit application process: - -1. The project author needs to write the reason for the project exit in writing or email and email it to [pmc@dromara.org](mailto:pmc@dromara.org). -2. After the dromara jury receives the application, they will communicate with the project author as soon as possible to confirm the exit intention. If the author insists on exiting, it will be approved for processing. -3. Please note that if the project applying for exit is a GVP project, you need to contact the Gitee official staff to cancel the GVP and then transfer it back to your personal name. After a successful transfer, contact the Gitee official staff to restore it. Non-GVP projects can be operated directly. -4. After the project exits, the dromara organization will push announcements on major platforms and community groups to inform, and archive the project's historical records. - -Finally, we hope that project authors can communicate with us before considering exiting the dromara organization, point out where the dromara organization is doing poorly, so that we can improve and develop together. - -### Rights and Organizational Form - -1. Each sub-project team within the community shares all open source resources, including but not limited to the organizational brand, community official account, official community, exclusive accounts and channels of mainstream technical websites and media. -2. In principle, each project is self-governing, and the organizational committee is composed of the main person in charge of each project. -3. Relevant decision-making matters of the dromara organization (designating and modifying rules, joining or exiting projects, etc.) need to be voted on by the organizational committee before execution. Daily affairs are handled by the organization's permanent head. -4. After making a decision at the organizational level, each project's management team must unconditionally obey the organization, or cooperate with the work and tasks of other projects, so that the entire organization is consistent in terms of publicity activities, task assignments, etc. -5. The dromara open source organization entered a trial operation from March 2021 to March 2022. This principle is the trial operation guide for the organization. Each sub-project has complete autonomy and management rights during this period, as well as the copyright and actual ownership of the sub-brand of the project, without violating the law, moral principles, plagiarism infringement, and harm to the organization and not having a bad impact on the community and users. -6. During the trial run, the community will establish a formal committee. For the subsequent development of the community, the management and authority constraints of the sub-projects will be officially launched and will - -### The dromara committee has the final interpretation right for the above regulations. - -## Sponsorship/Support - -Join the Knowledge Planet - - - -**Dromara** community's continuous operation and provision of higher quality services couldn’t be possible without everyone’s support. If you want to become a sponsor or supporter of the **Dromara** community, please consider contacting: - -[xiaoyu@dromara.org](mailto:xiaoyu%40dromara.org) - - - -**Dromara** community promises that all received sponsorship and support funds will be fully disclosed, and subsequent funds will only be used for **Dromara** community operational expenses. For sponsoring anything, you can contact **xiaoyu@dromara.org**. Current sponsors are (for sponsors without a displayed name, default: Anonymous): - -| Sponsor | Sponsorship Amount (Yuan) | Message | Sponsorship Date | -| ------------------- | ------------------------- | --------------------------------------------------------- | ---------------- | -| 椰子 | 100 | | 2024-10-14 | -| 无名 | 200 | 一如既往的支持!感恩相遇! | 2024-10-08 | -| 岳秋林 | 1 | 用过 hutool,超过 sa-token,支持开源 | 2024-08-23 | -| 无名 | 200 | 喝杯咖啡 | 2024-08-05 | -| one-fit | 100 | 希望 dromara 组织能成为中国的 apache | 2024-07-19 | -| 无名 | 100 | 加油!朋友 | 2024-06-04 | -| 无名 | 100 | 朋友,你好! | 2024-05-06 | -| 无名 | 100 | | 2024-04-14 | -| KoKo | 20 | 开源万岁 | 2024-03-13 | -| 无名 | 100 | 朋友们,元宵节快乐 | 2024-02-24 | -| IILee | 20 | 感谢为中国开源做出的贡献 | 2024-01-25 | -| 无名 | 5 | 加油 | 2023-11-06 | -| 无名 | 100 | 感恩相遇 | 2023-10-18 | -| 无名 | 6.66 | 封神之作 | 2023-07-28 | -| 无名 | 50 | | 2023-07-24 | -| JacKey | 10 | | 2023-07-06 | -| 呼噜 | 1 | 买个卫龙 | 2023-06-14 | -| 连洁 | 20 | 小小赞赏 | 2023-05-26 | -| 一夏千晨 | 50 | 一包辣条奉上 | 2023-05-22 | -| 无名 | 50 | | 2023-05-22 | -| 无名 | 100 | 五一快乐(勿回应) | 2023-04-29 | -| meta.木予 | 10 | | 2023-04-26 | -| 无名 | 100 | 纯粹赞助(无需回应) | 2023-04-24 | -| Java 开发小王 | 1 | 很强 | 2023-04-20 | -| 令狐冲 | 10 | 加油 | 2023-04-12 | -| 如愿 | 10 | 猫大人帅哥,祝社区越来越好 | 2023-04-04 | -| cszj | 100 | 帮助很大,希望越来越好 | 2023-04-03 | -| 海子 | 50 | 感谢诸位无私的奉献 | 2023-04-02 | -| 西岭千秋雪 | 50 | | 2023-03-29 | -| 是白菜 | 1 | | 2023-03-28 | -| 泛微网络刘启成 | 50 | 学习 ee,对我非常有帮助,感谢 | 2023-03-27 | -| 无名 | 10 | 加油! | 2023-03-19 | -| 无名 | 100 | 加油! | 2023-03-16 | -| Yyy | 1 | | 2023-03-01 | -| 无名 | 100 | | 2023-02-28 | -| Evan | 20 | | 2023-02-26 | -| 朔 | 10 | 钱不多,但是心意是满满的,加油! | 2023-02-24 | -| 无名 | 200 | | 2023-02-05 | -| 方 | 28.88 | 新年快乐,感谢开源 | 2023-01-21 | -| 无名 | 200 | 祝社区的兄弟姐妹们新年快乐 | 2023-01-21 | -| 无名 | 0.26 | | 2023-01-11 | -| 无名 | 50 | | 2023-01-06 | -| 紫气东来 | 50 | 感谢开源,坚持信念 | 2022-12-14 | -| 枫原万耶 | 16.66 | | 2022-11-13 | -| 岁月无声 | 10.24 | 希望越来越好 | 2022-11-10 | -| 邵昌明 | 20 | 买雪糕吃 | 2022-11-02 | -| 无名 | 1 | | 2022-10-27 | -| 大熊同学 | 9.9 | 希望我们开源社区长长久久! | 2022-10-26 | -| 曙光 | 50 | | 2022-10-25 | -| 无名 | 1 | Zdp 奉上 | 2022-10-04 | -| 无名 | 200 | | 2022-09-28 | -| 无名 | 200 | | 2022-09-28 | -| 无名 | 200 | | 2022-09-28 | -| 晚风 | 100 | 从接触 hutool 开始就喜欢上了,太好用了 | 2022-09-08 | -| 无名 | 100 | 支持开源,加油! | 2022-09-07 | -| 陈乐辉 | 100 | 祝开源的路上越走越远 | 2022-08-12 | -| 阿超 | 50 | 开源就是人人为我,我为人人 | 2022-07-27 | -| QZWML | 10 | | 2022-07-22 | -| 无名 | 100 | | 2022-06-30 | -| Eclipse | 20 | 支持! | 2022-06-28 | -| 朋云 | 10 | | 2022-06-24 | -| 红豆生南国 | 10 | 吃个雪糕 | 2022-06-22 | -| 修理工 | 80 | 代码规范,值得学习,用的方便,强烈推荐 | 2022-05-27 | -| 苗对我很重要 | 10 | | 2022-05-19 | -| 王磊 | 50 | | 2022-05-19 | -| Hi | 10.24 | 感谢开发 | 2022-04-24 | -| 哈哈 | 66.66 | 支持猫大人,祝社区越来越好~ | 2022-04-14 | -| liming | 50 | 祝社区越来越好! | 2022-04-14 | -| 胡泰室 | 50 | 祝社区越来越好 | 2022-04-14 | -| 金卫信曾小燕 | 10.24 | | 2022-04-14 | -| Chris | 150 | | 2022-04-12 | -| 无名 | 100 | | 2022-04-05 | -| 青衫烟雨客 | 10 | | 2022-03-20 | -| Zack | 20 | 省了我好多事,太 nb 了!!支持下 | 2022-03-10 | -| 许伟超 | 10 | 感恩,尽我绵薄之力 | 2022-03-09 | -| 无名 | 10 | 虽然钱不多,但是我会一直打赏 | 2022-03-09 | -| ^\_^ | 20 | 虎年吉祥 | 2022-02-03 | -| 无名 | 166 | 祝社区能做的越来越好 | 2022-01-17 | -| 吴南方 | 20 | 希望 hutool 能做的越来越好 | 2022-01-09 | -| 忘忧 | 50 | | 2021-12-30 | -| 达 | 5 | hutool 省了很多重复造轮子的时间,特别棒,小小心意赞助一下 | 2021-12-24 | -| 吃瓜群众丙 | 100 | 为往圣继绝学。加油吧,少年。 | 2021-12-23 | -| 早点下班吧 | 10 | | 2021-12-14 | -| Amiron 今天早睡了吗 | 20 | | 2021-12-02 | -| phantom | 100 | | 2021-11-30 | -| 无名 | 11.27 | 感谢,表示支持 | 2021-11-27 | -| 夏和顺顺 | 50 | 为往圣继绝学 | 2021-11-25 | -| Chris | 100 | | 2021-11-24 | -| 光明星辰 | 50 | 辣条钱 | 2021-11-04 | -| 无名 | 3 | 辣条钱 | 2021-10-29 | -| 奥利弗 | 15 | | 2021-10-13 | -| XXXL | 50 | 聊表心意,祝越来越好 | 2021-09-30 | -| 用用 | 20 | | 2021-09-17 | -| 宋不醒 | 10 | 加油 | 2021-09-10 | -| 🐶 | 20 | 聊表心意,有大用了 | 2021-08-24 | -| Coffee | 10 | 冲冲 | 2021-07-29 | -| aliyu | 10 | aly | 2021-07-22 | -| 段大志 | 20 | 国产开源蒸蒸日上 | 2021-07-14 | -| 喜世 | 20 | 感谢铁锚助教辛勤解答问题 | 2021-06-28 | -| Group | 1.00 | 蚊子腿也是肉 | 2021-06-08 | -| 大大的时间小小的我 | 20 | | 2021-05-26 | -| Leo | 15 | 真心不错,加油! | 2021-05-20 | -| 辉辉同学 | 66.66 | hutool 工具个人认为是封神佳作,顶礼膜拜! | 2021-05-19 | -| 0X17 | 100 | | 2021-05-16 | -| 鑫爷 | 16.8 | fans99 | 2021-05-11 | -| こうう | 3 | | 2021-05-06 | -| 一只想要奋斗的咸鱼 | 66 | | 2021-04-29 | -| 锄禾 | 20 | 能力有限,聊表心意 | 2021-04-27 | -| 然而 | 64 | 越开源越进步 | 2021-04-23 | -| 路,还很远 | 20 | 国产开源牛逼 | 2021-04-23 | -| 纳兰-曼孚 | 20 | | 2021-04-15 | -| Stone | 200 | 跟着猫大人搞开源,芜湖起飞 | 2021-04-14 | -| 疯子 | 200 | 国产开源,加油! | 2021-04-14 | -| tom | 50 | 祝越来越好 | 2021-04-14 | -| 子豪 sirius | 50 | 支持猫大人 | 2021-04-14 | -| Sissie | 50 | 感谢开源,支持喵大人 | 2021-04-14 | -| WilliamSky | 50 | 感谢开源,支持喵大人 | 2021-04-14 | -| Sink | 10 | 向大佬致敬 | 2021-04-14 | -| 石启蒙 | 10 | 祝开源社区越来越好 | 2021-04-14 | -| 吴俊杰\_ken | 66.88 | 学习加交流,社区越来越好 | 2021-04-13 | -| 正是在下李某人 | 20 | 猫大人牛批 | 2021-04-13 | -| Zhw | 400 | 做大做强,再创辉煌 | 2021-04-13 | -| 彭伟伦 | 20 | 猫大人的华子 | 2021-04-13 | -| 心中的明月 | 30 | Tshirt 好漂亮,好想要 | 2021-04-13 | -| 刘进 | 20 | 撸下胸毛 | 2021-04-13 | -| 黄灿达 | 20 | 支持 | 2021-04-13 | -| 赵镇 | 33 | 做大做强,再创辉煌 | 2021-04-13 | -| kimmking | 2000 | 祝福社区越来越好 | 2021-04-12 | -| John | 400 | 猫大人抽华子 | 2021-04-12 | -| Charlyfeng | 10 | 为往圣继绝学 | 2021-04-12 | -| 梁胜芳 | 6.66 | 祝组织越来越好 | 2021-04-12 | -| Chris | 200 | 祝福社区越来越好 | 2021-04-11 | -| Jtiag | 20 | 做大做强 | 2021-04-02 | -| Nelson | 66 | 做大做强,走向辉煌 | 2021-04-02 | -| 好方 | 60 | 我们一起学猫叫,一起喵喵喵喵喵~ | 2021-04-02 | -| 哼干嘛 | 20 | 加入的第一个组织,感谢猫大人 | 2021-04-01 | -| 静晓晨曦 | 20 | 支持猫大人,祝福社区越来越好 | 2021-03-31 | -| Attract | 20 | 聊表心意,祝福社区越来越好 | 2021-03-31 | -| 杨大侠 | 60 | 跟着猫大人搞开源 | 2021-03-31 | -| ian | 200 | Dromara 最棒 | 2021-03-31 | -| 白 | 20 | | 2021-03-31 | -| 静静 | 80 | 强 | 2021-03-31 | -| blizzard | 20 | 感谢猫大人花费巨大的心血 | 2021-03-31 | -| Jenkins | 100 | 参加开源,共建社区 | 2021-03-31 | -| Sean | 128 | 祝社区越走越远 | 2021-03-31 | -| 肖邦 | 20 | 全力支持参与开源贡献自己的微博力量 | 2021-03-31 | -| Ted 丶 L | 100 | 祝社区越办越好 | 2021-03-31 | -| hb | 60 | 支持开源 | 2021-03-31 | -| 大黄蜂 | 60 | 感谢猫大人,请猫大人抽根华子 | 2021-03-30 | -| 半盏清茶 | 20 | 越来越好 | 2021-03-30 | -| 小旭 | 20 | | 2021-03-30 | -| 阳有白 | 60 | 请猫大人喝几吨白开水 | 2021-03-30 | -| 韩小波 | 20 | 中国开源加油 | 2021-03-30 | -| Vilochen | 60 | 希望猫大人的开源组织越来越壮大 | 2021-03-30 | -| 滴流乱转的小胖子 | 60 | 向猫大人老师致敬 | 2021-03-30 | -| 泊 | 20 | 支持猫大人的开源 | 2021-03-30 | -| 赵瑞 | 20 | | 2021-03-30 | -| 心想事成 | 1 | | 2021-03-30 | -| 李 | 20 | 支持一下 | 2021-03-30 | -| Yul | 20 | | 2021-03-30 | -| bing | 20 | | 2021-03-30 | -| 榕 | 20 | | 2021-03-30 | -| keepCarry | 160 | dromara 永存 | 2021-03-30 | -| xiaochun | 20 | 猫大人加油,看好你哦 | 2021-03-30 | -| 梁超-ISAAC | 20 | 支持支持 | 2021-03-30 | -| 子豪 sirius | 20 | 支持开源 | 2021-03-30 | -| 瓦让 | 80 | 大家一起加油 | 2021-03-30 | -| wyj | 20 | 支持支持 | 2021-03-30 | -| 小小的太阳 | 20 | 猫大人牛批 | 2021-03-30 | -| 小雨淅淅 | 20 | 请猫大人喝奶茶 | 2021-03-30 | -| 苦了芥末 | 20 | | 2021-03-30 | -| 成龙 | 50 | 谢谢猫大人对于开源的贡献,感谢引路 | 2021-03-30 | -| Chasen | 80 | 哈哈,支持支持 | 2021-03-30 | -| Menglg | 20 | | 2021-03-30 | -| 闫兵 | 66.66 | | 2021-03-30 | -| 王一飞 | 60 | | 2021-03-30 | -| 张民鹏 | 60 | 祝 Dromara 社区越办越好 | 2021-03-30 | -| Jasper | 60 | 猫大人牛逼! | 2021-03-30 | -| 子不语 | 60 | 一直想做开源,这也是离我最近的社区,社区加油! | 2021-03-30 | -| 阿行 | 20 | 猫大人的小迷弟 | 2021-03-30 | -| Wincher | 10.24 | 为往圣继绝学 | 2021-03-30 | -| Marcus | 20 | 一个人可能走的更快,但一群人会走得更远 | 2021-03-30 | -| 欧葵 | 60 | 猫大人加油 | 2021-03-30 | -| 有。无。 | 20 | | 2021-03-30 | -| 哲一 | 20 | | 2021-03-30 | -| 刘耿华 | 66 | 猫大人牛逼 | 2021-03-30 | -| 乔帝鸽 | 20 | 做大做强,再创辉煌 | 2021-03-30 | -| 和尘同光 | 20 | 请猫大人喝咖啡 | 2021-03-30 | -| ~zZZachary | 60 | | 2021-03-30 | -| 唐甜 | 20 | 跟着猫大人做开源 | 2021-03-30 | -| 小程故事多 | 60 | 加油,一如既往的支持 | 2021-03-30 | -| 东东 | 60 | 做大做强 | 2021-03-30 | -| Truing | 20 | 猫大人牛逼 | 2021-03-30 | -| sikm | 20 | 为往圣继绝学,大风起不可当石立 | 2021-03-30 | -| One Day | 299 | | 2021-03-30 | -| jihe | 20 | 开源冲 | 2021-03-30 | -| 豆豆 | 99.99 | 支持开源 | 2021-03-30 | -| 斯普特尼克 | 60 | 祝社区越来越繁荣 | 2021-03-30 | -| orange | 20 | 猫大人帅气 | 2021-03-30 | -| 钟如雷 | 200 | 请老板抽和天下 | 2021-03-30 | -| Goku | 20 | 加油,愿社区越来越好 | 2021-03-30 | -| Try Everything | 20 | 支持猫大人 | 2021-03-30 | -| 郡鸿 | 20 | 支持猫大人,支持开源 | 2021-03-30 | -| FangQ | 20 | 猫大人加油,Dromara 加油 | 2021-03-30 | -| cycle | 120 | 愿中国的开源事业越来越好,猫大人及其同仁们威武 | 2021-03-30 | -| 夜羽 | 20 | 向开源精神,脱帽致敬 | 2021-03-30 | -| SUDONG | 80 | | 2021-03-30 | -| 婷 | 20 | 猫大人牛批,超大声! | 2021-03-30 | -| Tusi | 20 | | 2021-03-30 | -| 任富飞 | 88 | 越来越好 | 2021-03-30 | -| 时间会咬人 | 60 | 再接再励,做大做强 | 2021-03-30 | -| sakila | 60 | 猫大人抽华子 | 2021-03-30 | -| 虎宝 | 66.66 | 婷宝&虎宝,崔猫求疵 | 2021-03-30 | -| 一条小青龙 | 20 | 请猫大人吃鸡排 | 2021-03-30 | -| 徐培 | 60 | 请猫大人抽华子 | 2021-03-30 | -| pengshao | 20 | 猫大人加油 | 2021-03-30 | -| F.C | 20 | 猫大人威武,祝 Dromara 社区越办越好 | 2021-03-30 | -| hellboy0621 | 66.66 | 为往圣继绝学 | 2021-03-30 | -| 吴哭哭 | 60 | 精神股东 | 2021-03-30 | -| 不忘初心 | 120 | | 2021-03-29 | -| 陈宏 | 20 | | 2021-03-29 | -| 恬恬好甜 | 20 | | 2021-03-29 | -| 瑞 | 120 | | 2021-03-29 | -| Uncle George | 10.24 | | 2021-03-29 | -| Tree | 20 | | 2021-03-29 | -| Shallwetalk | 20 | | 2021-03-29 | -| 莫生 | 60 | 请猫大人抽包华子 | 2021-03-29 | -| YEZJ | 20 | | 2021-03-29 | -| Silent | 60 | | 2021-03-29 | -| SIGN | 20 | 猫大人的狸猫 | 2021-03-29 | -| 张\* | 20 | | 2021-03-29 | -| 荼九 | 66 | | 2021-03-29 | -| ? | 20 | | 2021-03-29 | -| 睡觉大王小汤 | 1 | | 2021-03-29 | -| 守候 | 20 | 猫大人牛逼 | 2021-03-29 | -| Bruce | 10 | | 2021-03-29 | -| 晏鹏 | 10.24 | 猫大人加油 | 2021-03-29 | -| 一条小路的距离 | 20 | 猫大人的华子 | 2021-03-29 | -| 嘉宝 | 80 | 请师父喝咖啡 | 2021-03-29 | -| PeiXy | 60 | 猫大人的胸毛 | 2021-03-29 | -| L3nvy | 13 | 猫大人 lol 分队第一塞拉斯 | 2021-03-29 | -| comforyo | 20 | | 2021-03-29 | -| 莫白 | 9.99 | 感恩 | 2021-03-29 | -| 做成一件小事 | 20 | 猫大人的小学徒 | 2021-03-29 | -| zendwang | 20 | | 2021-03-29 | -| 星尘 | 9.99 | 气氛搞起来 | 2021-03-29 | -| 萧 | 60 | 为往圣继绝学 | 2021-03-29 | -| 陈龙 | 20 | 开源无敌,拒绝 996 | 2021-03-29 | -| \*\*涛 | 50 | | 2021-03-29 | -| yarne | 66.66 | 共同的事业,深深的友情 | 2021-03-29 | -| E.S. | 60 | 大锅,喝阔落! | 2021-03-29 | -| 东江 DJ | 20 | | 2021-03-29 | -| ZhW | 20 | | 2021-03-29 | -| Mr...犯 | 20 | | 2021-03-29 | -| 林 | 20 | | 2021-03-29 | -| 天地 | 20 | | 2021-03-29 | -| 项峥 | 20 | 猫大人牛逼 | 2021-03-29 | -| Easley | 200 | 猫大人牛逼 | 2021-03-29 | -| 秦旭 | 60 | | 2021-03-29 | -| Neal | 20 | 猫大人的错别字检查员 | 2021-03-29 | -| Chovi.Wu | 10.24 | 表哥加油 | 2021-03-29 | -| Niverkk | 20 | | 2021-03-29 | -| ~阿槑~ | 20 | | 2021-03-29 | -| Witt | 20 | 支持猫大人 | 2021-03-29 | -| 屈仁能 | 218.88 | 猫大人家的菜鸟 | 2021-03-29 | -| 侯瑞哲 | 20 | 请猫大人喝咖啡 | 2021-03-29 | -| Phoenix | 20 | 感谢猫大人带我们认识开源 | 2021-03-29 | -| 黄放 | 20 | 为往圣继绝学 | 2021-03-29 | -| 那会 | 20 | 感谢猫大人 | 2021-03-29 | -| Liquid | 20 | | 2021-03-29 | -| audi | 15 | 喵小喵 | 2021-03-29 | -| KaitoShy | 6.66 | | 2021-03-29 | -| 氺 | 20 | | 2021-03-29 | -| Ncy | 20 | 喵喵喵 | 2021-03-29 | -| HelloWorld | 20 | 靡不有初,鲜克有终 | 2021-03-29 | -| 孙龙 | 60 | | 2021-03-29 | -| maybe | 10.24 | | 2021-03-29 | -| biao | 20 | | 2021-03-29 | -| BetterWp | 66.66 | 跟着猫大人冲 | 2021-03-29 | -| 502819 | 20 | 猫大人威武霸气 | 2021-03-29 | -| Z | 10 | 猫大人抽华子 | 2021-03-29 | -| 陈曦 | 66.66 | | 2021-03-29 | -| 一朵云 | 20 | | 2021-03-29 | -| 姜吉宁 | 60 | 做大做强 | 2021-03-29 | -| 飞鸿雪泥 | 60 | 开源万岁 | 2021-03-29 | +--- +title: Donation +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +sidebar: false +--- + +## Project Donation + +### Basic Conditions + +Dromara open source community organization has the following basic requirements for donated projects: + +- **Projects must be original and cannot be Fork versions.** +- Projects must be complete application solutions. +- Projects must have good code comments and continuously improved documentation. +- Projects must be hosted on the Gitee platform with Stars exceeding 100. +- Projects must have valid code submission records in the past 2 months. +- **Projects must be approved by the dromara community organization's jury.** +- Preference is given to Gitee recommended projects or those that have obtained GVP. (GVP projects need to contact Gitee official staff to cancel GVP before transferring to the organization, and restore GVP after a successful transfer) +- Priority is given to projects in the fields of container tools, microservice frameworks and tools, distributed transactions, distributed middleware, big data processing, artificial intelligence, IoT, and development/testing/operations related tool chains. + +### Source Repository Impact + +Currently, donated projects are handled by transferring to the dromara organization repository. After the project is transferred, the following impacts will occur: + +- gitee :`https://gitee.com/dromara/repository-name` +- github:`https://github.com/dromara/repository-name` + +If there are absolute path references (images, files, etc.) in the project source code, they need to be changed to relative paths. + +### Repository Permissions + +- The head of the dromara organization will set super admin permissions for the new project leader. +- Project PMC/Committers will be invited to join the dromara organization and set relevant read and write permissions. The project is independently submitted and operated. +- Project PMC/Committers will be assigned emails with the suffix `@dromara.org`. + +### Repository Documentation + +- In principle, you need to contact the person in charge of the dromara official website and update the transfer document at `https://dromara.org`. + +### Version Release + +- In principle, the next version is required to change the package name prefix to `org.dromara` and be published to the MAVEN repository. + +### Organization/Community + +- The head or secretary of the dromara open source organization invites you to join the dromara community group and makes an announcement to all members. +- The dromara open source community's official account releases a welcome article and synchronizes it to the technical community. +- The original repository community group adds the `dromara-xxx` prefix. + +### Donation Project Exit Matters (Preliminary Version) + +The dromara community organization always has a positive, open and tolerant attitude. If the author of the donated project cannot identify with the development philosophy of the organization after joining the dromara organization or wants to transfer to another organization/person, they can apply to the dromara jury for exit. Exit application process: + +1. The project author needs to write the reason for the project exit in writing or email and email it to [pmc@dromara.org](mailto:pmc@dromara.org). +2. After the dromara jury receives the application, they will communicate with the project author as soon as possible to confirm the exit intention. If the author insists on exiting, it will be approved for processing. +3. Please note that if the project applying for exit is a GVP project, you need to contact the Gitee official staff to cancel the GVP and then transfer it back to your personal name. After a successful transfer, contact the Gitee official staff to restore it. Non-GVP projects can be operated directly. +4. After the project exits, the dromara organization will push announcements on major platforms and community groups to inform, and archive the project's historical records. + +Finally, we hope that project authors can communicate with us before considering exiting the dromara organization, point out where the dromara organization is doing poorly, so that we can improve and develop together. + +### Rights and Organizational Form + +1. Each sub-project team within the community shares all open source resources, including but not limited to the organizational brand, community official account, official community, exclusive accounts and channels of mainstream technical websites and media. +2. In principle, each project is self-governing, and the organizational committee is composed of the main person in charge of each project. +3. Relevant decision-making matters of the dromara organization (designating and modifying rules, joining or exiting projects, etc.) need to be voted on by the organizational committee before execution. Daily affairs are handled by the organization's permanent head. +4. After making a decision at the organizational level, each project's management team must unconditionally obey the organization, or cooperate with the work and tasks of other projects, so that the entire organization is consistent in terms of publicity activities, task assignments, etc. +5. The dromara open source organization entered a trial operation from March 2021 to March 2022. This principle is the trial operation guide for the organization. Each sub-project has complete autonomy and management rights during this period, as well as the copyright and actual ownership of the sub-brand of the project, without violating the law, moral principles, plagiarism infringement, and harm to the organization and not having a bad impact on the community and users. +6. During the trial run, the community will establish a formal committee. For the subsequent development of the community, the management and authority constraints of the sub-projects will be officially launched and will + +### The dromara committee has the final interpretation right for the above regulations. + +## Sponsorship/Support + +Join the Knowledge Planet + + + +**Dromara** community's continuous operation and provision of higher quality services couldn’t be possible without everyone’s support. If you want to become a sponsor or supporter of the **Dromara** community, please consider contacting: + +[xiaoyu@dromara.org](mailto:xiaoyu%40dromara.org) + + + +**Dromara** community promises that all received sponsorship and support funds will be fully disclosed, and subsequent funds will only be used for **Dromara** community operational expenses. For sponsoring anything, you can contact **xiaoyu@dromara.org**. Current sponsors are (for sponsors without a displayed name, default: Anonymous): + +| Sponsor | Sponsorship Amount (Yuan) | Message | Sponsorship Date | +| ------------------- | ------------------------- | --------------------------------------------------------- | ---------------- | +| 椰子 | 100 | | 2024-10-14 | +| 无名 | 200 | 一如既往的支持!感恩相遇! | 2024-10-08 | +| 岳秋林 | 1 | 用过 hutool,超过 sa-token,支持开源 | 2024-08-23 | +| 无名 | 200 | 喝杯咖啡 | 2024-08-05 | +| one-fit | 100 | 希望 dromara 组织能成为中国的 apache | 2024-07-19 | +| 无名 | 100 | 加油!朋友 | 2024-06-04 | +| 无名 | 100 | 朋友,你好! | 2024-05-06 | +| 无名 | 100 | | 2024-04-14 | +| KoKo | 20 | 开源万岁 | 2024-03-13 | +| 无名 | 100 | 朋友们,元宵节快乐 | 2024-02-24 | +| IILee | 20 | 感谢为中国开源做出的贡献 | 2024-01-25 | +| 无名 | 5 | 加油 | 2023-11-06 | +| 无名 | 100 | 感恩相遇 | 2023-10-18 | +| 无名 | 6.66 | 封神之作 | 2023-07-28 | +| 无名 | 50 | | 2023-07-24 | +| JacKey | 10 | | 2023-07-06 | +| 呼噜 | 1 | 买个卫龙 | 2023-06-14 | +| 连洁 | 20 | 小小赞赏 | 2023-05-26 | +| 一夏千晨 | 50 | 一包辣条奉上 | 2023-05-22 | +| 无名 | 50 | | 2023-05-22 | +| 无名 | 100 | 五一快乐(勿回应) | 2023-04-29 | +| meta.木予 | 10 | | 2023-04-26 | +| 无名 | 100 | 纯粹赞助(无需回应) | 2023-04-24 | +| Java 开发小王 | 1 | 很强 | 2023-04-20 | +| 令狐冲 | 10 | 加油 | 2023-04-12 | +| 如愿 | 10 | 猫大人帅哥,祝社区越来越好 | 2023-04-04 | +| cszj | 100 | 帮助很大,希望越来越好 | 2023-04-03 | +| 海子 | 50 | 感谢诸位无私的奉献 | 2023-04-02 | +| 西岭千秋雪 | 50 | | 2023-03-29 | +| 是白菜 | 1 | | 2023-03-28 | +| 泛微网络刘启成 | 50 | 学习 ee,对我非常有帮助,感谢 | 2023-03-27 | +| 无名 | 10 | 加油! | 2023-03-19 | +| 无名 | 100 | 加油! | 2023-03-16 | +| Yyy | 1 | | 2023-03-01 | +| 无名 | 100 | | 2023-02-28 | +| Evan | 20 | | 2023-02-26 | +| 朔 | 10 | 钱不多,但是心意是满满的,加油! | 2023-02-24 | +| 无名 | 200 | | 2023-02-05 | +| 方 | 28.88 | 新年快乐,感谢开源 | 2023-01-21 | +| 无名 | 200 | 祝社区的兄弟姐妹们新年快乐 | 2023-01-21 | +| 无名 | 0.26 | | 2023-01-11 | +| 无名 | 50 | | 2023-01-06 | +| 紫气东来 | 50 | 感谢开源,坚持信念 | 2022-12-14 | +| 枫原万耶 | 16.66 | | 2022-11-13 | +| 岁月无声 | 10.24 | 希望越来越好 | 2022-11-10 | +| 邵昌明 | 20 | 买雪糕吃 | 2022-11-02 | +| 无名 | 1 | | 2022-10-27 | +| 大熊同学 | 9.9 | 希望我们开源社区长长久久! | 2022-10-26 | +| 曙光 | 50 | | 2022-10-25 | +| 无名 | 1 | Zdp 奉上 | 2022-10-04 | +| 无名 | 200 | | 2022-09-28 | +| 无名 | 200 | | 2022-09-28 | +| 无名 | 200 | | 2022-09-28 | +| 晚风 | 100 | 从接触 hutool 开始就喜欢上了,太好用了 | 2022-09-08 | +| 无名 | 100 | 支持开源,加油! | 2022-09-07 | +| 陈乐辉 | 100 | 祝开源的路上越走越远 | 2022-08-12 | +| 阿超 | 50 | 开源就是人人为我,我为人人 | 2022-07-27 | +| QZWML | 10 | | 2022-07-22 | +| 无名 | 100 | | 2022-06-30 | +| Eclipse | 20 | 支持! | 2022-06-28 | +| 朋云 | 10 | | 2022-06-24 | +| 红豆生南国 | 10 | 吃个雪糕 | 2022-06-22 | +| 修理工 | 80 | 代码规范,值得学习,用的方便,强烈推荐 | 2022-05-27 | +| 苗对我很重要 | 10 | | 2022-05-19 | +| 王磊 | 50 | | 2022-05-19 | +| Hi | 10.24 | 感谢开发 | 2022-04-24 | +| 哈哈 | 66.66 | 支持猫大人,祝社区越来越好~ | 2022-04-14 | +| liming | 50 | 祝社区越来越好! | 2022-04-14 | +| 胡泰室 | 50 | 祝社区越来越好 | 2022-04-14 | +| 金卫信曾小燕 | 10.24 | | 2022-04-14 | +| Chris | 150 | | 2022-04-12 | +| 无名 | 100 | | 2022-04-05 | +| 青衫烟雨客 | 10 | | 2022-03-20 | +| Zack | 20 | 省了我好多事,太 nb 了!!支持下 | 2022-03-10 | +| 许伟超 | 10 | 感恩,尽我绵薄之力 | 2022-03-09 | +| 无名 | 10 | 虽然钱不多,但是我会一直打赏 | 2022-03-09 | +| ^\_^ | 20 | 虎年吉祥 | 2022-02-03 | +| 无名 | 166 | 祝社区能做的越来越好 | 2022-01-17 | +| 吴南方 | 20 | 希望 hutool 能做的越来越好 | 2022-01-09 | +| 忘忧 | 50 | | 2021-12-30 | +| 达 | 5 | hutool 省了很多重复造轮子的时间,特别棒,小小心意赞助一下 | 2021-12-24 | +| 吃瓜群众丙 | 100 | 为往圣继绝学。加油吧,少年。 | 2021-12-23 | +| 早点下班吧 | 10 | | 2021-12-14 | +| Amiron 今天早睡了吗 | 20 | | 2021-12-02 | +| phantom | 100 | | 2021-11-30 | +| 无名 | 11.27 | 感谢,表示支持 | 2021-11-27 | +| 夏和顺顺 | 50 | 为往圣继绝学 | 2021-11-25 | +| Chris | 100 | | 2021-11-24 | +| 光明星辰 | 50 | 辣条钱 | 2021-11-04 | +| 无名 | 3 | 辣条钱 | 2021-10-29 | +| 奥利弗 | 15 | | 2021-10-13 | +| XXXL | 50 | 聊表心意,祝越来越好 | 2021-09-30 | +| 用用 | 20 | | 2021-09-17 | +| 宋不醒 | 10 | 加油 | 2021-09-10 | +| 🐶 | 20 | 聊表心意,有大用了 | 2021-08-24 | +| Coffee | 10 | 冲冲 | 2021-07-29 | +| aliyu | 10 | aly | 2021-07-22 | +| 段大志 | 20 | 国产开源蒸蒸日上 | 2021-07-14 | +| 喜世 | 20 | 感谢铁锚助教辛勤解答问题 | 2021-06-28 | +| Group | 1.00 | 蚊子腿也是肉 | 2021-06-08 | +| 大大的时间小小的我 | 20 | | 2021-05-26 | +| Leo | 15 | 真心不错,加油! | 2021-05-20 | +| 辉辉同学 | 66.66 | hutool 工具个人认为是封神佳作,顶礼膜拜! | 2021-05-19 | +| 0X17 | 100 | | 2021-05-16 | +| 鑫爷 | 16.8 | fans99 | 2021-05-11 | +| こうう | 3 | | 2021-05-06 | +| 一只想要奋斗的咸鱼 | 66 | | 2021-04-29 | +| 锄禾 | 20 | 能力有限,聊表心意 | 2021-04-27 | +| 然而 | 64 | 越开源越进步 | 2021-04-23 | +| 路,还很远 | 20 | 国产开源牛逼 | 2021-04-23 | +| 纳兰-曼孚 | 20 | | 2021-04-15 | +| Stone | 200 | 跟着猫大人搞开源,芜湖起飞 | 2021-04-14 | +| 疯子 | 200 | 国产开源,加油! | 2021-04-14 | +| tom | 50 | 祝越来越好 | 2021-04-14 | +| 子豪 sirius | 50 | 支持猫大人 | 2021-04-14 | +| Sissie | 50 | 感谢开源,支持喵大人 | 2021-04-14 | +| WilliamSky | 50 | 感谢开源,支持喵大人 | 2021-04-14 | +| Sink | 10 | 向大佬致敬 | 2021-04-14 | +| 石启蒙 | 10 | 祝开源社区越来越好 | 2021-04-14 | +| 吴俊杰\_ken | 66.88 | 学习加交流,社区越来越好 | 2021-04-13 | +| 正是在下李某人 | 20 | 猫大人牛批 | 2021-04-13 | +| Zhw | 400 | 做大做强,再创辉煌 | 2021-04-13 | +| 彭伟伦 | 20 | 猫大人的华子 | 2021-04-13 | +| 心中的明月 | 30 | Tshirt 好漂亮,好想要 | 2021-04-13 | +| 刘进 | 20 | 撸下胸毛 | 2021-04-13 | +| 黄灿达 | 20 | 支持 | 2021-04-13 | +| 赵镇 | 33 | 做大做强,再创辉煌 | 2021-04-13 | +| kimmking | 2000 | 祝福社区越来越好 | 2021-04-12 | +| John | 400 | 猫大人抽华子 | 2021-04-12 | +| Charlyfeng | 10 | 为往圣继绝学 | 2021-04-12 | +| 梁胜芳 | 6.66 | 祝组织越来越好 | 2021-04-12 | +| Chris | 200 | 祝福社区越来越好 | 2021-04-11 | +| Jtiag | 20 | 做大做强 | 2021-04-02 | +| Nelson | 66 | 做大做强,走向辉煌 | 2021-04-02 | +| 好方 | 60 | 我们一起学猫叫,一起喵喵喵喵喵~ | 2021-04-02 | +| 哼干嘛 | 20 | 加入的第一个组织,感谢猫大人 | 2021-04-01 | +| 静晓晨曦 | 20 | 支持猫大人,祝福社区越来越好 | 2021-03-31 | +| Attract | 20 | 聊表心意,祝福社区越来越好 | 2021-03-31 | +| 杨大侠 | 60 | 跟着猫大人搞开源 | 2021-03-31 | +| ian | 200 | Dromara 最棒 | 2021-03-31 | +| 白 | 20 | | 2021-03-31 | +| 静静 | 80 | 强 | 2021-03-31 | +| blizzard | 20 | 感谢猫大人花费巨大的心血 | 2021-03-31 | +| Jenkins | 100 | 参加开源,共建社区 | 2021-03-31 | +| Sean | 128 | 祝社区越走越远 | 2021-03-31 | +| 肖邦 | 20 | 全力支持参与开源贡献自己的微博力量 | 2021-03-31 | +| Ted 丶 L | 100 | 祝社区越办越好 | 2021-03-31 | +| hb | 60 | 支持开源 | 2021-03-31 | +| 大黄蜂 | 60 | 感谢猫大人,请猫大人抽根华子 | 2021-03-30 | +| 半盏清茶 | 20 | 越来越好 | 2021-03-30 | +| 小旭 | 20 | | 2021-03-30 | +| 阳有白 | 60 | 请猫大人喝几吨白开水 | 2021-03-30 | +| 韩小波 | 20 | 中国开源加油 | 2021-03-30 | +| Vilochen | 60 | 希望猫大人的开源组织越来越壮大 | 2021-03-30 | +| 滴流乱转的小胖子 | 60 | 向猫大人老师致敬 | 2021-03-30 | +| 泊 | 20 | 支持猫大人的开源 | 2021-03-30 | +| 赵瑞 | 20 | | 2021-03-30 | +| 心想事成 | 1 | | 2021-03-30 | +| 李 | 20 | 支持一下 | 2021-03-30 | +| Yul | 20 | | 2021-03-30 | +| bing | 20 | | 2021-03-30 | +| 榕 | 20 | | 2021-03-30 | +| keepCarry | 160 | dromara 永存 | 2021-03-30 | +| xiaochun | 20 | 猫大人加油,看好你哦 | 2021-03-30 | +| 梁超-ISAAC | 20 | 支持支持 | 2021-03-30 | +| 子豪 sirius | 20 | 支持开源 | 2021-03-30 | +| 瓦让 | 80 | 大家一起加油 | 2021-03-30 | +| wyj | 20 | 支持支持 | 2021-03-30 | +| 小小的太阳 | 20 | 猫大人牛批 | 2021-03-30 | +| 小雨淅淅 | 20 | 请猫大人喝奶茶 | 2021-03-30 | +| 苦了芥末 | 20 | | 2021-03-30 | +| 成龙 | 50 | 谢谢猫大人对于开源的贡献,感谢引路 | 2021-03-30 | +| Chasen | 80 | 哈哈,支持支持 | 2021-03-30 | +| Menglg | 20 | | 2021-03-30 | +| 闫兵 | 66.66 | | 2021-03-30 | +| 王一飞 | 60 | | 2021-03-30 | +| 张民鹏 | 60 | 祝 Dromara 社区越办越好 | 2021-03-30 | +| Jasper | 60 | 猫大人牛逼! | 2021-03-30 | +| 子不语 | 60 | 一直想做开源,这也是离我最近的社区,社区加油! | 2021-03-30 | +| 阿行 | 20 | 猫大人的小迷弟 | 2021-03-30 | +| Wincher | 10.24 | 为往圣继绝学 | 2021-03-30 | +| Marcus | 20 | 一个人可能走的更快,但一群人会走得更远 | 2021-03-30 | +| 欧葵 | 60 | 猫大人加油 | 2021-03-30 | +| 有。无。 | 20 | | 2021-03-30 | +| 哲一 | 20 | | 2021-03-30 | +| 刘耿华 | 66 | 猫大人牛逼 | 2021-03-30 | +| 乔帝鸽 | 20 | 做大做强,再创辉煌 | 2021-03-30 | +| 和尘同光 | 20 | 请猫大人喝咖啡 | 2021-03-30 | +| ~zZZachary | 60 | | 2021-03-30 | +| 唐甜 | 20 | 跟着猫大人做开源 | 2021-03-30 | +| 小程故事多 | 60 | 加油,一如既往的支持 | 2021-03-30 | +| 东东 | 60 | 做大做强 | 2021-03-30 | +| Truing | 20 | 猫大人牛逼 | 2021-03-30 | +| sikm | 20 | 为往圣继绝学,大风起不可当石立 | 2021-03-30 | +| One Day | 299 | | 2021-03-30 | +| jihe | 20 | 开源冲 | 2021-03-30 | +| 豆豆 | 99.99 | 支持开源 | 2021-03-30 | +| 斯普特尼克 | 60 | 祝社区越来越繁荣 | 2021-03-30 | +| orange | 20 | 猫大人帅气 | 2021-03-30 | +| 钟如雷 | 200 | 请老板抽和天下 | 2021-03-30 | +| Goku | 20 | 加油,愿社区越来越好 | 2021-03-30 | +| Try Everything | 20 | 支持猫大人 | 2021-03-30 | +| 郡鸿 | 20 | 支持猫大人,支持开源 | 2021-03-30 | +| FangQ | 20 | 猫大人加油,Dromara 加油 | 2021-03-30 | +| cycle | 120 | 愿中国的开源事业越来越好,猫大人及其同仁们威武 | 2021-03-30 | +| 夜羽 | 20 | 向开源精神,脱帽致敬 | 2021-03-30 | +| SUDONG | 80 | | 2021-03-30 | +| 婷 | 20 | 猫大人牛批,超大声! | 2021-03-30 | +| Tusi | 20 | | 2021-03-30 | +| 任富飞 | 88 | 越来越好 | 2021-03-30 | +| 时间会咬人 | 60 | 再接再励,做大做强 | 2021-03-30 | +| sakila | 60 | 猫大人抽华子 | 2021-03-30 | +| 虎宝 | 66.66 | 婷宝&虎宝,崔猫求疵 | 2021-03-30 | +| 一条小青龙 | 20 | 请猫大人吃鸡排 | 2021-03-30 | +| 徐培 | 60 | 请猫大人抽华子 | 2021-03-30 | +| pengshao | 20 | 猫大人加油 | 2021-03-30 | +| F.C | 20 | 猫大人威武,祝 Dromara 社区越办越好 | 2021-03-30 | +| hellboy0621 | 66.66 | 为往圣继绝学 | 2021-03-30 | +| 吴哭哭 | 60 | 精神股东 | 2021-03-30 | +| 不忘初心 | 120 | | 2021-03-29 | +| 陈宏 | 20 | | 2021-03-29 | +| 恬恬好甜 | 20 | | 2021-03-29 | +| 瑞 | 120 | | 2021-03-29 | +| Uncle George | 10.24 | | 2021-03-29 | +| Tree | 20 | | 2021-03-29 | +| Shallwetalk | 20 | | 2021-03-29 | +| 莫生 | 60 | 请猫大人抽包华子 | 2021-03-29 | +| YEZJ | 20 | | 2021-03-29 | +| Silent | 60 | | 2021-03-29 | +| SIGN | 20 | 猫大人的狸猫 | 2021-03-29 | +| 张\* | 20 | | 2021-03-29 | +| 荼九 | 66 | | 2021-03-29 | +| ? | 20 | | 2021-03-29 | +| 睡觉大王小汤 | 1 | | 2021-03-29 | +| 守候 | 20 | 猫大人牛逼 | 2021-03-29 | +| Bruce | 10 | | 2021-03-29 | +| 晏鹏 | 10.24 | 猫大人加油 | 2021-03-29 | +| 一条小路的距离 | 20 | 猫大人的华子 | 2021-03-29 | +| 嘉宝 | 80 | 请师父喝咖啡 | 2021-03-29 | +| PeiXy | 60 | 猫大人的胸毛 | 2021-03-29 | +| L3nvy | 13 | 猫大人 lol 分队第一塞拉斯 | 2021-03-29 | +| comforyo | 20 | | 2021-03-29 | +| 莫白 | 9.99 | 感恩 | 2021-03-29 | +| 做成一件小事 | 20 | 猫大人的小学徒 | 2021-03-29 | +| zendwang | 20 | | 2021-03-29 | +| 星尘 | 9.99 | 气氛搞起来 | 2021-03-29 | +| 萧 | 60 | 为往圣继绝学 | 2021-03-29 | +| 陈龙 | 20 | 开源无敌,拒绝 996 | 2021-03-29 | +| \*\*涛 | 50 | | 2021-03-29 | +| yarne | 66.66 | 共同的事业,深深的友情 | 2021-03-29 | +| E.S. | 60 | 大锅,喝阔落! | 2021-03-29 | +| 东江 DJ | 20 | | 2021-03-29 | +| ZhW | 20 | | 2021-03-29 | +| Mr...犯 | 20 | | 2021-03-29 | +| 林 | 20 | | 2021-03-29 | +| 天地 | 20 | | 2021-03-29 | +| 项峥 | 20 | 猫大人牛逼 | 2021-03-29 | +| Easley | 200 | 猫大人牛逼 | 2021-03-29 | +| 秦旭 | 60 | | 2021-03-29 | +| Neal | 20 | 猫大人的错别字检查员 | 2021-03-29 | +| Chovi.Wu | 10.24 | 表哥加油 | 2021-03-29 | +| Niverkk | 20 | | 2021-03-29 | +| ~阿槑~ | 20 | | 2021-03-29 | +| Witt | 20 | 支持猫大人 | 2021-03-29 | +| 屈仁能 | 218.88 | 猫大人家的菜鸟 | 2021-03-29 | +| 侯瑞哲 | 20 | 请猫大人喝咖啡 | 2021-03-29 | +| Phoenix | 20 | 感谢猫大人带我们认识开源 | 2021-03-29 | +| 黄放 | 20 | 为往圣继绝学 | 2021-03-29 | +| 那会 | 20 | 感谢猫大人 | 2021-03-29 | +| Liquid | 20 | | 2021-03-29 | +| audi | 15 | 喵小喵 | 2021-03-29 | +| KaitoShy | 6.66 | | 2021-03-29 | +| 氺 | 20 | | 2021-03-29 | +| Ncy | 20 | 喵喵喵 | 2021-03-29 | +| HelloWorld | 20 | 靡不有初,鲜克有终 | 2021-03-29 | +| 孙龙 | 60 | | 2021-03-29 | +| maybe | 10.24 | | 2021-03-29 | +| biao | 20 | | 2021-03-29 | +| BetterWp | 66.66 | 跟着猫大人冲 | 2021-03-29 | +| 502819 | 20 | 猫大人威武霸气 | 2021-03-29 | +| Z | 10 | 猫大人抽华子 | 2021-03-29 | +| 陈曦 | 66.66 | | 2021-03-29 | +| 一朵云 | 20 | | 2021-03-29 | +| 姜吉宁 | 60 | 做大做强 | 2021-03-29 | +| 飞鸿雪泥 | 60 | 开源万岁 | 2021-03-29 | diff --git a/src/members/README.md b/src/members/README.md index 9b0ed24ab5..559de01c50 100644 --- a/src/members/README.md +++ b/src/members/README.md @@ -4,24 +4,6 @@ pageInfo: false contributors: false editLink: false lastUpdated: false +sidebar: false +layout: members --- - - - - - - diff --git a/src/news/Apache ShenYu-2.7.0.md b/src/news/Apache ShenYu-2.7.0.md new file mode 100644 index 0000000000..3b70c7598f --- /dev/null +++ b/src/news/Apache ShenYu-2.7.0.md @@ -0,0 +1,420 @@ +--- +title: Apache ShenYu 2.7.0 Released! +author: Liu Hongyu +date: 2025-02-13 +cover: /assets/img/news/Apache ShenYu-2.7.0-0.png +head: + - - meta + - name: News +--- + +About Apache ShenYu + +**Apache ShenYu** is a reactive **API** gateway developed using **Java Reactor**. With its high performance, dynamic and flexible traffic management, hot-pluggable, and easy deployment features, it provides users with a full lifecycle **API** gateway out-of-the-box, including **API** registration, service proxy, protocol conversion, **API** documentation, and **API** governance. + +Official Website: https://shenyu.apache.org + +GitHub: https://github.com/apache/shenyu + + +Version Preview + +After a year, **Apache ShenYu** has released version 2.7.0. This version includes a total of 254+ Pull Requests, approximately 17+ new features, several enhancements, numerous refactorings, and several bug fixes. A total of 61 contributors participated, with cumulative contributors reaching 350+. + +Version Record: +https://github.com/apache/shenyu/compare/v2.6.0...v2.7.0 + +New Features + +1. Upgrade Dockerfile Java version from JDK8 to JDK17 + pr: https://github.com/apache/shenyu/pull/5374 + +2. Upgrade SpringBoot version to 3.x + pr: https://github.com/apache/shenyu/pull/5583 + +3. Support ShenYu Admin cluster mode + pr: + https://github.com/apache/shenyu/pull/5544 + https://github.com/apache/shenyu/pull/5592 + +4. Upgrade checkstyle plugin version to 3.4.0 + pr: https://github.com/apache/shenyu/pull/5614 + +5. Data source support for OceanBase + pr: https://github.com/apache/shenyu/pull/5617 + +6. Support batch modification of selector/rule status + pr: https://github.com/apache/shenyu/pull/5499 + +7. Support batch modification of AppAuth status + pr: https://github.com/apache/shenyu/pull/5488 + +8. Upgrade Apache Dubbo version + pr: https://github.com/apache/shenyu/pull/5527 + +9. Support Gitpod development + pr: https://github.com/apache/shenyu/pull/5610 + +10. Support configuration import and export + pr: https://github.com/apache/shenyu/pull/5474 + +11. Add Shenyu client heartbeat reporting function + pr: https://github.com/apache/shenyu/pull/5659 + +12. Add Namespace functionality + pr: + https://github.com/apache/shenyu/pull/5584 + https://github.com/apache/shenyu/pull/5715 + https://github.com/apache/shenyu/pull/5716 + https://github.com/apache/shenyu/pull/5719 + https://github.com/apache/shenyu/pull/5729 + https://github.com/apache/shenyu/pull/5734 + https://github.com/apache/shenyu/pull/5735 + https://github.com/apache/shenyu/pull/5740 + https://github.com/apache/shenyu/pull/5746 + https://github.com/apache/shenyu/pull/5757 + https://github.com/apache/shenyu/pull/5760 + https://github.com/apache/shenyu/pull/5765 + https://github.com/apache/shenyu/pull/5769 + https://github.com/apache/shenyu/pull/5771 + https://github.com/apache/shenyu/pull/5779 + https://github.com/apache/shenyu/pull/5786 + https://github.com/apache/shenyu/pull/5787 + https://github.com/apache/shenyu/pull/5790 + https://github.com/apache/shenyu/pull/5798 + https://github.com/apache/shenyu/pull/5799 + https://github.com/apache/shenyu/pull/5823 + https://github.com/apache/shenyu/pull/5847 + https://github.com/apache/shenyu/pull/5857 + +13. Support k8s dynamic scaling + pr: https://github.com/apache/shenyu/pull/5686 + +14. Invalidate previous token after re-login + pr: https://github.com/apache/shenyu/pull/5600 + +15. Divide plugin supports canary release functionality + pr: https://github.com/apache/shenyu/pull/5763 + +16. Support Kubernetes registry + pr: https://github.com/apache/shenyu/pull/5679 + +Enhancements + +1. Add RocketMQ log e2e test + pr: https://github.com/apache/shenyu/pull/5439 + +2. Enhance rate limiter metric collection + pr: https://github.com/apache/shenyu/pull/5461 + +3. Enhance Sentinel, Resilience4j, and Hystrix metric collection + pr: https://github.com/apache/shenyu/pull/5468 + +4. Organize sofa-common-tools dependency + pr: https://github.com/apache/shenyu/pull/5609 + +5. Add missing licenses + pr: https://github.com/apache/shenyu/pull/5503 + +6. Set callback for Kafka message sending + pr: https://github.com/apache/shenyu/pull/5748 + +7. Use load balancing configuration from Dubbo metadata + pr: https://github.com/apache/shenyu/pull/5806 + +8. Add non-null validation for Upstream obtained from selector + pr: https://github.com/apache/shenyu/pull/5804 + +9. Set timeout from rule processing into Dubbo RPC context + pr: https://github.com/apache/shenyu/pull/5778 + +10. Publish events when enabling selectors and rules + pr: https://github.com/apache/shenyu/pull/5762 + +11. Remove closed sessions from namespace session map + pr: https://github.com/apache/shenyu/pull/5734 + +12. Add test cases for ShenyuClientURIExecutorSubscriber + pr: https://github.com/apache/shenyu/pull/5413 + +13. Add test cases for ShenyuClientIllegalArgumentException + pr: https://github.com/apache/shenyu/pull/5408 + +14. Add test cases for ShenyuClientRegisterEventPublisher + pr: https://github.com/apache/shenyu/pull/5417 + +15. Add test cases for ShenyuClientMetadataExecutorSubscriber + pr: https://github.com/apache/shenyu/pull/5404 + +16. Add test cases for AbstractWasmPluginDataHandler + pr: https://github.com/apache/shenyu/pull/5451 + +17. Add test cases for ShenyuClientRegisterRepositoryFactoryTest + pr: https://github.com/apache/shenyu/pull/5443 + +18. Add test cases for AbstractWasmDiscoveryHandler + pr: https://github.com/apache/shenyu/pull/5453 + +19. Upgrade SOFA RPC version support + pr: https://github.com/apache/shenyu/pull/5526 + +20. Add signature plugin request header keys to CORS filter configuration + pr: https://github.com/apache/shenyu/pull/5627 + +21. Encrypt passwords + pr: https://github.com/apache/shenyu/pull/5436 + +22. Add AbstractShenyuWasmPlugin test cases + pr: https://github.com/apache/shenyu/pull/5450 + +23. Rewrite plugin/context path plugin supports cross-application and plugins + pr: https://github.com/apache/shenyu/pull/5438 + +24. Remove duplicate path check + pr: https://github.com/apache/shenyu/pull/5514 + +25. Remove Alibaba Dubbo related dependencies + pr: https://github.com/apache/shenyu/pull/5500 + +26. Support setting HTTP path via Docker environment variables + pr: https://github.com/apache/shenyu/pull/5833 + +27. Add code refactoring improvements + pr: https://github.com/apache/shenyu/pull/5613 + +28. Support obtaining tokens from cookies, headers, and parameters + pr: https://github.com/apache/shenyu/pull/5547 + +29. Adjust ShenyuDubboService annotation default values to align with DubboService annotation + pr: https://github.com/apache/shenyu/pull/5816 + +30. Add database scripts to admin package + pr: https://github.com/apache/shenyu/pull/5724 + +31. Clean up unused code and make improvements + pr: + https://github.com/apache/shenyu/pull/5849 + https://github.com/apache/shenyu/pull/5803 + https://github.com/apache/shenyu/pull/5789 + +32. Optimize MotanServiceEventListener test cases + pr: https://github.com/apache/shenyu/pull/5745 + +33. Remove duplicate Maven configuration in shenyu-registry-eureka.xml + pr: https://github.com/apache/shenyu/pull/5836 + +34. Update JWT dependency + pr: https://github.com/apache/shenyu/pull/5480 + +35. Print plugin execution time + pr: https://github.com/apache/shenyu/pull/5437 + +36. Admin local discovery supports upstream health checks + pr: https://github.com/apache/shenyu/pull/5596 + +37. Disable rule cache + pr: https://github.com/apache/shenyu/pull/5589 + +38. Reduce concurrency + pr: https://github.com/apache/shenyu/pull/5587 + +39. Optimize logic to avoid "orElse" execution, update VersionTwoExtractor + pr: https://github.com/apache/shenyu/pull/5415 + +Refactoring + +1. Use spring-integration-jdbc to implement Admin distributed lock + pr: https://github.com/apache/shenyu/pull/5457 + +2. Refactor beanUtils + pr: https://github.com/apache/shenyu/pull/5497 + +3. Remove macOS CI + pr: https://github.com/apache/shenyu/pull/5559 + +4. Update deprecated DataBuffer methods in log plugin + pr: https://github.com/apache/shenyu/pull/5620 + +5. Modify e2e k8s test to docker compose + pr: https://github.com/apache/shenyu/pull/5710 + +6. Migrate Admin swagger from springfox to springdoc + pr: https://github.com/apache/shenyu/pull/5630 + +7. Refactor springcloud plugin + pr: https://github.com/apache/shenyu/pull/5695 + +8. Refactor partial code + pr: https://github.com/apache/shenyu/pull/5568 + +9. Delete SO_SNDBUF and SO_RCVBUF + pr: https://github.com/apache/shenyu/pull/5502 + +10. Replace log %s with {} + pr: https://github.com/apache/shenyu/pull/5465 + +11. Optimize node type listener + pr: https://github.com/apache/shenyu/pull/5435 + +12. Refactor plugin lifecycle + pr: https://github.com/apache/shenyu/pull/5432 + +13. Adjust code order and remove invalid input parameters + pr: https://github.com/apache/shenyu/pull/5397 + +Fixes + +1. Fix duplicate request header issue in request plugin + pr: https://github.com/apache/shenyu/pull/5846 + +2. Fix issue where proxy selector and discovery are not deleted when deleting divide selector + pr: https://github.com/apache/shenyu/pull/5845 + +3. Fix log plugin error log capture issue + pr: https://github.com/apache/shenyu/pull/5842 + +4. Fix log plugin sample bug + pr: https://github.com/apache/shenyu/pull/5429 + +5. Fix memory overflow issue + pr: https://github.com/apache/shenyu/pull/5407 + +6. Fix rewrite integration test + pr: https://github.com/apache/shenyu/pull/5445 + +7. Fix AbstractWasmPluginDataHandlerTest + pr: https://github.com/apache/shenyu/pull/5464 + +8. Fix missing primary key in sql-script/h2/schema.sql + pr: https://github.com/apache/shenyu/pull/5481 + +9. Fix abnormal data sorting on data dictionary page + pr: https://github.com/apache/shenyu/pull/5483 + +10. Fix documentation errors + pr: https://github.com/apache/shenyu/pull/5505 + +11. Resolve dashboard route and context path update mismatch issue + pr: https://github.com/apache/shenyu/pull/5510 + +12. Fix etcd synchronization configuration issue + pr: https://github.com/apache/shenyu/pull/5535 + +13. Fix consul synchronization issue + pr: https://github.com/apache/shenyu/pull/5546 + +14. Fix error where unregistered cannot be queried + pr: https://github.com/apache/shenyu/pull/5578 + +15. Correct plugin ID query and update data type + pr: https://github.com/apache/shenyu/pull/5622 + +16. Fix spelling error in AdminConstants class + pr: https://github.com/apache/shenyu/pull/5637 + +17. Fix shenyu-examples-springmvc startup failure issue + pr: https://github.com/apache/shenyu/pull/5664 + +18. Fix dashboard menu sub-item sorting not taking effect issue + pr: https://github.com/apache/shenyu/pull/5691 + +19. ShenyuApacheDubboXmlProviderApplication configuration fix + pr: https://github.com/apache/shenyu/pull/5811 + +20. Fix non-unique data synchronization ID issue for proxy_selector and discovery + pr: https://github.com/apache/shenyu/pull/5783 + +21. Filter disabled dictionary options + pr: https://github.com/apache/shenyu/pull/5776 + +22. Fix SpringCloudParser metadata empty data issue + pr: https://github.com/apache/shenyu/pull/5737 + +23. Fix client registration validation + pr: https://github.com/apache/shenyu/pull/5764 + +24. Configure dubbo serialization check status as disabled + pr: https://github.com/apache/shenyu/pull/5756 + +25. Fix sample TestApacheDubboXmlApplication startup failure issue + pr: https://github.com/apache/shenyu/pull/5754 + +26. Fix nacos data synchronization model missing context path configuration + pr: https://github.com/apache/shenyu/pull/5722 + +27. Fix SPI creating non-singleton objects in multi-threaded scenarios + pr: https://github.com/apache/shenyu/pull/5713 + +28. Fix incorrect SQL syntax exception + pr: https://github.com/apache/shenyu/pull/5707 + +29. Fix ListUtil->merge exception + pr: https://github.com/apache/shenyu/pull/5642 + +30. Fix metadata disable not filtered issue + pr: https://github.com/apache/shenyu/pull/5638 + +31. Fix divide log request method + pr: https://github.com/apache/shenyu/pull/5607 + +32. Fix e2e chunk header error + pr: https://github.com/apache/shenyu/pull/5593 + +33. Fix cookie error and SQL check + pr: https://github.com/apache/shenyu/pull/5567 + +34. Fix null pointer exception issue + pr: + https://github.com/apache/shenyu/pull/5539 + https://github.com/apache/shenyu/pull/5530 + +35. Fix invalid path error + pr: https://github.com/apache/shenyu/pull/5533 + +36. Fix hot reload issue + pr: https://github.com/apache/shenyu/pull/5509 + +37. Fix e2e test case unable to run wget command + pr: https://github.com/apache/shenyu/pull/5519 + +38. Fix downgrade issue + pr: https://github.com/apache/shenyu/pull/5496 + +39. Resolve SQL error in rule-sqlmap.xml + pr: https://github.com/apache/shenyu/pull/5644 + +40. Fix readYmlBuildRepository null pointer exception + pr: https://github.com/apache/shenyu/pull/5819 + +41. Fix nacos unable to register in Shenyu-examples-SpringCloud project issue + pr: https://github.com/apache/shenyu/pull/5825 + +42. Fix springCloud rule data path setting not used issue + pr: + https://github.com/apache/shenyu/pull/5841 + https://github.com/apache/shenyu/pull/5843 + +43. Fix shenyu-plugin-logging-elasticsearch: modify ElasticSearchLogConfig setIndexName + pr: https://github.com/apache/shenyu/pull/5830 + +44. Fix issue where service is not taken offline from gateway first when stopping service + pr: https://github.com/apache/shenyu/pull/5507 + +45. Fix k8s liveness probe unable to run wget command error + pr: https://github.com/apache/shenyu/pull/5513 + +46. Fix AbstractNodeDataSyncService loading discoveryUpstream on startup issue + pr: https://github.com/apache/shenyu/pull/5473 + +Contributors + +Special thanks to the following contributors for their support and participation in the **2.7.0** version (in no particular order). + +**0xmkzt**, **Divyansh200102**, **IceFoxs**, **JJellyfish**, **Misaya295**, **KerwinBryant**, **M.G.Ting**, **NanMu**, **QiXu**, **RayayChung**, **RiccoChen**, **Sinsy**, **VampireAchao**, **WindSearcher**, **Wweiei**, **YuSiheng**, **aias00**, **caaaaaat**, **crazyStar**, **crudboy**, **dragon-zhang**, **dyjxg4xygary**, **dyp314417995**, **eye-gu**, **frank**, **hdgaadd**, **hql0312**, **j@ckzh0u**, **jerbo99**, **loongs-zhang**, **mmengLong**, **moremind**, **po-168**, **tomsun28**, **ttfont**, **wlngo**, **wyfvsfy**, **xcsnx**, **xiangqianZ**, **xiaoyu**, **yunlongn**, **ywwana**, **zhengke zhou**, **zhengpeng**, **ywj1352** + +Become a Contributor + +We welcome every contributor to join ShenYu and encourage contributors to participate in ShenYu in the spirit of the Apache Way! + +Contributor Guide: +https://shenyu.apache.org/zh/community/contributor-guide \ No newline at end of file diff --git a/src/news/Apache-Hertzbeat-1.6.1.md b/src/news/Apache-Hertzbeat-1.6.1.md new file mode 100644 index 0000000000..826e361e1e --- /dev/null +++ b/src/news/Apache-Hertzbeat-1.6.1.md @@ -0,0 +1,98 @@ +--- +title: Apache Hertzbeat Version 1.6.1 Release Announcement +author: November 1, 2024, 08:54 +date: 2024-11-01 +cover: /assets/img/news/Apache-Hertzbeat-1.6.1-0.png +head: + - - meta + - name: News +--- + +# Apache Hertzbeat Version 1.6.1 Release Announcement + +Dear community members! 🎉️ + +We are thrilled to announce the official release of Apache Hertzbeat version 1.6.1! This release merges 468 PRs, bringing numerous new features and improvements. This article details the key updates in version 1.6.1. We welcome more developers and users to join our open-source community! + +![](/assets/img/news/Apache-Hertzbeat-1.6.1-0.png) + +What is Hertzbeat? + +![](/assets/img/news/Apache-Hertzbeat-1.6.1-1.png) + +## Download & Documentation + +* **Apache Hertzbeat 1.6.1 Download**: https://hertzbeat.apache.org/zh-cn/docs/download +* **Apache Hertzbeat Documentation**: https://hertzbeat.apache.org/zh-cn/docs/ + +## Key Updates + +### New Features & Enhancements + +* **New Monitoring Support**: Added monitoring for Apache HBase, InfluxDB, VictoriaMetrics cluster, HDFS, Yarn, Linux processes, HBase RegionServer, OpenAI accounts, Redfish protocol, and more. +* **Prometheus Support**: Added Prometheus parser and Prometheus-like push mode. +* **Internationalization (i18n) Support**: Added i18n support for metric names of ClickHouse, DynamicTp, Airflow, IoTDB, RocketMQ, and other monitors. +* **Custom Monitoring Menu**: Monitoring templates now support custom main menus. +* **NebulaGraph Support**: Added support for querying NebulaGraph monitoring data using `ngql`. +* **SMS Functionality**: Added support for sending SMS via Alibaba Cloud. +* **Docker Support**: Added support for running Hertzbeat via Docker Compose. + +### Bug Fixes + +* **Startup Issues**: Fixed issues where the Collector couldn't start independently, MySQL dependency problems, and MongoDB monitoring being unavailable in Spring Boot 3. +* **Data Issues**: Fixed JPA data saving logic errors, Redis cluster node test errors, old data decoding errors, and more. +* **Null Pointer Exception (NPE) Fixes**: Fixed multiple issues related to Null Pointer Exceptions (NPE). +* **Other Bug Fixes**: Includes fixes for command window data loss, MongoDB template command errors, and others. + +### Code Refactoring & Optimization + +* **Code Simplification**: Optimized code structure, used Assert class to simplify null checks, removed unnecessary if-else statements, and adopted new Java 17 syntax. +* **Dependency Management Optimization**: Removed unnecessary dependencies and refactored some packages into independent modules. +* **Performance Improvements**: Improved performance by optimizing WebSocket connections, Redis URI building, and other aspects. +* **Logging & Configuration Updates**: Updated logback configurations for the Collector and Manager. + +### Documentation Translation & Improvements + +* **Translation Work**: Translated numerous class descriptions, blog posts, and monitoring template documents from Chinese to English. +* **Added Help Documentation**: Added help documentation for monitoring items like ClickHouse, DNS, Flink, etc. +* **Documentation Structure Updates**: Updated website documentation, contribution guidelines, homepage introduction, and more. + +### Security Updates + +* **Dependency Upgrades**: Upgraded H2 database dependency library to fix related security vulnerabilities. +* **Other Security Improvements**: Fixed issues with SSL certificate remaining days and Jexl expression security matching. + +### Test Case Additions + +* **Improved Test Coverage**: Added test cases for Redis, Nginx, Telnet monitoring functionalities, and more, increasing test coverage. + +## Acknowledgments + +Thanks to **@zqr10159** for supporting this release work. We also extend our gratitude to the following community members whose joint efforts made this release possible: + +> LinuxSuRen, transactional, JavaProgrammerLB, westboy, xuziyang, makechoicenow, crossoverJie, xfl12345, boatrainlsz, lw-yang, tomsun28, Alanxtl, Aias00, Clownsw, zhangshenghang, zqr10159, LiuTianyou, handy-git, hudongdong129, dukbong, 15613060203, yqxxgh, miki-hmt, PeixyJ, allcontributors, Ceilzcx, lwjxy, starmilkxin, leo-934, zuobiao-zhou, tomorrowshipyltm, LLP2333, lwqzz, wang1027-wqh, gjjjj0101, ZY945, yuluo-yx, HeartLinked, alpha951, Hi-Mr-Wind, TJxiaobao, YxYL6125, MananPoojara, a-little-fool, Pzz-2021, Yanshuming1, Thespica, Calvin979, WinterKi1ler + +# Apache Hertzbeat + +**Repository:** + +https://github.com/apache/hertzbeat + +**Website:** + +https://hertzbeat.apache.org/ + +**Apache Hertzbeat Download:** + +https://hertzbeat.apache.org/zh-cn/docs/download + +**Apache Hertzbeat Docker Images:** + +> Apache HertzBeat produces Docker images for each release. You can pull them from Docker Hub. +> +> * HertzBeat https://hub.docker.com/r/apache/hertzbeat +> * HertzBeat Collector https://hub.docker.com/r/apache/hertzbeat-collector + +**How to Contribute to the Apache Hertzbeat Open Source Community?** + +https://hertzbeat.apache.org/zh-cn/docs/community/contribution \ No newline at end of file diff --git a/src/news/Carbon-0.md b/src/news/Carbon-0.md new file mode 100644 index 0000000000..8074ea70b3 --- /dev/null +++ b/src/news/Carbon-0.md @@ -0,0 +1,118 @@ +--- +title: New Open Source Project Carbon Joins Dromara, A Time Tool for Gophers +author: carbon +date: 2024-10-24 +cover: /assets/img/news/Carbon-0-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/Carbon-0-0.png) + +For `gopher`s, time processing is a common yet complex issue, especially when relying solely on the built-in `time.Time` package. A particular pain point is time formatting, where a fixed `Layout` must be used, such as: + +```go +now := time.Now() +// These numbers must be written exactly like this, otherwise the converted time will be莫名其妙 (utterly confusing) +strNow := now.Format("2006/01/02 03:04:05") +``` + +This is where `carbon` shines. If you work with dates and times in the `Golang` ecosystem and haven't heard of the `Carbon` library, I strongly advise you to review your existing date and time related code to see how much time `Carbon` could save you. + +`Carbon` is a lightweight, semantic, and developer-friendly time manipulation library designed specifically for `Golang`, hailed as the Swiss Army knife for time handling in Golang. It provides a series of concise yet powerful `APIs`, making time operations exceptionally simple. Whether it's basic datetime calculations or complex timezone conversions, `Carbon` handles it with ease. Furthermore, `Carbon` has been included in `awesome-go` and awarded the Annual Most Valuable Project (`GVP`) by `gitee`, demonstrating its popularity within the `Golang` community. + +#### Installation & Usage + +##### Golang version >= 1.17 (Recommended) + +```bash +# Using github library +go get -u github.com/golang-module/carbon/v2 + +import "github.com/golang-module/carbon/v2" + +# Using gitee library +go get -u gitee.com/golang-module/carbon/v2 + +import "gitee.com/golang-module/carbon/v2" +``` + +##### Golang version < 1.17 (Required) + +```bash +# Using github library +go get -u github.com/golang-module/carbon + +import "github.com/golang-module/carbon" + +# Using gitee library +go get -u gitee.com/golang-module/carbon + +import "gitee.com/golang-module/carbon" +``` + +#### Usage Examples + +> Assuming the current time is 2020-08-05 13:14:15.999999999 +0800 CST + +##### Set Global Defaults + +```go +carbon.SetDefault(carbon.Default{ + Layout: carbon.DateTimeLayout, + Timezone: carbon.PRC, + WeekStartsAt: carbon.Sunday, + Locale: "zh-CN", // Valid values: translation file names in the lang directory, excluding file extensions +}) +``` + +##### Conversion between `Carbon` and `time.Time` + +```go +// Convert standard time.Time to Carbon +carbon.CreateFromStdTime(time.Now()) +// Convert Carbon to standard time.Time +carbon.Now().StdTime() +``` + +##### Yesterday, Today, Tomorrow + +*(Numerous examples showing various string representations and timestamps for today, yesterday, and tomorrow across different timezones)* + +##### Create `Carbon` Instances + +*(Examples showing creation from timestamps (seconds, milliseconds, microseconds, nanoseconds), from datetime components (year, month, day, hour, minute, second, with optional fractional seconds), from date components only, and from time components only (using current date))* + +##### Parse `Time String` into `Carbon` Instance + +*(Extensive examples showing parsing of various string formats including empty strings, relative strings ("now", "yesterday"), partial dates ("2020", "2020-8"), full datetimes with different separators and precision levels, ISO 8601 formats with timezone offsets, and compact numeric formats)* + +`Carbon` also provides support for time travel, time differences, time extremes, time judgments, zodiac signs, constellations, the lunar calendar, Julian Day / Modified Julian Day, and the Persian/Iranian calendar. + +##### Convert `Gregorian Calendar` to `Lunar Calendar` + +*(Examples demonstrating various methods to get lunar calendar properties: animal zodiac, festival, year, month, leap month, day, hour, minute, second, string representations in Chinese, checks for zero value, leap year, leap month, and specific animal years)* + +##### Convert `Lunar Calendar` to `Gregorian Calendar` + +```go +// Convert Lunar 二零二三年腊月十一 (23rd year of the cycle, 12th month, 11th day) to Gregorian +carbon.CreateFromLunar(2023, 12, 11, 0, 0, 0, false).ToDateTimeString() // 2024-01-21 00:00:00 +// Convert Lunar 二零二三年二月十一 (23rd year, 2nd month, 11th day) to Gregorian +carbon.CreateFromLunar(2023, 2, 11, 0, 0, 0, false).ToDateTimeString() // 2023-03-02 00:00:00 +// Convert Lunar 二零二三年闰二月十一 (23rd year, Leap 2nd month, 11th day) to Gregorian +carbon.CreateFromLunar(2023, 2, 11, 0, 0, 0, true).ToDateTimeString() // 2023-04-01 00:00:00 +``` + +It currently supports 26 languages including Simplified Chinese, Traditional Chinese, English, Japanese, German, Spanish, French, Arabic, and more. + +![](/assets/img/news/Carbon-0-1.png) + +##### Conclusion + +Whether you are a novice or an experienced `Golang` developer, `Carbon` is a library worth trying. With it, you can handle time issues more elegantly, improving code readability and development efficiency. Introduce `Carbon` into your project now and enjoy the fun of programming! + +GitHub: https://github.com/dromara/carbon + +Gitee: https://gitee.com/dromara/carbon \ No newline at end of file diff --git a/src/news/Carbon-2.6.0.md b/src/news/Carbon-2.6.0.md new file mode 100644 index 0000000000..a0511b2dd7 --- /dev/null +++ b/src/news/Carbon-2.6.0.md @@ -0,0 +1,69 @@ +--- +title: "Carbon 2.6.0 Released: A Lightweight Golang Time Handling Library" +author: carbon +date: 2025-03-25 +cover: /assets/img/news/Carbon-2.6.0-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/Carbon-2.6.0-0.png) + +Carbon is a lightweight, semantic, and developer-friendly Golang time handling library. It supports time travel, time difference calculations, time extremes, time judgments, zodiac signs, constellations, the lunar calendar, Julian Day / Modified Julian Day, and the Persian (Iranian) calendar. + +Carbon has been donated to the dromara\[1] open-source organization, included in awesome-go\[2], and has received the gitee\[3] 2024 Most Valuable Project (GVP\[4]) award and the gitcode\[5] 2024 G-Star\[6] award. If you find it useful, please give it a star! + +github.com/dromara/carbon\[7] + +gitee.com/dromara/carbon\[8] + +gitcode.com/dromara/carbon\[9] + +#### Changelog + +* Minimum `golang` version requirement upgraded to `1.18` +* `carbon`, `julian`, `lunar`, and `persian` changed from value passing to pointer passing +* Added `ZoneName` method to retrieve the timezone name +* Added `HasError` method to check for errors +* Added `IsNil` method to check if the value is `nil` +* Added `Copy` method for deep copying a `carbon` instance +* Added example files `xxx_example.go` +* Added `constant.go` file; constants migrated from `carbon.go` to this file +* Default global timezone changed from `Local` to `UTC` +* Renamed `Offset` method to `ZoneOffset` +* Renamed `IsSetTestNow` method to `IsTestNow` +* Renamed `UnSetTestNow` method to `CleanTestNow` +* Removed `Location` method, replaced by `Timezone` method +* Modified the logic of `IsValid` and `IsInvalid` methods; `zero time` is no longer considered invalid +* Setting the global default timezone now simultaneously updates `time.Local` +* Refactored `database.go`; removed `carbon.DateTime`, `carbon.DateTimeMilli`, `carbon.DateTimeMicro`, `carbon.DateTimeNano`, `carbon.Date`, `carbon.DateMilli`, `carbon.DateMicro`, `carbon.DateNano`, `carbon.Time`, `carbon.TimeMilli`, `carbon.TimeMicro`, `carbon.TimeNano`, `carbon.Timestamp`, `carbon.TimestampMilli`, `carbon.TimestampMicro`, and `carbon.TimestampNano` field types. Replaced with generic fields to enable custom output formatting during `MarshalJSON/UnmarshalJSON` + +References + +\[1] +dromara: _https://dromara.org/_ + +\[2] +awesome-go: _https://github.com/avelino/awesome-go#date-and-time_ + +\[3] +gitee: _https://gitee.com_ + +\[4] +GVP: _https://gitee.com/gcp_ + +\[5] +gitcode: _https://gitcode.com_ + +\[6] +G-Star: _https://gitcode.com/g-star_ + +\[7] +github.com/dromara/carbon: _https://github.com/dromara/carbon_ + +\[8] +gitee.com/dromara/carbon: _https://gitee.com/dromara/carbon_ + +\[9] +gitcode.com/dromara/carbon: _https://gitcode.com/dromara/carbon_ \ No newline at end of file diff --git a/src/news/Carbon-2.6.2.md b/src/news/Carbon-2.6.2.md new file mode 100644 index 0000000000..ed54b5fa7b --- /dev/null +++ b/src/news/Carbon-2.6.2.md @@ -0,0 +1,51 @@ +--- +title: "Carbon 2.6.2 Released: A Golang Time Handling Library" +author: carbon +date: 2025-04-11 +cover: /assets/img/news/carbon-2.6.2-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/carbon-2.6.2-0.png) + +Carbon is a lightweight, semantic, and developer-friendly Golang time handling library. It supports time travel, time difference calculations, time extremes, time judgments, zodiac signs, seasons, the lunar calendar, Julian Day / Modified Julian Day, and the Persian (Iranian) calendar. + +Carbon has been donated to the dromara[1] open-source organization, included in awesome-go[2], and has received the gitee[3] 2024 Most Valuable Project (GVP[4]) award and the gitcode[5] 2024 G-Star[6] award. If you find it useful, please give it a star! + +github.com/dromara/carbon[7] +gitee.com/dromara/carbon[8] +gitcode.com/dromara/carbon[9] + +#### Changelog + +* Removed `hour`, `minute`, and `second` parameters from the `CreateFromLunar` and `CreateFromPersian` methods +* Modified the definitions of some format symbols, including `U`, `V`, `X`, `S`, `T`, `Z`, `u`, `v`, `x`, and `z` +* Fixed a bug in the lunar calendar where `IsLeapMonth` returned incorrect results +* Fixed a bug where `AtomFormat` and `AtomLayout` returned inconsistent values +* Fixed a bug where `RFC3339Format` and `RFC3339Layout` returned inconsistent values +* Setting the global default timezone no longer updates `time.Local` simultaneously +* Added the format symbol `o` to retrieve the timezone offset +* Added constants: `TimestampLayout`, `TimestampMilliLayout`, `TimestampMicroLayout`, and `TimestampNanoLayout` +* Added constants: `TimestampFormat`, `TimestampMilliFormat`, `TimestampMicroFormat`, and `TimestampNanoFormat` +* Added field types: `DateTimeMilli`, `DateTimeMicro`, and `DateTimeNano` +* Added field types: `DateMilli`, `DateMicro`, and `DateNano` +* Added field types: `TimeMilli`, `TimeMicro`, and `TimeNano` +* Fixed a bug where the `IsDST` method lost timezone information +* Fixed a bug where some `StartOfXXX` and `EndOfXXX` methods lost timezone information +* Fixed a bug where timezone information was missing when converting from other calendars to the Gregorian calendar +* Setting the default timezone no longer updates `time.Local` simultaneously +* Added `MaxDuration` and `MinDuration` methods + +References + +[1] dromara: https://dromara.org/ +[2] awesome-go: https://github.com/avelino/awesome-go#date-and-time +[3] gitee: https://gitee.com +[4] GVP: https://gitee.com/gcp +[5] gitcode: https://gitcode.com +[6] G-Star: https://gitcode.com/g-star +[7] github.com/dromara/carbon: https://github.com/dromara/carbon +[8] gitee.com/dromara/carbon: https://gitee.com/dromara/carbon +[9] gitcode.com/dromara/carbon: https://gitcode.com/dromara/carbon \ No newline at end of file diff --git a/src/news/Carbon-v2.5.0.md b/src/news/Carbon-v2.5.0.md new file mode 100644 index 0000000000..833337ef92 --- /dev/null +++ b/src/news/Carbon-v2.5.0.md @@ -0,0 +1,75 @@ +--- +title: Lightweight Golang Time Library Carbon v2.5.0 Released +author: carbon +date: 2024-11-28 +cover: /assets/img/news/Carbon-v2.5.0-0.png +head: + - - meta + - name: News +--- + +Carbon is a lightweight, semantic, and developer-friendly Golang time processing library. It provides support for time travel, time differences, time extremes, time judgments, zodiac signs, the lunar calendar, Julian Day / Modified Julian Day, and the Persian/Iranian calendar. + +Carbon has been donated to the dromara[[1]](#user-content-fn-1){: .footnote-backref} open-source organization. It has been included in awesome-go[[2]](#user-content-fn-2){: .footnote-backref} and has received the Gitee[[3]](#user-content-fn-3){: .footnote-backref} 2024 Most Valuable Project (GVP[[4]](#user-content-fn-4){: .footnote-backref}) award and the GitCode[[5]](#user-content-fn-5){: .footnote-backref} 2024 Annual G-Star[[6]](#user-content-fn-6){: .footnote-backref} project award. If you find it useful, please give it a star! + +github.com/dromara/carbon[[7]](#user-content-fn-7){: .footnote-backref} + +gitee.com/dromara/carbon[[8]](#user-content-fn-8){: .footnote-backref} + +gitcode.com/dromara/carbon[[9]](#user-content-fn-9){: .footnote-backref} + +![](/assets/img/news/Carbon-v2.5.0-0.png) + +#### Changelog + +* Repository address changed from `github.com/golang-module/carbon` to `github.com/dromara/carbon` +* Added translation support for `Hungarian`, translated by @kenlas +* Retranslated the `Japanese version README` file, translated by Japanese friend @You-saku +* Fixed a bug where `DiffInMonths()` calculation was incorrect +* Replaced `Lock()`, `Unlock()` with `RLock()`, `RUnlock()` +* Removed methods `ToDateTimeStruct()`, `ToDateTimeMilliStruct()`, `ToDateTimeMicroStruct()`, `ToDateStruct()`, etc., replaced by newly added methods `NewDateTime()`, `NewDateTimeMilli()`, `NewDateTimeMicro()`, `NewDate()`, etc. +* Structs like `DateTime`, `DateTimeXXX`, `Date`, `DateXXX`, `Time`, `TimeXXX` now implement the `Scan`, `Value`, `MarshalJSON`, `UnmarshalJSON` interfaces +* The `Scan` interface implementation supports parsing formats of `string`, `[]byte`, and `time.Time` +* When `UnmarshalJSON` parses JSON-formatted time strings, it uniformly uses the global default timezone `defaultTimezone` +* Upgraded `codecov/codecov-action` from `v4` to `v5` + +Reference Materials + +[1] +dromara: https://dromara.org/ + +[2] +awesome-go: https://github.com/avelino/awesome-go#date-and-time + +[3] +gitee: https://gitee.com + +[4] +GVP: https://gitee.com/gvp + +[5] +gitcode: https://gitcode.com + +[6] +G-Star: https://gitcode.com/g-star + +[7] +github.com/dromara/carbon: https://github.com/dromara/carbon + +[8] +gitee.com/dromara/carbon: https://gitee.com/dromara/carbon + +[9] +gitcode.com/dromara/carbon: https://gitcode.com/dromara/carbon + +--- + +About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, and scheduling orchestration. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, and is dedicated to providing microservices cloud-native solutions for global users. It aims to allow every participating open-source enthusiast to experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of members, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to interact with me** + +![](/assets/img/news/Carbon-v2.5.0-1.webp) \ No newline at end of file diff --git a/src/news/Dante-Cloud-3.3.5.0.md b/src/news/Dante-Cloud-3.3.5.0.md new file mode 100644 index 0000000000..dcd473fd0a --- /dev/null +++ b/src/news/Dante-Cloud-3.3.5.0.md @@ -0,0 +1,218 @@ +--- +title: "Dante Cloud 3.3.5.0 Released: China's First Microservices Platform Supporting Both Blocking and Reactive Fusion" +author: Dante Cloud +date: 2024-10-31 +cover: /assets/img/news/Dante-Cloud-3.3.5.0-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/Dante-Cloud-3.3.5.0-0.png) + +**Dante Cloud** is China's first microservices platform that supports the fusion of both blocking and reactive programming models. With a core focus on "**high-quality code and low security vulnerabilities**", it adopts Domain-Driven Design (DDD) principles, is fully based on the Spring ecosystem's open-source technologies and the OAuth2.1 protocol, supports authentication for smart TVs, IoT devices, and other IoT equipment, meets **China's Level 3 Cybersecurity Protection requirements**, supports **national cryptographic digital envelope encryption and decryption** for interfaces, and provides a **multi-tenant microservices solution** with a comprehensive security system including anti-brute force, high-level XSS, and SQL injection protection. + +## [I] Release Background + +**Dante Cloud** has always adhered to the philosophy of "Simplicity, Efficiency, Inclusiveness, and Pragmatism". It is built using various emerging or mainstream technologies in the microservices domain and its surrounding areas, continuously refined and meticulously crafted. The goal is to create a product with `high code quality, low maintenance effort, and strong security protection` that can help users quickly bypass the stages of architectural technology selection and technical research exploration. It aims to reduce the high maintenance costs caused by potential hidden dangers such as security vulnerabilities, technical debt, and low-quality code in traditional projects. Like the meaning of its name, it hopes to bridge the gap during industry transformation and assist enterprises in their informatization construction and transformation. + +## [II] Update Content + +* **Major Updates** + +* [Upgrade] Spring Boot version upgraded to 3.3.5 + +* [Upgrade] Spring Authorization Server version upgraded to 1.3.3 + +* [Upgrade] Spring Boot Admin version upgraded to 3.3.4 + +* [Upgrade] Debezium version upgraded to 3.0 + +* [Upgrade] Camunda version upgraded to 7.22.0 + +* [Upgrade] Nacos version upgraded to 2.4.3 + +* [Refactor] Open-source version project code package name changed from `cn.herodotus` to `org.dromara` to align with the community project. + +* [Refactor] The monolithic version system has been merged into the microservices version project. The monolithic version or microservices version can now be started from the same project. This resolves the previous mode where the microservices version needed to be compiled separately and the monolithic version started in another project, improving development and usability convenience. + +* [New] Added inter-service file upload and download transmission mechanism, supporting both OpenFeign and Grpc modes, switchable via hot-plug mode. + +* [New] Added OSS file operation GRPC definition module + +* [New] Added local common file management mechanism for services + +* [New] Added cross-module, cross-service authentication enable/disable control unit + +* [New] Added client dynamic registration business information synchronous creation function + +* [New] Mqtt user account management function + +* [New] Added NoSQL related component auto-configuration Starters. + +* [New] Added support for both columnar and row-based storage of device reported data in Influxdb. + +* **Other Updates** + +* [Fix] Rebuilt Nacos Server image supporting Postgresql. Fixed the issue of failed namespace creation. + +* [Fix] Fixed the issue where data encryption policy configuration did not take effect. + +* [Fix] Fixed Kafka Docker Compose configuration error causing Kafka image startup to throw errors. + +* [Fix] Fixed frontend Vite CSS style configuration incompatibility causing page startup errors. + +* [Fix] Fixed abnormal dictionary aggregation data summary issue caused by incorrect use of distributed events in microservices environment. + +* [Fix] Fixed issue where pre-signed URLs created using AWS SDK V2 still used AWS default service endpoints and did not locate to custom hosts. + +* [Fix] Fixed error in log identification Class introduced in auto-configuration classes. + +* [Fix] Fixed grpc compilation error: `error: emptyList() is not public in LazyStringArrayList; cannot be accessed from outside package com.google.protobuf.LazyStringArrayList.emptyList()`. fix: #IAWQ4C + +* [Fix] Fixed Docker Compose image address configuration error. fix: #IAXUFB + +* [Fix] Fixed inconsistency between Influxdb2 default configuration and the default configuration provided by the system's Docker Compose, which caused some Influxdb test codes to pass partially. + +* [Fix] Fixed frontend `Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0` warning error. + +* [Refactor] Refactored AWS SDK V2 high-level OSS operation code, added high-level operation unit tests. + +* [Refactor] Changed basic file operations within services to use NIO operations. + +* [Refactor] Mybatis Plus changed to Bom import, simultaneously adapting to the latest version of Mybatis Plus. + +* [Refactor] Refactored part of the OAuth2 core code to enhance module cohesion and reduce code coupling. + +* [Refactor] Refactored the logic for dynamically enabling and disabling authentication for IoT devices, simplifying and removing the original multi-event jump approach. + +* [Refactor] Refactored service local file management definitions and certificate generation logic code. + +* [Optimize] Optimized OSS model basic operation class naming for easier identification of code purpose. + +* [Optimize] Optimized OSS module code, pooled S3Presigner object management to improve efficiency. + +* [Optimize] Optimized OSS module code, extracted independent pre-signing operation Service. + +* [Optimize] Improved the interface implementation logic for product and device management by referencing Alibaba Cloud IoT usage. + +* [Optimize] Optimized OIDC client dynamic registration logic for better compatibility with IoT device management requirements. + +* [Optimize] Removed duplicate ApplicationEvent message channel definitions,改用 unified message channel definition. + +* [Optimize] Optimized Emqx client status detection strategy configuration method. + +* [Optimize] Merged some system configuration parameter class definitions to enhance the rationality of configuration parameter division and categorization. + +* [Optimize] Deleted unused duplicate constant definitions. + +* [Optimize] Extracted common Spring ParameterizedTypeReference definition. + +* [Optimize] Optimized custom functional interface ListConverter code logic, removed IDE null value warnings. + +* [Upgrade] Liberica JDK base image versions upgraded to 17.0.13-12 and 21.0.5-11 respectively. + +* [Upgrade] minio docker image version upgraded to RELEASE.2024-10-13T13-34-11Z + +* [Upgrade] loki docker image version upgraded to 3.2.0 + +* [Upgrade] promtail docker image version upgraded to 3.2.0 + +* [Upgrade] grafana docker image version upgraded to 11.2.2 + +* [Upgrade] zipkin docker image version upgraded to 3.4.2 + +* [Upgrade] emqx image version upgraded to 5.8.0 + +* **Dependency Updates** + +* [Upgrade] aws-java-sdk-s3 version upgraded to 1.12.777 + +* [Upgrade] software.amazon.awssdk version upgraded to 2.28.29 + +* [Upgrade] software.amazon.awssdk.crt version upgraded to 0.31.3 + +* [Upgrade] alipay-sdk-java version upgraded to 4.39.234.ALL + +* [Upgrade] mysql version upgraded to 9.1.0 + +* [Upgrade] mybatis plus version upgraded to 3.5.9 + +* [Upgrade] sqlite-jdbc version upgraded to 3.47.0.0 + +* [Upgrade] quasar webjars version upgraded to 2.17.1 + +* [Upgrade] sweetalert2 webjars version upgraded to 11.14.4 + +* [Upgrade] grpc version upgraded to 1.68.0 + +* [Upgrade] protobuf version upgraded to 3.25.5 + +* [Upgrade] redisson version upgraded to 3.37.0 + +* [Upgrade] hutool version upgraded to 6.0.0-M17 + +* [Upgrade] checker-qual version upgraded to 3.48.1 + +* [Upgrade] nacos-client version upgraded to 2.4.3 + +* [Upgrade] opengauss-jdbc version upgraded to 6.0.0-og + +* [Upgrade] vue webjars version upgraded to 3.5.12 + + +## [III] Design Q&A + +### 1\. Why Not "Pure" Reactive? + +Reactive programming certainly has its advantages, but using it also forces one to face some practical issues: + +* To achieve "pure" reactive, first, there must be ecosystem support. Currently, the acceptance of reactive programming is not very high, and many components still do not support it. Unless one has the energy to rewrite all the unsupported components used, it is difficult to achieve purity, especially for microservices systems. + +* The vast majority of applications need to use databases. Existing ORM components in the Java field either do not support reactive (e.g., JPA), do not support it particularly well (e.g., Hibernate reactive), or require too much manual coding (e.g., R2DBC). Therefore, from an input-output ratio perspective, doing reactive at the data layer is not "cost-effective" at the moment. + + +Therefore, it still depends on the type of application system. There is no need to pursue "pure" `reactive` when conditions are not met. + +### 2\. What Benefits Can Reactive Bring? + +There are numerous articles online comparing the benefits of `reactive` over `blocking`, so specifics won't be repeated here. The more obvious advantages in practical applications are: + +* `Reactive` utilizes resources more efficiently. Its advantages are more prominent for functions with high resource consumption. + +* Microservices systems often need to integrate more content, especially at the data layer, where multiple types of data storage might be used simultaneously, along with data flow and migration. The "stream" thinking of event-driven and reactive programming fits better with this than traditional blocking methods. + +* Reactive programming can better cooperate with event-driven architecture. The Spring ecosystem uses event-driven extensively in many aspects, and the core design philosophy of reactive is also殊途同归 (converges with) event-driven. + + +> Precisely because Dante Cloud uses a lot of Spring Integration content, the traditional blocking method became increasingly awkward to use, making it more and more necessary to support reactive. If you have time, take a good look at Spring Integration; it might open up a new world for you. + +### 3\. What Are the Difficulties in Learning Reactive Programming? + +* If learning reactive programming based on Reactor, the difficulty and breakthrough point lie in the `Flux` and `Mono` classes. Thoroughly understanding and mastering the methods of these two classes can basically eliminate all development obstacles. + +* The biggest difficulty in reactive programming is the shift in programming mindset. Being accustomed to blocking programming makes it hard to adapt to the `stream`-based development thinking of `reactive` initially. + + +> Where there is a will, there is a way. + +## [IV] System Documentation + +To better help everyone understand and learn Dante Cloud, a new documentation site has been added: https://www.herodotus.vip. This site currently includes corrected and reorganized system deployment-related content. Plans are underway to gradually supplement it with articles on detailed knowledge points, module-specific design implementations, and conceptual understanding related to the system. The original site will be retained unless there are special reasons not to. + +--- + +**Welcome to Star us to show your support!** + +**Gitee**: https://gitee.com/dromara/dante-cloud +**Github**: https://github.com/dromara/dante-cloud + +About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, scheduling orchestration, etc. The technology stack is fully open-source and co-built, maintaining community neutrality, and is committed to providing microservices cloud-native solutions for global users. It aims to let every participating open-source enthusiast experience the joy of open source. + +The Dromara open-source community currently boasts 10+ GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of people, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to interact with me** + +![](/assets/img/news/Dante-Cloud-3.3.5.0-1.webp) \ No newline at end of file diff --git a/src/news/Dante-Cloud-3.4.3.3.md b/src/news/Dante-Cloud-3.4.3.3.md new file mode 100644 index 0000000000..add4bd91ac --- /dev/null +++ b/src/news/Dante-Cloud-3.4.3.3.md @@ -0,0 +1,138 @@ +--- +title: Dante Cloud 3.4.3.3 Released – Demo System Is Ready, Explore It Now! +author: Dante Cloud +date: 2025-03-17 +cover: /assets/img/news/Dante-Cloud-3.4.3.3-0.png +head: + - - meta + - name: News +--- + +## \[1] Project Introduction + +**Dante Cloud** is the first microservices platform in China that supports both blocking and reactive services in parallel. It adopts **Domain-Driven Design (DDD)** principles, with a core focus on **high-quality code and low security vulnerabilities**. Based on the full range of open-source technologies in the Spring ecosystem, it features highly **modular and componentized design**, supports authentication for **smart TVs, IoT, and other connected devices**, meets **National Level 3 Cybersecurity Requirements**, and includes a series of security systems such as **interface-level national cryptographic digital envelope encryption and decryption**. It is a multi-tenant microservices solution that enables **"one codebase for both microservices and monolithic architectures"** for enterprise application systems. + +![](/assets/img/news/Dante-Cloud-3.4.3.3-0.png) + +## \[2] Project Philosophy + +**Dante Cloud** has always adhered to the principles of "Simplicity, Efficiency, Inclusiveness, and Pragmatism." It leverages various emerging or mainstream technologies in the microservices domain and its peripherals for development, continuously refining and optimizing with dedication. The goal is to build a product with `high code quality, low maintenance overhead, and strong security protections`. It aims to help users quickly bypass the stages of architectural technology selection and technical research exploration, reducing the high maintenance costs caused by potential issues such as security vulnerabilities, technical debt, and low-quality code in traditional projects. Like the meaning of its name, Dante Cloud hopes to serve as a bridge during industry transformation, supporting enterprises in their信息化 (digital/informatization) construction and transformation. + +> The core focus of **Dante Cloud** is: **"High-quality system code"**, **"Reasonable system architecture"**, **"Low-coupled module division"**, **"High-security system implementation"**, **"Flexible functional extensibility"**, and **"Excellent microservices implementation"**, rather than pursuing the **richness** of stacked **business features**. + +## \[3] Feature Introduction + +The Dante Cloud demo system has been released, currently including an online version, a monolithic offline version (Docker), and a microservices image version. Welcome to try it out! + +Experience address: https://www.herodotus.vip/get-started/preview/online.html + +Any comments and suggestions can be submitted via 【ISSUE】. + +**Effect Demo** + +## \[4] Update Content + +* **Major Updates** + +* \[Upgrade\] Spring Boot Admin version upgraded to 3.4.5 + +* \[New\] SAS-related exceptions and error feedback no longer display only monotonous messages; now support outputting personalized interfaces as text/html in browser access scenarios + +* \[Optimization\] Significantly improved the usability of the monolithic version and frontend when running under Context Path and Nginx reverse proxy environments + +* **Other Updates** + +* \[Refactor\] Consolidated all exception handling for the authorization server and resource server into facade configuration classes, achieving unified HttpSecurity error configuration invocation. Reduced the number of Beans that need to be injected for SAS core configuration. + +* \[Refactor\] Extracted facade configuration classes for the authorization server and authentication server, reducing the Beans that need to be injected by the application side, facilitating use and unified maintenance. + +* \[Refactor\] Refactored the core processing logic code of RequestMapping using Lambda expressions. + +* \[New\] Added a production environment browser developer tools protection control environment variable, facilitating debugging in production environments by turning off protection. + +* \[New\] Fixed frontend deployment to Nginx page caching strategy configuration. + +* \[New\] Added H2 in-memory database support to the system. + +* \[New\] Added a demo mode Profile based on the H2 in-memory database for the monolithic version. + +* \[New\] Added base path configuration to the frontend Vite configuration, fixing 404 errors for static resources when using reverse proxy pointing to sub-paths. + +* \[New\] Extended the original SAS error response that only supported application/json type to support text/html type processing, making exception information accessed via browsers more user-friendly. + +* \[New\] Added Thymeleaf manual page rendering tool to solve the issue where manual rendering does not support '@{}', throwing "Link base "/error/css/style.css" cannot be context relative (/...) unless the context used for executing the engine implements the org.thymeleaf.context.IWebContext interface" error. + +* \[Fix\] Fixed the issue where SAS DefaultAuthenticationEventPublisher would throw an unrecognized exception breaking out of the system error system if no default event was specified. + +* \[Fix\] Fixed the issue where some SAS exceptions broke out of the system's custom error system, throwing exceptions without custom error information. + +* \[Fix\] Fixed the issue where native OAuth2 exceptions could not be converted into the system error system's standard exceptions. + +* \[Fix\] Fixed the issue where the authorization code page was intercepted after adjusting the error handler configuration method. + +* \[Fix\] Fixed the issue where obtaining IP could sometimes get 0:0:0:0:0:0:0:1, causing exceptions. + +* \[Fix\] Fixed the issue where environment variables were not effective when the frontend was packaged into a Docker image. + +* \[Fix\] Fixed the inconsistency between frontend Typescript definitions and backend entities, which caused display and operation abnormalities in the frontend OAuth2Application functionality. + +* \[Fix\] Optimized frontend production compilation configuration, fixed 404 errors for font references via css url in scenarios with a specified Base. + +* \[Fix\] Fixed the logic error in AccessDenied exception handling in the Servlet environment, which resulted in insufficiently precise exception messages. + +* \[Fix\] Fixed the warning for incorrect import types output during frontend compilation. + +* \[Fix\] Fixed the compilation warning caused by duplicate exports of frontend pinia store ts. + +* \[Fix\] Fixed compilation warnings caused by syntax errors in frontend Vue page exports. + +* \[Fix\] Fixed the issue where images on custom login pages were not displayed in environments with a specified context path. + +* \[Fix\] Fixed the issue where the system lacked the initialization script for the oauth2_authorization_resource table. + +* \[Fix\] Fixed the issue where Cookie Path setting anomalies in Context Path environments caused login failures. + +* \[Fix\] Fixed permission verification errors containing placeholders in Context Path environments. + +* \[Fix\] Fixed the issue where interface permission conversion in Context Path environments did not include the Context Path, causing permission verification failures. + +* \[Optimization\] Removed duplicate definitions of the DefaultOAuth2AuthenticationEventPublisher Bean configuration in the code. + +* \[Optimization\] Added static resource filtering (js, css, image, etc.) protection to request audit logs to prevent recording too much static resource call information. + +* \[Optimization\] Removed unused compilation and packaging settings from the Vite configuration file. + +* \[Upgrade\] Minio Docker image version upgraded to RELEASE.2025-03-12T18-04-18Z + +* \[Upgrade\] EMQX Docker image version upgraded to 5.8.5 + +* \[Upgrade\] TDengine image version upgraded to 3.3.5.8 + +* **Dependency Updates** + +* \[Upgrade\] hypersistence-utils-hibernate-63 version upgraded to 3.9.5 + +* \[Upgrade\] software.amazon.awssdk version upgraded to 2.30.38 + +* \[Upgrade\] software.amazon.awssdk.crt version upgraded to 0.36.2 + +* \[Upgrade\] audience-annotations version upgraded to 0.15.1 + +* \[Upgrade\] grpc version upgraded to 1.71.0 + +* \[Upgrade\] quasar webjars version upgraded to 2.18.1 + +* \[Upgrade\] sms4j-spring-boot-starter version upgraded to 3.3.4 + +* \[Upgrade\] webauthn4j version upgraded to 0.28.6.RELEASE + +* \[Upgrade\] checker-qual version upgraded to 3.49.1 + + +--- + +**If this project is helpful to you, welcome to Star us for support!** + +**Gitee**: https://gitee.com/dromara/dante-cloud + +**Github**: https://github.com/dromara/dante-cloud \ No newline at end of file diff --git a/src/news/Dante-Cloud-3.5.0.0.md b/src/news/Dante-Cloud-3.5.0.0.md new file mode 100644 index 0000000000..67731402d3 --- /dev/null +++ b/src/news/Dante-Cloud-3.5.0.0.md @@ -0,0 +1,122 @@ +--- +title: Major Upgrade! Dante Cloud Fully Embraces Spring Boot 3.5 & Spring Cloud 2025 +author: Dante Cloud +date: 2025-06-03 +cover: /assets/img/news/Dante-Cloud-3.5.0.0-0.png +head: + - - meta + - name: News +--- + +## \[1] Project Introduction + +**Dante Cloud** is the first microservices platform in China that supports both blocking and reactive services in parallel. Designed with **Domain-Driven Design (DDD)** principles, it focuses on **"high-quality code, low security vulnerabilities"**. Based on the full spectrum of open-source technologies in the Spring ecosystem, it features highly **modular and componentized design**, supports authentication for **smart TVs, IoT, and other connected devices**, meets **China's Level 3 Cybersecurity Classification Protection** requirements, and includes a series of security systems such as **interface national cryptographic digital envelope encryption and decryption**. It is a multi-tenant microservices solution that can **"implement both microservices and monolithic architectures with one codebase"** for enterprise application systems. + +![](/assets/img/news/Dante-Cloud-3.5.0.0-0.png) + + + +## \[2] Project Philosophy + +**Dante Cloud** has always adhered to the philosophy of "Simplicity, Efficiency, Inclusiveness, and Pragmatism." It utilizes various emerging or mainstream technologies in the microservices domain and its periphery for construction, continuously refining, discarding the rough and selecting the fine, and meticulously crafting. The goal is to build a product with `high code quality, low maintenance investment, and strong security protection` that can help users quickly bypass the stages of architectural technology selection and technical research exploration. It aims to reduce the high maintenance costs caused by potential hidden dangers such as security vulnerabilities, technical debt, and low-quality code in traditional projects. Like the meaning of its name, it hopes to bridge the past and the future during this period of industry transformation, assisting enterprises in their informatization construction and transformation. + +The core focus of **Dante Cloud** is: **「High-quality system code」**, **「Reasonable system architecture」**, **「Low-coupled module division」**, **「High-security system implementation」**, **「Flexible functional expansion capabilities」**, and **「Excellent microservices implementation」**, rather than pursuing the **richness** of stacked **business functions**. + +## \[3] Architectural Design + +Besides providing a complete microservices architecture, Dante Cloud also supports running as a monolithic architecture. The microservices architecture and the monolithic architecture are not two separate codebases, nor are they two separate projects. They are a fully integrated single codebase. Users can choose to run in microservices mode or monolithic mode as needed. This is one of the biggest features of Dante Cloud microservices: **"One Codebase, Two Architectures"**. + +> The microservices architecture based on `Spring Boot` and `Spring Cloud` has become the mainstream solution for enterprise application construction. However, it is undeniable that the infrastructure required to set up a microservices architecture is becoming increasingly numerous and complex. Just setting up a development and debugging environment on a development computer involves considerable complexity and resource requirements. For many applications, especially small ones in their early stages or with a small user base in the beginning, a single, front-end and back-end separated backend is often sufficient. There is no need to deploy a full set of microservices, which adds extra complexity. + +Dante Cloud's feature of **"One Codebase, Two Architectures"** can help enterprises quickly build projects with a monolithic architecture in the early stages, facilitate local development for developers, and enable research on new technologies. As user scale grows and concurrency requirements increase in the later stages of the project, it can quickly and seamlessly migrate to a microservices architecture. + +## \[4] Feature Introduction + +The Dante Cloud demo system has been released. It currently includes an online version, a monolithic offline version (Docker), and a microservices image version. Welcome to experience and use it! + +Experience address: https://www.herodotus.vip/get-started/preview/online.html + +For any comments and suggestions, you can 【File an ISSUE】 to leave a message. + +## \[5] Summary of New Spring Ecosystem Features + +| Feature Area | Spring Boot 3.5 | Spring Cloud 2025.0.0 | +| :--------------------- | :--------------------------------------------- | :---------------------------------------------------------- | +| Java Support | Java 17+ (possibly optimized for Java 21) | Depends on Spring Boot 3.5 | +| Web/HTTP | HTTP/3 (experimental), WebSocket optimizations | API Gateway (Gateway) optimizations | +| Security | Spring Security 6.x enhancements | Configuration encryption/decryption optimizations | +| Data Access | Hibernate 6.x, JDBC optimizations | Config management optimizations | +| Monitoring | Micrometer 1.11.x, Actuator enhancements | Sleuth/OpenTelemetry optimizations | +| Cloud Native | Kubernetes/Docker optimizations | Service discovery, circuit breaking, streaming enhancements | +| Development Experience | Build tool optimizations, Kotlin support | Dynamic configuration, intelligent routing | + +## \[6] Update Content + +* **Major Updates** + * \[Upgrade] Spring Boot version upgraded to 3.5.0 + * \[Upgrade] Spring Authorization Server version upgraded to 1.5.0 + * \[Upgrade] Spring Cloud version upgraded to 2025.0.0 + * \[Upgrade] Spring Cloud Tencent version upgraded to 2.0.1.0-2023.0.3 + * \[Upgrade] Spring Boot Admin version upgraded to 3.5.0 + * \[Upgrade] Nacos version upgraded to 3.0.1. A self-packaged Nacos Docker image supporting PostgreSQL has been uploaded to Docker Hub and Quay.IO + +* **Other Updates** + * \[Add] Added parameter validation control for setting device properties and calling device services + * \[Add] Added interfaces for setting device properties and calling device services + * \[Add] Added composite file manager definitions to support combined local and object storage management for files of different purposes. Also resolved the issue of unclear logic in the original FileTemplate and FileTransformer definitions. + * \[Add] Added a default JsonSchema composite file manager and injected it by default in the core-autoconfigure module to ensure correct code operation. + * \[Add] Added platform-level JsonSchema composite file storage manager definitions. + * \[Fix] Fixed the issue where importing SCT via Import interfered with the Springdoc version, preventing upgrade. + * \[Fix] Fixed certificate factory test case execution errors. + * \[Optimize] Optimized Hikari and database connection-related configurations to further improve database connection and usage efficiency. + * \[Optimize] Optimized MqttTopic definitions to support more MQTT topic application scenarios. + * \[Optimize] Spring Boot Test Starter is no longer globally configured; modified to be imported on-demand by each module. + * \[Optimize] Adjusted the location of JustAuth dependency and related code to improve code cohesion. + * \[Refactor] Refactored core base module code, adjusted some code package paths and structures to reduce dependencies and coupling between modules. + * \[Refactor] Refactored OAuth2 Client code to adapt to the latest version of Spring Security OAuth2. + * \[Refactor] Refactored WebPathUtils utility class to adapt to the latest version of Spring Security. + * \[Refactor] Refactored Spring Authorization Server authentication-related code to support DPoP. + * \[Refactor] Refactored Spring Authorization Server authentication-related code to support PAR. + * \[Refactor] Refactored FileTemplate and FileTransformer definitions, reducing unnecessary method definitions and interactions. + * \[Upgrade] Minio Docker image version upgraded to RELEASE.2025-05-24T17-08-30Z + * \[Upgrade] Grafana Docker image version upgraded to 12.0.1 + * \[Upgrade] Loki Docker image version upgraded to 3.5.1 + * \[Upgrade] Promtail Docker image version upgraded to 3.5.1 + * \[Upgrade] EMQX Docker image version upgraded to 5.9.0 + * \[Upgrade] InfluxDB Docker image version upgraded to 2.7.12 + * \[Upgrade] ClickHouse Docker image version upgraded to 25.5.1 + * \[Upgrade] TDengine Docker image version upgraded to 3.3.6.6 + +* **Dependency Updates** + * \[Upgrade] alipay-sdk-java version upgraded to 4.40.237.ALL + * \[Upgrade] com.baidu.aip version upgraded to 4.16.20 + * \[Upgrade] grpc version upgraded to 1.73.0 + * \[Upgrade] json-schema-validator version upgraded to 1.5.7 + * \[Upgrade] protobuf version upgraded to 3.25.8 + * \[Upgrade] redisson version upgraded to 3.48.0 + * \[Upgrade] software.amazon.awssdk version upgraded to 2.31.53 + * \[Upgrade] software.amazon.awssdk.crt version upgraded to 0.38.3 + * \[Upgrade] sweetalert2 webjars version upgraded to 11.22.0 + * \[Upgrade] vue webjars version upgraded to 3.5.16 + * \[Upgrade] weixin java version upgraded to 4.7.5-20250529.111829 + * \[Upgrade] okio version upgraded to 3.12.0 + * \[Upgrade] influxdb-client version upgraded to 7.3.0 + * \[Upgrade] json version upgraded to 20250517 + +## \[7] Documentation Notes + +The original documentation site https://www.herodotus.cn has ceased service due to server expiration. + +Friends who need to consult the Dante Cloud project documentation can view the 【Enterprise Edition】 documentation at https://www.herodotus.vip. (Apart from functional differences, the deployment methods and usage logic are completely consistent with the open-source version, and the content is more refined and easier to understand, which will not affect the use of the open-source version). + +Alternatively, you can access the 【Community Edition】 documentation for this project at https://dante-cloud.dromara.org. Please note that initial access to this site might be slow, and there might be times when it is inaccessible in some regions. + +Please be informed! + +* * * + +**If this project is helpful to you, welcome to Star us to show your support!** + +**Gitee**: https://gitee.com/dromara/dante-cloud + +**Github**: https://github.com/dromara/dante-cloud \ No newline at end of file diff --git a/src/news/Dante-Cloud-3.5.5.0.md b/src/news/Dante-Cloud-3.5.5.0.md new file mode 100644 index 0000000000..f4cfd45ac8 --- /dev/null +++ b/src/news/Dante-Cloud-3.5.5.0.md @@ -0,0 +1,134 @@ +--- +title: 'Dante Cloud 3.5.5.0 Released: "Is Your Project a Monolith or Microservices?" "We Are Both."' +author: Dante Cloud +date: 2025-08-25 +cover: /assets/img/news/Dante-Cloud-3.5.5.0-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/Dante-Cloud-3.5.5.0-0.png) + +## [I] Project Introduction + +**Dante Cloud** is the first microservices platform in China that supports both blocking and reactive services in parallel. Designed with **Domain-Driven Design (DDD)** principles at its core, it focuses on **"high-quality code, low security vulnerabilities"**. Built on the full spectrum of open-source technologies within the Spring ecosystem, it features a highly **modular and componentized design**. It supports authentication for **smart TVs, IoT, and other connected devices**, meets **National Level 3 Protection Requirements**, and includes a series of security systems such as **interface national cryptographic digital envelope encryption and decryption**. It is a multi-tenant microservices solution. It uniquely enables enterprises to **"switch flexibly between microservices and monolithic architectures with one codebase"**. + +## [II] Project Philosophy + +**Dante Cloud** has always adhered to the principles of "Simplicity, Efficiency, Inclusiveness, and Pragmatism." It utilizes various emerging or mainstream technologies in and around the microservices domain for development, continuously refining, eliminating the crude to extract the essential, and meticulously crafted. The goal is to build a product with `high code quality, low maintenance investment, and strong security protection`. It aims to help users quickly bypass the stages of architectural technology selection and technical research exploration, reducing the high maintenance costs caused by potential hidden dangers such as security vulnerabilities, technical debt, and low-quality code in traditional projects. It is hoped that, like the meaning of its name, it will serve as a bridge during this period of industry transformation, supporting enterprise IT infrastructure development and change. + +The core focus of **Dante Cloud** is: **「High-quality system code」**, **「Reasonable system architecture」**, **「Low-coupled module division」**, **「High-security system implementation」**, **「Flexible functional expansion capabilities」**, and **「Excellent microservices implementation」**. Unlike some other systems that pursue the **richness** of **business functions**, piling on a large number of functions that cannot be truly universal would become a burden and interference. It is better for users to design and implement flexibly according to their needs. + +## [III] Architectural Design + +Besides providing a complete microservices architecture, Dante Cloud also supports running as a monolithic architecture. The microservices architecture and the monolithic architecture are not two separate codebases, nor are they two separate projects. They are completely integrated into one set of code, allowing you to choose to run in microservices mode or monolithic mode as needed. This is one of the biggest features of Dante Cloud microservices: **"One Codebase, Two Architectures"**. + +> The microservices architecture based on `Spring Boot` and `Spring Cloud` has become the mainstream solution for enterprise application construction. However, it is undeniable that the infrastructure required to set up a microservices architecture is becoming increasingly numerous and complex. Just setting up a development and debugging environment on a development computer involves considerable complexity and resource requirements. For many applications, especially small ones in their early stages or with low user traffic in the beginning, a monolithic, front-end and back-end separated backend is often sufficient. There is no need to implement a full set of microservices, which adds unnecessary complexity. + +The **"One Codebase, Two Architectures"** feature of Dante Cloud can help enterprises quickly build projects with a monolithic architecture in the early stages, facilitate local development for developers, and enable research on new technologies. In the later stages of the project, as user scale grows and concurrency requirements increase, it can seamlessly and quickly migrate to a microservices architecture. + +## [IV] Implementation Components + +This project does not use any complex, difficult-to-understand, or hard-to-master technologies. Among the core key components involved, **nearly 80% are native components of the Spring ecosystem**. The technical implementations are all combinations and applications of the standard usage methods of each component. The coding style and code design have always strived to remain consistent with the standard specifications and usage of the Spring ecosystem, although a certain level of encapsulation and abstraction has gradually formed after numerous version iterations and refactorings. + +Learning and using this project requires a high level of **knowledge of Java and the Spring ecosystem fundamentals**, as well as an **understanding of microservices concepts**. Therefore, if you find this project has a steep learning curve and is difficult to master, unlike other similar open-source projects that seem "**simple**", it is likely that you have not **truly** understood or mastered the relevant components of the Spring ecosystem. + +* **Hearing about it is not the same as knowing it.** +* **Knowing it is not the same as understanding it.** +* **Understanding it is not the same as being able to use it.** +* **Being able to use it is not the same as mastering it.** + +From another perspective, this project can also be an excellent case study for deeply learning and mastering the various components of the Spring ecosystem. **It is recommended to read the book "The Way of IT Architecture Transformation: Alibaba's Middle Platform Strategy Thought and Architecture Practice" (you can read the first few chapters first) before getting started with this project, especially for those only proficient in monolithic applications—you must read it!** + +> This project provides "Suggestions on Learning Methods and Paths for Dante Cloud and Related Knowledge". Interested friends are welcome to read it [Read Online]. + +The core components used by Dante Cloud are as follows: + +| No. | Spring Ecosystem Components | Domestic Open-Source Components | +| :--- | :-------------------------- | :------------------------------ | +| 1 | Spring Boot | Spring Cloud Alibaba | +| 2 | Spring Security | Spring Cloud Tencent | +| 3 | Spring Security OAuth2 | JetCache | +| 4 | Spring Authorization Server | Mybatis Plus | +| 5 | Spring Data JPA | JustAuth | +| 6 | Spring Data MongoDB | WxJava | +| 7 | Spring Data Redis | Hutool | +| 8 | Spring Data Envers | sms-spring-boot-starter | +| 9 | Spring Cloud | grpc-spring-boot-starter | +| 10 | Spring Cloud Bus | -- | +| 11 | Spring Cloud Stream | -- | +| 12 | Spring Cloud Gateway | -- | +| 13 | Spring Cloud Loadbalancer | -- | +| 14 | Spring Cloud OpenFeign | -- | +| 15 | Spring Cloud Zookeeper | -- | +| 16 | Spring Session | -- | +| 17 | Spring Integration | -- | +| 18 | Spring Kafka | -- | +| 19 | Spring WebSocket | -- | +| 20 | Spring RSocket | -- | +| 21 | Spring Webflux | -- | +| 22 | Micrometer | -- | +| 23 | SpringDoc | -- | +| 24 | Spring Boot Admin | -- | + +## [V] Communication and Feedback + +To facilitate communication among users of Dante Cloud Open Source and Enterprise editions, enable a deeper understanding and mastery of the technologies used in Dante Cloud, and quickly solve practical application problems, starting from August 18, 2025, Dante Cloud has reopened its **Technical Exchange Group**. All friends who `sincerely` wish to exchange technology are welcome to join. + +**How to join the group**: Please see 【Technical Exchange Group】 for details. + +## [VI] This Update + +* **Main Updates** + * [Upgrade] Spring Boot version upgraded to 3.5.5 + * [Upgrade] Spring Boot Admin version upgraded to 3.5.2 + * [Upgrade] Spring Authorization Server version upgraded to 1.5.2 + * [Upgrade] Nacos version upgraded to 3.0.3. The self-packaged Nacos Docker image supporting PostgreSQL has been uploaded to Docker Hub and Quay.IO + * [Optimization] The system now supports Redis version 8.2.1. + +* **Other Updates** + * [New] Frontend project adds theme switching effects. + * [Fix] Fixed an issue where the custom extended Client Credentials mode Provider was incompatible with the latest SAS configuration method, causing instability in using the Client Credentials mode. + * [Fix] Fixed an issue in IoT device client dynamic registration where circular initiation of authentication caused the generated Registered Client information to be overwritten. + * [Fix] Fixed an issue where Spring Session would throw `java.lang.IllegalStateException: LettuceConnectionFactory has been STOPPED. Use start() to initialize it` during system logout. fix:#ICTVGU + * [Fix] Fixed incorrect return information from the front-end device code verification polling API. + * [Fix] Fixed an issue where multiple entries with the same clientId appeared in the `oauth2_authorization_resource` table during client dynamic registration, causing query errors. + * [Fix] Fixed an incorrect default redirect URL after successful device code authorization verification. + * [Fix] Fixed an issue in the front-end framework kernel module where incorrect import information caused the module package to be excessively large. + * [Fix] Fixed an issue where upgrading @vue/tsconfig to 0.8.0, which enables `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes` configurations by default, caused error prompts during packaging and compilation. + * [Refactor] Refactored type definitions in the front-end Axios component abstraction for more accurate type validation and reduced unnecessary type conversions. + * [Optimization] Updated the IP address library database to 2025-08-13. + * [Optimization] Optimized default parameters for front-end client dynamic registration to avoid generating unnecessary authorization modes during registration. + * [Optimization] Removed the Baidu OCR OpenAPI encapsulation module. + * [Optimization] Removed dependency on the Velocity component and related configurations. + * [Optimization] Optimized Maven configuration, removing the unified version control for fastjson introduced earlier to control dependency vulnerabilities. + * [Optimization] Optimized Gitee ISSUE Template. + * [Optimization] Removed useless configurations from the packaging configuration of the front-end Bpmn designer module. + * [Optimization] Optimized front-end vite.config.mts and tsconfig.json configurations, adopting more reasonable definition configurations and removing useless or outdated content. + * [Optimization] Optimized front-end application Vite configuration, adjusted the location of auto-generated configuration files, and optimized auto-import configuration. + * [Optimization] Optimized the export configuration in the front-end module's package.json, simplifying the long paths for importing module styles. + * [Upgrade] Tempo docker image version upgraded to 2.8.2. + * [Upgrade] Cassandra docker image version upgraded to 5.0.5. + * [Upgrade] Clickhouse docker image version upgraded to 25.7.4. + * [Upgrade] Kestra docker image version upgraded to v0.24.2. + +* **Dependency Updates** + * [Upgrade] alipay-sdk-java version upgraded to 4.40.411.ALL + * [Upgrade] grpc version upgraded to 1.75.0 + * [Upgrade] skywalking agent version upgraded to 9.5.0 + * [Upgrade] software.amazon.awssdk version upgraded to 2.32.27 + * [Upgrade] software.amazon.awssdk.crt version upgraded to 0.38.9 + * [Upgrade] springdoc version upgraded to 2.8.10 + * [Upgrade] sweetalert2 webjars version upgraded to 11.22.4 + * [Upgrade] wxjava version upgraded to 4.7.7-20250808.182223 + +--- + +**If this project is helpful to you, welcome to Star us to show your support!** + +**Gitee**: https://gitee.com/dromara/dante-cloud + +**Github**: https://github.com/dromara/dante-cloud + +**Gitcode**: https://gitcode.com/dromara/dante-cloud \ No newline at end of file diff --git a/src/news/DyJava.md b/src/news/DyJava.md new file mode 100644 index 0000000000..26cc331755 --- /dev/null +++ b/src/news/DyJava.md @@ -0,0 +1,71 @@ +--- +title: Here it comes! TikTok Development Tool DyJava Joins Dromara Open Source Community +author: danmo +date: 2024-04-29 +cover: /assets/img/news/DyJava-0.png +head: + - - meta + - name: News +--- + +## Introduction to the Author: + +**Author: danmo +Member of Dromara open source organization, Dromara/dy-java author +Java development engineer with 7 years of experience +https://gitee.com/dromara/dy-java.git** +**![](/assets/img/news/DyJava-0.png)** + +## Reason + +> With the popularity of tremolo short videos, more and more developers hope to realize their own ideas and applications on the tremolo platform. +> in order to meet these 1 needs, we have launched DyJava, the 1 Java development kit specially designed for tremolo, to help developers easily realize tremolo back-end development. + +## Introduction to DyJava + +DyJava is the 1 powerful tremolo Java development kit, which supports the back-end development of a variety of tremolo development function modules, including but not limited to mobile/website applications, open platforms, tremolo shops and applets. +DyJava is committed to simplifying the development process, improving development efficiency, and enabling developers to focus more on innovation and business logic implementation. + +## DyJava Features + +#### Rich function modules: + +> DyJava supports various functional modules of the tremolo platform to meet the needs of developers in different scenarios. + +#### Simple API design: + +> DyJava API design is simple, easy to understand and use, so that developers can quickly get started. + +#### Efficient performance: + +> DyJava uses a high-performance Java framework to ensure the stability and responsiveness of back-end services. + +#### Sound documentation and community support: + +> DyJava provides detailed development documentation and an active developer community to help developers solve problems encountered in the development process. + +## Detailed explanation of Dyjava application scenarios + +#### Mobile/Website Application Development: + +> With DyJava, developers can quickly build mobile apps and websites with TikTok features, providing a consistent user experience. Whether it is social interaction, content sharing or e-commerce shopping, DyJava provides powerful technical support for developers. + +#### Open Platform Access: + +> DyJava allows developers to easily access the tremolo open platform and realize interconnection with other tremolo applications. Whether it's getting user information, publishing or sharing content, DyJava helps developers quickly implement what they need. + +#### Shake Shop Development: + +For businesses that want to open a store on TikTok, DyJava provides a complete back-end solution. From product management, order processing to marketing promotion, DyJava can help merchants achieve efficient operation and user growth. + +#### Small Program Development: + +> DyJava supports back-end development of tremolo applets to help developers build lightweight, cross-platform applications. Whether it is game entertainment, tool assistant or life service, DyJava makes it easy for small program developers to realize their ideas. + +## At the end + +> as the 1 Java development kit specially built for tremolo, DyJava will undoubtedly become the preferred tool for tremolo back-end development with its rich functional modules, simple API design, efficient performance and perfect support system. Let's join hands with DyJava to open a new chapter in the back-end development of Douyin! + +**In this era full of infinite possibilities, let us use DyJava to create more wonderful Douyin applications and bring users a richer entertainment experience! * * + +**Warehouse address: https://gitee.com/dromara/dy-java** \ No newline at end of file diff --git a/src/news/DynamicTp-v1.2.0.md b/src/news/DynamicTp-v1.2.0.md new file mode 100644 index 0000000000..173c124d87 --- /dev/null +++ b/src/news/DynamicTp-v1.2.0.md @@ -0,0 +1,122 @@ +--- +title: "Dynamic Thread Pool v1.2.0 Released: Core Module Removes Dependency on Spring!" +author: February 18, 2025 09:35 +date: 2025-02-18 +cover: /assets/img/news/DynamicTp-v1.2.0-0.png +head: + - - meta + - name: News +--- + +## Introduction to DynamicTp + +DynamicTp is a lightweight dynamic thread pool monitoring and management tool based on a configuration center. Its main features can be summarized into several categories: dynamic parameter adjustment, notification alerts, runtime monitoring, and third-party package thread pool management. + +![](/assets/img/news/DynamicTp-v1.2.0-0.png) + +## Features of DynamicTp + +**After multiple iterations, the latest version v1.2.0 now includes the following features** ✅ + +![](/assets/img/news/DynamicTp-v1.2.0-1.png) + +## v1.2.0 Upgrade Notes + +* The Spring startup class annotation has been moved to the Spring module, and the package path has been adjusted. It must be re-imported. + +``` +org.dromara.dynamictp.spring.annotation.EnableDynamicTp +``` + +* The configuration file prefix has been adjusted from `spring.dynamic.tp` to `dynamictp`. + +``` +dynamictp: + enabled: true + enabledBanner: true + enabledCollect: true +``` + +## v1.2.0 Release Notes + +In versions v1.1.9 and earlier, the core module had a strong dependency on Spring, and the code utilized many Spring-specific features, which made it difficult to integrate into non-Spring projects. + +v1.2.0 is a major release that primarily focuses on decoupling from Spring. Spring-related features are now provided as an independent module, allowing non-Spring frameworks to integrate DynamicTp by simply including the core module. + +#### Feature + +* Removed dependency on Spring in the core module, making it easier to use in other non-Spring projects. + +``` +https://github.com/dromara/dynamic-tp/issues/527 +``` + +#### Bugfix + +* Compatibility fix for Dubbo versions between 3.0.9 and 3.1.8, where the executor name to be replaced should be `INTERNAL_SERVICE_EXECUTOR` instead of `ExecutorService.class.getName()`. + +``` +https://github.com/dromara/dynamic-tp/pull/495 +``` + +* Fixed an issue with the Redis rate limiter throwing errors in Redis Cluster mode. + +``` +https://github.com/dromara/dynamic-tp/pull/502 +``` + +* Fixed an error when canceling a `scheduledFuture`. + +``` +https://github.com/dromara/dynamic-tp/pull/516 +``` + +* Fixed an issue in the `adapter-grpc` module where the gRPC client channel executor was being shut down, causing thread pool shutdown errors during calls. + +``` +https://github.com/dromara/dynamic-tp/pull/520 +``` + +* Added compatibility for higher versions of OkHttp3, where the thread pool field in `Dispatcher` is named `executorServiceOrNull`. + +``` +https://github.com/dromara/dynamic-tp/pull/525 +``` + +#### Optimize + +* Improved the configuration file hint functionality for thread pools. + +``` +https://github.com/dromara/dynamic-tp/pull/498 +``` + +* Added timeout control for etcd `kvClient` get operations. + +``` +https://github.com/dromara/dynamic-tp/pull/518 +``` + +* Zookeeper-starter client initialization now supports ZK authentication. + +``` +https://gitee.com/dromara/dynamic-tp/pulls/61 +``` + +* Optimized and refactored parts of the code design. + +## Join the Community + +The above covers all the content of this release. Everyone is welcome to upgrade and try it out! + +If you encounter any issues during use or have ideas or suggestions for the project, feel free to join our community and discuss with over 1,500 members. + +![](/assets/img/news/DynamicTp-v1.2.0-2.png) + +## Project Links + +``` +Official Website: https://dynamictp.cn +Gitee: https://gitee.com/dromara/dynamic-tp +GitHub: https://github.com/dromara/dynamic-tp +``` \ No newline at end of file diff --git a/src/news/Easy-Es-2.1.0.md b/src/news/Easy-Es-2.1.0.md new file mode 100644 index 0000000000..388cb57cd3 --- /dev/null +++ b/src/news/Easy-Es-2.1.0.md @@ -0,0 +1,80 @@ +--- +title: "Yi Shui Han: A Spark Ignites a Prairie Fire (Easy-Es 2.1.0: Blazing Heaven, Shattering Formations)" +author: Old Han +date: 2025-02-06 +cover: /assets/img/news/Easy-Es-2.1.0-0.png +head: + - - meta + - name: News +--- + +[![](/assets/img/news/Easy-Es-2.1.0-0.png)](https://mp.weixin.qq.com/s?__biz=MzUzNTY2NjAzMg==&mid=2247489578&idx=1&sn=9dd419422155a093397d183257e0e797&scene=21#wechat_redirect) + +**Yi Shui Han: A Spark Ignites a Prairie Fire (Easy-Es 2.1.0: Blazing Heaven, Shattering Formations)** + +![](/assets/img/news/Easy-Es-2.1.0-1.png) +Though Old Han narrowly prevailed in the Battle of Dingjun Mountain, the barren land atop his head grew increasingly desolate, earning him the martial world moniker "The Center-Parting War God." One night, while observing the stars, he suddenly noticed the movement of a black star, with a faint crimson glow emerging from the southwest. He pinched his fingers and calculated: "Dire! This is an omen that the remnants of the Three Blades, borrowing foreign dark arts from beyond the frontier, are making a comeback!" + +![](/assets/img/news/Easy-Es-2.1.0-2.jpg) + +Before his words faded, a scout urgently reported: The remnants of the Three Blades had obtained secret teachings from the Western Regions' "Elastic Sacred Sect" and trapped our city with the **Seven Swords Soul-Locking Formation**, which concealed seven layers of lethal peril within: + +First, **Version Shackles** (RestHighLevelClient 7.14); second, **Framework Isolation** (Old Spring system); third, **Index Labyrinth** (Missing regex wildcards); fourth, **Validation Paradox** (Redundant Conditions); fifth, **Reverse Engineering Chasm** (Difficulty generating entity classes); sixth, **Empty Box Thunderclap** (Boost null pointer); seventh, **Chaotic Order Certain Kill** (Sorting conflicts). With the seven swords unleashed simultaneously, the Han army was in peril! + +![](/assets/img/news/Easy-Es-2.1.0-3.jpg) + +Just as Old Han was at his wits' end, he suddenly heard ethereal music amidst the clouds. A Taoist priest in blue robes, stepping on a rainbow, arrived. His robe was embroidered with the star patterns of "Solon," and he held a dark iron token, proclaiming: "I am the foremost disciple under the Southern Patriarch, the 'Earless True One,' specially here to resolve your seven-sword predicament!" With that, he threw out three silken bags: + +**Scroll One: Primal Chaos Formation-Breaking Art (Framework Adaptation)** +"Blend Solon's agility with Spring's solidity to break the version shackles!" The True One lightly pointed his fingertip, and instantly the RestHighLevelClient soared directly to **7.17.8**, further using a technique of balancing yin and yang to compatible with both **ES7.x/8.x** dual cores. The old Spring troops also received life-extending secret methods, merging the old and new armies, their sword energy shooting straight into the clouds! + +> Interpretation: Adapted for the domestic Solon framework and Spring, and made compatible with ES8.x on top of the original ES7.x support, enabling the framework to be used in a wider range of systems. + +**Scroll Two: Divine Craftsmanship Chapter (Feature Innovation)** +The True One waved his sleeve and summoned three great artifacts: + +* **Regex Spirit Talisman** (Interceptor supports regex wildcards): The index mist disperses completely, all manner of fields can be retrieved remotely! +* **Condition Severance** (Condition logic optimization): Only validate if true, saving thirty percent internal energy consumption! +* **Thousand-Eyes Stargazing Technique** (MapperScan multi-package scanning): Even with a hundred mountains of code, all can be captured in one mirror's view! + +> Interpretation: Interceptor plugin supports regex wildcard method functionality; MapperScan supports multi-package scanning; Condition judgment logic optimized. + +He further bestowed the **Reverse City-Building Technique**: Blueprints of indices entered the furnace, instantly smelting into armies of entity class automatons, increasing city-building speed tenfold! + +> Interpretation: Added reverse engineering, which can fully automatically generate entity classes based on index structure, liberating hands. + +**Scroll Three: Heaven-Mending Healing Hand (Defect Fixes)** +The True One pointed his sword at the sky: "Boost empty box? Watch this old master turn stone into gold!" The NPE thunderclap vanished instantly; he then cast the **Chronological Unification Spell**, effortlessly resolving the danger of the chaotic order certain kill. The Han army's old wounds were completely healed, morale greatly boosted! Plus the **Doppelganger Technique**, copy_to wreaks havoc. + +> Interpretation: Fixed a defect where unspecified Boost could cause NPE; fixed an error in searchAfter queries when sorting was set in mixed queries; added copy_to index functionality, which copies multiple field values to a specified field, improving query performance in specific scenarios. + +Seeing their formation broken, the Three Blades remnants desperately tried to flee. Old Han sneered: "You feigned surrender before, now witness the true fire!" He invoked his newly realized ultimate technique—**【Solon-ES联动技】** (Solon-ES联动技: likely "Synergy Skill" or "Linkage Technique"), and behold: + +* Annotation-driven like a meteor chasing the moon, configuration simplified ninefold +* Dependency injection like flowing rivers, components interoperate seamlessly +* Ecological integration as if heaven and earth reversed, plugins are plug-and-play + +![](/assets/img/news/Easy-Es-2.1.0-4.png) + +For a moment, heaven and earth changed color. The Seven Swords Soul-Locking Formation backfired on its masters, and the Three Blades remnants were annihilated! + +After the battle, the True One stroked his beard and laughed: "Easy-Es now possesses the true teachings of Solon, lighter than ever before, performance like phantoms. From now on, you shall be revered in the world of domestic frameworks!" Having spoken, he transformed into blue smoke and disappeared, leaving only a star chart inscribed with the documentation address: +**https://easy-es.cn** + +Now, new sprouts actually grew on Old Han's center-parted head. Astonished, the crowd asked the reason. He replied: "This is nourished by the Samadhi True Fire of **Watch/Star/Fork** from countless warriors!" Then he danced with his sword by the banks of the Icy River. Where the sword wind passed, code fell like scattered petals, and Bugs vanished like morning dew meeting the sun. +![](/assets/img/news/Easy-Es-2.1.0-5.jpg) + +Suddenly, a youth asked: "Since you are already invincible, why still observe the stars at night?" +Old Han gazed into the distance towards the east: "Elasticsearch 9.0 is coming... the martial world always needs the next version." + +(Curtain falls, BGM rises: "Yi Shui Han" - "Code surges, a thousand sails compete; open-source sparks can ignite a prairie fire.") + +![](/assets/img/news/Easy-Es-2.1.0-6.jpg) + +**Temple Incense for EE: Light a Star to Pray for Lifeforce**: + +Gitee: https://gitee.com/dromara/easy-es + +Gitcode: https://gitcode.com/dromara/easy-es + +GitHub: https://github.com/dromara/easy-es \ No newline at end of file diff --git a/src/news/EasyAI-v1.3.8.md b/src/news/EasyAI-v1.3.8.md new file mode 100644 index 0000000000..a25e1baca5 --- /dev/null +++ b/src/news/EasyAI-v1.3.8.md @@ -0,0 +1,146 @@ +--- +title: Native Java Artificial Intelligence Algorithm Framework EasyAI-v1.3.8 Version Update +author: easyAI +date: 2025-03-11 +cover: /assets/img/news/EasyAI-v1.3.8-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/EasyAI-v1.3.8-0.png) + +# Preface + +The significance of EasyAI for Java is equivalent to the significance of the emergence of Spring in the JavaWeb field—to be out-of-the-box, enabling every developer to use EasyAI to develop small and micro models that meet their own artificial intelligence business needs. This is its mission! + +# Introduction to EasyAI + +EasyAI has no dependencies; it is a native Java artificial intelligence algorithm framework. Firstly, **it can be smoothly introduced into our Java project with Maven in one step, without any additional environment configuration or dependencies, achieving out-of-the-box usability**. Furthermore, it includes pre-packaged modules for image target detection and AI customer service, as well as provides various underlying algorithm tools such as deep learning, machine learning, reinforcement learning, heuristic learning, matrix operations, etc. Developers can, through simple learning, develop small and micro models that deeply align with their own business needs. + +* EasyAI Gitee download link: https://gitee.com/dromara/easyAi +* EasyAI technical documentation address: https://www.myeasyai.cn/ +* EasyAI detailed video tutorial: https://www.bilibili.com/video/av89134035 +* EasyAI framework zero-based deep development and complete artificial intelligence system tutorial: https://www.bilibili.com/cheese/play/ss17600 + +# v1.3.8 Update Content + +## Image Matting + +* Performs pixel-level segmentation of image semantics; the former inputs the original image, the latter outputs the image after matting. + +![](/assets/img/news/EasyAI-v1.3.8-1.png)![](/assets/img/news/EasyAI-v1.3.8-2.png) + +## How to Use GPU Acceleration + +### Environmental Requirements (Graphics Card, Driver, CUDA) + +* The machine must have an `NVIDIA graphics card` because EasyAI uses CUDA for GPU acceleration. Non-`NVIDIA graphics cards` (such as `AMD graphics cards`) do not support CUDA. +* The machine's graphics driver must support `CUDA 12.0.0` or a higher version (usually supporting a higher version also means supporting lower versions). +* Install `CUDA 12.0.0` and the corresponding `cuDNN` for that CUDA version. Installation reference: https://blog.csdn.net/m0_45447650/article/details/123704930 + +### Version Correspondence Between easyai and CUDA Extensions. + +| EasyAI Version | CUDA Extension Version | Platform Architecture | +| :------------- | :--------------------- | :----------------------------- | +| ≥1.3.4 | 1.0.0 | windows-x86_64
linux-x86_64 | + +### Introducing CUDA Extension Dependencies + +Choose the corresponding CUDA extension version based on the dependent EasyAI version. + +```xml + + org.dromara.easyai.extensions + easyai-extensions-cuda-12.0.0 + ${CUDA_Extension_Version} + ${Platform_Architecture} + +``` + +## Run & Verify + +After successfully completing the previous steps, directly call EasyAI for training. If the `console` outputs the following content, it indicates successful CUDA extension integration. + +``` +EasyAI CUDA-12.0.0 extensions init success. +``` + +### easyAI Package Has Been Uploaded to Maven Central Repository + +> From now on, you can introduce the following JAR package address in the pom file without needing to download and package it manually. + +```xml + + org.dromara.easyai + easyAi + 1.3.8 + +``` + +## Face Detection Effect Demonstration + +![](/assets/img/news/EasyAI-v1.3.8-3.png) + +### FastYolo Image Recognition Effect Display + +* Using EasyAI to implement the visual core of an image-based checkout automatic vending machine. + +![](/assets/img/news/EasyAI-v1.3.8-4.jpg) + +### sayOrder AI Customer Service + +* sayOrder is an AI customer service system packaged based on EasyAI. +* It can analyze the semantics of user input to identify user behavior and distinguish user intent IDs through typeID. It captures keyword categories set in the background to extract content the system cares about contained in the user's statement, such as time, location, etc. +* It can also autonomously interact with users through Q&A, answering questions autonomously or engaging in communication about other intents. +* Project link: https://gitee.com/dromara/sayOrder + +### Demonstration of sayOrder Interactive Basic Business Process + +* The user first inputs to express their idea. + ![](/assets/img/news/EasyAI-v1.3.8-5.png) +* SayOrder finds that the user's description lacks necessary order information and asks a follow-up question. The user receives SayOrder's question and further supplements their idea. + ![](/assets/img/news/EasyAI-v1.3.8-6.png) +* The user's second input still does not meet the key information requirements for the background 14-category legal consultation order. They continue to add information, finally completing the order information supplement and generating the order. + ![](/assets/img/news/EasyAI-v1.3.8-7.png) +* The user inputs a question they want to consult, and SayOrder autonomously answers the user's query. + ![](/assets/img/news/EasyAI-v1.3.8-8.png) + +### Architecture Design + +**Common Underlying Algorithm Modules** + +* Basic Matrix and Linear Algebra Calculation Module: + 1. Built-in matrix class and matrix calculation class can complete common matrix arithmetic, parity, multiple linear regression, logistic regression, Euclidean distance, cosine similarity, im2col, inverse im2col, finding algebraic cofactors, finding inverses, finding adjoint matrices, inner products, differentiation, and a series of other APIs. + 2. RGB three-channel matrix, which can perform image conversion, cropping, blocking, generating image matrices, and other operations to facilitate subsequent calculations. +* Machine Learning - Clustering: + k-means clustering, Gaussian mixture clustering, density clustering, learning vector quantization clustering, etc. +* Machine Learning - Classification and Fitting: Multi-layer feedforward neural network, multi-layer recurrent neural network, residual network, multi-layer residual recurrent neural network, convolutional neural network, decision tree, random forest, k-nearest neighbors, etc. +* Heuristic Algorithms: Particle swarm optimization, ant colony optimization, simulated annealing. +* Reinforcement Learning: Dynamic programming, Monte Carlo analysis, Markov, temporal difference. + +**Common Upper-Level Algorithm Modules** + +* Visual Image: Image recognition, image summarization, object detection. +* Natural Language: Semantic understanding, word segmentation, inferring sensitive and key words, sentence completion, language interaction. +* Game Bots: Autonomous strategy, autonomous action. + +### Usage + +1. Download the project and package it into the local Maven repository. +2. Introduce the easyAi pom file address into the project. + +### Follow the Project + +* For any ideas, suggestions, or learning related to the project, you can add my work WeChat. + ![](/assets/img/news/EasyAI-v1.3.8-9.jpg) + +### About Dromara + +Dromara is an open-source community composed of top domestic open-source project authors. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservice RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, scheduling orchestration, etc. The technology stack is fully open-source and co-built, maintaining community neutrality, and is committed to providing global users with microservice cloud-native solutions. Let every open-source enthusiast participating experience the joy of open source. + +The Dromara open-source community currently owns 10+ GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of people, with thousands of individuals and teams using the open-source projects from the Dromara community. + +**Welcome everyone to join the Knowledge Planet to interact with me** + +![](/assets/img/news/EasyAI-v1.3.8-10.webp) \ No newline at end of file diff --git a/src/news/ElectronEgg-v4.md b/src/news/ElectronEgg-v4.md new file mode 100644 index 0000000000..71c68dc5c5 --- /dev/null +++ b/src/news/ElectronEgg-v4.md @@ -0,0 +1,113 @@ +--- +title: "ElectronEgg v4 Released: A Brand New Development Experience" +author: ElectronEgg +date: 2025-03-04 +cover: /assets/img/news/ElectronEgg-v4-0.jpg +head: + - - meta + - name: News +--- + +Hello everyone, `electron-egg v4` is finally here. This version represents a comprehensive optimization of the EE framework over the past few years. For us, it can be considered an ideal release, and no major changes are expected in the coming years, though compatibility upgrades with Electron will continue to be followed. We highly recommend updating. + +v4 refactors the core of the framework, offering a better development experience, encryption, TypeScript support, code optimization, structural adjustments, and more. + +Currently, the framework is widely used in various client applications across fields such as accounting, government services, enterprises, healthcare, education, stock trading, ERP, entertainment, and video. Feel free to use it with confidence! + +![](/assets/img/news/ElectronEgg-v4-0.jpg) + +### Why Use It? + +Desktop software (for office use and personal tools) remains one of the needs on the PC end for the next decade, improving work efficiency. + +Electron technology is a growing trend, used by QQ, TikTok, Bilibili, Baidu Translate, Alibaba Netdisk, Thunder, Youdao Note, and more. + +### Open Source + +gitee: https://gitee.com/dromara/electron-egg 5300+ +github: https://github.com/dromara/electron-egg 2000+ + +### This Update + +#### 4.0.0 + +1. 【Added】ee-core now supports TypeScript with type definitions. +2. 【Optimized】ee-core code refactored to provide more standard APIs. +3. 【Optimized】ee-core adds an app module with a new framework startup process. +4. 【Optimized】ee-core config rewritten for configuration loading logic. +5. 【Optimized】ee-core controller rewritten for controller loading logic. +6. 【Optimized】ee-core core module streamlined, removing redundant code and features. +7. 【Optimized】ee-core Electron functionality rewritten to provide APIs. +8. 【Optimized】ee-core jobs improved. +9. 【Optimized】ee-core loader redundant methods removed. +10. 【Optimized】ee-core log improved. +11. 【Added】ee-core ps ambiguous APIs removed; new APIs added: appVersion, getDataDir, getBundleDir, getBaseDir, getUserHomeDir, getUserHomeAppDir, getUserHomeHiddenAppDir. +12. 【Optimized】ee-core socket improved. +13. 【Optimized】ee-core storage jsondb removed; sqlitedb modified for storage path and type support. +14. 【Optimized】ee-core utils improved. +15. 【Added】ee-bin now supports TypeScript with esbuild as the build tool. +16. 【Added】ee-bin adds encryption for frontend code. +17. 【Optimized】ee-bin hot reload functionality improved. +18. 【Optimized】ee-bin configuration file modified. +19. 【Optimized】ee-bin build functionality improved. +20. 【Optimized】ee-bin move command modified. +21. 【Upgraded】ee-bin@4.1.4 & ee-core@4.0.1. +22. 【Upgraded】node@20.16.0 & electron@31.7.6. + +### Download + +```bash +# gitee +git clone https://gitee.com/dromara/electron-egg.git + +# github +git clone https://github.com/dromara/electron-egg.git +``` + +### Installation + +```bash +# Root directory: Install Electron dependencies +npm i + +# Enter the frontend directory to install frontend dependencies +cd frontend +npm i +``` + +### Run the Project + +```bash +npm run start +``` + +### User Case Showcase + +#### Cloud Notes + +![](/assets/img/news/ElectronEgg-v4-1.jpg) +![](/assets/img/news/ElectronEgg-v4-2.png) + +#### Remote Control + +![](/assets/img/news/ElectronEgg-v4-3.png) +![](/assets/img/news/ElectronEgg-v4-4.png) + +#### Music + +![](/assets/img/news/ElectronEgg-v4-5.jpg) +![](/assets/img/news/ElectronEgg-v4-6.png) + +### More + +Visit the official website: https://www.kaka996.com/ + +#### About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, and scheduling orchestration. The community is committed to full open-source collaboration and maintains neutrality, aiming to provide microservices cloud-native solutions for global users. It allows every participating open-source enthusiast to experience the joy of open-source. + +The Dromara open-source community currently boasts 10+ GVP projects, with total stars exceeding 100,000. It has built a community of tens of thousands of members, with countless individuals and teams using Dromara's open-source projects. + +**Welcome to join the Knowledge Planet and interact with me** + +![](/assets/img/news/ElectronEgg-v4-7.webp) \ No newline at end of file diff --git a/src/news/FastRequest-2024.1.9.md b/src/news/FastRequest-2024.1.9.md new file mode 100644 index 0000000000..a882e4b022 --- /dev/null +++ b/src/news/FastRequest-2024.1.9.md @@ -0,0 +1,68 @@ +--- +title: OpenAPI and Solon Framework Support, Fast Request 2024.1.9 Released +author: Wang Sheng +date: 2024-12-12 +cover: https://api-buddy.cn/img/rfr.svg +tag: + - fastRequest +head: + - - meta + - name: News +--- + +[**Fast Request**](https://plugins.jetbrains.com/plugin/16988-restful-fast-request--api-buddy) is an IDEA plugin similar to Postman. It is a powerful RESTful API toolkit plugin that can help you quickly and automatically generate URLs and parameters based on existing methods. Restful Fast Request = API Debugging Tool + API Management Tool + API Search Tool. It provides a beautiful interface to complete requests, inspect server responses, store your API requests, and export API requests. The plugin helps you debug your APIs faster and more efficiently within the IDEA interface. + +**Latest Domain**: **api-buddy.cn** + +**Fast Request is born to simplify API debugging. Debugging a Spring interface in 3 seconds is not a dream.** So, young warrior, **download it now**! + +**Listening to users' voices and constantly improving ourselves**, the main updates for this **Fast Request** release are as follows: + +- **OpenAPI Export Support** +- **Solon Framework Parsing Support** +- **Configurable Tab Switching After Execution** +- **Response Documentation Display Support** +- **Customizable Default Export Directory for Files** +- **Header Presets** +- **Binary Support** + +## OpenAPI Export Support + +![](/assets/img/news/FastRequest-2024.1.9-1.png) +*OpenAPI Export Support* + +## Solon Framework Parsing Support + +![](/assets/img/news/FastRequest-2024.1.9-2.png) +*Support for parsing the domestic Solon framework.* + +## Configurable Tab Switching After Execution + +![](/assets/img/news/FastRequest-2024.1.9-3.png) +*APIs can be run directly from the APIs list without switching to the execution interface; this is configurable. This allows running one API after another on the APIs page.* + +## Response Documentation Display Support + +![](/assets/img/news/FastRequest-2024.1.9-4.png) +*View the meaning of response fields under the Response tab.* + +## Customizable Default Export Directory for Files + +![](/assets/img/news/FastRequest-2024.1.9-5.png) +*The default directory for exporting files (e.g., exporting APIs to markdown, html, word, openapi, etc.) can be customized.* + +## Header Presets + +![](/assets/img/news/FastRequest-2024.1.9-6.png) +![](/assets/img/news/FastRequest-2024.1.9-7.png) +*Header Presets support creating different Header groups, managing their respective request headers, and easily adding preset Header parameters via dropdown selection.* + +## Binary Support +![](/assets/img/news/FastRequest-2024.1.9-8.png) +*Support for Binary format uploads.* + +## More Details + +Please click -------------> **Here** [https://api-buddy.cn/guide/history.html#\_2024-1-9](https://api-buddy.cn/guide/history.html#_2024-1-9) + +**Haven't downloaded it yet? What are you waiting for?** \ No newline at end of file diff --git a/src/news/Forest-v1.7.md b/src/news/Forest-v1.7.md new file mode 100644 index 0000000000..dfb0ae028b --- /dev/null +++ b/src/news/Forest-v1.7.md @@ -0,0 +1,218 @@ +--- +title: Forest v1.7 Released! High Energy Ahead, A Wave of New Features is Coming! +author: Forest +date: 2025-07-01 +cover: /assets/img/news/Forest-v1.7-0.png +head: + - - meta + - name: News +--- + +## + +![](/assets/img/news/Forest-v1.7-0.png) + +## Introduction to Forest + +Forest is an open-source Java HTTP client framework. It can bind all the information of an HTTP request (including URL, Header, Body, etc.) to your custom interface methods, allowing you to send HTTP requests by calling local interface methods. + +### A Simple Example + +* **Declarative Interface** + + +Create an interface and annotate the interface method with `@Get`. + +```java +public interface MyClient { + @Get("http://localhost:8080/hello") + String hello(); +} +``` + +Through the `@Get` annotation, the `simpleRequest()` method in the MyClient interface above is bound to an HTTP request. Its URL is `http://localhost:8080/hello`, it uses the GET method by default, and returns the response data as a String to the caller. + +* **Programmatic Interface** + + +```java +Forest.get("http://localhost:8080/hello").execute(); +``` + +The programmatic interface is more straightforward and simple. + +## v1.7 Version Upgrade + +This release includes many significant updates. We have enhanced the string template syntax, supporting null-safe syntax and deep variable references, and provided more user-friendly error messages. Enhancements have also been made to Cookies, providing a mechanism for automatic Cookie saving and reading, along with more comprehensive API interfaces. We have also optimized and improved interceptors; directly using the `Interceptor` interface and its `onSuccess` method is no longer recommended. Instead, `ForestInterceptor` and the `onResponse` method are provided, which are safer and offer better performance than the former. Additionally, this update includes comprehensive optimizations to request performance. When using the programmatic interface by default and not printing logs, the time consumption can be similar to that of hutool's HttpUtil. + +## A Wave of New Features in v1.7 + +* Null-safe syntax +* Elvis expressions +* Deep references +* Nested string templates +* Safer interceptors +* Request-level log switch + +#### Null-safe Syntax + +In previous versions, referencing an undefined variable in a string template would result in an error. Now, with null-safe syntax, it will no longer throw an error but directly return a null value. + +```java +// The testVar variable was never defined, but it can be referenced normally using ? +Forest.get("/test/{testVar?}"); +``` + +Alternatively, if you reference a variable that doesn't exist or is null and then try to access its properties with a dot `.`, it would directly throw an error in older versions. Now, you can use the `?.` symbol to automatically check for null. + +```java +// It will first check if testVar is null, then if testVar.a is null. If any is null, it directly returns null. +// No error will be thrown. +Forest.get("/test/{testVar?.a?.name}"); +``` + +#### Elvis Expressions + +The new version supports the Elvis operator `??`, also known as the null coalescing operator. Simply put, you append two question marks (`??`) after a variable or an expression, and follow it with another expression on the right, which serves as the default value returned if the left-side variable or expression is null. + +```java +// If variable a is null or undefined, return the string 'ok' +// The final URL will be http://localhost:8080/ok +// If variable a is not null, return its own value +Forest.get("http://localhost:8080/{a ?? 'ok'}") +``` + +Elvis expressions can also be combined with the null-safe property access operator `?.`. + +```java +// If variable a is null or undefined, return the string 'ok', and it won't continue to read a.b +// The final URL will be http://localhost:8080/ok +Forest.get("http://localhost:8080/{a?.b ?? 'ok'}") +``` + +#### Deep References + +In previous versions, variables in string templates could only reference the first layer. If the value of that variable referenced other variables, the string template would not parse them. Now, no matter how many layers of variables are referenced, they can be parsed to the bottom. + +```yaml +forest: + variables: + var1: "{user.name}" + var2: "{user.password}" + user: + name: foo + password: bar +``` + +Reference directly in Java code: + +```java +// Final URL is: /test/foo/bar +Forest.get("/test/{var1}/{var2}"); +``` + +If you don't want deep references, you can use the deep reference stop syntax by adding a `!` symbol after the variable. Then it will only reference the value of that variable for one layer. Whether the value of that variable contains content from other string templates will not be parsed further. + +```java +// Final URL is: /test/{user.name}/{user.password} +// The values of variables var1 and var2 are returned directly as strings, without any parsing. +Forest.get("/test/{var1!}/{var2!}"); +``` + +#### Nested String Templates + +Use two backticks `` ` `` to wrap the string content to be concatenated (like `` `string template content` ``), and you can use nested expression syntax such as `` `#{config property}` ``, `` `${expression}` ``, `` `{expression}` `` inside. + +```java +// If a is null, return the string spliced with b and c separated by a slash +// If a is null, b is foo, c is bar, then the URL is /foo/bar +Forest.get("/{a ?? `{b}/{c}`}") + +// Any form of content is acceptable. It can be understood as a string, a dynamically concatenable string inside an expression. +Forest.get("/{a ?? `?b={b}&c={c}`}") +``` + +#### Safer Interceptors + +After this version update, using `Interceptor` is no longer recommended (though it can still be used, it won't affect old code), and the safer `ForestInterceptor` interface is introduced. + +```java +public class MyInterceptor implements ForestInterceptor { + + @Override + public ResponseResult onResponse(ForestRequest request, ForestResponse response) { + if (response.isError()) { + // Return error flag + return error(response.getException()); + } + // Use response.getResult() or response.get(Data Type.class) to get the response data + + // Return continue execution flag + return proceed(); + } +} +``` + +#### Request-Level Log Switch + +No longer need to create a new LogConfiguration object. The log switch can be set directly in the chain call of ForestRequest. + +```java +Forest.post("/test") + .logEnabled(true) // Master switch for request logging + .logRequest(true) // Request content log switch + .logRequestHeaders(true) // Request headers log switch + .logRequestBody(true) // Request body log switch + .logResponseStatus(true) // Response status log switch + .logResponseHeaders(true) // Response headers log switch + .logResponseContent(false) // Response body content log switch + .execute(); +``` + +## Official Website and Repository Addresses + +> #### Official Website: +> +> http://forest.dtflyx.com +> +> #### Gitee Repository: +> +> https://gitee.com/dromara/forest +> +> #### Github Repository: +> +> https://github.com/dromara/forest + +## This Update's Content + +* feat: String templates support null-safe syntax +* feat: More user-friendly string template error messages +* feat: String template `{`, `${`, etc., symbols support escaping `\\{`, `\\${` +* feat: String templates support deep variable references +* feat: String templates support syntax to stop deep references +* feat: Support for nested string templates +* feat: Added the safer Forest interceptor interface ForestInterceptor +* feat: Configure custom async thread pool rejection policy +* feat: Support Bear authenticator +* feat: Support `@Var` as a variable binding annotation for methods and classes +* feat: Added ForestRequest-level log switch interface +* feat: Enhanced Cookie-related API interfaces +* feat: Background automatic cleanup of expired Cookies +* feat: Support automatic Cookie storage and retrieval mechanism +* fix: Conflict with older forest versions, new version Forest class lacks method signatures like get(url), post(url), causing errors (#IC7LIH) +* fix: Body log Chinese garbled characters in some environments +* fix: Modified constant names in interfaces to avoid user confusion between them and methods that only differ in case +* fix: Calling the hashCode() method on a declarative interface causes an infinite loop +* fix: Issue with subclass matching when using ForestResponse as the return type +* fix: Error when reading the response stream repeatedly +* fix: Nested JSON strings cannot be parsed correctly +* refactor: Refactored URL parsing process +* refactor: Refactored variable scope +* refactor: Refactored Forest variable system +* refactor: ForestCookie no longer depends on OkHttp +* refactor: ForestCookie.parse() interface +* refactor: Added key-value pair type request body deletion interface ForestBody.removeNameValueBody +* optimize: Optimized request performance +* optimize: Dynamically determine if the response closes automatically based on the Response type +* optimize: Default backend changed to httpclient +* optimize: Interceptor optimization \ No newline at end of file diff --git a/src/news/Gopher-0.md b/src/news/Gopher-0.md new file mode 100644 index 0000000000..4116e83afb --- /dev/null +++ b/src/news/Gopher-0.md @@ -0,0 +1,838 @@ +--- +title: Dongle, a New Open-Source Encryption Library for Gophers, Joins Dromara Community +author: Dongle +date: 2024-11-13 +cover: /assets/img/news/Gopher-0-0.png +head: + - - meta + - name: News + +--- + +In the field of modern computing, information security has gradually become a focal point. Cryptography, as a key technology for information protection, allows us to encrypt (secure) and decrypt (reveal) data. There are many libraries in Golang that help us easily achieve these functions, among which **dongle** is a powerful and widely used library. This article aims to delve into the usage of the dongle library and how to leverage it to perform common encryption and decryption tasks. + +![](/assets/img/news/Gopher-0-0.png) + +**Dongle** https://github.com/dromara/dongle is a widely used lightweight, semantic, and developer-friendly open-source encryption library for Golang, dedicated to providing a series of powerful tools for cryptography and data security. This project aims to simplify the implementation of encryption algorithms, allowing developers to focus on their application logic rather than the underlying encryption details. The following sections will introduce this library from multiple aspects: + + + +#### Main Features: + +Dongle includes many classic and modern encryption algorithms, such as AES (Advanced Encryption Standard), RSA, DH (Diffie-Hellman) key exchange, and various hash functions (like MD5 and SHA). It also provides digital signatures, random number generation, and other useful cryptographic operations. + +#### Installation and Usage + +``` +// Use the GitHub library +go get -u github.com/dromara/dongle + +import ( + "github.com/dromara/dongle" +) + +// Use the Gitee library +go get -u gitee.com/dromara/dongle + +import ( + "gitee.com/dromara/dongle" +) + +// Use the GitCode library +go get -u gitcode.com/dromara/dongle + +import ( + "gitcode.com/dromara/dongle" +) +``` + +#### Usage Examples + +#### Encoding & Decoding + +##### Hex Encoding and Decoding + +``` +// Hex encode a string and output a string +dongle.Encode.FromString("hello world").ByHex().ToString() // 68656c6c6f20776f726c64 +// Hex decode a string and output a string +dongle.Decode.FromString("68656c6c6f20776f726c64").ByHex().ToString() // hello world + +// Hex encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByHex().ToBytes() // []byte("68656c6c6f20776f726c64") +// Hex decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("68656c6c6f20776f726c64")).ByHex().ToBytes() // []byte("hello world") +``` + +##### Base16 Encoding and Decoding + +``` +// Base16 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase16().ToString() // 68656c6c6f20776f726c64 +// Base16 decode a string and output a string +dongle.Decode.FromString("68656c6c6f20776f726c64").ByBase16().ToString() // hello world + +// Base16 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase16().ToBytes() // []byte("68656c6c6f20776f726c64") +// Base16 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("68656c6c6f20776f726c64")).ByBase16().ToBytes() // []byte("hello world") +``` + +##### Base32 Encoding and Decoding + +``` +// Base32 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase32().ToString() // NBSWY3DPEB3W64TMMQ====== +// Base32 decode a string and output a string +dongle.Decode.FromString("NBSWY3DPEB3W64TMMQ======").ByBase32().ToString() // hello world + +// Base32 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase32().ToBytes() // []byte("NBSWY3DPEB3W64TMMQ======") +// Base32 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("NBSWY3DPEB3W64TMMQ======")).ByBase32().ToBytes() // []byte("hello world") +``` + +##### Base45 Encoding and Decoding + +``` +// Base45 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase45().ToString() // +8D VD82EK4F.KEA2 +// Base45 decode a string and output a string +dongle.Decode.FromString("+8D VD82EK4F.KEA2").ByBase45().ToString() // hello world + +// Base45 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase45().ToBytes() // []byte("+8D VD82EK4F.KEA2") +// Base45 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("+8D VD82EK4F.KEA2")).ByBase45().ToBytes() // []byte("hello world") +``` + +##### Base58 Encoding and Decoding + +``` +// Base58 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase58().ToString() // StV1DL6CwTryKyV +// Base58 decode a string and output a string +dongle.Decode.FromString("StV1DL6CwTryKyV").ByBase58().ToString() // hello world + +// Base58 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase58().ToBytes() // []byte("StV1DL6CwTryKyV") +// Base58 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("StV1DL6CwTryKyV")).ByBase58().ToBytes() // []byte("hello world") +``` + +##### Base62 Encoding and Decoding + +``` +// Base62 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase62().ToString() // AAwf93rvy4aWQVw +// Base62 decode a string and output a string +dongle.Decode.FromString("AAwf93rvy4aWQVw").ByBase62().ToString() // hello world + +// Base62 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase62().ToBytes() // []byte("AAwf93rvy4aWQVw") +// Base62 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("AAwf93rvy4aWQVw")).ByBase62().ToBytes() // []byte("hello world") +``` + +##### Base64 Encoding and Decoding + +``` +// Base64 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase64().ToString() // aGVsbG8gd29ybGQ= +// Base64 decode a string and output a string +dongle.Decode.FromString("aGVsbG8gd29ybGQ=").ByBase64().ToString() // hello world + +// Base64 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase64().ToBytes() // []byte("aGVsbG8gd29ybGQ=") +// Base64 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("aGVsbG8gd29ybGQ=")).ByBase64().ToBytes() // []byte("hello world") +``` + +##### Base64URL Encoding and Decoding + +``` +// Base64URL encode a URL string and output a string +dongle.Encode.FromString("www.gouguoyin.cn").ByBase64URL().ToString() // d3d3LmdvdWd1b3lpbi5jbg== +// Base64URL decode a URL string and output a string +dongle.Decode.FromString("d3d3LmdvdWd1b3lpbi5jbg==").ByBase64URL().ToString() // www.gouguoyin.cn + +// Base64URL encode a URL byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("www.gouguoyin.cn")).ByBase64URL().ToBytes() // []byte("d3d3LmdvdWd1b3lpbi5jbg==") +// Base64URL decode a URL byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("d3d3LmdvdWd1b3lpbi5jbg==")).ByBase64URL().ToBytes() // []byte("www.gouguoyin.cn") +``` + +##### Base85 Encoding and Decoding + +``` +// Base85 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase85().ToString() // BOu!rD]j7BEbo7 +// Base85 decode a string and output a string +dongle.Decode.FromString("BOu!rD]j7BEbo7").ByBase85().ToString() // hello world + +// Base85 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase85().ToBytes() // []byte("BOu!rD]j7BEbo7") +// Base85 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("BOu!rD]j7BEbo7")).ByBase85().ToBytes() // []byte("hello world") +``` + +##### Base91 Encoding and Decoding + +``` +// Base91 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase91().ToString() // TPwJh>Io2Tv!lE +// Base91 decode a string and output a string +dongle.Decode.FromString("TPwJh>Io2Tv!lE").ByBase91().ToString() // hello world + +// Base91 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase91().ToBytes() // []byte("TPwJh>Io2Tv!lE") +// Base91 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("BOu!rD]j7BEbo7")).ByBase91().ToBytes() // []byte("hello world") +``` + +##### Base100 Encoding and Decoding + +``` +// Base100 encode a string and output a string +dongle.Encode.FromString("hello world").ByBase100().ToString() // 👟👜👣👣👦🐗👮👦👩👣👛 +// Base100 decode a string and output a string +dongle.Decode.FromString("👟👜👣👣👦🐗👮👦👩👣👛").ByBase100().ToString() // hello world + +// Base100 encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("hello world")).ByBase100().ToBytes() // []byte("👟👜👣👣👦🐗👮👦👩👣👛") +// Base100 decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("👟👜👣👣👦🐗👮👦👩👣👛")).ByBase100().ToBytes() // []byte("hello world") +``` + +##### SafeURL Encoding and Decoding + +``` +// Escape encode a URL string and output a string +dongle.Encode.FromString("www.gouguoyin.cn?sex=男&age=18").BySafeURL().ToString() // www.gouguoyin.cn%3Fsex%3D%E7%94%B7%26age%3D18 +// Escape decode a URL string and output a string +dongle.Decode.FromString("www.gouguoyin.cn%3Fsex%3D%E7%94%B7%26age%3D18").BySafeURL().ToString() // www.gouguoyin.cn?sex=男&age=18 + +// Escape encode a URL byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("www.gouguoyin.cn?sex=男&age=18")).BySafeURL().ToBytes() // []byte("www.gouguoyin.cn%3Fsex%3D%E7%94%B7%26age%3D18") +// Escape decode a URL byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("www.gouguoyin.cn%3Fsex%3D%E7%94%B7%26age%3D18")).BySafeURL().ToBytes() // []byte("www.gouguoyin.cn?sex=男&age=18") +``` + +##### Morse Encoding and Decoding + +> The default separator is `/` + +``` +// Morse encode a string and output a string +dongle.Encode.FromString("dongle").ByMorse().ToString() // -../---/-./--./.-../. +// Morse decode a string and output a string +dongle.Decode.FromString("-../---/-./--./.-../.").ByMorse().ToString() // dongle + +// Morse encode a byte slice and output a byte slice +dongle.Encode.FromBytes([]byte("dongle")).ByMorse("|").ToBytes() // []byte("-..|---|-.|--.|.-..|.") +// Morse decode a byte slice and output a byte slice +dongle.Decode.FromBytes([]byte("-..|---|-.|--.|.-..|.")).ByMorse("|").ToBytes() // []byte("dongle") +``` + +#### Encryption & Decryption + +##### Md2 Encryption + +``` +// Md2 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByMd2().ToHexString() // d9cce882ee690a5c1ce70beff3a78c77 +// Md2 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByMd2().ToBase64String() // 2czogu5pClwc5wvv86eMdw== + +// Md2 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd2().ToHexBytes() // []byte("d9cce882ee690a5c1ce70beff3a78c77") +// Md2 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd2().ToBase64Bytes() // []byte("2czogu5pClwc5wvv86eMdw==") + +``` + +##### Md4 Encryption + +``` +// Md4 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByMd4().ToHexString() // aa010fbc1d14c795d86ef98c95479d17 +// Md4 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByMd4().ToBase64String() // qgEPvB0Ux5XYbvmMlUedFw== + +// Md4 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd4().ToHexBytes() // []byte("aa010fbc1d14c795d86ef98c95479d17") +// Md4 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd4().ToBase64Bytes() // []byte("qgEPvB0Ux5XYbvmMlUedFw==") + +``` + +##### Md5 Encryption + +``` +// Md5 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByMd5().ToHexString() // 5eb63bbbe01eeed093cb22bb8f5acdc3 +// Md5 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByMd5().ToBase64String() // XrY7u+Ae7tCTyyK7j1rNww== + +// Md5 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd5().ToHexBytes() // []byte("5eb63bbbe01eeed093cb22bb8f5acdc3") +// Md5 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd5().ToBase64Bytes() // []byte("XrY7u+Ae7tCTyyK7j1rNww==") +``` + +##### Sha1 Encryption + +``` +// Sha1 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").BySha1().ToHexString() // 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed +// Sha1 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").BySha1().ToBase64String() // Kq5sNclPz7QV2+lfQIuc6R7oRu0= + +// Sha1 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha1().ToHexBytes() // []byte("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed") +// Sha1 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha1().ToBase64Bytes() // []byte("Kq5sNclPz7QV2+lfQIuc6R7oRu0=") +``` + +##### Sha3 Encryption + +> Includes sha3-224, sha3-256, sha3-384, sha3-512 + +``` +// Sha3-224 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").Sha3(224).ToHexString() // dfb7f18c77e928bb56faeb2da27291bd790bc1045cde45f3210bb6c5 +// Sha3-224 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").Sha3(224).ToBase64String() // 37fxjHfpKLtW+ustonKRvXkLwQRc3kXzIQu2xQ== +// Sha3-224 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(224).ToHexBytes() // []byte("dfb7f18c77e928bb56faeb2da27291bd790bc1045cde45f3210bb6c5") +// Sha3-224 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(224).ToBase64Bytes() // []byte("37fxjHfpKLtW+ustonKRvXkLwQRc3kXzIQu2xQ==") + +// Sha3-256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").Sha3(256).ToHexString() // 644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938 +// Sha3-256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").Sha3(256).ToBase64String() // ZEvMflZDcwQJmarInnYi88px+6HZcv2Uoxw7+/JOOTg= +// Sha3-256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(256).ToHexBytes() // []byte("644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938") +// Sha3-256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(256).ToBase64Bytes() // []byte("ZEvMflZDcwQJmarInnYi88px+6HZcv2Uoxw7+/JOOTg=") + +// Sha3-384 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").Sha3(384).ToHexString() // 83bff28dde1b1bf5810071c6643c08e5b05bdb836effd70b403ea8ea0a634dc4997eb1053aa3593f590f9c63630dd90b +// Sha3-384 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").Sha3(384).ToBase64String() // g7/yjd4bG/WBAHHGZDwI5bBb24Nu/9cLQD6o6gpjTcSZfrEFOqNZP1kPnGNjDdkL +// Sha3-384 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(384).ToHexBytes() // []byte("83bff28dde1b1bf5810071c6643c08e5b05bdb836effd70b403ea8ea0a634dc4997eb1053aa3593f590f9c63630dd90b") +// Sha3-384 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(384).ToBase64Bytes() // []byte("g7/yjd4bG/WBAHHGZDwI5bBb24Nu/9cLQD6o6gpjTcSZfrEFOqNZP1kPnGNjDdkL") + +// Sha3-512 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").Sha3(512).ToHexString() // 840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a +// Sha3-512 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").Sha3(512).ToBase64String() // hAAGZT6ayelRF6FckVyquBZikY6SXengBPd0/4LXB5pA1NJ7GzcmV8YdRtRwMEyIx4izpFJ60HTR3MvuXbqpmg== +// Sha3-512 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(512).ToHexBytes() // []byte("840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a") +// Sha3-512 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(512).ToBase64Bytes() // []byte("hAAGZT6ayelRF6FckVyquBZikY6SXengBPd0/4LXB5pA1NJ7GzcmV8YdRtRwMEyIx4izpFJ60HTR3MvuXbqpmg==") + +``` + +##### Sha224 Encryption + +``` +// Sha224 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").BySha224().ToHexString() // 2f05477fc24bb4faefd86517156dafdecec45b8ad3cf2522a563582b +// Sha224 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").BySha224().ToBase64String() // LwVHf8JLtPrv2GUXFW2v3s7EW4rTzyUipWNYKw== + +// Sha224 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha224().ToHexBytes() // []byte("2f05477fc24bb4faefd86517156dafdecec45b8ad3cf2522a563582b") +// Sha224 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha224().ToBase64Bytes() // []byte("LwVHf8JLtPrv2GUXFW2v3s7EW4rTzyUipWNYKw==") +``` + +##### Sha256 Encryption + +``` +// Sha256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").BySha256().ToHexString() // b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +// Sha256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").BySha256().ToBase64String() // uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek= + +// Sha256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha256().ToHexBytes() // []byte("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9") +// Sha256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha256().ToBase64Bytes() // []byte("uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=") +``` + +##### Sha384 Encryption + +``` +// Sha384 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").BySha384().ToHexString() // fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcbb83578b3e417cb71ce646efd0819dd8c088de1bd +// Sha384 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").BySha384().ToBase64String() // /b2OdaZ/KfcBpOBAOF4uI5hjA+oQI5IRr5B/y7g1eLPkF8txzmRu/QgZ3YwIjeG9 + +// Sha384 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha384().ToHexBytes() // []byte("fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcbb83578b3e417cb71ce646efd0819dd8c088de1bd") +// Sha384 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha384().ToBase64Bytes() // []byte("/b2OdaZ/KfcBpOBAOF4uI5hjA+oQI5IRr5B/y7g1eLPkF8txzmRu/QgZ3YwIjeG9") +``` + +##### Sha512 Encryption + +> Includes sha512, sha512-224, sha512-256 + +``` +// Sha512 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").BySha512().ToHexBytes() // 309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f +// Sha512 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").BySha512().ToBase64String() // MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw== +// Sha512 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512().ToHexBytes() // []byte("309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f") +// Sha512 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512().ToBase64Bytes() // []byte("MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw==") + +// Sha512-224 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").BySha512(224).ToHexBytes() // 22e0d52336f64a998085078b05a6e37b26f8120f43bf4db4c43a64ee +// Sha512-224 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").BySha512(224).ToBase64String() // IuDVIzb2SpmAhQeLBabjeyb4Eg9Dv020xDpk7g== +// Sha512-224 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512(224).ToHexBytes() // []byte("22e0d52336f64a998085078b05a6e37b26f8120f43bf4db4c43a64ee") +// Sha512-224 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512(224).ToBase64Bytes() // []byte("IuDVIzb2SpmAhQeLBabjeyb4Eg9Dv020xDpk7g==") + +// Sha512-256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").BySha512(256).ToHexBytes() // 0ac561fac838104e3f2e4ad107b4bee3e938bf15f2b15f009ccccd61a913f017 +// Sha512-256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").BySha512(256).ToBase64String() // CsVh+sg4EE4/LkrRB7S+4+k4vxXysV8AnMzNYakT8Bc= +// Sha512-256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512(256).ToHexBytes() // []byte("0ac561fac838104e3f2e4ad107b4bee3e938bf15f2b15f009ccccd61a913f017") +// Sha512-256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512(256).ToBase64Bytes() // []byte("CsVh+sg4EE4/LkrRB7S+4+k4vxXysV8AnMzNYakT8Bc=") +``` + +##### Shake128 Encryption + +``` +// Shake128-256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByShake128(256).ToHexString() // 3a9159f071e4dd1c8c4f968607c30942e120d8156b8b1e72e0d376e8871cb8b8 +// Shake128-256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByShake128(256).ToBase64String() // OpFZ8HHk3RyMT5aGB8MJQuEg2BVrix5y4NN26IccuLg= +// Shake128-256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake128(256).ToHexBytes() // []byte("3a9159f071e4dd1c8c4f968607c30942e120d8156b8b1e72e0d376e8871cb8b8") +// Shake128-256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake128(256).ToBase64Bytes() // []byte("OpFZ8HHk3RyMT5aGB8MJQuEg2BVrix5y4NN26IccuLg=") + +// Shake128-512 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByShake128(512).ToHexString() // 3a9159f071e4dd1c8c4f968607c30942e120d8156b8b1e72e0d376e8871cb8b899072665674f26cc494a4bcf027c58267e8ee2da60e942759de86d2670bba1aa +// Shake128-512 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByShake128(512).ToBase64String() // OpFZ8HHk3RyMT5aGB8MJQuEg2BVrix5y4NN26IccuLiZByZlZ08mzElKS88CfFgmfo7i2mDpQnWd6G0mcLuhqg== +// Shake128-512 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake128(512).ToHexBytes() // []byte("3a9159f071e4dd1c8c4f968607c30942e120d8156b8b1e72e0d376e8871cb8b899072665674f26cc494a4bcf027c58267e8ee2da60e942759de86d2670bba1aa") +// Shake128-512 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake128(512).ToBase64Bytes() // []byte("OpFZ8HHk3RyMT5aGB8MJQuEg2BVrix5y4NN26IccuLiZByZlZ08mzElKS88CfFgmfo7i2mDpQnWd6G0mcLuhqg==") +``` + +##### Shake256 Encryption + +``` +// Shake256-384 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByShake256(384).ToHexString() // 369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df2 +// Shake256-384 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByShake256(384).ToBase64String() // Npdxuyy50rBMHVTMpIfjctnxh/c/e6P2W5XI7neYxSf088LVXC1Gop8ulF1GnD3y +// Shake256-384 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake256(384).ToHexBytes() // []byte("369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df2") +// Shake256-384 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake256(384).ToBase64Bytes() // []byte("Npdxuyy50rBMHVTMpIfjctnxh/c/e6P2W5XI7neYxSf088LVXC1Gop8ulF1GnD3y") + +// Shake256-512 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByShake256(512).ToHexString() // 369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df27853a8735271f5cc2d9e889544357116 +// Shake256-512 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByShake256(512).ToBase64String() // Npdxuyy50rBMHVTMpIfjctnxh/c/e6P2W5XI7neYxSf088LVXC1Gop8ulF1GnD3yeFOoc1Jx9cwtnoiVRDVxFg== +// Shake256-512 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake256(512).ToHexBytes() // []byte("369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df27853a8735271f5cc2d9e889544357116") +// Shake256-512 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake256(512).ToBase64Bytes() // []byte("Npdxuyy50rBMHVTMpIfjctnxh/c/e6P2W5XI7neYxSf088LVXC1Gop8ulF1GnD3yeFOoc1Jx9cwtnoiVRDVxFg==") +``` + +##### Ripemd160 Encryption + +``` +// Ripemd160 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByRipemd160().ToHexString() // 98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f +// Ripemd160 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByRipemd160().ToBase64String() // mMYVeEzLX+WTb7wMvp39tAjZLw8= + +// Ripemd160 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByRipemd160().ToHexBytes() // []byte("98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f") +// Ripemd160 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByRipemd160().ToBase64Bytes() // []byte("mMYVeEzLX+WTb7wMvp39tAjZLw8=") +``` + +##### Blake2b Encryption + +> Includes blake2b-256, blake2b-384, blake2b-512 + +``` +// Blake2b-256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByBlake2b(256).ToHexBytes() // 256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610 +// Blake2b-256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByBlake2b(256).ToBase64String() // JWyDspcRTSAbMBefPw7wys6Xg2ItpZdDJrQ2F4ru9hA= +// Blake2b-256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(256).ToHexBytes() // []byte("256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610") +// Blake2b-256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(256).ToBase64Bytes() // []byte("JWyDspcRTSAbMBefPw7wys6Xg2ItpZdDJrQ2F4ru9hA=") + +// Blake2b-384 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByBlake2b(384).ToHexBytes() // 8c653f8c9c9aa2177fb6f8cf5bb914828faa032d7b486c8150663d3f6524b086784f8e62693171ac51fc80b7d2cbb12b +// Blake2b-384 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByBlake2b(384).ToBase64String() // jGU/jJyaohd/tvjPW7kUgo+qAy17SGyBUGY9P2UksIZ4T45iaTFxrFH8gLfSy7Er +// Blake2b-384 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(384).ToHexBytes() // []byte("8c653f8c9c9aa2177fb6f8cf5bb914828faa032d7b486c8150663d3f6524b086784f8e62693171ac51fc80b7d2cbb12b") +// Blake2b-384 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(384).ToBase64Bytes() // []byte("jGU/jJyaohd/tvjPW7kUgo+qAy17SGyBUGY9P2UksIZ4T45iaTFxrFH8gLfSy7Er") + +// Blake2b-512 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByBlake2b(512).ToHexBytes() // 021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0 +// Blake2b-512 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByBlake2b(512).ToBase64String() // Ahzth5kpbOylV4MquUGlC0oR+DR4zxQfUfkz9lOrn7zAWgN83b7QbjCb8zSULE5YzfGkbiN5EczX/Pl4fLx/0A== +// Blake2b-512 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(512).ToHexBytes() // []byte("021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0") +// Blake2b-512 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(512).ToBase64Bytes() // []byte("Ahzth5kpbOylV4MquUGlC0oR+DR4zxQfUfkz9lOrn7zAWgN83b7QbjCb8zSULE5YzfGkbiN5EczX/Pl4fLx/0A==") +``` + +##### Blake2s Encryption + +> Currently only supports blake2s-256 + +``` +// Blake2s-256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByBlake2s(256).ToHexString() // 9aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b +// Blake2s-256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByBlake2s(256).ToBase64String() // muxoBnlFYRB+WUsfaoprDJKgy6ms9eXpPMoG94GBOws= + +// Blake2s-256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2s(256).ToHexBytes() // []byte("9aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b") +// Blake2s-256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2s(256).ToBase64Bytes() // []byte("muxoBnlFYRB+WUsfaoprDJKgy6ms9eXpPMoG94GBOws=") +``` + +##### Hmac-md2 Encryption + +``` +// Hmac-md2 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacMd2("dongle").ToHexString() // 88ed6ef9ab699d03a702f2a6fb1c0673 +// Hmac-md2 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacMd2("dongle").ToBase64String() // iO1u+atpnQOnAvKm+xwGcw== + +// Hmac-md2 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd2([]byte("dongle")).ToHexBytes() // []byte("88ed6ef9ab699d03a702f2a6fb1c0673") +// Hmac-md2 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd2([]byte("dongle")).ToBase64Bytes() // []byte("iO1u+atpnQOnAvKm+xwGcw==") +``` + +##### Hmac-md4 Encryption + +``` +// Hmac-md4 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacMd4("dongle").ToHexString() // 7a9df5247cbf76a8bc17c9c4f5a75b6b +// Hmac-md4 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacMd4("dongle").ToBase64String() // ep31JHy/dqi8F8nE9adbaw== + +// Hmac-md4 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd4([]byte("dongle")).ToHexBytes() // []byte("7a9df5247cbf76a8bc17c9c4f5a75b6b") +// Hmac-md4 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd4([]byte("dongle")).ToBase64Bytes() // []byte("ep31JHy/dqi8F8nE9adbaw==") +``` + +##### Hmac-md5 Encryption + +``` +// Hmac-md5 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacMd5("dongle").ToHexString() // 4790626a275f776956386e5a3ea7b726 +// Hmac-md5 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacMd5("dongle").ToBase64String() // R5Biaidfd2lWOG5aPqe3Jg== + +// Hmac-md5 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd5([]byte("dongle")).ToHexBytes() // []byte("4790626a275f776956386e5a3ea7b726") +// Hmac-md5 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd5([]byte("dongle")).ToBase64Bytes() // []byte("R5Biaidfd2lWOG5aPqe3Jg==") +``` + +##### Hmac-sha1 Encryption + +``` +// Hmac-sha1 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha1("dongle").ToHexString() // 91c103ef93ba7420902b0d1bf0903251c94b4a62 +// Hmac-sha1 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha1("dongle").ToBase64String() // kcED75O6dCCQKw0b8JAyUclLSmI= + +// Hmac-sha1 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha1([]byte("dongle")).ToHexBytes() // []byte("91c103ef93ba7420902b0d1bf0903251c94b4a62") +// Hmac-sha1 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha1([]byte("dongle")).ToBase64Bytes() // []byte("kcED75O6dCCQKw0b8JAyUclLSmI=") +``` + +##### Hmac-sha3 Encryption + +> Includes hmac-sha3-224, hmac-sha3-256, hmac-sha3-384, hmac-sha3-512 + +``` +// Hmac-sha3-224 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 224).ToHexString() // fb8f061d9d1dddd2f5d3b9064a5e98e3e4b6df27ea93ce67627583ce +// Hmac-sha3-224 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 224).ToBase64String() // +48GHZ0d3dL107kGSl6Y4+S23yfqk85nYnWDzg== +// Hmac-sha3-224 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 224).ToHexBytes() // []byte("fb8f061d9d1dddd2f5d3b9064a5e98e3e4b6df27ea93ce67627583ce") +// Hmac-sha3-224 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 224).ToBase64Bytes() // []byte("+48GHZ0d3dL107kGSl6Y4+S23yfqk85nYnWDzg==") + +// Hmac-sha3-256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 256).ToHexString() // 8193367fde28cf5c460adb449a04b3dd9c184f488bdccbabf0526c54f90c4460 +// Hmac-sha3-256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 256).ToBase64String() // gZM2f94oz1xGCttEmgSz3ZwYT0iL3Mur8FJsVPkMRGA= +// Hmac-sha3-256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 256).ToHexBytes() // []byte("8193367fde28cf5c460adb449a04b3dd9c184f488bdccbabf0526c54f90c4460") +// Hmac-sha3-256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 256).ToBase64Bytes() // []byte("gZM2f94oz1xGCttEmgSz3ZwYT0iL3Mur8FJsVPkMRGA=") + +// Hmac-sha3-384 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 384).ToHexString() // 3f76f5cda69cada3ee6b33f8458cd498b063075db263dd8b33f2a3992a8804f9569a7c86ffa2b8f0748babeb7a6fc0e7 +// Hmac-sha3-384 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 384).ToBase64String() // P3b1zaacraPuazP4RYzUmLBjB12yY92LM/KjmSqIBPlWmnyG/6K48HSLq+t6b8Dn +// Hmac-sha3-384 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 384).ToHexBytes() // []byte("3f76f5cda69cada3ee6b33f8458cd498b063075db263dd8b33f2a3992a8804f9569a7c86ffa2b8f0748babeb7a6fc0e7") +// Hmac-sha3-384 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 384).ToBase64Bytes() // []byte("P3b1zaacraPuazP4RYzUmLBjB12yY92LM/KjmSqIBPlWmnyG/6K48HSLq+t6b8Dn") + +// Hmac-sha3-512 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 512).ToHexString() // a99653d0407d659eccdeed43bb7cccd2e2b05a2c34fd3467c4198cf2ad26a466738513e88839fb55e64eb49df65bc52ed0fec2775bd9e086edd4fb4024add4a2 +// Hmac-sha3-512 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 512).ToBase64String() // qZZT0EB9ZZ7M3u1Du3zM0uKwWiw0/TRnxBmM8q0mpGZzhRPoiDn7VeZOtJ32W8Uu0P7Cd1vZ4Ibt1PtAJK3Uog== +// Hmac-sha3-512 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 512).ToHexBytes() // []byte("a99653d0407d659eccdeed43bb7cccd2e2b05a2c34fd3467c4198cf2ad26a466738513e88839fb55e64eb49df65bc52ed0fec2775bd9e086edd4fb4024add4a2") +// Hmac-sha3-512 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 512).ToBase64Bytes() // []byte("qZZT0EB9ZZ7M3u1Du3zM0uKwWiw0/TRnxBmM8q0mpGZzhRPoiDn7VeZOtJ32W8Uu0P7Cd1vZ4Ibt1PtAJK3Uog==") +``` + +##### Hmac-sha224 Encryption + +``` +// Hmac-sha224 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha224("dongle").ToHexString() // e15b9e5a7eccb1f17dc81dc07c909a891936dc3429dc0d940accbcec +// Hmac-sha224 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha224("dongle").ToBase64String() // 4VueWn7MsfF9yB3AfJCaiRk23DQp3A2UCsy87A==== + +// Hmac-sha224 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha224([]byte("dongle")).ToHexBytes() // []byte("e15b9e5a7eccb1f17dc81dc07c909a891936dc3429dc0d940accbcec") +// Hmac-sha224 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha224([]byte("dongle")).ToBase64Bytes() // []byte("4VueWn7MsfF9yB3AfJCaiRk23DQp3A2UCsy87A==") +``` + +##### Hmac-sha256 Encryption + +``` +// Hmac-sha256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha256("dongle").ToHexString() // b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +// Hmac-sha256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha256("dongle").ToBase64String() // uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek= + +// Hmac-sha256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha256([]byte("dongle")).ToHexBytes() // []byte("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9") +// Hmac-sha256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha256([]byte("dongle")).ToBase64Bytes() // []byte("uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=") +``` + +##### Hmac-sha384 Encryption + +``` +// Hmac-sha384 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha384("dongle").ToHexString() // 421fcaa740216a31bbcd1f86f2212e0c68aa4b156a8ebc2ae55b3e75c4ee0509ea0325a0570ae739006b61d91d817fe8 +// Hmac-sha384 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha384("dongle").ToBase64String() // Qh/Kp0AhajG7zR+G8iEuDGiqSxVqjrwq5Vs+dcTuBQnqAyWgVwrnOQBrYdkdgX/o +// Hmac-sha384 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha384([]byte("dongle")).ToHexBytes() // []byte("421fcaa740216a31bbcd1f86f2212e0c68aa4b156a8ebc2ae55b3e75c4ee0509ea0325a0570ae739006b61d91d817fe8") +// Hmac-sha384 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha384([]byte("dongle")).ToBase64Bytes() // []byte("Qh/Kp0AhajG7zR+G8iEuDGiqSxVqjrwq5Vs+dcTuBQnqAyWgVwrnOQBrYdkdgX/o") +``` + +##### Hmac-sha512 Encryption + +> Includes hmac-sha512, hmac-sha512-224, hmac-sha512-256 + +``` +// Hmac-sha512 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle").ToHexString() // d971b790bbc2a4ac81062bbffac693c9c234bae176c8faf5e304dbdb153032a826f12353964b4a4fb87abecd2dc237638a630cbad54a6b94b1f6ef5d5e2835d1 +// Hmac-sha512 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle").ToBase64String() // 2XG3kLvCpKyBBiu/+saTycI0uuF2yPr14wTb2xUwMqgm8SNTlktKT7h6vs0twjdjimMMutVKa5Sx9u9dXig10Q== +// Hmac-sha512 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle")).ToHexBytes() // []byte("d971b790bbc2a4ac81062bbffac693c9c234bae176c8faf5e304dbdb153032a826f12353964b4a4fb87abecd2dc237638a630cbad54a6b94b1f6ef5d5e2835d1") +// Hmac-sha512 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle")).ToBase64Bytes() // []byte("2XG3kLvCpKyBBiu/+saTycI0uuF2yPr14wTb2xUwMqgm8SNTlktKT7h6vs0twjdjimMMutVKa5Sx9u9dXig10Q==") + +// Hmac-sha512-224 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle", 224).ToHexString() // c4145bcc385c29f0e5683cd5450be9deb522d556de3b046a7ffa1eb3 +// Hmac-sha512-224 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle", 224).ToBase64String() // xBRbzDhcKfDlaDzVRQvp3rUi1VbeOwRqf/oesw== +// Hmac-sha512-224 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle"), 224).ToHexBytes() // []byte("c4145bcc385c29f0e5683cd5450be9deb522d556de3b046a7ffa1eb3") +// Hmac-sha512-224 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle"), 224).ToBase64Bytes() // []byte("xBRbzDhcKfDlaDzVRQvp3rUi1VbeOwRqf/oesw==") + +// Hmac-sha512-256 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle", 256).ToHexString() // e99fae71bcb43651ae10e952989eadf897faccb43966ee5122bb1b1d82f7a7c2 +// Hmac-sha512-256 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle", 256).ToBase64String() // 6Z+ucby0NlGuEOlSmJ6t+Jf6zLQ5Zu5RIrsbHYL3p8I= +// Hmac-sha512-256 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle"), 256).ToHexBytes() // []byte("e99fae71bcb43651ae10e952989eadf897faccb43966ee5122bb1b1d82f7a7c2") +// Hmac-sha512-256 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle"), 256).ToBase64Bytes() // []byte("6Z+ucby0NlGuEOlSmJ6t+Jf6zLQ5Zu5RIrsbHYL3p8I=") +``` + +##### Hmac-ripemd160 Encryption + +``` +// Hmac-ripemd160 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacRipemd160("dongle").ToHexString() // 3691ad040e80c43dc6e8ffe9bc6ef3d5bd8786b8 +// Hmac-ripemd160 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacRipemd160("dongle").ToBase64String() // NpGtBA6AxD3G6P/pvG7z1b2Hhrg= + +// Hmac-ripemd160 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacRipemd160([]byte("dongle")).ToHexBytes() // []byte("3691ad040e80c43dc6e8ffe9bc6ef3d5bd8786b8") +// Hmac-ripemd160 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacRipemd160([]byte("dongle")).ToBase64Bytes() // []byte("NpGtBA6AxD3G6P/pvG7z1b2Hhrg=") +``` + +##### Hmac-sm3 Encryption + +``` +// Hmac-sm3 encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSm3("dongle").ToHexString() // 8c733aae1d553c466a08c3e9e5daac3e99ae220181c7c1bc8c2564961de751b3 +// Hmac-sm3 encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByHmacSm3("dongle").ToBase64String() // jHM6rh1VPEZqCMPp5dqsPpmuIgGBx8G8jCVklh3nUbM= + +// Hmac-sm3 encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSm3([]byte("dongle")).ToHexBytes() // []byte("8c733aae1d553c466a08c3e9e5daac3e99ae220181c7c1bc8c2564961de751b3") +// Hmac-sm3 encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSm3([]byte("dongle")).ToBase64Bytes() // []byte("jHM6rh1VPEZqCMPp5dqsPpmuIgGBx8G8jCVklh3nUbM=") +``` + +##### Aes Encryption and Decryption + +``` +cipher := dongle.NewCipher() +cipher.SetMode(dongle.CBC) // CBC, CFB, OFB, CTR, ECB +cipher.SetPadding(dongle.PKCS7) // No, Empty, Zero, PKCS5, PKCS7, AnsiX923, ISO97971 +cipher.SetKey("0123456789abcdef") // key length must be 16, 24, or 32 bytes +cipher.SetIV("0123456789abcdef") // iv length must be 16 bytes, ECB mode does not require iv + +// Aes encrypt a string and output the raw unencoded string +rawString := dongle.Encrypt.FromString("hello world").ByAes(cipher).ToRawString() +// Aes decrypt the raw unencoded string and output a string +dongle.Decrypt.FromRawString(rawString).ByAes(cipher).ToString() // hello world + +// Aes encrypt a string and output a hex-encoded string +dongle.Encrypt.FromString("hello world").ByAes(cipher).ToHexString() // c1e9b4529aac9793010f4677f6358efe +// Aes decrypt a hex-encoded string and output a string +dongle.Decrypt.FromHexString("c1e9b4529aac9793010f4677f6358efe").ByAes(cipher).ToString() // hello world + +// Aes encrypt a string and output a base64-encoded string +dongle.Encrypt.FromString("hello world").ByAes(cipher).ToBase64String() // wem0Upqsl5MBD0Z39jWO/g== +// Aes decrypt a base64-encoded string and output a string +dongle.Decrypt.FromBase64String("wem0Upqsl5MBD0Z39jWO/g==").ByAes(cipher).ToString() // hello world + +// Aes encrypt a byte slice and output the raw unencoded byte slice +rawBytes := dongle.Encrypt.FromBytes([]byte("hello world")).ByAes(cipher).ToRawBytes() +// Aes decrypt the raw unencoded byte slice and output a byte slice +dongle.Decrypt.FromRawBytes(rawBytes).ByAes(cipher).ToBytes() // []byte("hello world") + +// Aes encrypt a byte slice and output a hex-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByAes(cipher).ToHexBytes() // []byte("c1e9b4529aac9793010f4677f6358efe") +// Aes decrypt a hex-encoded byte slice and output a byte slice +dongle.Decrypt.FromHexBytes([]byte("c1e9b4529aac9793010f4677f6358efe")).ByAes(cipher).ToBytes() // []byte("hello world") + +// Aes encrypt a byte slice and output a base64-encoded byte slice +dongle.Encrypt.FromBytes([]byte("hello world")).ByAes(cipher).ToBase64Bytes() // []byte("ZdgjvfHFgaHe0cukLgPKUg==") +// Aes decrypt a base64-encoded byte slice and output a byte slice +dongle.Decrypt.FromBase64Bytes(()byte("wem0Upqsl5MBD0Z39jWO/g==")).ByAes(cipher).ToBytes() // []byte("hello world") +``` + +Currently supported symmetric, asymmetric encryption, hashing, and message authentication codes include: + +* [x] Hex encoding and decoding +* [x] Base16 encoding and decoding +* [x] Base32 encoding and decoding +* [x] Base45 encoding and decoding +* [x] Base58 encoding and decoding +* [x] Base62 encoding and decoding +* [x] Base64 encoding and decoding +* [x] Base64URL encoding and decoding +* [x] SafeURL encoding and decoding +* [x] Base85 encoding and decoding +* [x] Base91 encoding and decoding +* [x] Base100 encoding and decoding +* [x] Morse encoding and decoding +* [x] Md2 encryption +* [x] Md4 encryption +* [x] Md5 encryption +* [x] Sha1 encryption +* [x] Sha3-224 encryption +* [x] Sha3-256 encryption +* [x] Sha3-384 encryption +* [x] Sha3-512 encryption +* [x] Sha224 encryption +* [x] Sha256 encryption +* [x] Sha384 encryption +* [x] Sha512 encryption +* [x] Sha512-224 encryption +* [x] Sha512-256 encryption +* [x] Shake128 encryption +* [x] Shake256 encryption +* [x] Ripemd160 encryption +* [x] Blake2b-256 encryption +* [x] Blake2b-384 encryption +* [x] Blake2b-512 encryption +* [x] Blake2s-256 encryption +* [x] Hmac-md2 encryption +* [x] Hmac-md4 encryption +* [x] Hmac-md5 encryption +* [x] Hmac-sha1 encryption +* [x] Hmac-sha3-224 encryption +* [x] Hmac-sha3-256 encryption +* [x] Hmac-sha3-384 encryption +* [x] Hmac-sha3-512 encryption +* [x] Hmac-sha224 encryption +* [x] Hmac-sha256 encryption +* [x] Hmac-sha384 encryption +* [x] Hmac-sha512 encryption +* [x] Hmac-sha512-224 encryption +* [x] Hmac-sha512-256 encryption +* [x] Hmac-ripemd160 encryption +* [x] Hmac-sm3 encryption +* [ ] Rc2 encryption and decryption +* [x] Rc4 encryption and decryption +* [ ] Rc5 encryption and decryption +* [ ] Rc6 encryption and decryption +* [x] Tea encryption and decryption +* [ ] Xtea encryption and decryption +* [x] Aes encryption and decryption +* [x] Blowfish encryption and decryption +* [x] Des encryption and decryption +* [x] 3Des encryption and decryption +* [x] Rsa encryption and decryption +* [ ] Ecc encryption and decryption +* [ ] Sm2 encryption and decryption +* [x] Sm3 encryption +* [ ] Sm4 encryption and decryption +* [ ] Sm7 encryption and decryption +* [ ] Sm9 encryption and decryption +* [x] Bcrypt signing and verification +* [x] Ed25519 signing and verification +* [x] Rsa signing and verification +* [ ] Dsa signing and verification + +![](/assets/img/news/Gopher-0-1.png) + +**Github: https://github.com/dromara/dongle** \ No newline at end of file diff --git a/src/news/LiteFlow-v2.13.0.md b/src/news/LiteFlow-v2.13.0.md new file mode 100644 index 0000000000..b0ffb8fe5b --- /dev/null +++ b/src/news/LiteFlow-v2.13.0.md @@ -0,0 +1,214 @@ +--- +title: LiteFlow v2.13.0 is Here! Leading a New Era for Open-Source Rule Engines +author: February 25, 2025, 10:11 +date: 2025-02-25 +cover: /assets/img/news/LiteFlow-v2.13.0-0.png +head: + - - meta + - name: News +--- + +## Foreword + +It's been a while since the last release. + +On one hand, this update indeed includes a lot of content. On the other hand, more companies and developers are using LF, so I needed considerable time to gather community feedback. Some features also required careful consideration for rationality, and the underlying code needed frequent reviews for robustness. + +Moreover, I tend to be quite laid-back. I never set strict deadlines for LF because I believe working in a relaxed state leads to better outcomes. Whenever a task comes with time pressure or KPIs, I feel it can't be driven by genuine passion. + +While developing LF, I review all the code I write, including contributions from others, down to the level of variable naming合理性 (rationality) and the completeness and合理性 of test cases. Documentation合理性 is also reviewed with each release. So, from its open-source inception until now, LF has accumulated over 2,200 test cases and relatively standardized code and documentation. + +LF might not be the most frequently updated project, but it is certainly a long-term, thoughtfully iterated open-source project that has persisted for over four years. + +Enough digression, let's get to the point. + +This version 2.13.0 addresses 22 issues in total, including 7 new features, 8 enhancements, and 7 fixes. + +This version is not fully backward compatible, but the upgrade process should be very quick. Please refer to the upgrade guide on the official website for specific changes needed. + +Below, I'll highlight some of the important updates. + +## Javax-pro Plugin + +We previously had a java plugin, then later a javax plugin. What? Now there's a javax-pro plugin? Will there be a javax-pro-max in the future? 🤣 + +Those who have used Java scripts in LF might know that we have the `liteflow-script-java` plugin based on Janino and the `liteflow-script-javax` plugin based on Liquor. The former only supports JDK6 syntax, while the latter supports all JDK syntax. + +This new javax-pro plugin should be the final form. The biggest difference compared to `liteflow-script-javax` is that the scripts supported by javax-pro are completely consistent with the writing style of static class components. This means you can override methods from superclasses within scripts. + +Simultaneously, we are retiring the `liteflow-script-java` plugin; it will not be provided in version 2.13.0. So, in the new version, there are two plugins for Java: + +* liteflow-script-javax +* liteflow-script-javax-pro + +However, we recommend migrating to the javax-pro plugin eventually, as `liteflow-script-javax` is only retained for compatibility reasons. It might be retired in the future. + +Special thanks here to noear, the author of Liquor. During a chat, I complained to him about Janino and discussed my困惑 (confusions) regarding Java scripts. Without hesitation, he tailored a Java compiler executor specifically for LF, leading to significant progress in LF's Java script capabilities. He has also open-sourced this framework; everyone can check it out: + +https://gitee.com/noear/liquor + +## Bind Keyword + +LF previously supported setting parameters on components within rules using the keywords `tag` and `data`. + +This new version introduces the `bind` keyword, which allows you to bind a key and value to an object: + +``` +THEN(a.bind("k1","v1"), b) +``` + +It can act not only on components but also on expressions, sub-variables, chains—almost anything can use `bind`. + +The reason for introducing `bind` despite having `tag` and `data` is that `bind` also provides dynamic binding capabilities. You can use placeholders: + +``` +THEN(a, b).bind("k1", "${user.name}") +``` + +For the above expression, LF will match this placeholder expression against your various contexts and assign the value to both `a` and `b`. + +The usage of the `bind` keyword enriches the forms of component parameters. There are many details in its usage; please refer to the official documentation for specific instructions. + +## Thread Pool Overhaul + +In previous versions, LF's thread pool usage in asynchronous scenarios was somewhat chaotic. Users weren't sure how to set thread pools for diverse scenarios. + +Therefore, we have redesigned the thread pool architecture. It's now divided into three dimensions: + +* Global Thread Pool +* Chain-level Thread Pool +* Expression-level Thread Pool + +The parameter names for the global thread pool have changed from before, and a new method for setting thread pools at the Chain level has been introduced: + +``` + + WHEN(a,b); + +``` + +This thread pool overhaul might break compatibility with previous versions' configuration parameters. But I've always believed that rationality and elegant design are more worth pursuing than mere compatibility. + +Furthermore, to clearly explain the thread pool structure in LF, the new documentation dedicates a large chapter specifically to thread pools. The upgrade guide also specifically explains how to upgrade the thread pool configuration to 2.13.X. This shouldn't take developers much time. + +Please refer to the documentation for specific usage and settings. + +## Scripts Can Also Be Lazily Loaded + +LF introduced a new parameter in version 2.12.0 to control lazy loading: + +``` +liteflow.parse-mode=PARSE_ONE_ON_FIRST_EXEC +``` + +But this only affected rules, not scripts. + +Based on community feedback, many people also needed lazy loading for scripts. So now this parameter controls both rules and scripts. + +## SQL Plugin Supports Specifying Multiple Data Sources + +The LF SQL plugin has always had issues in multi-data source scenarios, often failing to automatically detect the appropriate data source. + +This time, LF supports the two most widely used data source frameworks: + +* Baomidou community's `dynamic-datasource` framework. +* Shardingsphere community's `shardingsphere-jdbc` framework. + +The logic for obtaining connection objects has also been significantly optimized. + +As the officially recommended storage plugin, the SQL plugin has the broadest support. Give it a try in the new version. + +## Supporting Custom Process Data in Steps + +You can now use `this.setStepData(xx)` to add custom step data, enabling observation of data changes within the context throughout a process. + +This feature was actually proposed via a PR long ago, but I initially didn't see the necessity. + +However, later community feedback made me realize this feature is indeed quite necessary. Sometimes community feedback corrects my initially incorrect ideas. + +## Persistence of Node Instance ID + +In the new version, we added something called `instanceId` to the Node object. This ID represents a unique orchestration ID. As long as your rules remain unchanged, this ID remains constant forever, even across application restarts. + +This feature is implemented for both local rule files and the SQL storage plugin. + +You might notice this feature isn't highlighted in the documentation, but it is a very important feature in 2.13.0. That's because this feature is more aimed at secondary developers (二开开发者); pure users might not need to know about it. + +This feature might appear in the upcoming **development documentation**. + +Yes, LF will not only have usage documentation but is also considering creating development documentation to help developers who want to extend LF. + +## Full Update List + +``` +Feature #IBI6A3 New javax-pro plugin based on native form +https://gitee.com/dromara/liteFlow/issues/IBI6A3 + +Feature #IBL9CK Add bind keyword, enabling binding keys and values anywhere +https://gitee.com/dromara/liteFlow/issues/IBL9CK + +Feature #IB2BKP Make PARSE_ONE_ON_FIRST_EXEC effective for scripts as well +https://gitee.com/dromara/liteFlow/issues/IB2BKP + +Feature #IBLJ4A Ability to add custom data in steps +https://gitee.com/dromara/liteFlow/issues/IBLJ4A + +Feature #IAPI07 Thread pool isolation by chain dimension +https://gitee.com/dromara/liteFlow/issues/IAPI07 + +Feature #IAUS2R SQL plugin hopes to support specifying data sources +https://gitee.com/dromara/liteFlow/issues/IAUS2R + +Feature #IB0SJ1 Node instance ID persistence +https://gitee.com/dromara/liteFlow/issues/IB0SJ1 + +Enhancement #IBNPZN LiteFlow thread pool system重塑 (overhauled) +https://gitee.com/dromara/liteFlow/issues/IBNPZN + +Enhancement #IBJO4X Establish a unified metadata operation class, the entry point for all metadata operations +https://gitee.com/dromara/liteFlow/issues/IBJO4X + +Enhancement #IBCLUJ Step information display改造 (overhaul) && Thread information recording +https://gitee.com/dromara/liteFlow/issues/IBCLUJ + +Enhancement #IBA89R Expect Java script engine to support inheritance features +https://gitee.com/dromara/liteFlow/issues/IBA89R + +Enhancement #IB1YLX Simplify comments in rules, simplify the complex +https://gitee.com/dromara/liteFlow/issues/IB1YLX + +Enhancement #IAWJG1 Redesign of the node wrapper type pattern in ELBus EL expression construction +https://gitee.com/dromara/liteFlow/issues/IAWJG1 + +Enhancement #IAVYME Hope to add an instance Id to Node instances, reflected in steps +https://gitee.com/dromara/liteFlow/issues/IAVYME + +Enhancement #IAUS4H SQL plugin database connection acquisition optimization +https://gitee.com/dromara/liteFlow/issues/IAUS4H + +Fix #IBLWJG Boolean node setting isAccess to false throws error +https://gitee.com/dromara/liteFlow/issues/IBLWJG + +Fix #IBL9HC Fix issue where operators紧跟 (immediately following) nodes in EL expressions cause multiple clones +https://gitee.com/dromara/liteFlow/issues/IBL9HC + +Fix #IBGBLN Java exception: Comparison method violates its general contract +https://gitee.com/dromara/liteFlow/issues/IBGBLN + +Fix #IBAI9U FlowBus's getNodesByChainId returns null +https://gitee.com/dromara/liteFlow/issues/IBAI9U + +Fix #IB7EQJ Issue where target after SWITCH cannot select nodes if maxWaitMilliseconds is added +https://gitee.com/dromara/liteFlow/issues/IB7EQJ + +Fix #IB0X4Q Fix bug caused by asynchronous loops in version 2.12.4 +https://gitee.com/dromara/liteFlow/issues/IB0X4Q + +Fix #IB0K9Y Allow FlowExecutor to accept null context +https://gitee.com/dromara/liteFlow/issues/IB0K9Y +``` + +## Knowledge Planet (Knowledge Base/Community) + +Join the Knowledge Planet now to enjoy a year of high-quality Q&A at a very low price. There are also exclusive LF series analysis articles available only on the Knowledge Planet. Interested friends can scan the QR code to go directly there. +![](/assets/img/news/LiteFlow-v2.13.0-0.png) \ No newline at end of file diff --git a/src/news/MaxKey-4.1.1.md b/src/news/MaxKey-4.1.1.md index bef7cb6182..7701a7f6a5 100644 --- a/src/news/MaxKey-4.1.1.md +++ b/src/news/MaxKey-4.1.1.md @@ -14,11 +14,11 @@ head: # Overview -**MaxKey** single sign-on authentication system, homophonic Marx's key implies the largest key, is * * the industry's leading IAM-IDaas identity management and authentication product * *, supports OAuth 2.x/ OpenID Connect, SAML 2.0, JWT, CAS, SCIM and other standard protocols, provides **secure, standard, and open** user identity management (IDM), identity authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. +**MaxKey** single sign-on authentication system, homophonic Marx's key implies the largest key, is **the industry's leading IAM-IDaas identity management and authentication product**, supports OAuth 2.x/ OpenID Connect, SAML 2.0, JWT, CAS, SCIM and other standard protocols, provides **secure, standard, and open** user identity management (IDM), identity authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. -Official website: **http:// www.maxkey.top * * +Official website: **http:// www.maxkey.top** -Official QQ:**1054466084 * * +Official QQ: **1054466084** Email: support@maxsso.net @@ -105,11 +105,11 @@ MaxKey v 4.1.1 GA 2024/08/20 # Roadmap -**Enhanced Identity Management * * +**Enhanced Identity Management** -**Data synchronization optimization * * +**Data synchronization optimization** -**Permission Management Optimization * * +**Permission Management Optimization** About Dromara @@ -119,6 +119,6 @@ Dromara is an open source community composed of top open source project authors Dromara open source community currently has 10 GVP projects, with a total number of star exceeding 100,000. It has built an open source community of tens of thousands of people, and thousands of individuals and teams are using the open source projects of Dromara community. -**Welcome to the knowledge planet and I interact * * +**Welcome to the knowledge planet and I interact** ![](/assets/img/qrcode_zsxq.webp) \ No newline at end of file diff --git a/src/news/MaxKey-4.1.2.md b/src/news/MaxKey-4.1.2.md new file mode 100644 index 0000000000..7652ea195b --- /dev/null +++ b/src/news/MaxKey-4.1.2.md @@ -0,0 +1,134 @@ +--- +title: MaxKey Single Sign-On Authentication System 4.1.2, Major National Day Edition Update +author: maxkey +date: 2024-09-30 +cover: /assets/img/news/MaxKey-4.1.2-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/MaxKey-4.1.2-0.png) + +**Industry-leading IAM-IDaas Identity Management and Authentication Product** + +# Overview + +**MaxKey** Single Sign-On Authentication System, phonetically meaning "Marx's Key,"寓意 is the maximum key. It is an **industry-leading IAM-IDaas identity management and authentication product**. It supports standard protocols such as OAuth 2.x/OpenID Connect, SAML 2.0, JWT, CAS, and SCIM, providing **secure, standard, and open** user identity management (IDM), authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. It is open source, secure, compliant, and independently controllable. + +Official Website: **http://www.maxkey.top** + +Official QQ: **1054466084** + +Email: support@maxsso.net + +Code Hosting: + +Gitee: https://gitee.com/dromara/MaxKey + +GitHub: https://github.com/dromara/MaxKey + +# Product Features + +1. Standard Protocols + +| No. | Protocol | Support | +| ---- | ------------------------ | ------- | +| 1.1 | OAuth 2.0/OpenID Connect | High | +| 1.2 | SAML 2.0 | High | +| 1.3 | JWT | High | +| 1.4 | CAS | High | +| 1.5 | SCIM 2.0 | High | +| 1.6 | FormBased | Medium | +| 1.7 | TokenBased(Post/Cookie) | Medium | +| 1.8 | ExtendApi | Low | +| 1.9 | EXT | Low | + +2. Login Support + +| No. | Login Method | +| ---- | ------------------------------------------------------------ | +| 2.1 | Dynamic Verification Code: Letter/Number/Arithmetic | +| 2.2 | Two-Factor Authentication | +| 2.3 | SMS Authentication: Tencent Cloud SMS/Aliyun SMS/Netease YunXin | +| 2.4 | Login Yi/Google/Microsoft Authenticator/FreeOTP/Supports TOTP or HOTP | +| 2.5 | Kerberos/SPNEGO/AD Domain | +| 2.6 | OpenLDAP/ActiveDirectory/Standard LDAP Server | +| 2.7 | Social Accounts: WeChat/QQ/Weibo/DingTalk/Google/Facebook/Others | +| 2.8 | Scan Code Login: Enterprise WeChat/DingTalk/Feishu Scan Code Login | + +3. Provides standard authentication interfaces for other applications to integrate SSO, secure mobile access, secure APIs, third-party authentication, and internet authentication integration. + +4. Provides user lifecycle management, supports SCIM 2 protocol; out-of-the-box connectors (Connector) for identity provisioning and synchronization. + +5. Simplifies Microsoft Active Directory domain control, standard LDAP server structure, and account management; self-service password reset. + +6. IDaas multi-tenant functionality, supports independent management of multiple enterprises under a group or data isolation between different departments within an enterprise, reducing operational costs. + +7. The authentication center is platform-independent and supports diverse environments, including Web, mobile phones, and mobile devices such as Apple iOS and Android, extending authentication capabilities from B/S to mobile applications. + +8. Configurable password policies and access policies; supports accurate IP location using Ip2region or GeoLite2 geographic database; powerful security auditing, including full user lifecycle auditing, access behavior traceability auditing, security compliance auditing, and security risk warnings. + +9. Based on the Java EE platform, microservices architecture, using open-source technologies such as Spring, MySQL, Tomcat, Redis, and MQ, with strong scalability. + +10. Open source, secure, compliant, and independently controllable, licensed under Apache 2.0 License. + +# Interface + +![](/assets/img/news/MaxKey-4.1.2-1.png) + +# Download + +Current Version Network Disk Download + +| Version | Date | Download Link | +| ------- | ---------- | ------------- | +| v 4.1.2 | 2024/09/30 | Download | + +# Version Description + +MaxKey v 4.1.2 GA 2024/09/30 + +``` + *(MAXKEY-240601) timebased otp optimization + *(MAXKEY-240602) appCategoryService optimization + *(MAXKEY-240603) Open Source Summer Flutter Authentication APP Project + *(MAXKEY-240604) Open Source Summer Synchronizer Adds Attribute Configuration Management Project + *(MAXKEY-240605) APP Scan Code Login Function + *(MAXKEY-240606) maxkey-web-openapi access path /maxkey-openapi + *(MAXKEY-240607) APP Scan Code Login Support + *(MAXKEY-240608) Group and Role Optimization, Adds Dynamic Role Tasks, Removes resumeTime and suspendTime + *(MAXKEY-240609) ProvisionAction Changed to ProvisionAct + *(MAXKEY-240610) MaxKeyGatewayApplication Removes exclude DruidDataSourceAutoConfigure.class + *(MAXKEY-240611) Login Background Redirect Function Fix + *(MAXKEY-240612) Oauth20Client Code Optimization, Removes Oauth20ClientAutoConfiguration + *(MAXKEY-240613) Core Repository Moved to maxkey-persistence, to be Merged in Future Versions + *(MAXKEY-240613) Dependency References, Updates, and Upgrades + mybatis-jpa-extra 3.3.1 + springVersion 6.1.13 + springBootVersion 3.3.4 + springDataVersion 3.3.4 + tomcatVersion 10.1.30 +``` + +# Roadmap + +**Enhanced Identity Management** + +**Data Synchronization Optimization** + +**Permission Management Optimization** + + + +About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, and scheduling orchestration. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, and is dedicated to providing microservices cloud-native solutions for global users. It aims to allow every participating open-source enthusiast to experience the joy of open-source. + + + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of members, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to Interact with Me** + +![](/assets/img/news/MaxKey-4.1.2-2.webp) \ No newline at end of file diff --git a/src/news/MaxKey-4.1.3.md b/src/news/MaxKey-4.1.3.md new file mode 100644 index 0000000000..9433a28763 --- /dev/null +++ b/src/news/MaxKey-4.1.3.md @@ -0,0 +1,141 @@ +--- +title: MaxKey Single Sign-On Authentication System 4.1.3, Major Persistence Layer Update +author: MaxKey +date: 2024-11-29 +cover: /assets/img/news/MaxKey-4.1.3-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/MaxKey-4.1.3-0.png) + +**Industry-Leading IAM-IDaas Identity Management and Authentication Product** + +# Overview + +**MaxKey** Single Sign-On Authentication System is **Industry-Leading IAM-IDaas Identity Management and Authentication Product**. Its name, which sounds like Marx's key, symbolizes that it can unlock complex enterprise security needs like a master key (the biggest key), providing a simple and efficient solution. The product supports standard protocols such as OAuth 2.x/OpenID Connect, SAML 2.0, JWT, CAS, and SCIM, providing secure, standard, and open user identity management (IDM), identity authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. + +MaxKey focuses on performance, security, and ease of use in enterprise scenarios and is widely used in industries such as healthcare, finance, government, and manufacturing. + +Official Website: **Official Website** + +Official QQ: **1054466084** + +Email: \*\*support@maxsso.net\*\* + +Code Hosting: **Gitee** | **GitHub** + +# Product Features + +1. Standard Protocols + +| Serial Number | Protocol | Supported | +| ------------- | ------------------------ | --------- | +| 1.1 | OAuth 2.0/OpenID Connect | High | +| 1.2 | SAML 2.0 | High | +| 1.3 | JWT | High | +| 1.4 | CAS | High | +| 1.5 | SCIM 2.0 | High | +| 1.6 | FormBased | Medium | +| 1.7 | TokenBased (Post/Cookie) | Medium | +| 1.8 | ExtendApi | Low | +| 1.9 | EXT | Low | + +2. Login Support + +| Number | Login Method | +| ------ | ------------------------------------------------------------ | +| 2.1 | Dynamic Verification Code: Alphabets/Numbers/Arithmetic | +| 2.2 | Two-Factor Authentication | +| 2.3 | SMS Authentication: Tencent Cloud SMS/Alibaba Cloud SMS/NetEase Cloud Messenger | +| 2.4 | EasyLogin/Google/Microsoft Authenticator/FreeOTP/TOTP or HOTP Supported | +| 2.5 | Kerberos/SPNEGO/AD Domain | +| 2.6 | OpenLDAP/ActiveDirectory/Standard LDAP Server | +| 2.7 | Social Account: WeChat/QQ/Weibo/DingTalk/Google/Facebook/Other | +| 2.8 | QR Code Login: WeChat Enterprise/DingTalk/Feishu QR Code Login | + +3. Provides standard authentication interfaces to facilitate SSO integration with other applications, secure mobile access, secure API integration, third-party authentication, and internet authentication integration. + +4. Provides user lifecycle management and supports the SCIM 2 protocol; an out-of-the-box connector enables identity provisioning synchronization. + +5. Simplifies organization and account management for Microsoft Active Directory domain controllers and standard LDAP servers, and provides self-service password reset. + +6. IDaaS multi-tenancy supports independent management of multiple enterprises within a group or data isolation between different departments within an enterprise, reducing operation and maintenance costs. + +7. The authentication center is platform-agnostic and supports diverse environments, including web, mobile, and mobile devices such as Apple iOS and Android, providing comprehensive authentication capabilities from B/S to mobile applications. + +8. Configurable password and access policies; supports precise IP location using Ip2region or GeoLite2 geographic databases; and robust security auditing, including full user lifecycle audits, access behavior traceability audits, security compliance audits, and security risk warnings. + +9. Based on the Java EE platform and a microservices architecture, it utilizes open source technologies such as Spring, MySQL, Tomcat, Redis, and MQ for strong scalability. + +10. Open source, secure, compliant, and self-controlled. License: Apache 2.0 License. + +# Interface + +![](/assets/img/news/MaxKey-4.1.3-1.png) + +# Download + +Current Version Network Download + +| Version | Date | Download Location | +| ------- | ---------- | ----------------- | +| v 4.1.3 | 2024/11/29 | Download | + +# Version Notes + +MaxKey v 4.1.3 GA 2024/11/29 + +``` +*(MAXKEY-240701) #IAZNZS OAuth2 single sign-out issue not working +*(MAXKEY-240702) #238 Feishu QR code login failure +*(MAXKEY-240703) Upgrade mybatis-jpa-extra-3.3.2 to add service interface and impl implementation +*(MAXKEY-240704) Move Active Directory related content to org.dromara.maxkey.ldap.activedirectory +*(MAXKEY-240705) Added maxkey-authentication-provider-mgt +*(MAXKEY-240706) Renamed maxkey.auth.session.timeout to maxkey.auth.jwt.refresh.expires +*(MAXKEY-240707) Added common rule ConstsRegex +*(MAXKEY-240708) Optimized post-authentication redirection +*(MAXKEY-240709) #IB1BC9 Client calls single sign-out API or exits maxkey-hmt admin page with error: Circular view path [logout] +*(MAXKEY-240710) Optimization of the oidc wellknown interface +*(MAXKEY-240711) Issue with the response_types_supported returned by the #IAEWN7 well-known/openid-configuration interface +*(MAXKEY-240712) Optimization of the SMS verification code service code +*(MAXKEY-240713) Separation of the login scan code interface +*(MAXKEY-240714) Optimization of login sessions and log queries, adding IP address location display, and removing the loginUrl field +*(MAXKEY-240715) Baota Panel installation and deployment integration +*(MAXKEY-240716) Optimization of bean initialization judgment +*(MAXKEY-24 0717) ALL Projects UTF-8 +*(MAXKEY-240718) maxkey-authentications\maxkey-authentication-social -> maxkey-starter\maxkey-starter-social +*(MAXKEY-240719) Database fields automatically populate instId, createdBy, createdDate, modifiedBy, modifiedDate +*(MAXKEY-240720) #235 from khangdc2/circleci-project-setup +*(MAXKEY-240721) docker-compose & docker deployment updates and optimizations +*(MAX KEY-240722) Optimize Gradle configuration files +*(MAXKEY-240723) Remove invalid references from code and standardize code +*(MAXKEY-240724) Rename HistorySignOnAppInterceptor to HistorySingleSignOnInterceptor +*(MAXKEY-240725) Upgrade MySQL to 8.4.2 +*(MAXKEY-240726) #IB71WC 404 issue when starting the front-end locally +*(MAXKEY-240727) SAML metadata email address support@maxsso.net +*(MAXKEY-240728) Dependency references, updates, and upgrades +Spring 6.2 .0 +springBoot 3.3.6 +springSecurity 6.4.1 +springData 3.4.0 +springkafka 3.3.0 +springcloud 4.1.4 +httpcomponentscore5 5.3.1 +httpcomponentsclient5 5.4.1 +tomcat 10.1.33 +log4j 2.24.1 +gson 2.11.0 +freemarker  2.3.33 +``` + +About Dromara + +Dromara is an open source community formed by top open source project authors in China. We provide a range of open source products, solutions, consulting, technical support, and training and certification services, including distributed transactions, popular tools, enterprise-level certification, microservices RPC, operations monitoring, agent monitoring, distributed logging, and scheduling and orchestration. Our technology stack is fully open source and community-neutral, dedicated to providing microservices and cloud-native solutions to users worldwide. Let every open source enthusiast experience the joy of open source. + +The Dromara open source community currently boasts over 10 GVP projects with over 100,000 stars, building an open source community of over 10,000 people. Thousands of individuals and teams are using Dromara's open source projects. + +**Welcome to join me on Knowledge Planet** + +![](/assets/img/news/MaxKey-4.1.3-2.webp) \ No newline at end of file diff --git a/src/news/MaxKey-4.1.4.md b/src/news/MaxKey-4.1.4.md new file mode 100644 index 0000000000..5e428628e4 --- /dev/null +++ b/src/news/MaxKey-4.1.4.md @@ -0,0 +1,136 @@ +--- +title: MaxKey Single Sign-On Authentication System 4.1.4, Access Map Optimization +author: MaxKey +date: 2024-12-27 +cover: /assets/img/news/MaxKey-4.1.4-0.webp +head: + - - meta + - name: News +--- + +![](/assets/img/news/MaxKey-4.1.4-0.webp) + +**Industry-Leading IAM-IDaas Identity Management and Authentication Product** + +# Overview + +Dromara's **MaxKey** Single Sign-On Authentication System is **an industry-leading IAM-IDaas identity management and authentication product**. Its name, which sounds like Marx's key, symbolizes that it can unlock complex enterprise security needs like a master key (the biggest key), providing a simple and efficient solution. The product supports standard protocols such as OAuth 2.x/OpenID Connect, SAML 2.0, JWT, CAS, and SCIM, providing secure, standard, and open user identity management (IDM), identity authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. + +MaxKey focuses on performance, security, and ease of use in enterprise scenarios and is widely used in industries such as healthcare, finance, government, and manufacturing. + +MaxKey is licensed under the Apache License, Version 2.0 and is open source and free. It is open source, secure, compliant, and self-controlled. + +Official Website: **Official Website: https://www.maxkey.top/** + +Official QQ: **1054466084** + +Email: support@maxsso.net + +Code Hosting: + +Gitee: https://gitee.com/dromara/MaxKey + +GitHub: https://github.com/dromara/MaxKey + +# Product Features + +1. Standard Protocols + +| Serial Number | Protocol | Supported | +| ------------- | ------------------------- | --------- | +| 1.1 | OAuth 2.0/OpenID Connect | High | +| 1.2 | SAML 2.0 | High | +| 1.3 | JWT | High | +| 1.4 | CAS | High | +| 1.5 | SCIM 2.0 | High | +| 1.6 | Form-Based | Medium | +| 1.7 | Token-Based (Post/Cookie) | Medium | +| 1.8 | ExtendApi | Low | +| 1.9 | EXT | Low | + +2. Login Support + +| Serial Number | Login Method | +| ------------- | ------------------------------------------------------------ | +| 2.1 | Dynamic Verification Code: Alphabetic/Number/Arithmetic | +| 2.2 | Two-Factor Authentication | +| 2.3 | SMS Authentication: Tencent Cloud SMS/Alibaba Cloud SMS/NetEase Cloud Messenger | +| 2.4 | EasyLogin/Google/Microsoft Authenticator/FreeOTP/TOTP or HOTP Support | +| 2.5 | Kerberos/SPNEGO/AD Domain | +| 2.6 | OpenLDAP/ActiveDirectory/Standard LDAP Server | +| 2.7 | Social Accounts: WeChat/QQ/Weibo/DingTalk/Google/Facebook/Other | +| 2.8 | QR Code Login: WeChat for Business/DingTalk/Feishu QR Code Login | + +3. Provides standard authentication interfaces to facilitate SSO integration with other applications, secure mobile access, secure APIs, third-party authentication, and internet authentication integration. + +4. Provides user lifecycle management, supports the SCIM 2 protocol, and provides out-of-the-box connectors for identity provisioning synchronization. + +5. Simplifies organization and account management for Microsoft Active Directory domain controllers and standard LDAP servers, and enables self-service password reset. + +6. IDaaS multi-tenancy supports independent management of multiple enterprises within a group or data isolation between different departments within an enterprise, reducing operational costs. + +7. The authentication center is platform-agnostic and supports diverse environments, including web, mobile, and mobile devices such as Apple iOS and Android, providing comprehensive authentication capabilities from B/S to mobile applications. + +8. Configurable password and access policies; supports precise IP location using IP2region or GeoLite2 geographic databases; and robust security auditing, including user lifecycle audits, access behavior traceability audits, security compliance audits, and security risk alerts. + +9. Based on the Java EE platform and a microservices architecture, it utilizes open-source technologies such as Spring, MySQL, Tomcat, Redis, and MQ for strong scalability. + +10. Open source, secure, compliant, and self-sustainable. Licensed under the Apache 2.0 License: https://www.maxkey.top/zh/about/licenses.html. + +# Interface + +![](/assets/img/news/MaxKey-4.1.4-1.png) + +# Download + +Current Version Network Download + +| Version | Date | Download Location | +| ------- | ---------- | ----------------- | +| v 4.1.4 | 2024/12/27 | Download | + +# Version Notes + +MaxKey v 4.1.4 GA 2024/12/27 + +``` + +*(MAXKEY-240801) Deploy the online demonstration system +*(MAXKEY-240802) MySQL database connection parameter: serverTimezone=GMT+8 +*(MAXKEY-240803) Map optimization +*(MAXKEY-240804) Internationalization translation +*(MAXKEY-240805) Added the "category" parameter to the login log to distinguish between authentication 1 and management 5 +*(MAXKEY-240806) Changed single sign-on from authentication to management to CAS +*(MAXKEY-240807) Added the Dromara logo +*(MAXKEY-240808) Dashboard optimization and Updates +*(MAXKEY-240809) Dashboard Optimization - Added Domestic Map Display +*(MAXKEY-240810) Session Management Optimization +*(MAXKEY-240811) CAS Adapter Name Correction +*(MAXKEY-240812) #IB8ZT6 Authentication Platform Unable to Modify Password +*(MAXKEY-240813) Default InstId for Login Logs +*(MAXKEY-240814) InstitutionsService Optimization and Integration +*(MAXKEY-240815) DefaultTokenServices RefreshToken Optimization +*(MAXKEY-240816) Login Integration From HistoryRepository to HistoryLoginService +*(MAXKEY-240817) Authentication code integration and optimization +*(MAXKEY-240818) Login integration and optimization of LoginRepository and LoginServiceImpl +*(MAXKEY-240819) Online documentation upgrades and optimizations +*(MAXKEY-240820) Dependency references, updates, and upgrades +springVersion                   6.2.1 +springBootVersion                   3.4.1 +springSecurityVersion       6.4.2 +springretryVersion                2.0.11 +log4jVersion                    2.24.3 +jacksonVersion                  2.18.2 +springcloudVersion              4.2.0 +springcloudalibabaVersion       2023.0.1.2 +``` + +About Dromara + +Dromara is an open source community formed by top open source project authors in China. We provide a range of open source products, solutions, consulting, technical support, and training and certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operations monitoring, agent monitoring, distributed logging, and scheduling and orchestration. Our technology stack is fully open source and community-neutral, and we are committed to providing microservices cloud-native solutions to users worldwide. Let every open source enthusiast experience the joy of open source. + +The Dromara open source community currently boasts over 10 GVP projects with over 100,000 stars, building an open source community of over 10,000 people. Thousands of individuals and teams are using Dromara's open source projects. + +**Welcome to interact with me on Knowledge Planet** + +![](/assets/img/news/MaxKey-4.1.4-2.webp) \ No newline at end of file diff --git a/src/news/MaxKey-4.1.5.md b/src/news/MaxKey-4.1.5.md new file mode 100644 index 0000000000..c1d275f7ea --- /dev/null +++ b/src/news/MaxKey-4.1.5.md @@ -0,0 +1,106 @@ +--- +title: MaxKey Single Sign-On Authentication System 4.1.5, Swagger Vulnerability Fix +author: maxkey +date: 2025-01-09 +cover: /assets/img/news/MaxKey-4.1.5-0.webp +head: + - - meta + - name: News +--- + +![](/assets/img/news/MaxKey-4.1.5-0.webp) + +**Industry-Leading IAM-IDaas Identity Management and Authentication Product** + +# Overview + +Dromara's **MaxKey** single sign-on authentication system is **an industry-leading IAM-IDaas identity management and authentication product**. Its name, which sounds like Marx's key, symbolizes its ability to unlock complex enterprise security needs like a master key (the biggest key), providing a simple and efficient solution. The product supports standard protocols such as OAuth 2.x/OpenID Connect, SAML 2.0, JWT, CAS, and SCIM, providing secure, standard, and open user identity management (IDM), identity authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. + +MaxKey focuses on performance, security, and ease of use in enterprise scenarios and is widely used in industries such as healthcare, finance, government, and manufacturing. + +MaxKey is licensed under the Apache License, Version 2.0 and is open source and free. It is open source, secure, compliant, and self-controlled. + +Official Website: **https://www.maxkey.top/** + +Official QQ: **1054466084** + +Email: support@maxsso.net + +Code Hosting: + +Gitee:  https://gitee.com/dromara/MaxKey + +GitHub:  https://github.com/dromara/MaxKey + +# Product Features + +1. Standard Protocols + +| Serial Number | Protocol | Supported | +| ------------- | ------------------------- | --------- | +| 1.1 | OAuth 2.0/OpenID Connect | High | +| 1.2 | SAML 2.0 | High | +| 1.3 | JWT | High | +| 1.4 | CAS | High | +| 1.5 | SCIM 2.0 | High | +| 1.6 | Form-Based | Medium | +| 1.7 | Token-Based (Post/Cookie) | Medium | +| 1.8 | ExtendApi | Low | +| 1.9 | EXT | Low | + +2. Login Support + +| Serial Number | Login Method | +| ------------- | ------------------------------------------------------------ | +| 2.1 | Dynamic Verification Code: Alphabetic/Number/Arithmetic | +| 2.2 | Two-Factor Authentication | +| 2.3 | SMS Authentication: Tencent Cloud SMS/Alibaba Cloud SMS/NetEase Cloud Messenger | +| 2.4 | EasyLogin/Google/Microsoft Authenticator/FreeOTP/TOTP or HOTP Support | +| 2.5 | Kerberos/SPNEGO/AD Domain | +| 2.6 | OpenLDAP/ActiveDirectory/Standard LDAP Server | +| 2.7 | Social Accounts: WeChat/QQ/Weibo/DingTalk/Google/Facebook/Other | +| 2.8 | QR Code Login: WeChat for Business/DingTalk/Feishu QR Code Login | + +3. Provides standard authentication interfaces to facilitate SSO integration with other applications, secure mobile access, secure APIs, third-party authentication, and internet authentication integration. + +4. Provides user lifecycle management, supports the SCIM 2 protocol, and provides out-of-the-box connectors for identity provisioning synchronization. + +5. Simplifies organization and account management for Microsoft Active Directory domain controllers and standard LDAP servers, and enables self-service password reset. + +6. IDaaS multi-tenancy supports independent management of multiple enterprises within a group or data isolation between different departments within an enterprise, reducing operational costs. + +7. The authentication center is platform-agnostic and supports diverse environments, including web, mobile, and mobile devices such as Apple iOS and Android, providing comprehensive authentication capabilities from B/S to mobile applications. + +8. Configurable password and access policies; supports precise IP location using IP2region or GeoLite2 geographic databases; and robust security auditing, including user lifecycle audits, access behavior traceability audits, security compliance audits, and security risk alerts. + +9. Based on the Java EE platform and a microservices architecture, it utilizes open-source technologies such as Spring, MySQL, Tomcat, Redis, and MQ for strong scalability. + +10. Open source, secure, compliant, and self-controllable. License: Apache 2.0 License https://www.maxkey.top/zh/about/licenses.html. + +# Interface + +![](/assets/img/news/MaxKey-4.1.5-1.png) + +# Download + +Current Version Network Disk Download + +| Version | Date | Download Link | +| ------- | ---------- | ------------- | +| v 4.1.5 | 2025/01/09 | Download | + +# Version Notes + +MaxKey v 4.1.5 GA January 9, 2025 + +``` + +*(MAXKEY-250101) Login verification code configuration optimization, now enabled by default +*(MAXKEY-250102) SQL syntax expression enhancements +*(MAXKEY-250103) Changed unlockdate to unlocktime +*(MAXKEY-250104) XSS security optimization +*(MAXKEY-250105) #IBFGSI knife4j bug by zwj +*(MAXKEY-250106) Online documentation upgrade and optimization +*(MAXKEY-250107) Dependency reference, update, and upgrade +mybatis-jpa-extra 3.3.3 +``` \ No newline at end of file diff --git a/src/news/MaxKey-4.1.6.md b/src/news/MaxKey-4.1.6.md new file mode 100644 index 0000000000..4eb8c21ace --- /dev/null +++ b/src/news/MaxKey-4.1.6.md @@ -0,0 +1,134 @@ +--- +title: MaxKey Single Sign-On Authentication System 4.1.6, World Map Access Statistics +author: MaxKey +date: 2025-02-20 +cover: /assets/img/news/MaxKey-4.1.6-0.png +head: + - - meta + - name: News +--- + +**![](/assets/img/news/MaxKey-4.1.6-1.webp)** + +**The Industry-Leading IAM-IDaas Identity Management and Authentication Product** + +# Overview + +Dromara **MaxKey** Single Sign-On Authentication System is an **industry-leading IAM-IDaas identity management and authentication product**. Its name is a homophone for "Marx's Key," symbolizing its ability to act like a master key (the greatest key) that unlocks complex enterprise security needs with simple and efficient solutions. The product supports standard protocols such as OAuth 2.x/OpenID Connect, SAML 2.0, JWT, CAS, and SCIM, providing **secure, standard, and open** user identity management (IDM), authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. + +MaxKey focuses on performance, security, and ease of use in enterprise scenarios and is widely used in industries such as healthcare, finance, government, and manufacturing. + +MaxKey **follows the Apache License, Version 2.0, and is open source and free**. It is open source, secure, compliant, and independently controllable. + +Official Website: **https://www.maxkey.top/** + +Official QQ: **1054466084** + +Email: support@maxsso.net + +Code Hosting: + +Gitee: https://gitee.com/dromara/MaxKey + +GitHub: https://github.com/dromara/MaxKey + +# Product Features + +1. Standard Protocols + +| No. | Protocol | Support Level | +| ---- | ------------------------ | ------------- | +| 1.1 | OAuth 2.0/OpenID Connect | High | +| 1.2 | SAML 2.0 | High | +| 1.3 | JWT | High | +| 1.4 | CAS | High | +| 1.5 | SCIM 2.0 | High | +| 1.6 | FormBased | Medium | +| 1.7 | TokenBased (Post/Cookie) | Medium | +| 1.8 | ExtendApi | Low | +| 1.9 | EXT | Low | + +2. Authentication Methods + +| No. | Authentication Method | +| ---- | ------------------------------------------------------------ | +| 2.1 | Dynamic CAPTCHA (letters/numbers/arithmetic) | +| 2.2 | Two-Factor Authentication (2FA) | +| 2.3 | SMS Authentication (Tencent Cloud SMS/Aliyun SMS/NetEase YunXin) | +| 2.4 | LoginEasy/Google/Microsoft Authenticator/FreeOTP (supports TOTP or HOTP) | +| 2.5 | Kerberos/SPNEGO/AD Domain | +| 2.6 | OpenLDAP/Active Directory/Standard LDAP Server | +| 2.7 | Social Account Login (WeChat/QQ/Weibo/DingTalk/Google/Facebook/Others) | +| 2.8 | QR Code Login (Enterprise WeChat/DingTalk/Feishu QR Code Login) | + +3. Provides standard authentication interfaces for integrating other applications with SSO, secure mobile access, secure APIs, third-party authentication, and internet authentication integration. + +4. Offers user lifecycle management and supports the SCIM 2 protocol; includes out-of-the-box connectors for identity provisioning and synchronization. + +5. Simplifies Microsoft Active Directory domain control, standard LDAP server architecture, and account management, with self-service password reset. + +6. IDaas multi-tenancy functionality supports independent management of multiple enterprises within a group or data isolation between different departments within an enterprise, reducing operational costs. + +7. The authentication center is platform-agnostic and supports diverse environments, including web, mobile, and mobile devices (e.g., Apple iOS, Android), extending authentication capabilities from B/S to mobile applications. + +8. Configurable password policies and access policies; supports Ip2region or GeoLite2 geographic database for precise IP positioning; robust security auditing with full user lifecycle auditing, access behavior traceability, security compliance auditing, and security risk alerts. + +9. Based on the Java EE platform with a microservices architecture, using open-source technologies such as Spring, MySQL, Tomcat, Redis, and MQ, ensuring high scalability. + +10. Open source, secure, compliant, and independently controllable, licensed under Apache 2.0 License: https://www.maxkey.top/zh/about/licenses.html. + +# Interface + +![](/assets/img/news/MaxKey-4.1.6-2.png) + +# Downloads + +| Version | Date | Download Link | +| ------- | ---------- | ------------- | +| v 4.1.6 | 2025/02/20 | Download | + +# Release Notes + +MaxKey v 4.1.6 GA 2025/02/20 + +``` +*(MAXKEY-250201) Global optimization for invalid request addresses +*(MAXKEY-250202) Fixed loginConfig and captchaType property write-back; resolved openApi runtime failure issue by zhangzhongjie +*(MAXKEY-250203) #IBGVOI Set default values for LoginConfig +*(MAXKEY-250204) Added 30-day world map access statistics +*(MAXKEY-250205) Optimized JWT validation code +*(MAXKEY-250206) Optimized audit query +*(MAXKEY-250207) Optimized current organization reading +*(MAXKEY-250208) #IBI3NO Fixed issue where fetch query condition cannot be empty +*(MAXKEY-250209) Optimized login success information update +*(MAXKEY-250210) Optimized session termination issue on the authentication end +*(MAXKEY-250211) Fixed issue where visited session should be saved even when a token already exists by zwj +*(MAXKEY-250212) Fixed issue where string type was incorrectly processed as a string by mappingJacksonHttpMessageConverter due to converter processing order by Ning Pengtao +*(MAXKEY-250213) Enhanced complexity of image calculation CAPTCHA +*(MAXKEY-250214) Added source code link address in the management end +*(MAXKEY-250215) Dependency reference, update, and upgrade: + springDataVersion 3.4.2 + springkafkaVersion 3.3.2 + freemarkerVersion 2.3.34 + tomcatVersion 10.1.34 + slf4jVersion 2.0.16 + guavaVersion 33.4.0-jre + zxingcoreVersion 3.5.3 + gsonVersion 2.12.1 + jakartaannotationVersion 3.0.0 + jakartaactivationVersion 2.1.3 + jakartamailapiVersion 2.1.3 + jakartavalidationapiVersion 3.1.1 + jakartaxmlbindapiVersion 4.0.2 + mybatis-jpa-extra 3.3.4 +``` + +## About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, and scheduling orchestration. The community is committed to fully open-source collaboration, maintaining community neutrality, and providing microservices cloud-native solutions for global users. It aims to allow every open-source enthusiast involved to experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP projects, with a total of over 100,000 stars. It has built a community of tens of thousands of members, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to interact with me on the Knowledge Planet** + +![](/assets/img/news/MaxKey-4.1.6-3.webp) \ No newline at end of file diff --git a/src/news/MaxKey-4.1.7.md b/src/news/MaxKey-4.1.7.md new file mode 100644 index 0000000000..9c6284db5c --- /dev/null +++ b/src/news/MaxKey-4.1.7.md @@ -0,0 +1,114 @@ +--- +title: MaxKey Single Sign-On Authentication System 4.1.7, Critical Bug Fixes +author: MaxKey +date: 2025-04-01 +cover: /assets/img/news/MaxKey-4.1.7-0.webp +head: + - - meta + - name: News +--- + +**![](/assets/img/news/MaxKey-4.1.7-0.webp)** + +**The Industry-Leading IAM-IDaas Identity Management and Authentication Product** + +# Overview + +Dromara **MaxKey** Single Sign-On Authentication System is an **industry-leading IAM-IDaas identity management and authentication product**. Its name is a homophone for "Marx's Key," symbolizing its ability to act like a master key (the greatest key) that unlocks complex enterprise security needs with simple and efficient solutions. The product supports standard protocols such as OAuth 2.x/OpenID Connect, SAML 2.0, JWT, CAS, and SCIM, providing **secure, standard, and open** user identity management (IDM), authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. + +MaxKey focuses on performance, security, and ease of use in enterprise scenarios and is widely used in industries such as healthcare, finance, government, and manufacturing. + +MaxKey **follows the Apache License, Version 2.0, and is open source and free**. It is open source, secure, compliant, and independently controllable. + +Official Website: **https://www.maxkey.top/** + +Official QQ: **1054466084** + +Email: support@maxsso.net + +Code Hosting: + +Gitee: https://gitee.com/dromara/MaxKey + +GitHub: https://github.com/dromara/MaxKey + +# Product Features + +1. Standard Protocols + +| No. | Protocol | Support Level | +| :--: | :----------------------: | :-----------: | +| 1.1 | OAuth 2.0/OpenID Connect | High | +| 1.2 | SAML 2.0 | High | +| ... | ... | ... | + +2. Login Support + +| No. | Login Methods | +| :--: | :----------------------------------------------------------- | +| 2.1 | Dynamic Verification Code (Letters/Numbers/Arithmetic) | +| 2.2 | Two-Factor Authentication (2FA) | +| 2.3 | SMS Authentication (Tencent Cloud SMS/Aliyun SMS/NetEase YunXin) | +| 2.4 | LoginEasy/Google/Microsoft Authenticator/FreeOTP (Supports TOTP or HOTP) | +| 2.5 | Kerberos/SPNEGO/AD Domain | +| 2.6 | OpenLDAP/Active Directory/Standard LDAP Server | +| 2.7 | Social Account Login (WeChat/QQ/Weibo/DingTalk/Google/Facebook/Others) | +| 2.8 | QR Code Login (Enterprise WeChat/DingTalk/Feishu QR Code Login) | + +3. Provides standard authentication interfaces for integrating other applications with SSO, secure mobile access, secure APIs, third-party authentication, and internet authentication integration. + +4. Offers user lifecycle management and supports the SCIM 2 protocol; includes out-of-the-box connectors for identity provisioning and synchronization. + +5. Simplifies Microsoft Active Directory domain control, standard LDAP server architecture, and account management, with self-service password reset. + +6. IDaas multi-tenancy functionality supports independent management of multiple enterprises within a group or data isolation between different departments within an enterprise, reducing operational costs. + +7. The authentication center is platform-agnostic and supports diverse environments, including web, mobile, and mobile devices (e.g., Apple iOS, Android), extending authentication capabilities from B/S to mobile applications. + +8. Configurable password policies and access policies; supports Ip2region or GeoLite2 geographic database for precise IP positioning; robust security auditing with full user lifecycle auditing, access behavior traceability, security compliance auditing, and security risk alerts. + +9. Based on the Java EE platform with a microservices architecture, using open-source technologies such as Spring, MySQL, Tomcat, Redis, and MQ, ensuring high scalability. + +10. Open source, secure, compliant, and independently controllable, licensed under Apache 2.0 License: https://www.maxkey.top/zh/about/licenses.html. + +# Interface + +![](/assets/img/news/MaxKey-4.1.7-1.png) + +# Download + +Current Version Network Disk Download + +| Version | Date | Download Link | +| ------- | ---------- | ------------- | +| v 4.1.7 | 2025/04/01 | Download | + +# Release Notes + +MaxKey v 4.1.7 GA 2025/04/01 + +``` +*(MAXKEY-250301) #IBQEYU Error when using AccessToken for authentication: session is null +*(MAXKEY-250302) Moved org.dromara.maxkey.web.component to org.dromara.maxkey.entity +*(MAXKEY-250303) Removed query submitLoading +*(MAXKEY-250304) Fixed issue where images might not display after compilation +*(MAXKEY-250305) Optimized sessionStatus +*(MAXKEY-250306) Optimized menu names +*(MAXKEY-250307) Added user password generation function +*(MAXKEY-250308) Changed front-end storage INST value in management end to inst_mgt +*(MAXKEY-250309) Code optimization +*(MAXKEY-250310) Dependency reference, update, and upgrade: + springVersion 6.2.5 + springBootVersion 3.4.4 + springSecurityVersion 6.4.4 + springDataVersion 3.4.4 + springkafkaVersion 3.3.4 + tomcatVersion 10.1.39 + slf4jVersion 2.0.17 + jacksonVersion 2.18.3 + druidVersion 1.2.24 + druidspringbootstarterVersion 1.2.24 + mybatisVersion 3.5.19 + mybatisspringVersion 3.0.4 + mybatis-jpa-extra 3.3.5 +``` \ No newline at end of file diff --git a/src/news/MaxKey-4.1.8.md b/src/news/MaxKey-4.1.8.md new file mode 100644 index 0000000000..c9c557f0f1 --- /dev/null +++ b/src/news/MaxKey-4.1.8.md @@ -0,0 +1,120 @@ +--- +title: "MaxKey Single Sign-On Authentication System 4.1.8: Secondary Authentication is Here" +author: maxkey +date: 2025-08-02 +cover: /assets/img/news/MaxKey-4.1.8-0.webp +head: + - - meta + - name: News +--- + +![](/assets/img/news/MaxKey-4.1.8-0.webp) + +**The Leading IAM-IDaas Identity Management and Authentication Product** + +# Overview + +Dromara **MaxKey** Single Sign-On Authentication System is **the leading IAM-IDaas identity management and authentication product**. Its name is a homophone for "Marx's Key," symbolizing its ability to act like a master key (the greatest key) that unlocks complex enterprise security needs, providing a simple yet efficient solution. The product supports standard protocols such as OAuth 2.x/OpenID Connect, SAML 2.0, JWT, CAS, and SCIM, offering **secure, standard, and open** user identity management (IDM), identity authentication (AM), single sign-on (SSO), RBAC permission management, and resource management. + +MaxKey emphasizes performance, security, and ease of use in enterprise scenarios and is widely used in industries such as healthcare, finance, government, and manufacturing. + +MaxKey **follows the Apache-2.0 open-source license**, making it open-source, secure, compliant, and independently controllable. + +Official Website: **https://www.maxkey.top/** + +Official QQ: **1054466084** + +Email: **support@maxsso.net** + +Code Hosting: + +Gitee: https://gitee.com/dromara/MaxKey + +GitHub: https://github.com/dromara/MaxKey + +# Product Features + +1. Standard Protocols + +| No. | Protocol | Support Level | +| ---- | ------------------------ | ------------- | +| 1.1 | OAuth 2.0/OpenID Connect | High | +| 1.2 | SAML 2.0 | High | +| 1.3 | JWT | High | +| 1.4 | CAS | High | +| 1.5 | SCIM 2.0 | High | +| 1.6 | FormBased | Medium | +| 1.7 | TokenBased (Post/Cookie) | Medium | +| 1.8 | ExtendApi | Low | +| 1.9 | EXT | Low | + +2. Login Support + +| No. | Login Method | +| ---- | ------------------------------------------------------------ | +| 2.1 | Dynamic CAPTCHA: Letters/Numbers/Arithmetic | +| 2.2 | Two-Factor Authentication | +| 2.3 | SMS Authentication: Tencent Cloud SMS/Aliyun SMS/Netease YunXin | +| 2.4 | LoginEasy/Google/Microsoft Authenticator/FreeOTP (Supports TOTP or HOTP) | +| 2.5 | Kerberos/SPNEGO/AD Domain | +| 2.6 | OpenLDAP/ActiveDirectory/Standard LDAP Server | +| 2.7 | Social Accounts: WeChat/QQ/Weibo/DingTalk/Google/Facebook/Others | +| 2.8 | Scan Code Login: Enterprise WeChat/DingTalk/Feishu Scan Code Login | + +3. Provides standard authentication interfaces for integrating SSO with other applications, secure mobile access, secure APIs, and integration with third-party and internet authentication. + +4. Offers user lifecycle management and supports the SCIM 2 protocol. Comes with out-of-the-box connectors for identity provisioning and synchronization. + +5. Simplifies Microsoft Active Directory domain control, standard LDAP server structures, and account management, with self-service password reset. + +6. IDaas multi-tenancy functionality supports independent management of multiple enterprises under a group or data isolation between different departments within an enterprise, reducing operational costs. + +7. The authentication center is platform-independent and supports diverse environments, including web, mobile, and mobile devices (e.g., Apple iOS, Android), extending authentication capabilities from B/S to mobile applications. + +8. Configurable password policies and access policies; supports precise IP location using Ip2region or GeoLite2 geographic databases. Provides robust security auditing, including full user lifecycle auditing, access behavior traceability auditing, security compliance auditing, and security risk warnings. + +9. Based on the Java EE platform with a microservices architecture, using open-source technologies such as Spring, MySQL, Tomcat, Redis, and MQ, ensuring high scalability. + +10. Open-source, secure, compliant, and independently controllable, licensed under Apache 2.0 License: https://www.maxkey.top/zh/about/licenses.html. + +# Interface + +![](/assets/img/news/MaxKey-4.1.8-1.png) + +# Download + +Current Version Network Disk Download + +| Version | Date | Download Link | +| ------- | ---------- | ------------- | +| v4.1.8 | 2025/08/01 | Download | + +# Version Notes + +MaxKey v4.1.8 GA 2025/08/01 + +``` +*(MAXKEY-250401) Secondary Authentication +*(MAXKEY-250402) MaxKeyOpenApiApplication renamed to MaxKeyApiApplication +*(MAXKEY-250403) Cache optimization +*(MAXKEY-250404) maxkey-core split into maxkey-entity and maxkey-cache +*(MAXKEY-250405) maxkey-common split into maxkey-crypto, maxkey-ldap, and maxkey-commons +*(MAXKEY-250406) Fixed cache CAPTCHA expiration exception issue +*(MAXKEY-250407) Configuration for secondary authentication +*(MAXKEY-250408) /functionList to retrieve application function permission list +*(MAXKEY-250409) #IBY0OL Long periods of inactivity causing CAPTCHA login failure +*(MAXKEY-250410) User and organization sorting optimization +*(MAXKEY-250411) Password modification entry optimization +*(MAXKEY-250412) Groups.service.ts renamed to groups.service.ts +*(MAXKEY-250413) Code integration and optimization +*(MAXKEY-250414) Explanation and details of Open Source Summer tasks +*(MAXKEY-250415) angular.json optimization +*(MAXKEY-250416) Dependency references, updates, and upgrades + springVersion 6.2.9 + springBootVersion 3.4.8 + springSecurityVersion 6.5.2 + springDataVersion 3.5.2 + springkafkaVersion 3.3.8 + tomcatVersion 10.1.43 + jacksonVersion 2.18.4 +``` \ No newline at end of file diff --git a/src/news/Mayfly-Go-1.10.0.md b/src/news/Mayfly-Go-1.10.0.md new file mode 100644 index 0000000000..3c9c0edb2d --- /dev/null +++ b/src/news/Mayfly-Go-1.10.0.md @@ -0,0 +1,83 @@ +--- +title: "Mayfly-Go 1.10.0 New Version Released: Unified IT Management and Control Platform" +author: mayfly go +date: 2025-05-30 +cover: /assets/img/news/Mayfly-Go-1.10.0-0.jpg +head: + - - meta + - name: News +--- + +## **Introduction** + +A web-based unified management and operation platform that offers comprehensive support for Linux system operations—including terminal management (with session playback and command filtering), file management, script execution, process monitoring, and scheduled task setup. It also provides data manipulation, synchronization, and migration for various databases (such as MySQL, PostgreSQL, Oracle, SQL Server, Dameng, Gauss, SQLite, etc.). Additionally, it supports operation and management for Redis (standalone, sentinel, and cluster modes), MongoDB, and Elasticsearch. Combined with a ticket-based approval workflow, it offers an all-in-one operation, maintenance, and management solution for enterprises. + +![](/assets/img/news/Mayfly-Go-1.10.0-0.jpg) + +## **Features** + +* **Linux**: SSH terminal (with operation recording and playback), file viewing (with syntax highlighting for common file extensions and keyword highlighting), editing, uploading, downloading, deletion, script management and execution, scheduled tasks, process management, and system status monitoring (can be used as a bastion host). + +* **DBMS (Currently supports MySQL, PostgreSQL, Oracle, SQL Server, SQLite, Gauss, Dameng, Kingbase, Vastbase)**: Visual data addition, deletion, modification, and query; SQL statement hints; viewing table information, index details, and CREATE TABLE statements; table creation, etc. (similar to a mini Navicat). + +* **DBMS - Data Synchronization**: Supports data synchronization between heterogeneous databases. + +* **DBMS - Database Migration**: Supports migration between heterogeneous databases (e.g., migrating to different database file formats). + +* **Redis (Standalone, Sentinel, Cluster)**: Add, delete, modify, and query Redis data; view basic Redis information such as version, memory, and CPU usage; view cluster node information. + +* **MongoDB**: Add, delete, modify, and query MongoDB document data; view database and collection status; create and delete collections. + +* **Elasticsearch**: Add, delete, modify, and query Elasticsearch data; check status; perform index operations. + +* **SSH Tunnel Support**: All operations for Linux machines, databases, Redis, MongoDB, and Elasticsearch support SSH tunnel access. + +* **Ticket-Based Approval Workflow**: Dangerous write operations (e.g., in DBMS, Redis) require approval via a ticket workflow. + +* **System Management**: Includes a comprehensive account, role, and resource permission control system, along with system configuration (OAuth2, LDAP login, login captcha, two-factor authentication, watermarking, etc.). It can also be used as a backend management system for secondary development. + + +## **Project Information** + +Documentation: https://www.yuque.com/may-fly/mayfly-go + +Gitee: https://gitee.com/dromara/mayfly-go + +GitHub: https://github.com/dromara/mayfly-go + +## **Development Language & Main Frameworks** + +* Frontend: TypeScript, Vue3, Element Plus + +* Backend: Golang, Gin, Gorm + + +## **Highlights** + +* Most common functionalities are encapsulated in both frontend and backend, making it concise and easy to use. The logical structure is clear, enabling quick learning and development, as well as secondary development or use as a backend management system. + +* Developed in Go, the project runs efficient applications with smaller memory and resource footprints. It is deployed as a binary for convenience and speed. + + +## **Updates** + +* 🌟 Added support for Elasticsearch operations + +* 🌟 Optimized ticket workflow, supporting OR-sign/AND-sign and flowchart design + +* 🌟 Upgraded frontend and backend dependencies; refactored and optimized parts of the code + +* 🐞 Fixed several issues + + +## Elasticsearch Operations + +![](/assets/img/news/Mayfly-Go-1.10.0-1.png) + +![](/assets/img/news/Mayfly-Go-1.10.0-2.png) + +## Ticket Workflow Optimization + +![](/assets/img/news/Mayfly-Go-1.10.0-3.png) + +![](/assets/img/news/Mayfly-Go-1.10.0-4.png) \ No newline at end of file diff --git a/src/news/Mayfly-Go-1.9.0.md b/src/news/Mayfly-Go-1.9.0.md new file mode 100644 index 0000000000..01b8160313 --- /dev/null +++ b/src/news/Mayfly-Go-1.9.0.md @@ -0,0 +1,80 @@ +--- +title: Mayfly-Go 1.9.0 New Version Released, Unified IT Control Platform +author: Mayfly-Go +date: 2024-10-25 +cover: /assets/img/news/Mayfly-Go-1.9.0-0.png +head: + - - meta + - name: News +--- + +## **Introduction** + +mayfly-go is a unified management and operation platform that integrates web-based Linux, databases, Redis, MongoDB, and workflow approval processes. It aims to provide users with a unified operation and management experience, helping organizations achieve efficient resource utilization and risk control, improve system security and compliance, reduce risks, and enhance team collaboration and accountability. + +## **Feature Overview** + +* **Linux:** SSH terminal (with terminal operation recording and playback), file viewing (with keyword highlighting based on common file extensions), modification, upload, download, deletion, script management and execution, scheduled tasks, process operations, system status monitoring, etc. (Can be used as a bastion host). +* **DBMS (Currently supports MySQL, PostgreSQL, Oracle, SQL Server, SQLite, GaussDB, Dameng, Kingbase, Vastbase):** Visual data addition, deletion, modification, and query; SQL statement hints; viewing table information, index information, and DDL statements; table creation, etc. (Similar to a mini version of Navicat). +* **DBMS - Data Synchronization:** Supports data synchronization between heterogeneous databases. +* **DBMS - Database Migration:** Supports migration between heterogeneous databases (e.g., migrating to heterogeneous database files). +* **Redis (Standalone, Sentinel, Cluster):** Add, delete, modify, and query Redis data; view basic Redis information such as version, memory, CPU usage, and cluster node information. +* **MongoDB:** Add, delete, modify, and query MongoDB document data; view database and collection status; create and delete collections, etc. +* **SSH Tunnel Access Support:** Linux machines, databases, Redis, and MongoDB all support operation via SSH tunnels. +* **Workflow Approval Support:** Dangerous write operations for DBMS, Redis, etc., support execution through workflow approval processes. +* **System Management:** Comprehensive account, role, resource permission control, and system configuration (OAuth2, LDAP login, login captcha, two-factor authentication, watermarking, etc.). It can also be二次开发 (custom developed) based on this project as a backend management system. + +## **Project Information** + +Project Documentation: https://www.yuque.com/may-fly/mayfly-go + +Gitee: https://gitee.com/objs/mayfly-go + +GitHub: https://github.com/may-fly/mayfly-go + +## **Development Language & Main Frameworks** + +* Frontend: TypeScript, Vue3, Element-Plus +* Backend: Golang, Gin, Gorm + +## **Characteristics** + +* Most common functions are encapsulated for both frontend and backend, making usage more concise. The functional logic is clear, enabling quick learning, development, and secondary development or use as a backend management system. +* The project is developed using Go language, running more efficient applications with smaller memory and resource footprint. Binary file deployment is convenient and fast. + +## **Upgrades** + +* 🌟 Removed the 'code' (number) field from DBMS, Machine, Redis, and MongoDB edit forms. +* 🌟 Added a unified base file path configuration; removed file path configuration items related to machines and other settings. +* 🌟 Process definitions now support triggering approval operations based on specified conditions. +* 🌟 DBMS and Redis work order applications have been统一转移至 (consolidated and moved to) the "Workflow Process - My Processes" section for initiation. +* 🌟 DBMS - Database migration now supports scheduled migration to heterogeneous database files (can be used for backup and restore). +* 🌟 DBMS - The query box now supports executing multiple SQL statements at once (multiple SQL statements correspond to multiple result set tabs). +* 🌟 DBMS - SQL parser replaced with ANTLR4 for self-parsing. +* 🐞 Changed SQL encryption method to AES to avoid potential issues with base64 interception or other problems. + +### Added Unified Base File Path Configuration + +![](/assets/img/news/Mayfly-Go-1.9.0-0.png) + +### Process Definitions Support Triggering Approval Based on Specified Conditions + +![](/assets/img/news/Mayfly-Go-1.9.0-1.png) + +### DBMS and Redis Work Orders Consolidated into "Workflow Process - My Processes" + +![](/assets/img/news/Mayfly-Go-1.9.0-2.png) + +### DBMS - Database Migration Supports Scheduled Migration to Heterogeneous Database Files (Can be used for Backup and Restore) + +![](/assets/img/news/Mayfly-Go-1.9.0-3.png) + +![](/assets/img/news/Mayfly-Go-1.9.0-4.png) + +### DBMS - Query Box Supports Executing Multiple SQL Statements at Once (Multiple result set tabs) + +![](/assets/img/news/Mayfly-Go-1.9.0-5.png) + +GitHub: https://github.com/dromara/mayfly-go + +Gitee: https://gitee.com/dromara/mayfly-go \ No newline at end of file diff --git a/src/news/Mayfly-Go-1.9.4.md b/src/news/Mayfly-Go-1.9.4.md new file mode 100644 index 0000000000..c4da46e79c --- /dev/null +++ b/src/news/Mayfly-Go-1.9.4.md @@ -0,0 +1,60 @@ +--- +title: "Mayfly-Go 1.9.4 New Version Released: Unified IT Management and Control Platform" +author: mayfly-go +date: 2025-04-17 +cover: /assets/img/news/Mayfly-Go-1.9.4-0.png +head: + - - meta + - name: News +--- + +## **Introduction** + +A web-based unified management and operation platform that offers comprehensive support for Linux system operations—including terminal management (with session playback and command filtering), file management, script execution, process monitoring, and scheduled task configuration. It also provides data operations, data synchronization, and data migration for various databases (such as MySQL, PostgreSQL, Oracle, SQL Server, Dameng, Gauss, SQLite, etc.). Additionally, it supports operation and management for Redis (standalone, sentinel, and cluster modes) and MongoDB. Combined with a workflow approval process, it offers an all-in-one operation, maintenance, and management solution for enterprises. + +![](/assets/img/news/Mayfly-Go-1.9.4-0.png) + +## **Features** + +- **Linux**: SSH terminal (with session playback), file viewing (with syntax highlighting for common file extensions and keywords), editing, uploading, downloading, deletion, script management and execution, scheduled tasks, process operations, and system status monitoring (can be used as a bastion host). + +- **DBMS (Currently supports MySQL, PostgreSQL, Oracle, SQL Server, SQLite, Gauss, Dameng, Kingbase, Vastbase)**: Visual data addition, deletion, modification, and query; SQL statement hints; viewing table information, index details, and CREATE TABLE statements; table creation (similar to a mini version of Navicat). + +- **DBMS - Data Synchronization**: Supports data synchronization between heterogeneous databases. + +- **DBMS - Database Migration**: Supports migration between heterogeneous databases (e.g., migrating to heterogeneous database files). + +- **Redis (Standalone, Sentinel, Cluster)**: Add, delete, modify, and query Redis data; view basic Redis information such as version, memory, CPU usage, and cluster node details. + +- **MongoDB**: Add, delete, modify, and query MongoDB document data; view database and collection status; create and delete collections. + +- **SSH Tunnel Access Support**: Supports SSH tunnel access for Linux machines, databases, Redis, and MongoDB operations. + +- **Workflow Approval Process**: Supports workflow approval for high-risk write operations such as those in DBMS and Redis. + +- **System Management**: Includes a comprehensive account, role, and resource permission control system, as well as system configuration (OAuth2, LDAP login, login captcha, two-factor authentication, watermarking, etc.). It can also be used as a backend management system for secondary development. + +## **Project Information** + +Documentation: https://www.yuque.com/may-fly/mayfly-go +Gitee: https://gitee.com/dromara/mayfly-go +GitHub: https://github.com/dromara/mayfly-go + +## **Development Language & Main Frameworks** + +- Frontend: TypeScript, Vue3, Element Plus +- Backend: Golang, Gin, Gorm + +## **Key Highlights** + +- Most common functionalities are encapsulated in both frontend and backend, making it easier to use, with clear logic for quick learning and development. Suitable for secondary development or as a backend management system. +- The project is developed in Go, enabling more efficient applications with lower memory and resource usage. It can be deployed as a binary file for convenience and speed. + +## **Updates** + +- 🌟 Added message channel management. Supports notifications via email, DingTalk bot, Enterprise WeChat bot, and Feishu bot. +- 🌟 Added message template management. Supports setting templates in TXT, Markdown, and HTML formats. +- 🌟 Process definitions now support associating message templates for work order approval notifications. +- 🌟 Upgraded frontend and backend dependencies; optimized partial code. +- 🐞 Fixed issue with database migration where longblob was incorrectly converted to longblog with length. +- 🐞 Fixed precision loss issue when displaying PostgreSQL decimal types on the frontend. \ No newline at end of file diff --git a/src/news/MayflyGo-1.10.2.md b/src/news/MayflyGo-1.10.2.md new file mode 100644 index 0000000000..b902d3c48e --- /dev/null +++ b/src/news/MayflyGo-1.10.2.md @@ -0,0 +1,72 @@ +--- +title: "mayfly-go 1.10.2 New Version Released: Unified IT Management and Control Platform" +author: mayfly-go +date: 2025-09-02 +cover: /assets/img/news/MayflyGo-1.10.2-0.png +head: + - - meta + - name: News +--- + +## **Introduction** + +A web-based unified operation and maintenance management platform that fully supports Linux system operations (terminal management, file management, script execution, process monitoring, etc.), container operations, and data operations and migration for various databases (MySQL, PostgreSQL, Oracle, SQL Server, Dameng, Gauss, SQLite). It also provides full lifecycle management for Redis (standalone/sentinel/cluster), MongoDB, and ES, and integrates a ticket approval process to create an all-in-one IT operation and maintenance solution for enterprises. + +## **Feature Overview** + +* **Linux**: SSH terminal (with terminal operation recording and playback), file viewing (with keyword highlighting for common file extensions), modification, upload, download, deletion, script management and execution, scheduled tasks, process operations, runtime status viewing, etc. (can be used as a bastion host). +* **Container Operations**: Supports container/image management. +* **DBMS (Currently supports MySQL, PostgreSQL, Oracle, SQL Server, SQLite, Gauss, Dameng, Kingbase, Vastbase)**: Visual data addition, deletion, modification, and query; SQL statement hints; viewing table information, index information, and DDL statements; table creation, etc. (similar to a mini Navicat). +* **DBMS - Data Synchronization**: Supports data synchronization between heterogeneous databases. +* **DBMS - Database Migration**: Supports migration between heterogeneous databases (e.g., migrating to heterogeneous database files). +* **Redis (Standalone, Sentinel, Cluster)**: Add, delete, modify, and query Redis data; view basic Redis information such as version, memory, CPU usage, and cluster node information. +* **MongoDB**: Add, delete, modify, and query MongoDB document data; view database and collection status; create and delete collections, etc. +* **Elasticsearch (ES)**: Add, delete, modify, and query Elasticsearch data; view status; perform index operations, etc. +* **SSH Tunnel Access Support**: Supports SSH tunnel access for Linux machines, databases, Redis, MongoDB, and Elasticsearch. +* **Ticket Approval Process Support**: Dangerous write operations for DBMS, Redis, etc., require ticket approval for execution. +* **System Management**: Includes comprehensive account, role, resource permission control, and system configuration (OAuth2, LDAP login, login CAPTCHA, two-factor authentication, watermarking, etc.). It can also be used as a backend management system for secondary development. + +## **Project Information** + +Project Documentation: https://www.yuque.com/may-fly/mayfly-go + +Gitee: https://gitee.com/dromara/mayfly-go + +GitHub: https://github.com/dromara/mayfly-go + +## **Development Language & Main Frameworks** + +* Frontend: TypeScript, Vue3, Element Plus +* Backend: Golang, Gin, Gorm + +## **Features** + +* Modular design with well-encapsulated common frontend and backend functionalities. The code structure is clear and easy to understand, enabling quick onboarding for learning or secondary development, and easy construction of custom backend management systems. +* Developed in Go, it features low memory usage and high operational efficiency. One-click deployment via binary files greatly simplifies the deployment process. + +## **Updates** + +* 🌟 Unified resource operation entry point +* 🌟 Preliminary support for container operations +* 🌟 Optimization and refactoring of the message notification module +* 🐞 Optimized and fixed several other issues + +## Unified Resource Operation Entry Point + +![](/assets/img/news/MayflyGo-1.10.2-0.png) + +![](/assets/img/news/MayflyGo-1.10.2-1.png) + +(Image description) + +## Preliminary Support for Container Operations + +![](/assets/img/news/MayflyGo-1.10.2-2.png) + +![](/assets/img/news/MayflyGo-1.10.2-3.png) + +![](/assets/img/news/MayflyGo-1.10.2-4.png) + +## Optimization and Refactoring of the Message Notification Module + +![](/assets/img/news/MayflyGo-1.10.2-5.png) \ No newline at end of file diff --git a/src/news/Mica-MQTT-0.md b/src/news/Mica-MQTT-0.md new file mode 100644 index 0000000000..2545536312 --- /dev/null +++ b/src/news/Mica-MQTT-0.md @@ -0,0 +1,157 @@ +--- +title: Mica-MQTT Officially Joins the Dromara Open Source Community +author: mica +date: 2024-11-26 +cover: /assets/img/news/Mica-MQTT-0-0.png +head: + - - meta + - name: News +--- + +# Mica-MQTT Officially Joins the Dromara Community! + +## 1. A New Beginning + +Thanks to the invitation from `Das`, the author of `MaxKey` in the Dromara community, mica-mqtt has officially joined the Dromara community. One can go fast alone, but a group can go far together. We believe that under the guidance of the Dromara community, mica-mqtt will continue to grow and improve. + +Mica-MQTT is an open-source, simple, easy-to-use, low-latency, high-performance Java MQTT client component and Java MQTT broker service built on Java AIO. It was born out of the author's need to deeply understand MQTT upon transitioning into the IoT field, leading to the creation of this project. Through the open-sourcing of mica-mqtt, the author has gained a lot, though not without encountering challenges. It wasn't until version 2.2.x that the project truly stabilized (with the complete resolution of encoding and decoding issues). Thanks to the active users, feedback, and contributors—it is because of you that mica-mqtt has become more stable and user-friendly! Special thanks to `@冷月宫主`, `@willianfu`, `@Symous`, `@hjkJOJO`, `@hongfeng11`, `@胡萝博`, `@一片小雨滴`, `@杨钊`, `@iTong`, `@hpz`, `@tan90`, `@DoubleH`, `@那一刹的容颜`, `@皮球`, `@powerxie`, `@yinyuncan`, `@zkname`, `@彭蕾`, `@litongjava`, `@YYGuo`, `@xiaochonzi`, `@HY`, `@tangjj`, `@peigen`, and many others! Thank you all!!! + +Mica-MQTT **2.4.0-M1** has been officially released. This version migrates the Maven groupId to `org.dromara.mica-mqtt`, switches the package name to `org.dromara`, and transitions to Central Sonatype (which does not support snapshot versions). Everything else remains consistent with the previous version. + +![](/assets/img/news/Mica-MQTT-0-0.png) + +## 2. Features + +* Supports MQTT v3.1, v3.1.1, and v5.0 protocols. +* Supports WebSocket MQTT sub-protocol (compatible with mqtt.js). +* Supports HTTP REST API. `HTTP API documentation:` https://gitee.com/dromara/mica-mqtt/blob/master/docs/http-api.md +* Supports MQTT client. +* Supports MQTT server (broker). +* Supports MQTT Last Will and Testament (LWT) messages. +* Supports MQTT retained messages. +* Supports custom message (MQ) processing and forwarding for cluster implementation. +* Includes demo for connecting MQTT client to Alibaba Cloud MQTT. +* Supports compilation into native executables via GraalVM. +* Supports rapid integration with Spring Boot, Solon, and JFinal projects. +* mica-mqtt-spring-boot-starter supports integration with Prometheus + Grafana. +* Implements clustering based on Redis Stream. For details, see the `mica-mqtt-broker module:` https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-broker. + +## 3. Use Cases + +* Internet of Things (cloud-based MQTT broker) +* Internet of Things (edge device message communication) +* Group-based Instant Messaging (IM) +* Message push +* Simple and easy-to-use MQTT client + +## 4. Quick Start + +### Spring Boot Project + +**Client:** + +```xml + + org.dromara.mica-mqtt + mica-mqtt-client-spring-boot-starter + ${mica-mqtt.version} + +``` + +**mica-mqtt-client-spring-boot-starter Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-spring-boot-starter/README.md + +**Server:** + +```xml + + org.dromara.mica-mqtt + mica-mqtt-server-spring-boot-starter + ${mica-mqtt.version} + +``` + +**mica-mqtt-server-spring-boot-starter Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-spring-boot-starter/README.md + +### Solon Project + +**Client:** + +```xml + + org.dromara.mica-mqtt + mica-mqtt-client-solon-plugin + ${mica-mqtt.version} + +``` + +**mica-mqtt-client-solon-plugin Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-solon-plugin/README.md + +**Server:** + +```xml + + org.dromara.mica-mqtt + mica-mqtt-server-solon-plugin + ${mica-mqtt.version} + +``` + +**mica-mqtt-server-solon-plugin Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-solon-plugin/README.md + +### JFinal Project + +**Client:** + +```xml + + org.dromara.mica-mqtt + mica-mqtt-client-jfinal-plugin + ${mica-mqtt.version} + +``` + +**mica-mqtt-client-jfinal-plugin Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-jfinal-plugin/README.md + +**Server:** + +```xml + + org.dromara.mica-mqtt + mica-mqtt-server-jfinal-plugin + ${mica-mqtt.version} + +``` + +**mica-mqtt-server-jfinal-plugin Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-jfinal-plugin/README.md + +### Other Projects + +**Client:** + +```xml + + org.dromara.mica-mqtt + mica-mqtt-client + ${mica-mqtt.version} + +``` + +**mica-mqtt-client Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-client/README.md + +**Server:** + +```xml + + org.dromara.mica-mqtt + mica-mqtt-server + ${mica-mqtt.version} + +``` + +**mica-mqtt-server Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-server/README.md + +## Open Source Repositories + +* Gitee: https://gitee.com/dromara/mica-mqtt +* GitHub: https://github.com/dromara/mica-mqtt +* GitCode: https://gitcode.com/dromara/mica-mqtt \ No newline at end of file diff --git a/src/news/MilvusPlus-v2.2.1.md b/src/news/MilvusPlus-v2.2.1.md new file mode 100644 index 0000000000..a436a662f6 --- /dev/null +++ b/src/news/MilvusPlus-v2.2.1.md @@ -0,0 +1,40 @@ +--- +title: MilvusPlus Vector Database Enhanced Operations Library v2.2.1, Major Updates in Text Search +author: Coder Construction +date: 2024-12-02 +cover: /assets/img/news/MilvusPlus-v2.2.1-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/MilvusPlus-v2.2.1-0.png) + +![](/assets/img/news/MilvusPlus-v2.2.1-1.gif) + +🔥🔥🔥MilvusPlus (abbreviated as MP) is an operational tool for Milvus, designed to simplify interactions with the Milvus vector database. + +## Version + +MilvusPlus v2.2.1 GA 2024/11/29 + +New Features: (Note: Database support requires version 2.5.x) + +1. Sparse-BM25 Text Search +2. Tantivy Text Matching + +![](/assets/img/news/MilvusPlus-v2.2.1-2.png) + +![](/assets/img/news/MilvusPlus-v2.2.1-3.png) + +--- + +### About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, scheduling orchestration, and more. The community is committed to full open-source collaboration and maintains neutrality, striving to provide microservices cloud-native solutions for global users. Every open-source enthusiast involved can experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built a community of tens of thousands of members and is used by thousands of individuals and teams. + +**Welcome everyone to join the Knowledge Planet and interact with me!** + +![](/assets/img/news/MilvusPlus-v2.2.1-4.png) \ No newline at end of file diff --git a/src/news/NeutrinoProxy-2.0.2.md b/src/news/NeutrinoProxy-2.0.2.md new file mode 100644 index 0000000000..7d5cf8d9fb --- /dev/null +++ b/src/news/NeutrinoProxy-2.0.2.md @@ -0,0 +1,118 @@ +--- +title: "NeutrinoProxy 2.0.2 Released: Port Mapping Now Supports Binding Multiple Domain Names" +author: neutrino +date: 2024-12-10 +cover: /assets/img/news/NeutrinoProxy-2.0.2-0.png +head: + - - meta + - name: News +--- + +## Project Overview + +* Neutrino Proxy (neutrino-proxy) is a powerful intranet penetration tool based on Solon and Netty. The project adopts the very permissive MIT license, allowing you to copy, modify, distribute, and use it for any personal or commercial purpose. +* Common products on the market based on intranet penetration include: Peanut Shell, TeamViewer, cpolar, etc. +* Common use cases: + * Local development and debugging of third-party callbacks + * Remote API debugging across different locations during local development + * Remote login to internal Windows machines + * Mapping local services to the public internet for demonstrations + +* GitCode: https://gitcode.com/dromara/neutrino-proxy +* Gitee Repository: https://gitee.com/dromara/neutrino-proxy +* Github Repository: https://github.com/dromara/neutrino-proxy +* Official Website: https://neutrino-proxy.dromara.org + +* ![](/assets/img/news/NeutrinoProxy-2.0.2-0.png) + +## Key Features: + +* 1. **Traffic Monitoring**: Multi-dimensional traffic monitoring through homepage charts and report management. Gain comprehensive insights into real-time and historical proxy data. +* 2. **User/License**: Supports multiple users and clients. Background disabling takes effect in real-time. +* 3. **Port Pool**: Unified management of external ports, supporting exclusive ports for users and Licenses. +* 4. **Port Mapping**: Real-time生效 for adding, editing, deleting, and disabling mappings. +* 5. **Docker**: Supports one-click Docker deployment for both server and client. +* 6. **SSL Certificates**: Tunnel communication supports SSL encryption to protect your data security. +* 7. **Domain Mapping**: Supports binding multiple subdomains, facilitating local debugging of third-party callbacks. +* 8. **Multiple Protocols**: Supports proxying TCP, HTTP, HTTPS, and UDP protocols. +* 9. **Native Deployment**: Can be compiled into native executable files for lower deployment barriers and less memory usage. +* 10. **Security Groups**: Supports blacklist/whitelist IP access restrictions. +* 11. **Speed Limiting**: Supports limiting upload/download speeds for Licenses and port mappings. +* 12. **Uses the very permissive MIT license**, eliminating your concerns. + +## Updates in This Release + +* **Bug Fixes** + * Fixed issue where security groups incorrectly blocked access in IPv6 scenarios, causing domain mapping failures. + * Fixed batch deletion error in the port pool after native compilation. + * Fixed issue where environment variables specifying parameters did not take effect in client Docker deployments. + * Server: Fixed error in the log cleanup scheduled task under native deployment. + * Fixed known issues with background pagination queries. + * Extended the client IP field length in port mapping to resolve field length issues when configuring Alibaba Cloud database domains. + * Server: Fixed native deployment warning log "you should use: nativeMetadata.registerField(field) at aot runtime" when accessing the background user list. + +* **New Features** + * Added background domain management, supporting addition, modification, deletion, and disabling of primary domains, and adding multi-level domains. + * Domain management supports uploading SSL certificates for corresponding domains, enabling forced HTTPS, and automatically loading corresponding server certificates for domain mappings. + * Port mapping now supports switching between different primary domains, binding multiple subdomains, and a single port can bind multiple different primary domains. + +## Installation and Usage Instructions + +* Quick Start Guide: https://neutrino-proxy.dromara.org/neutrino-proxy/pages/793dcb/ +* Supported Deployment Methods: + * Windows + * Linux + * Mac + * 1. JAR + * 2. Docker + * 3. Native + * 4. Docker-compose + +* **Upgrade Notes:** + * **Configuration of domains and SSL certificates has been moved from config files to the background domain management page.** + * The method of declaring server domains and certificates via config files has been removed; please manage them via the page and perform necessary data migration. + * Database additions include `domain table` and `domain mapping intermediate table`. The `port mapping table` has been modified. This involves schema changes; please execute the incremental SQL. + +## Running Examples + +#### License Speed Limiting + +![](/assets/img/news/NeutrinoProxy-2.0.2-1.png) + +#### Port Mapping Speed Limiting + +![](/assets/img/news/NeutrinoProxy-2.0.2-2.png) + +#### Port Mapping Binding Multiple Domains + +![](/assets/img/news/NeutrinoProxy-2.0.2-3.png) + +#### Security Groups + +![](/assets/img/news/NeutrinoProxy-2.0.2-4.png) + +![](/assets/img/news/NeutrinoProxy-2.0.2-5.png) + +## Contact Us + +The author's time and ability are limited, and an open-source project is not built overnight—issues are inevitable. If you encounter any problems during use or learning, please feel free to contact me. + +If you have any ideas or suggestions for the project, you can add my WeChat to join the discussion group or create issues to help improve the project together. + +* WeChat ID: yuyunshize +* Email: aoshiguchen@dromara.org +* WeChat QR Code (Please note "Neutrino Group" when adding): + +![](/assets/img/news/NeutrinoProxy-2.0.2-6.jpg) + +--- + +### About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, and scheduling orchestration. The community is committed to full open-source collaboration and maintains neutrality, striving to provide microservices cloud-native solutions for global users. We aim to let every participating open-source enthusiast experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built a community of tens of thousands of members and is used by thousands of individuals and teams. + +**Welcome everyone to join the Knowledge Planet and interact with me!** + +![](/assets/img/news/NeutrinoProxy-2.0.2-7.png) \ No newline at end of file diff --git a/src/news/README.md b/src/news/README.md index 827f2a697e..0157ddf25f 100644 --- a/src/news/README.md +++ b/src/news/README.md @@ -1,33 +1,10 @@ ---- -title: News -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - - - - - - +--- +title: News +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +sidebar: false +layout: siteLayout +--- + diff --git a/src/news/RedisFront-0.md b/src/news/RedisFront-0.md new file mode 100644 index 0000000000..66035c4bc9 --- /dev/null +++ b/src/news/RedisFront-0.md @@ -0,0 +1,35 @@ +--- +title: RedisFront 2025.1 Officially Released +author: RedisFront +date: 2025-03-26 +cover: /assets/img/news/RedisFront-0-0.png +head: + - - meta + - name: News +--- + +> 【🚀 RedisFront 2025.1 Officially Released】After months of intensive refinement, we present a comprehensive upgrade! + +## Content + +RedisFront is a cross-platform Redis client tool developed based on Java Swing. It is compatible with various mainstream operating systems (including Windows, macOS, and Linux) and enables Redis data management and server status monitoring through a visual interface. It is suitable for development, debugging, and production operation and maintenance scenarios. + +*This update focuses on two core aspects:* + +**✨ Refreshed Design** + +* Redesigned 80% of the UI interface +* Added support for dark/light theme switching +* Enhanced data visualization with charts + +**🔧 Performance Leap** + +* New dynamic connection pool improves speed by 50% + +![](/assets/img/news/RedisFront-0-0.png) + +![](/assets/img/news/RedisFront-0-1.png) + +![](/assets/img/news/RedisFront-0-2.png) + +👉 Download now: https://gitee.com/dromara/RedisFront/releases/tag/2025.1 \ No newline at end of file diff --git a/src/news/RuoYi-Vue-Plus-5.3.0.md b/src/news/RuoYi-Vue-Plus-5.3.0.md new file mode 100644 index 0000000000..398a2d13c1 --- /dev/null +++ b/src/news/RuoYi-Vue-Plus-5.3.0.md @@ -0,0 +1,248 @@ +--- +title: RuoYi-Vue-Plus 5.3.0 New Spring Edition Released - Powerful Integration with warm-flow +author: January 25, 2025 09:20 +date: 2025-01-25 +cover: /assets/img/news/RuoYi-Vue-Plus-5.3.0-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/RuoYi-Vue-Plus-5.3.0-0.png) + +* * * + +# Changelog + +### Major Updates + +* Refactored data permission implementation logic to support annotation on any mapper method, eliminating the need to find the real mapper for annotation. +* Rewrote the workflow module, integrating warm-flow workflow and removing flowable workflow (too complex, many people find it difficult to understand). + +### Dependency Upgrades + +* update springboot 3.2.11 => 3.4.1 +* update springboot-admin 3.2.3 => 3.4.1 +* update mybatis-plus 3.5.8 => 3.5.9 +* update snailjob 1.1.2 => 1.3.0 (Thanks dhb52) +* update springdoc 2.6.0 => 2.8.3 +* update redisson 3.37.0 => 3.43.0 +* update justauth 1.16.6 => 1.16.7 Supports multiple login methods, not limited to third-party logins. +* update mybatis-plus 3.5.9 => 3.5.10 +* update hutool 5.8.31 => 5.8.35 +* update mapstruct-plus 1.4.5 => 1.4.6 +* update lombok 1.18.34 => 1.18.36 +* update anyline 20241022 => 20250101 + +### Feature Updates + +* update Optimized the OSS image URL query interface to use the 'query' identifier. +* update Optimized third-party binding and unbinding to verify token existence. +* update Optimized the method for obtaining temporary URLs for OSS private buckets (Thanks 秋辞未寒). +* update Optimized the ws module to close session connections when replacing sessions. +* update Optimized data permissions; skips if the current annotation does not meet the template. +* update Optimized the use of request storage for dynamic tenants to avoid multiple Redis queries per request. +* update Optimized department information modification by adding transactions (Thanks AprilWind). +* update Optimized by adding menu selection extension parameters (Thanks 玲娜贝er). +* update Optimized the scheduled task pool when virtual threads are enabled in a JDK21 environment (Thanks 秋辞未寒). +* update Optimized SSE; if the obtained token list is empty, delete the storage corresponding to the userid. +* update Optimized the data permission handler by adding default value handling for cases where expression variables do not correspond to annotations or are null. +* update Optimized errors when using tools after closing SSE. +* update Optimized by adding one-click enable/disable for mybatis-plus logical deletion. +* update Optimized log time display color (Thanks 疯狂的牛子Li). +* update Adapted TOPIAM 2.0 single sign-on (SSO) (Thanks 马铃薯头). +* update Optimized and improved the WeChat Mini Program login interface logic. +* update Optimized and refactored the DateUtils tool class for greater practicality. +* update Optimized by adding common query methods for departments, roles, positions, and users. +* update Optimized by adding position data to the logged-in user. +* update Optimized by removing department query status validation, shifting filtering to the frontend for easier viewing of data under disabled departments. +* update Optimized the department tree by adding a disabled flag. +* update Optimized the workflow module by adding interface document generation functionality. +* update Optimized code generation by adding a default sorting rule for buildQueryWrapper. +* update Optimized code generation to fix the issue of creation/update time being overwritten. +* update Optimized code generation sorting (Thanks AprilWind). +* update Optimized online user queries to prioritize data under the tenant, reducing data volume. +* update Optimized tenant domain name matching to be case-insensitive. +* update Optimized the code generator to convert database field names to lowercase by default, avoiding issues with some databases using uppercase. +* update Optimized by reducing SSE authentication failure logs to debug level due to the retry mechanism causing excessive output. +* update Optimized the destruction method for bounded queues; a special destruction method should be used. +* update Optimized Redis serialization to support the faster Apache binary cross-language serialization scheme. +* update Optimized the tenant log module name. +* update Optimized by adding a default data permission option: "Department and below or own data permission". +* update Optimized the code generator for precise primary key retrieval in PostgreSQL databases. +* update Optimized code generator type retrieval. +* update Optimized the personal center force logout device interface path. +* update Optimized Dockerfile to eliminate warn warnings. +* update Optimized by adding comments to client tool classes (Thanks AprilWind). +* update Optimized by adding comments to Undertow custom configuration information (Thanks AprilWind). +* update Optimized by intercepting crawler tracking and other junk requests. +* update Optimized by changing the Log exception record length to 5000. +* update Optimized by expanding the Log parameter record length to 5000 to better suit actual needs. +* update Optimized XSS wrapper Parameter handling to be compatible with containers that do not allow parameter changes. +* update Optimized to support passing multiple roles and permission identifiers for desensitization. +* update Optimized role deletion to clear cache. +* update Optimized code encapsulation using new methods in ObjectUtils. +* update Optimized data permission queries by adding caching. +* update Optimized code generator numeric type judgment. +* update Optimized the logical deletion status to 1 to avoid misunderstandings. +* update Refactored by changing UserConstants to SystemConstants for unified constant usage, reducing difficulty and misunderstandings. +* update Optimized by encapsulating department query methods based on parent ID. +* update Optimized to skip data permission checks if no user ID is passed. +* update Optimized the display of multiple base points in the department tree, supporting nodes with the same name to be displayed side by side. +* update Optimized by removing OSS bucket detection; errors will naturally occur if the bucket doesn't exist, making extra detection unnecessary. +* update Optimized the rate limiting annotation by adding a fixed cleanup time. +* update Optimized the sys_social table by adding a default value for tenant ID. +* update Optimized deprecated Jackson methods. +* update Optimized the multi-tenant plugin initialization process. +* update Optimized by removing the GenUtils setCreateBy logic, unifying it through automatic injection settings. +* update Optimized by replacing deprecated methods getKeysStreamByPattern and trySetRate in RedisUtils (Thanks Lucien_Lu). +* update Optimized by removing the automatic bucket creation code logic (cloud vendors restrict bucket operations). +* update Optimized the code logic for clearing online users by role. + +### New Features + +* add Added export template required field and remark annotation implementation (Thanks liyang). +* add Added a Redisson-based ID generator tool (Thanks 秋辞未寒). +* add Added validation support for enum checks (Thanks 秋辞未寒). +* add Added validation support for enum checks (Thanks 秋辞未寒) - (Note: Appears twice in original). +* add Added object utility class (Thanks 秋辞未寒). +* add Added demo for email with multiple attachments. + +### Bug Fixes + +* fix Fixed file download issue where setting content-length was ineffective. +* fix Fixed Satoken DAO layer getting timeout in seconds, causing loss of millisecond precision (temporary fix, waiting for official Satoken solution). +* fix Fixed PostgreSQL table metadata lacking creation time (very peculiar), used new Date() as a substitute. +* fix Fixed incorrect logic for multi-role, multi-annotation containing ignore permission identifiers in data permissions. +* fix Fixed bean not found error when SSE is not enabled. +* fix Fixed errors in the personal center's avatar modification and password change interfaces caused by data permissions (Thanks QianRj). +* fix Fixed department data permission caching errors (Thanks QianRj). +* fix Fixed missing parameter issues in third-party authorization tools for some websites. +* fix Fixed code generation issue where special characters in the middle of table names were filtered; changed to filter only at the beginning. +* fix Fixed field length exceeding database limit issue. +* fix Fixed regular expression error in filters. +* fix Fixed monitor context-path causing 404 error upon logout and re-login. +* fix Fixed issues caused by multi-role and permission identifier共用 in data permissions: https://gitee.com/dromara/RuoYi-Vue-Plus/issues/IB4CS4. +* fix Fixed by excluding Tomcat dependencies contained within the websocket package (causing issues). +* fix Fixed PageQuery JSON conversion error. +* fix Fixed SSE close interface disconnection issue. +* fix Fixed typo in PlusSmsDao#clean method. +* fix Fixed excel cascading dropdown data error (Thanks Emil.Zhang). +* fix Fixed method errors in modules lacking mp dependency. +* fix Fixed code generation paging errors caused by the new mp version using the latest SQL Server syntax by default. +* fix Fixed OssClient rollback error modification. +* fix Fixed registration log status recording error. + +### Frontend Changes + +* update typescript 5.4.5 => 5.7.2 +* update vite 5.2.12 => 5.4.11 +* update vue 3.4.34 => 3.5.13 +* update element-plus 2.7.8 => 2.8.8 +* update eslint upgraded to v9 version (Thanks 玲娜贝er). +* update vue-i18n 10.0.5 +* update Optimized parseTime hint error issue. +* update Optimized i18n variable hints. +* update Optimized by rewriting workflow-related pages. +* update Optimized theme color display brightness in dark mode (Thanks LiuHao). +* update Optimized hasRoles method by adding super admin check. +* update Optimized user page by adding import/export permission identifiers. +* update Optimized TopNav internal link menu click highlight. +* update Optimized new/edit user by filtering disabled departments. +* update Optimized whitelist by adding regex matching examples. +* update Optimized whitelist to support wildcard path matching. +* update Optimized i18n $t method to support TS type hints (Thanks 玲娜贝er). +* update Optimized login page multi-language button style. +* update Optimized by adding i18n content to login and registration pages and adding language switch buttons (Thanks QianRj). +* update Optimized eslint upgrade to v9 & updated code that did not comply with validation rules (Thanks 玲娜贝er). +* update Optimized full code standardization. +* update Optimized code generation import dropdown default value handling. +* update Optimized menu breadcrumb navigation to support multi-level display. +* update Optimized parameter key-value replacement with multi-line text. +* update Optimized by adding default data permission option: "Department and below or own data permission". +* update Optimized permission loadView to avoid looping through entire modules, allowing view folders within views (Thanks admin_lijinfu). +* update Optimized personal center force logout device interface path. +* update Optimized by reading i18n language pack information directly from @/lang/*.ts files (Thanks QianRj). +* update Optimized by migrating the sync dictionary function to tenant management. +* update Optimized by refactoring operation log detail styles (Thanks 玲娜贝er). +* update Optimized dictionary caching using Map instead of Array for higher efficiency (Thanks 月夜). +* update Optimized by checking if filenames contain special characters. +* update Optimized getTenantList interface to dynamically decide whether to pass the token. +* fix Fixed issue where switching tenants with too many tabs caused freezing. +* fix Fixed incorrect permission string for the modify button in user management interface (Thanks QianRj). +* fix Fixed OSS configuration page to display configuration key and hide primary key ID. +* fix Fixed page API deprecation warnings. +* fix Fixed code generation list loading issue. +* fix Fixed issue where internal link pages couldn't open when Tags-Views were closed by default. +* fix Fixed user selection component ID type inconsistency issue. +* fix Fixed code generation querying the list twice after editing. +* fix Fixed 404 error when login lacks redirect parameter. +* fix Fixed monitor context-path causing 404 error upon logout and re-login. +* fix Fixed inconsistent redirect behavior between manual logout and token expiration logout. +* fix Fixed issue where SSE close requests were still sent after disabling SSE, causing errors. +* fix Fixed data caching issue in embedded pages causing inconsistency with external pages. + +## Platform Introduction + +> RuoYi-Vue-Plus is a rewrite of RuoYi-Vue, comprehensively upgraded for `distributed clusters and multi-tenancy` scenarios (not compatible with the original framework). + +> Project code and documentation are open source, free, and commercially usable. Just retain the open-source license file in the project. +> Code for a lifetime, open source for interest, open source for learning, open source to let everyone truly learn technology. + +> System Demo: https://plus-doc.dromara.org/#/common/demo_system + +> Frontend Project Address: https://gitee.com/JavaLionLi/plus-ui +> Member Frontend Project Address: Based on vben5 https://gitee.com/dapppp/ruoyi-plus-vben5 + +> Documentation Address: https://plus-doc.dromara.org + +# Functional Differences Between This Framework and RuoYi + +| Feature | This Framework | RuoYi | +| :------------------------------------ | :----------------------------------------------------------- | :----------------------------------------------------------- | +| **Frontend Project** | Rewritten using Vue3 + TS + ElementPlus | Based on Vue2/Vue3 + JS | +| **Backend Project Structure** | Plugin-based + extension package form, decoupled structure, easy to extend | Modules inject into each other, heavily coupled, difficult to extend | +| **Backend Code Style** | Strictly follows Alibaba规范 and project-unified code formatting | Code writing and structure differ from conventions, creating reading barriers | +| **Web Container** | Uses Undertow, a high-performance container based on XNIO | Uses Tomcat | +| **Permission Authentication** | Uses Sa-Token, Jwt; static use, full-featured, low coupling, high extensibility | Uses Spring Security; cumbersome configuration, poor extensibility | +| **Permission Annotations** | Uses Sa-Token; supports annotations for login check, role check, permission check, secondary auth check, HttpBasic check, ignore check. Role and permission checks support multiple conditions like `AND`, `OR`, or complex expressions like `Permission OR Role`. | Only supports existence matching | +| **Third-party Auth** | Uses JustAuth third-party login component; supports dozens of authentications like WeChat, DingTalk | None | +| **Relational DB Support** | Natively supports MySQL, Oracle, PostgreSQL, SQLServer. Supports heterogeneous switching (supports all databases mybatis-plus supports, just add JDBC dependency. Success cases include Dameng, Kingbase). | Supports Mysql, Oracle. Does not support concurrent use or heterogeneous switching. | +| **Cache Database** | Supports Redis 5-7, supports most new features like distributed rate limiting, distributed queues. | Simple Redis get/set support. | +| **Redis Client** | Uses Redisson (officially recommended by Redis), Netty-based client tool. Supports over 90% of Redis commands. Underlying optimizations avoid many incorrect usages (e.g., keys converted to scan). Supports standalone, sentinel, single-master cluster, multi-master cluster modes. | Lettuce + RedisTemplate; supports few modes, tools are cumbersome to use. Connection pool uses common-pool, many bugs, frequent issues. | +| **Cache Annotations** | Uses Spring-Cache annotations, extended implementation supports more features (e.g., TTL, max idle time, group max length). Data auto-cached with just an annotation. | Requires manual Redis code logic. | +| **ORM Framework** | Uses Mybatis-Plus; object-based, barely any SQL needed, full Java operation, powerful with many plugins (e.g., multi-tenant, paging, optimistic lock). | Uses Mybatis; XML-based, requires manual SQL writing. | +| **SQL Monitoring** | Uses p6spy; can output complete SQL and execution time monitoring. | Log output; requires manual SQL and parameter拼接, not easy for quick debugging. | +| **Data Paging** | Uses Mybatis-Plus paging plugin. Framework extended it, objectified paging object, supports multiple parameter passing methods, supports frontend multi-sorting, complex sorting. | Uses PageHelper; only supports single query paging, parameters only from param, only single sort, poor functionality and extensibility, bad experience. | +| **Data Permissions** | Uses Mybatis-Plus plugin;自行 analyzes and拼接 SQL, seamless filtering. Just set annotation conditions on Mapper, supports various customizations, not limited to department/role. | Uses annotation+aop implementation; based on department/role, generated SQL has poor compatibility, doesn't support other business extensions. Generated SQL needs manual splicing onto specific business SQL, doesn't work for multiple Mapper queries. | +| **Data Desensitization** | Uses annotation + jackson; desensitizes during serialization, supports different conditions per module. Supports multiple strategies: ID card, phone, address, email, bank card, etc. Extensible. | None | +| **Data Encryption** | Uses annotation + mybatis interceptor; auto encrypts/decrypts during data access. Supports multiple strategies: BASE64, AES, RSA, SM2, SM4, etc. | None | +| **Interface Transmission Encryption** | Uses dynamic AES + RSA to encrypt request body; each request has a different key, greatly reducing crackability. | None | +| **Data Translation** | Uses annotation + jackson; dynamically modifies data during serialization, translates data. Supports multiple modes: `mapping translation`, `direct translation`, `other extended condition translation`. Interface-based, two steps for custom extension. Built-in多种 translation implementations. | None | +| **Multi-Datasource Framework** | Uses dynamic-datasource; supports most databases on the market. Dynamically manage heterogeneous databases via yml config, or add datasources via frontend page. Supports SpEL expressions to switch datasource based on request header/parameters. | Based on druid; manual code configuration for datasources, cumbersome configuration, poor support. | +| **Multi-Datasource Transaction** | Uses dynamic-datasource; supports transaction rollback across different types of databases. | Not supported | +| **Database Connection Pool** | Uses HikariCP; Spring's built-in connection pool, simple configuration, famous for performance and stability. | Uses druid; many bugs, poor community maintenance, low activity, numerous cumbersome configurations, average performance. | +| **Database Primary Key** | Uses Snowflake ID; time-based, ordered, increasing, unique ID. No more worries about primary key conflicts during sharding/merging. | Uses database auto-increment ID; limited data volume support, doesn't support unique primary keys across multiple datasources. | +| **WebSocket Protocol** | Based on Spring's encapsulated WebSocket protocol; extended Token auth and distributed session sync, not just single-machine useless. | None | +| **SSE Push** | Uses Spring SSE implementation; extended Token auth and distributed session sync. | None | +| **Serialization** | Uses Jackson; Spring's built-in serialization, reliable!!! | Uses fastjson; infamous bugjson | +| **Distributed Idempotence** | Simplified implementation referencing Meituan's GTIS anti-duplication system (details in docs). | Manual annotation implementation based on aop. | +| **Distributed Lock** | Uses Lock4j,底层 based on Redisson. | None | +| **Distributed Task Scheduling** | Uses SnailJob; natively supports distribution, unified management center, supports multiple databases, sharding, retry, DAG task flows, etc. | Uses Quartz; database lock-based, poor performance, clusters require much configuration and modification. | +| **File Storage** | Uses Minio; distributed file storage, natively supports multi-machine, multi-disk, multi-shard, multi-replica storage. Supports permission management, secure, files can be encrypted. | Uses local file storage; files exposed, easily lost/leaked, doesn't support clusters, has single point of failure. | +| **Cloud Storage** | Uses AWS S3 protocol client; supports all vendors supporting S3 protocol: Qiniu, Alibaba, Tencent, etc. | Not supported | +| **SMS** | Uses sms4j SMS fusion package; supports dozens of SMS vendors. Just configure vendor keys in yml. Multiple vendors can be used together. | Not supported | +| **Email** | Uses mail-api universal protocol; supports most email vendors. | Not supported | +| **Interface Documentation** | Uses SpringDoc, javadoc; annotation-free, zero intrusion based on Java comments. Just write good comments, no need to write lots of doc annotations. | Uses Springfox; discontinued, requires writing大量 annotations to support doc generation. | +| **Validation Framework** | Uses Validation; supports annotation and utility class validation. Annotations support i18n. | Only supports annotations, and annotations don't support i18n. | +| **Excel Framework** | Uses Alibaba EasyExcel; plugin-based. Framework added many features: auto-merge相同 content, auto-arrange layout, dictionary translation, etc. | Based on POI manual implementation; limited functionality, complex, poor extensibility. | +| **Workflow Support** | Supports various complex approvals: transfer, delegate, add/sign subtract/sign,会签,或签,票签, etc. | None | +| **Utility Framework** | Uses Hutool, Lombok; hundreds of tools cover 90% of needs. Auto-generates get/set etc. based on annotations, simplifying framework code. | Handwritten tools, unstable, prone to issues. Limited number of tools. Code bloated, need to handwrite get/set etc. | +| **Monitoring Framework** | Uses SpringBoot-Admin; based on SpringBoot official actuator probe mechanism. Real-time service status monitoring. Framework extended it with online log viewing monitoring. | None | +| **Tracing** | Uses Apache SkyWalking; troubled not knowing where requests went or where problems occurred? Use it to see every node a request passes through in real-time. | None | +| **Code Generator** | Just design the table structure, generate all CRUD code and pages with one click. Reduces development load by 80%, focus on business design. Adapted for MP, SpringDoc standardized code. Supports dynamic multi-datasource code generation. | Code generation for native structure, only supports single datasource generation. | +| **Deployment** | Supports Docker orchestration; one-click setup for all environments. Developers no longer worry about environment setup. | Native jar deployment; other environments require manual download, installation, and setup. | +| **Project Path Modification** | Provides detailed modification plan documentation and made some changes; very easy to modify to your liking. | Requires much modification, limited documentation. | +| **Internationalization (i18n)** | Dynamically returns text in different languages based on request header. Low development difficulty, has corresponding utils, supports i18n for most annotation content. | Only provides basic functions, others need self-writing extension. | +| **Code Unit Testing** | Provides unit testing, usage methods, writing methods, and maven multi-environment unit test plugin. | Only provides basic functions, others need self-writing extension. | +| **Demo Cases** | Provides practical use cases of framework features; a separate module offers many comprehensive examples. | None | \ No newline at end of file diff --git a/src/news/SMS4j-3.3.4.md b/src/news/SMS4j-3.3.4.md new file mode 100644 index 0000000000..62f91a02ce --- /dev/null +++ b/src/news/SMS4j-3.3.4.md @@ -0,0 +1,52 @@ +--- +title: SMS Aggregation Framework SMS4j 3.3.4 Update Announcement +author: SMS4j +date: 2025-04-21 +cover: /assets/img/news/SMS4j-3.3.4-0.png +head: + - - meta + - name: News +--- + +## SMS4j 3.3.4 Update Announcement + +> It has been nearly half a year since the release of version 3.3.3. During this time, the author has faced a series of challenges—first, a family member fell ill, then I myself got sick, coming within ten minutes of meeting the King of Hell. Shortly after, I encountered "graduation" (job loss). Here, I also want to remind everyone to take care of their health. Health is the greatest asset. Whether it’s overworking or overtime, everyone should take it easy. After all, if you lose your life, you lose everything. Fortunately, everything is now back on track and improving day by day. + +![](/assets/img/news/SMS4j-3.3.4-0.png) + +**Carpe diem, for tomorrow may never come.** + +> Here, I also wish everyone: good health, no hair loss, yearly salary increases, and endless good fortune! + +* * * + +Below are the updates in this release: + +## Fixes + +* Fixed an issue where thread pool resources were not released when sending OA messages. +* Fixed missing request headers when calling Baidu. +* Fixed code inconsistencies between two versions of email functionality. +* Fixed an issue where Luosimao SMS did not require the "+86" prefix for phone numbers. +* Fixed an issue where certain vendor SMS failures would retry indefinitely under abnormal conditions. +* Fixed Bean conflicts when both OA and SMS are present. + +## Optimizations + +* Upgraded Solon support to version 3.0.1. +* Changed the daily maximum send count cutoff to 0:00 daily instead of a 24-hour timer. +* Removed duplicate dependencies. +* Added OA YAML configuration examples. +* Optimized parts of the code structure. + +## Additions + +* Added China Unicom Yixintong platform SMS support. +* Added HTTP proxy configuration support for SMS sending. + +Finally, we hope for your continued support. If you have any suggestions, please let us know via issues, and we will do our best to optimize. As long as I’m still around, SMS4j will keep updating. Please use your prosperous hands to give us a free star—your support is our greatest motivation for updates. + +Gitee Repository: https://gitee.com/dromara/sms4j +GitHub Repository: https://github.com/dromara/sms4j +GitCode Repository: https://gitcode.com/dromara/SMS4J +Official Documentation: https://sms4j.com \ No newline at end of file diff --git a/src/news/SQLREST-0.md b/src/news/SQLREST-0.md new file mode 100644 index 0000000000..4304fbac02 --- /dev/null +++ b/src/news/SQLREST-0.md @@ -0,0 +1,70 @@ +--- +title: "SQLREST: A Fully Open-Source Tool for Converting SQL to RESTful APIs" +author: sqlrest +date: 2025-08-05 +cover: /assets/img/news/SQLREST-0-0.png +head: + - - meta + - name: News +--- + +# A Fully Open-Source Tool for Converting SQL to RESTful APIs + +## Author Introduction + +* Alias: Sanpang (inrgihc) +* Member of the dromara open-source organization; author of projects dromara/dbswitch and dromara/sqlrest +* Project Address: https://gitee.com/dromara/sqlrest + +## Project Origin + +As a Java programmer, quickly writing CRUD RESTful interfaces for front-end calls is an essential but often tedious task. In reality, the main design work involves structuring database tables and crafting the SQL logic for the interfaces. + +In the context of today’s booming big data industry, there is often a need to expose data from data platforms to other systems, enabling data sharing and accessibility. + +## Project Overview + +SQLREST is an open-source project designed to provide a simple yet powerful way to convert SQL operations into RESTful APIs. It supports multiple databases, allowing users to create APIs by configuring SQL statements without writing complex backend logic. Users only need to select a data source, input SQL or scripts, and configure simple paths to quickly generate API interfaces. + +Key features of SQLREST include: + +- **Direct SQL-to-API Conversion**: Configure SQL for CRUD operations and parameters to generate RESTful APIs. +- **Multi-Database Support**: Compatible with 20+ common databases, including several domestic databases. +- **MyBatis Syntax Support**: Supports dynamic SQL syntax from MyBatis. +- **Groovy Script Support**: Allows the use of Groovy scripts for building interfaces in complex scenarios. +- **Parameter Type Support**: Supports various types including integers, floats, time, date, boolean, string, and objects. +- **ContentType Support**: Compatible with multiple request formats such as `application/x-www-form-urlencoded` and `application/json`. +- **Authentication Support**: Provides a token-based authentication mechanism to secure APIs. +- **Swagger Online Documentation**: Automatically generates Swagger-ui for online API documentation. +- **Cache Configuration Support**: Supports Hazelcast and Redis caching to improve API performance. +- **Flow Control Management**: Utilizes Sentinel for traffic control to prevent system overload. +- **Unified Alert Integration**: Supports integration with unified alert systems for triggering notifications. +- **LLM MCP Service**: Allows easy configuration to create MCP tools. + +## Deployment and Experience + +### 1. System Deployment Guide + +One-click installation for x86 architecture CentOS systems with internet access (based on docker-compose): + +```bash +curl -k -sSL https://gitee.com/inrgihc/sqlrest/attach_files/2241027/download -o /tmp/sr.sh && systemctl stop firewalld && bash /tmp/sr.sh && rm -f /tmp/sr.sh +``` + +For detailed instructions, refer to: +https://gitee.com/dromara/sqlrest/tree/master/build-docker/install + +### 2. System Usage Documentation + +User guide: +https://www.yuque.com/sanpang-jq7te/nys82g/hur636mthgyhaodb + +![](/assets/img/news/SQLREST-0-0.png) + +![](/assets/img/news/SQLREST-0-1.png) + +## Follow SQLREST + +We welcome you to try out SQLREST. Join the SQLREST WeChat group for discussions. We also look forward to more coding enthusiasts contributing to the project. + +Gitee: https://gitee.com/dromara/sqlrest \ No newline at end of file diff --git a/src/news/Sa-Token-v1.41.0.md b/src/news/Sa-Token-v1.41.0.md new file mode 100644 index 0000000000..9faea12e1d --- /dev/null +++ b/src/news/Sa-Token-v1.41.0.md @@ -0,0 +1,281 @@ +--- +title: Sa-Token v1.41.0 Released – Check Out the Exciting New Features! +author: Sa Kite +date: 2025-03-24 +cover: /assets/img/news/Sa-Token-v1.41.0-0.png +head: + - - meta + - name: News +--- + +Sa-Token is a lightweight Java permission authentication framework that primarily addresses a series of permission-related issues such as **login authentication**, **permission authentication**, **single sign-on (SSO)**, **OAuth2.0**, and **microservices gateway authentication**. 🔐 + +**The latest version `v1.41.0` has been pushed to the `Maven` Central Repository** 🎉. You can include it via the following method: + +```xml + + + cn.dev33 + sa-token-spring-boot-starter + 1.41.0 + +``` + +This version includes a large number of ⛏️ new features, ⛏️ underlying refactoring, ⛏️ code optimizations, and more. Below are some of the more important updates for your reference: + +### 🛡️ Update 1: Firewall Module Adds Hooks Extension Mechanism + +This update adds several new validation rules to the firewall. The previous rules were: + +* • Allowlist release for paths. +* • Blacklist interception for paths. +* • Dangerous character validation for paths. + +The newly added rules are: + +* • Forbidden character validation for paths. +* • Directory traversal character detection (with an optimized detection algorithm). +* • Request host detection. +* • Request method detection. +* • Request header detection. +* • Request parameter detection. + +Furthermore, this update opens up a hooks mechanism, allowing developers to register custom validation rules 🛠️. Refer to the example below: + +```java +@PostConstruct +public void saTokenPostConstruct() { + // Demo of registering a new hook: intercept all requests containing the "pwd" parameter and deny the response + SaFirewallStrategy.instance.registerHook((req, res, extArg) -> { + if (req.getParam("pwd") != null) { + throw new FirewallCheckException("Request must not contain the 'pwd' parameter"); + } + }); +} +``` + +Direct link to documentation: Sa-Token Firewall 🔗 + +### 💡 Update 2: New SPI-Based Plugin System + +Previously, Sa-Token also had a plugin system, but it relied on SpringBoot's SPI mechanism for component registration. + +This registration method had an issue: plugins could only work properly in a SpringBoot environment. In other environments, such as Solon projects, plugins had to be registered manually 😫. + +In other words, strictly speaking, these plugins were more like SpringBoot plugins rather than Sa-Token framework plugins 🌐. + +To improve plugin versatility, Sa-Token has designed its own SPI mechanism, allowing these plugins to function correctly in more project environments 🚀. + +**Step 1:** Implement the plugin registration class. This class needs to `implements SaTokenPlugin` 👨💻: + +```java +/** + * SaToken Plugin Installation: Description of the plugin's purpose + */ +public class SaTokenPluginForXxx implements SaTokenPlugin { + @Override + public void install() { + // Write code that needs to be executed when the project starts, for example: + // SaManager.setXxx(new SaXxxForXxx()); + } +} +``` + +**Step 2:** In the project's `resources/META-INF/satoken/` folder 📂, create a file named `cn.dev33.satoken.plugin.SaTokenPlugin`. The content should be the fully qualified name of the plugin registration class: + +``` +cn.dev33.satoken.plugin.SaTokenPluginForXxx +``` + +This allows the Sa-Token plugin manager to load this plugin during project startup, execute the `install` method of the plugin registration class, and complete the plugin installation ✅. + +Direct link to documentation: Sa-Token Plugin Development Guide 🔗 + +### 🎛️ Update 3: Refactored Caching System, Separating Data Read/Write from Serialization Operations + +In previous versions, Redis integration was often coupled with specific serialization methods. This not only caused a lot of redundant code in Redis-related plugins but also severely limited choices when selecting Redis plugins. ⚠️ + +This version update completely refactors this module, separating data read/write operations from serialization operations. Each part can now have its own custom implementation class, allowing for flexible expansion ✨. For example: + +* • 1️⃣ SaTokenDao data read/write can choose from different implementations: RedisTemplate, Redisson, ConcurrentHashMap, Hutool-Timed-Cache, etc. +* • 2️⃣ SaSerializerTemplate serializer can choose different methods: Base64 encoding, Hex encoding, ISO-8859-1 encoding, JSON serialization, etc. +* • 3️⃣ JSON serialization can choose different components: Jackson, Fastjson, Snack3, etc. + +All implementation classes can be selected as needed and freely combined, greatly improving flexibility 🏗️. + +### ⚙️ Update 4: SaLoginParameter Login Parameter Class Adds Numerous Configuration Options + +SaLoginParameter (formerly SaLoginModel) is used to control certain detailed behaviors during login operations. Newly added configuration options include: + +* • `isConcurrent`: Determines whether to allow concurrent logins for the same account from different locations (true allows simultaneous logins, false forces the new login to kick out the old one). 🌍 +* • `isShare`: When multiple users log into the same account, determines whether they share one token (true means all logins share one token, false means each login creates a new token). 🔄 +* • `maxLoginCount`: The maximum number of concurrent logins for the same account. Clients exceeding this number will be automatically logged out. -1 means unlimited. 🚫 +* • `maxTryTimes`: The maximum number of loop attempts when creating a token, used to ensure token uniqueness (-1 = no loop attempts, use directly). ⏳ +* • `deviceId`: The client device ID for this login, used to determine if a subsequent login is from a trusted device. 📱 +* • `terminalExtraData`: Custom extension data mounted to SaTerminalInfo for this login. 📦 + +Most of these configuration options were supported in previous versions, but they were defined in the global configuration class SaTokenConfig. This update supports defining these options within SaLoginParameter, making login strategy control more flexible. ✨ + +### 🚪 Update 5: New SaLogoutParameter Logout Parameter Class + +SaLogoutParameter is used to control certain detailed behaviors during logout operations 🎯. For example: + +Determine the logout scope via the `Range` parameter: +```java +// Logout scope: TOKEN = only logout the session of the current token, ACCOUNT = logout all client sessions for the loginId pointed to by the current token +StpUtil.logout(new SaLogoutParameter().setRange(SaLogoutRange.TOKEN)); +``` + +Determine which login device types participate in the logout via the `DeviceType` parameter 💻: +```java +// Logout user 10001 from all PC devices; other devices like APP are unaffected +StpUtil.logout(10001, new SaLogoutParameter().setDeviceType("PC")); +``` + +Other parameters are not listed here individually. Direct link to documentation: Sa-Token Login Parameters & Logout Parameters 🔗 + +### 🐞 Update 6: Fixed Null Pointer Issues with `StpUtil.setTokenValue("xxx")` and `loginParameter.getIsWriteHeader()` + +Nothing much to say here – bugs 🐛 must be fixed. + +fix issue:#IBKSM0 🔗 + +### ✨ Update 7: API Parameter Signature Module Upgrade + +* • 1. Added the `@SaCheckSign` annotation. The API parameter signature module now supports annotation-based authentication. 🆕 +* • 2. Added custom digest algorithms for signatures. Now supports not only MD5 but also SHA1, SHA256, etc., for calculating signatures. 🔐 +* • 3. Added multi-application mode: + +Multi-application mode allows using different secret keys and other configuration items when integrating with multiple systems. Configuration example 📝: + +```yaml +sa-token: + # API signature configuration - Multi-application mode + sign-many: + # Application 1 + xm-shop: + secret-key: 0123456789abcdefg + digest-algo: md5 + # Application 2 + xm-forum: + secret-key: 0123456789hijklmnopq + digest-algo: sha256 + # Application 3 + xm-video: + secret-key: 12341234aaaaccccdddd + digest-algo: sha512 +``` + +Then, specify the `appid` during signing to get the corresponding SignTemplate for operations 👨💻: + +```java +// Create signature example +String paramStr = SaSignMany.getSignTemplate("xm-shop").addSignParamsAndJoin(paramMap); + +// Check signature example +SaSignMany.getSignTemplate("xm-shop").checkRequest(SaHolder.getRequest()); +``` + +### ⚡ Update 8: New sa-token-caffeine Plugin for Integrating Caffeine + +Caffeine is a high-performance local caching library for Java. This update adds the sa-token-caffeine plugin to use Caffeine as Sa-Token's cache layer for storing session authentication data. 🚀 +This further enriches the ecosystem of cache layer plugins for Sa-Token. 🌱 + +```xml + + + cn.dev33 + sa-token-caffeine + 1.41.0 + +``` + +### 🎪 Update 9: New sa-token-serializer-features Serialization Extension Package + +Introducing this plugin provides Sa-Token with some interesting serialization schemes. (For entertainment purposes, not recommended for production 🎭) + +For example: Base64 encoding using the periodic table of elements 🧪, special symbols 🔣, or emojis 😊 as the character set for storing data: + +![](/assets/img/news/Sa-Token-v1.41.0-0.png) +sa-custom-serializer-yszqb.png + +![](/assets/img/news/Sa-Token-v1.41.0-1.png) +sa-custom-serializer-tsfh.png + +![](/assets/img/news/Sa-Token-v1.41.0-2.png) +sa-custom-serializer-emoji.png + +![](/assets/img/news/Sa-Token-v1.41.0-3.png) +sa-custom-serializer-emoji2.png + +### 📜 Full Changelog + +Besides the points mentioned above, there are more updates that cannot be detailed individually. Below is the complete changelog for version v1.41.0: + +* • core: + * • Fix: Fixed null pointer issues with `StpUtil.setTokenValue("xxx")` and `loginParameter.getIsWriteHeader()`. fix: #IBKSM0 + * • Fix: Changed the default ban level return value of `SaDisableWrapperInfo.createNotDisabled()` to -2 to ensure compatibility with previous versions. + * • Add: Added an SPI-based plugin system. **[Important]** + * • Refactor: Refactored the JSON converter module. **[Important]** + * • Add: Added a serializer module to control the serialization between `Object` and `String`. **[Important]** + * • Refactor: Refactored the firewall module, adding a hooks mechanism. **[Important]** + * • Add: Firewall added: Request path forbidden character validation, Host detection, Request method detection, Request header detection, Request parameter detection. Refactored directory traversal character detection algorithm. + * • Refactor: Refactored the `SaTokenDao` module, separating serialization from storage operations. **[Important]** + * • Refactor: Refactored the default implementation class of `SaTokenDao`, optimizing the underlying design. + * • Add: The `isLastingCookie` configuration item can now be defined in the global configuration. + * • Refactor: `SaLoginModel` -> `SaLoginParameter`. **[Not backward compatible]** + * • Refactor: `TokenSign` -> `SaTerminalInfo`. **[Not backward compatible]** + * • Add: `SaTerminalInfo` added `extraData` for custom extension data setting. + * • Add: `SaLoginParameter` supports configuring `isConcurrent`, `isShare`, `maxLoginCount`, `maxTryTimes`. + * • Add: Added `SaLogoutParameter` to control various details during session logout. **[Important]** + * • Add: Added `StpLogic#isTrustDeviceId` method to determine if a specified device is a trusted device. + * • Add: Added `StpUtil.getTerminalListByLoginId(loginId)`, `StpUtil.forEachTerminalList(loginId)` methods to更方便地实现单账号会话管理 (more conveniently implement single-account session management). + * • Upgrade: API parameter signature configuration supports custom digest algorithms. + * • Add: Added `@SaCheckSign` annotation authentication for API signature parameter validation. + * • Add: API parameter signature module added multi-application mode. fix: #IAK2BI, #I9SPI1, #IAC0P9 **[Important]** + * • Refactor: Global configuration `is-share` default value changed to false. **[Not backward compatible]** + * • Refactor: Kicking a user offline or replacing a user offline will now delete the corresponding token-session object by default. + * • Optimize: Optimized APIs related to logging out sessions. + * • Refactor: Default login device type value changed to DEF. **[Not backward compatible]** + * • Refactor: Marked `BCrypt` as `@Deprecated`. + * • Add: `sa-token-quick-login` supports `SpringBoot3` projects. fix: #IAFQNE, #673 + * • Add: `SaTokenConfig` added `replacedRange`, `overflowLogoutMode`, `logoutRange`, `isLogoutKeepFreezeOps`, `isLogoutKeepTokenSession` configuration items. +* • OAuth2: + * • Refactor: Refactored the sa-token-oauth2 plugin, changing the registration process of the annotation authentication handler to SPI plugin loading. +* • Plugins: + * • Add: `sa-token-serializer-features` plugin for implementing various forms of custom character set serialization schemes. + * • Add: `sa-token-fastjson` plugin. + * • Add: `sa-token-fastjson2` plugin. + * • Add: `sa-token-snack3` plugin. + * • Add: `sa-token-caffeine` plugin. +* • Unit Tests: + * • Add: `sa-token-json-test` JSON module unit tests. + * • Add: `sa-token-serializer-test` serializer module unit tests. +* • Documentation: + * • Add: QA "How to prevent conflicts when multiple projects share the same Redis?" + * • Optimize: Added missing related configuration items for the OAuth2 module. + * • Optimize: Optimized the description document for the OAuth2 overview chapter. + * • Optimize: Improved the documentation for the "SSO User Data Synchronization / Migration" chapter. + * • Fix: Added missing project directory structure introduction document. + * • Add: Added a new "Login Parameters & Logout Parameters" chapter to the documentation. + * • Optimize: Optimized the prompt text for the "Technical Help" button. + * • Add: Added `preview-doc.bat` file for one-click document preview startup. + * • Improve: Improved Redis integration documentation. + * • Add: Added operation examples for single-account session query. + * • Add: Added introduction to the kick-user-offline API. + * • Add: Added a chapter on custom serialization plugins. +* • Other: + * • Add: Added `sa-token-demo/pom.xml` to allow one-click import of all demo projects in IDEA. + * • Delete: Removed unnecessary `.gitignore` files. + * • Refactor: Refactored the `sa-token-solon-plugin` plugin. + * • Add: Added device lock login example. + +Direct link to the online changelog documentation: https://sa-token.cc/doc.html#/more/update-log + +### 🌟 Other + +Code Repository Address: https://gitee.com/dromara/sa-token + +Framework Functional Structure Diagram: + +![](/assets/img/news/Sa-Token-v1.41.0-4.png) \ No newline at end of file diff --git a/src/news/Sa-Token-v1.42.0.md b/src/news/Sa-Token-v1.42.0.md new file mode 100644 index 0000000000..5f769d58b5 --- /dev/null +++ b/src/news/Sa-Token-v1.42.0.md @@ -0,0 +1,262 @@ +--- +title: "Sa-Token v1.42.0 Released: New API Key, TOTP Verification Codes, RefreshToken Lookup, and More" +author: April 13, 2025 11:35 +date: 2025-04-13 +cover: /assets/img/news/Sa-Token-v1.42.0-0.png +head: + - - meta + - name: News +--- + +Sa-Token is a **free**, **open-source**, lightweight Java permission authentication framework designed to address a series of permission-related issues such as: **login authentication**, **permission authentication**, **single sign-on (SSO)**, **OAuth 2.0**, and **microservices gateway authentication**. 🔐 + +**The latest version, `v1.42.0`, has been pushed to the `Maven` Central Repository** 🎉. You can include it in your project as follows: + +```xml + + + cn.dev33 + sa-token-spring-boot-starter + 1.42.0 + +``` + +This version includes a large number of ⛏️ new features, ⛏️ underlying refactorings, ⛏️ code optimizations, and more. Below are some of the more important updates for your reference: + +### 🗝️ Update 1: New API Key Module + +If you have ever integrated with open APIs from platforms like ChatGPT or DeepSeek, you are undoubtedly familiar with API Keys. 🤝 + +An API Key is an interface call credential, similar to a session token, but with more flexible permission control. 🔑 + +This update introduces full lifecycle management for API Keys, supporting the issuance, validation, disabling, and deletion of API Keys for specified accounts. +Additionally, each API Key can be individually configured with different scope permissions, allowing the use of different API Keys in various scenarios to achieve key isolation and minimal authorization. 🛡️ + +To better demonstrate the capabilities of this module, we have prepared a demo example: + +![](/assets/img/news/Sa-Token-v1.42.0-0.png) + +sa-api-key + +Example repository address: [sa-token-demo-apikey](https://github.com/dromara/sa-token-demo-apikey) 🔗 + +In this example, you can log in with different test accounts, issue API Keys for them, set scope permissions, and test API calls using different API Keys to observe the response results. 🧪 + +By default, the framework stores all API Key information in the cache, which can be referred to as "cache mode." In this mode, data will be lost after restarting the cache storage. ⚠️ + +The framework provides the `SaApiKeyDataLoader` interface, allowing you to switch data loading to "database mode" for long-term effective data storage. 💾 + +Direct link to online documentation: [API Key Interface Call Credentials](https://sa-token.cc/doc.html#/plugin/apikey) 🔗 + +### 🔍 Update 2: Refactored TempToken Module with Added Value Lookup Mechanism + +In the Sa-Token documentation, there is an example like this: 📚 + +![](/assets/img/news/Sa-Token-v1.42.0-1.png) + +sa-refresh-token + +This example demonstrates how to use the temporary Token authentication module to create a `RefreshToken` for a login session, achieving a dual-Token effect. 🔄 + +However, one day, I received a query from a user via the official Sa-Token assistant: 💬 + +![](/assets/img/news/Sa-Token-v1.42.0-2.png) + +sa-refresh-token-wnglian-zixun + +The user pointed out whether a lookup mechanism could be provided for `RefreshToken` to retrieve all historically issued `RefreshToken`s for a specific account. + +Consider it done! 💪🏆 + +This version update allows the program to specify a third parameter when creating a refresh-token, indicating whether the framework should record Token index information: + +```java +SaTempUtil.createToken("10001", 2592000, true); +``` + +Specifying `false` means no index is recorded, only the token is generated. Specifying `true` means index information is recorded, enabling future lookup of all historically issued tokens by value. 🔍 + +For example, we can use the `SaTempUtil.getTempTokenList("xxx")` method to retrieve all historically issued `RefreshToken` records for a specified account: + +```java +List refreshTokenList = SaTempUtil.getTempTokenList("10001"); +``` + +Direct link to online documentation: [Temporary Token Authentication](https://sa-token.cc/doc.html#/up/temp-token) 🔗 + +### ⏱️ Update 3: Added TOTP Algorithm Implementation + +TOTP is a dynamic password algorithm used to generate short-lived numeric verification codes (typically 6-8 digits)️. Its core principle is to combine a secret key with the current time to generate a one-time password through a hash operation. ⏱ + +TOTP is generally used in the following scenarios: + +* • 1. **Two-factor authentication during login**: After entering their username and password, users must also enter a TOTP verification code to log in successfully. 🔐 +* • 2. **Secondary authentication for sensitive operations**: When performing high-risk sensitive operations, users need to enter a TOTP verification code to proceed. 🛡️ +* • 3. **Replacing SMS verification codes**: TOTP verification codes can be generated offline without a network connection, to some extent replacing SMS verification codes for identity verification. 📴 + +This version adds functionality for generating and validating TOTP verification codes, making it easier for everyone to add two-factor authentication capabilities to their systems. 🚀 + +### ⚙️ Update 4: Refactored and Upgraded `SaTokenContext` Context Read/Write Strategy + +This is likely the most low-level refactoring in recent versions, almost completely overhauling the previous design of the context module. 💥 + +In previous versions, Sa-Token needed to leverage the native context capabilities of different web frameworks to build its own context. 🌐 + +This update sees Sa-Token implementing its own context storage mechanism using ThreadLocal, which brings the following benefits: + +* • 1. Easier and simpler integration with more web frameworks. +* • 2. Ability to temporarily mock a context in asynchronous scenarios to call Sa-Token's synchronous APIs. +* • 3. Complete removal of the secondary context module, achieving context unification between web requests and RPC requests. +* • 4. Sa-Token synchronous APIs can now be called within firewall hooks. + +### 🌐 Update 5: Added `CORS` Cross-Origin Strategy Handler, Providing a Unified Cross-Origin Solution Across Different Architectures + +In previous versions, cross-origin processing always had to be written in the global authentication filter, treated as an "additional supplementary operation under authentication." ⏳ + +The new version specifically provides a CORS cross-origin handling strategy component. You no longer need to write a lengthy authentication filter component just for cross-origin handling. 🚀 + +```java +/** + * CORS Cross-Origin Handling + */ +@Bean +public SaCorsHandleFunction corsHandle() { + return (req, res, sto) -> { + res. + // Allow specified origins to access cross-origin resources + setHeader("Access-Control-Allow-Origin", "*") + // Allow all request methods + .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE") + // Validity period + .setHeader("Access-Control-Max-Age", "3600") + // Allowed header parameters + .setHeader("Access-Control-Allow-Headers", "*"); + + // If it's a preflight request, return immediately to the frontend + SaRouter.match(SaHttpMethod.OPTIONS) + .free(r -> System.out.println("--------OPTIONS preflight request, no processing")) + .back(); + }; +} +``` + +Open-source repository example: [sa-token-demo-cross](https://github.com/dromara/sa-token-demo-cross) 🔗 + +### 🔑 Update 6: `sa-token-quick-login` Plugin Now Supports Authentication via `Http Basic` + +`sa-token-quick-login` can quickly and conveniently add a login page to your project. After adding the dependency: + +```xml + + cn.dev33 + sa-token-quick-login + 1.42.0 + +``` + +Starter Class: + +```java +@SpringBootApplication +public class SaTokenQuickDemoApplication { + public static void main(String[] args) { + SpringApplication.run(SaTokenQuickDemoApplication.class, args); + + System.out.println("\n------ Startup Successful ------"); + System.out.println("name: " + SaQuickManager.getConfig().getName()); + System.out.println("pwd: " + SaQuickManager.getConfig().getPwd()); + } +} +``` + +Test Controller: + +```java +@RestController +public class TestController { + @RequestMapping({"/", "/index"}) + public String index() { + String str = "
" + + "

Resource Page (Login Required to Access This Page)

" + + "
" + + "

Sa-Token " + SaTokenConsts.VERSION_NO + "

"; + return str; + } +} +``` + +Start the project and access it using a browser: `http://localhost:8081`. The first visit will redirect you to the login page since you are not logged in 🚪: + +![](/assets/img/news/Sa-Token-v1.42.0-3.png) + +Login + +Use the default account: `sa / 123456` to log in, and you will see the resource page. + +![](/assets/img/news/Sa-Token-v1.42.0-4.png) + +Logged In + +The new version adds the ability to authenticate directly via Http Basic: + +``` +http://sa:123456@localhost:8081/ +``` + +This will be very helpful for testing quick-login related resource interfaces in dedicated API testing tools. 🧪 + +### 📜 Full Changelog + +In addition to the points mentioned above, there are more updates that cannot be detailed one by one. Below is the complete changelog for version v1.42.0: + +* • **core**: + * • Added: New `API Key` module. **[Important]** + * • Added: New `TOTP` implementation. **[Important]** + * • Refactor: Refactored `TempToken` module, added value lookup mechanism for tokens. **[Important]** + * • Upgrade: Refactored and upgraded `SaTokenContext` context read/write strategy. **[Important]** + * • Added: New Mock context module. **[Important]** + * • Removed: Removed secondary context module. + * • Added: New demo for usage in asynchronous scenarios. **[Important]** + * • Added: New `Base32` encoding utility class. + * • Added: New `CORS` cross-origin strategy handler, providing a unified cross-origin solution across different architectures. + * • Added: `renewTimeout` renewal method now includes token terminal information validity check. + * • Added: Global configuration item `cookieAutoFillPrefix`: whether to automatically fill the token prefix in cookie mode. + * • Added: Global configuration item `rightNowCreateTokenSession`: whether to immediately create the corresponding `Token-Session` upon login. + * • Optimization: Optimized `Token-Session` retrieval algorithm to reduce cache read次数. + * • Added: `SaLoginParameter` supports configuring `SaCookieConfig` for Cookie-related parameters. + * • Modified: The registration order of the firewall validation filter changed to -102. + * • Added: Firewall `hook` registration新增 `registerHookToFirst`, `registerHookToSecond` methods for more flexible control of hook order. + +* • **Plugins**: + * • Added: `sa-token-quick-login` plugin now supports authentication via `Http Basic`. + +* • **Unit Tests**: + * • Completion: Added unit tests for the `Temp Token` module. + +* • **Documentation**: + * • Completion: Updated the list of sponsors. + * • Fix: Fixed incorrect dependency example description in the `Thymeleaf` integration documentation. + * • Fix: Fixed erroneous description in the `unionid` section. + * • Optimization: Optimized the SSO Mode三 single sign-out steps with more detailed descriptions. + * • Added: Added Cookie viewing step demonstration diagram to the login authentication documentation. + * • Added: Added a note in the multi-account mode: `LoginType` cannot be changed at runtime. + * • Added: Added QA for multi-account mode: determining which system's account is currently logged in within an interface. + * • Added: Added QA: resolving errors when introducing Sa-Token in lower versions of `SpringBoot (<2.2.0)`. + * • Added: Added QA: In integrated frontend-backend projects, how to return to the original page after login when redirected to the login page due to interception for not being logged in? + * • Added: Added QA: How to cluster Sa-Token integrated with Redis? + * • Added: Added QA: How to customize the way the framework reads tokens? + * • Added: Added QA: Troubleshooting possible reasons why modifying the `hosts` file is ineffective. + * • Added: Added QA: How to prevent CSRF attacks. + * • Added: New chapter: "Asynchronous & Mock Context". + * • Upgrade: Upgraded the "Custom SaTokenContext Guide" chapter documentation. + +Direct link to the online changelog documentation: https://sa-token.cc/doc.html#/more/update-log + +### 🌟 Other + +Code repository address: https://gitee.com/dromara/sa-token + +Framework functional structure diagram: + +![](/assets/img/news/Sa-Token-v1.42.0-5.png) + diff --git a/src/news/Sa-Token-v1.43.0.md b/src/news/Sa-Token-v1.43.0.md new file mode 100644 index 0000000000..e93e5deeab --- /dev/null +++ b/src/news/Sa-Token-v1.43.0.md @@ -0,0 +1,295 @@ +--- +title: "Sa-Token v1.43.0 Released: Adds SSO Single-Device Logout, Message Push, and Multi Access-Token Coexistence Capability" +author: May 21, 2025 09:28 +date: 2025-05-21 +cover: /assets/img/news/Sa-Token-v1.43.0-0.png +head: + - - meta + - name: News +--- + +Sa-Token is a **free**, **open-source**, lightweight Java permission authentication framework designed to solve a series of permission-related issues: **login authentication**, **permission authentication**, **single sign-on (SSO)**, **OAuth2.0**, **microservices gateway authentication**, and more. 🔐 + +![](/assets/img/news/Sa-Token-v1.43.0-0.png) + +sa-token-jss--tran + +**The latest version, `v1.43.0`, has been pushed to the `Maven` Central Repository** 🎉. You can include it via the following dependency: + +```xml + + + cn.dev33 + sa-token-spring-boot-starter + 1.43.0 + +``` + +This version includes a large number of ⛏️ new features, ⛏️ underlying refactoring, ⛏️ code optimizations, and more. Below are some of the more important updates for your reference: + +### ⛏️ Update 1: SSO Module Adds "Single-Device Logout" Mode + +Where there is single sign-on, there must be single logout. The SSO module now includes three modes: single application logout, single device logout, and full-end logout. How to understand the differences? + +For example, a user logs into Application A, B, and C on Chrome browser, and also logs into Application A and B on Firefox browser. Then: + +* • Clicking "Single Application Logout": The user will only be logged out of Application A; other applications remain logged in. +* • Clicking "Single Device Logout": All applications logged into via the Chrome browser will log out together, but applications logged into via Firefox remain unaffected. +* • Clicking "Full-End Logout": All sessions across all browsers are logged out simultaneously. + +These logout methods can be achieved with just one line of code or a single API call. For details, refer to the online documentation: [Sa-Token SSO Single Logout](https://sa-token.cc/doc.html#/sso/sso-logout). + +### ⛏️ Update 2: SSO Module Adds Message Push Mechanism + +This proposal originated from a discussion in the community exchange group: + +![](/assets/img/news/Sa-Token-v1.43.0-1.png) + +sa-sso-message-push-tian.png + +The developer inquired about adding functionality for systems to notify each other of token renewals in the SSO module to achieve strong synchronization of session validity periods between systems. + +This update doesn't just add these two APIs but builds a underlying message push system from the ground up. It allows an sso-client to construct an HTTP request in a specific format and call the `/sso/pushS` endpoint on the sso-server. The sso-server processes the message and responds to the sso-client. + +Message pushing is mutual; the sso-server can also construct HTTP requests to call the `/sso/pushC` endpoint on an sso-client. + +The message push mechanism serves as a bridge for communication between applications and the authentication center. Actions like ticket validation and single logout rely on this mechanism. + +You can also extend the message push capability by customizing message handlers, which is very helpful for achieving custom data interactions between your application and the authentication center. + +Suppose we have the following requirement: An sso-client needs to pull the nickname, avatar, and other information (i.e., user profile data) for a specific account ID from the sso-server. + +First, we need to implement a message handler on the sso-server: + +```java +@RestController +public class SsoServerController { + + // Configure SSO parameters + @Autowired + private void configSso(SaSsoServerTemplate ssoServerTemplate) { + + // Add message handler: userinfo (Get user profile) (Used to expose an interface for client to pull data) + ssoServerTemplate.messageHolder.addHandle("userinfo", (ssoTemplate, message) -> { + System.out.println("Message received: " + message); + + // Custom return result (simulated) + return SaResult.ok() + .set("id", message.get("loginId")) + .set("name", "LinXiaoLin") + .set("sex", "Female") + .set("age", 18); + }); + + } + +} +``` + +The sso-client configuration file needs to specify the message push URL: + +```yaml +# sa-token configuration +sa-token: + # sso-client related configuration + sso-client: + # Application identifier + client: sso-client3 + # sso-server message push URL + push-url: http://sa-sso-server.com:9000/sso/pushS + # API call secret key + secret-key: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor +``` + +Then, where you need to pull the data: + +```java +// Query my account info: sso-client frontend -> sso-center backend -> sso-server backend +@RequestMapping("/sso/myInfo") +public Object myInfo() { + // If not logged in + if( ! StpUtil.isLogin()) { + return "Not logged in, unable to retrieve"; + } + + // Get local loginId + Object loginId = StpUtil.getLoginId(); + + // Build message object + SaSsoMessage message = new SaSsoMessage(); + message.setType("userinfo"); + message.set("loginId", loginId); + + // Push to sso-server and receive response data + SaResult result = SaSsoClientUtil.pushMessageAsSaResult(message); + + // Return to frontend + return result; +} +``` + +For details, refer to the online documentation: [Sa-Token SSO Message Push Mechanism](https://sa-token.cc/doc.html#/sso/sso-message). + +### ⛏️ Update 3: SSO Adds ReSdk Integration Mode + +The Sa-Token SSO module has always supported integration with non-Sa-Token tech stacks, even non-Java projects. Previously, the provided integration method was the NoSdk mode. + +NoSdk mode means not integrating Sa-Token and directly calling the SSO-Server interfaces via HTTP utility classes. + +Reference demo: [sa-token-demo-sso3-client-nosdk](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso3-client-nosdk) + +This demo assumes the application does not use any "permission authentication framework" and uses the most basic ServletAPI for session management, simulating the handling logic for the `/sso/login`, `/sso/logout`, and `/sso/logoutCall` interfaces. + +However, the NoSdk example will no longer be maintained for the following reasons: + +* • 1. The NoSdk demo essentially rewrites the Sa-Token SSO module code using HTTP utility classes, which is cumbersome and redundant. +* • 2. The rewritten code cannot possess the full capabilities of the Sa-Token SSO module; it can only handle basic integration, making it a simplified SDK. + +The latest version recommends using the ReSdk mode for integration: ReSdk Mode (Rewrite部分SDK methods): Integrate with the SSO-Server by overriding key framework methods. + +Reference demo: [sa-token-demo-sso3-client-resdk](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso3-client-resdk) + +Advantages of ReSdk Mode: + +1. Still supports the client using any technology stack. +2. Integration is achieved by overriding only a small number of key code sections. Almost all capabilities of the Sa-Token SSO module can be obtained. + +### ⛏️ Update 4: OAuth2 Adds Multi Access-Token Coexistence Capability + +A highly requested proposal: `Can the OAuth2 module keep the old access_token valid when a new one is generated?`. + +Fix issues: #IBHFD1, #IBLL4Q, #724 + +![](/assets/img/news/Sa-Token-v1.43.0-2.png) +sa-many-access-token-issue1 + +![](/assets/img/news/Sa-Token-v1.43.0-3.png) +sa-many-access-token-issue2 + +![](/assets/img/news/Sa-Token-v1.43.0-4.png) +sa-many-access-token-issue3 + +In previous versions, due to limitations in the index data structure design, applying for a new `access_token` would immediately invalidate the old one. The new version refactors the index data structure, adding the capability for multiple `Access-Tokens` to coexist. + +Relevant API examples: + +```java +// Get AccessTokenModel, returns null for invalid AccessToken +SaOAuth2Util.getAccessToken(accessToken); + +// Check Access-Token, returns AccessTokenModel on success, throws exception on failure +SaOAuth2Util.checkAccessToken(accessToken); + +// Get Access-Token list: All Access-tokens issued by this application for a specific user +SaOAuth2Util.getAccessTokenValueList(clientId, loginId); + +// Check if the specified Access-Token has the specified Scope list, returns true or false +SaOAuth2Util.hasAccessTokenScope(accessToken, ...scopes); + +// Verify if the specified Access-Token has the specified Scope list, throws exception if not +SaOAuth2Util.checkAccessTokenScope(accessToken, ...scopes); + +// Get the LoginId represented by the Access-Token +SaOAuth2Util.getLoginIdByAccessToken(accessToken); + +// Get the clientId represented by the Access-Token +SaOAuth2Util.getClientIdByAccessToken(accessToken); + +// Revoke an Access-Token +SaOAuth2Util.revokeAccessToken(accessToken); + +// Revoke all Access-Tokens: All Access-Tokens for a specified user under a specified application +SaOAuth2Util.revokeAccessTokenByIndex(clientId, loginId); +``` + +Relevant online documentation section: [Sa-Token-OAuth2 Common Methods](https://sa-token.cc/doc.html#/oauth2/oauth2-method) + +### ⛏️ Update 5: Simplify Core Package Function Modules, Split Packages + +Discussions in the community group mentioned that the sa-token-core package was overly crowded and complex. In response, some functional modules have been split off into separate plugin packages: + +* • Split: API Key module split into independent plugin package: `sa-token-apikey`. +* • Split: API Sign module split into independent plugin package: `sa-token-sign`. + +Furthermore, this update adds the following plugin packages: + +* • New: Added `sa-token-forest` plugin for integrating Forest in the HTTP request handler module. +* • New: Added `sa-token-okhttps` plugin for integrating OkHttps in the HTTP request handler module. + +### 📜 Full Changelog + +Besides the points mentioned above, there are more updates that cannot be detailed one by one. Below is the complete changelog for version v1.43.0: + +* • core: + * • New: `SaLogoutParameter` adds `deviceId` parameter for controlling logout of a specific device id. **[Important]** + * • New: Added `SaHttpTemplate` request handler module. + * • New: TOTP adds `issuer` field. merge: pr 329 + * • Fix: Fixed issue where HTTP Digest authentication failed when the URL contained query parameters. merge: pr 334 + • New: @SaCheckOr annotation adds `append` field for capturing undefined annotation types for batch annotation permission checks. + • New: Listener `doRenewTimeout` method adds loginType parameter. + • New: `SaInterceptor` adds `beforeAuth` authentication pre-function. + +* • SSO: + • New: Single logout supports single device logout. **[Important]** fix: #IA6ZK0, #747 + • New: Added message push mechanism. **[Important]** fix: #IBGXA7 + • New: Configuration item `clients` for individually configuring authorization information for each client. **[Important]** + • New: Configuration item `allowAnonClient` decides whether to enable anonymous clients. + • New: SSO module adds configuration file method to enable "different secrets for different clients" capability. + • Refactor: sso-client encapsulation for obtaining client identifier value. + • New: Added SSO Strategy class. + • New: sso-client adds `convertCenterIdToLoginId`, `convertLoginIdToCenterId` strategy functions to describe conversion rules between local LoginId and authentication center loginId. + • New: sso-server adds `jumpToRedirectUrlNotice` strategy for notification before authorization redirect jump. + • Optimize: Adjust overall SSO example code. + • New: Added ReSdk mode integration example: `sa-token-demo-sso3-client-resdk`. **[Important]** + • New: Added anonymous application mode integration example: `sa-token-demo-sso3-client-anon`. **[Important]** + +* • OAuth2: + • New: `SaClientModel` adds `isAutoConfirm` configuration item to decide whether an application can automatically confirm authorization. **[Important]** + • New: Multi `Access-Token` coexistence, multi `Refresh-Token` coexistence, multi `Client-Token` coexistence capability. **[Important]** fix: #IBHFD1, #IBLL4Q, #724 + • New: Scope separator supports plus sign. merge: pr 333 + • Fix: Fixed issue under oidc protocol where id_token contained old information after user data changes. + • Optimize: Added strong reminder that the OAuth2 Password grant mode requires rewriting the handler. + • Optimize: Moved authentication flow callback from `SaOAuth2ServerConfig` to `SaOAuth2Strategy`. + • New: Added `SaOAuth2Strategy.instance.userAuthorizeClientCheck` strategy to check if a specified user can authorize a specified application. fix: #553 + • Optimize: Optimized and adjusted the code structure and comments of the `sa-token-oauth2` module. + • New: `currentAccessToken()`, `currentClientToken()`, simplifying the steps to read `access_token`, `client_token`. + +* • Plugins: + • New: Added `sa-token-forest` plugin for integrating Forest in the HTTP request handler module. + • New: Added `sa-token-okhttps` plugin for integrating OkHttps in the HTTP request handler module. + • Split: API Key module split into independent plugin package: `sa-token-apikey`. + • Split: API Sign module split into independent plugin package: `sa-token-sign`. + • Fix: Fixed context control errors in certain scenarios in the `sa-token-dubbo` plugin. + • Fix: Fixed issue where `SaSessionForSnack3Customized:getModel` in `sa-token-sanck3` would error when receiving map values. merge: pr 330 + • Fix: Fixed deserialization error when using `sa-token-redis-template-jdk-serializer`. merge: pr 331 + • Fix: `sa-token-snack3` optimized `objectToJson` serialization handling (adds class name, but not root class name). + • Refactor: Refactored `update` method TTL acquisition to milliseconds in `sa-token-redis-template`, `sa-token-redis-template-jdk-serializer` plugins to reduce TTL calculation error during update. **[Important]** + +* • Examples: + • New: Added SSE authentication example. + +* • Documentation: + • New: Added offline version download for documentation. + • New: Added framework feature list illustration. + • New: Added example: How to call Sa-Token synchronous API in Filter under reactive environment. + • New: Added QA: Error when importing source code in idea: java: package cn.dev33.satoken.oauth2 does not exist. + • New: Added QA: Added QA: Error: SaTokenContext context not initialized. + • New: Added QA: Error when importing source code in idea: java: package cn.dev33.satoken.oauth2 does not exist. + • New: Rewrote route matching algorithm correction to the latest写法. + • New: Fixed incorrect descriptions in the OAuth2 UnionId section. + • Optimize: Improved QA: Accessed a non-existent route, error: SaTokenContext context not initialized. fix: #771 + • Optimize: Added missing configuration field descriptions in the sso module. + • Optimize: OAuth2-Server example added real form. + • New: Documentation added example for rewriting `PasswordGrantTypeHandler` handler. + • New: sso and oauth2 chapters added descriptions of overridable strategies. + +* • Others: + • New: Readme added framework feature introduction diagram. + • New: SSO module added mind map explanation. + • New: Readme added Forest friendship link. + +Direct link to the online changelog documentation: [https://sa-token.cc/doc.html#/more/update-log](https://sa-token.cc/doc.html#/more/update-log) + +### 🌟 Others + +Code repository address: [https://gitee.com/dromara/sa-token](https://gitee.com/dromara/sa-token) \ No newline at end of file diff --git a/src/news/Skyeye-April-update.md b/src/news/Skyeye-April-update.md new file mode 100644 index 0000000000..aa9bce8ad6 --- /dev/null +++ b/src/news/Skyeye-April-update.md @@ -0,0 +1,57 @@ +--- +title: Skyeye Cloud April Version Update Content +author: Resource Site +date: 2025-04-21 +cover: /assets/img/news/Skyeye-April-update-1.png +head: + - - meta + - name: News +--- + +Skyeye Cloud April Version Update Content + +VUE Version Refactoring: A total of 299 features, currently 80% refactored (specific feature list below). + +Additional new mini-tools added in the VUE version: code formatting, JSON formatting, image compression, image watermarking, image background replacement, image grayscale conversion, text conversion. + +Mobile version新增聊天功能 (Note: The first version currently only supports basic message sending and receiving; further improvements will follow). + +云校园 + 表白墙 (Cloud Campus + Confession Wall) version预计共 49 个 features (specific feature list below). + +Mobile interface optimization. + +Low-code platform新增虚拟属性 (virtual attributes), supporting dynamic attribute addition; currently supports text and numeric data types. + +Fixed several issues. + +新增多租户底层逻辑代码处理 (Note: Pending integration with business logic). + +Layui version framework overall optimization, e.g., adding input field text length limits, mandatory field restrictions for some components, text overflow handling, etc. + +![](/assets/img/news/Skyeye-April-update-0.png) + +**Multi-Tenancy** + +01 + +Pre-sale price is now available. Contact the author after joining the星球 (community) to purchase. + +02 + +Scheduled for release in October 2025. + +03 + +Pre-sale price deadline: May 31, 2025. + +VUE Version: + +Added a PC frontend for the VUE version, using the same backend as the Layui version. Currently 80% refactored, with the remaining 20% to be developed in the next version. For reference, see the image below. + +![](/assets/img/news/Skyeye-April-update-1.png) + +云校园 (Cloud Campus): + +Building a campus teaching and entertainment platform. The first version features are as follows: + +![](/assets/img/news/Skyeye-April-update-2.png) \ No newline at end of file diff --git a/src/news/Skyeye-v2.5.8.md b/src/news/Skyeye-v2.5.8.md new file mode 100644 index 0000000000..e5eae45daa --- /dev/null +++ b/src/news/Skyeye-v2.5.8.md @@ -0,0 +1,105 @@ +--- +title: 【Skyeye Cloud Major Update】10 Modules Fully Upgraded! Multi-Tenant Architecture + Smart Finance + Intelligent Store, A New Experience with Doubled Efficiency +author: Resource Site +date: 2025-08-11 +cover: /assets/img/news/Skyeye-v2.5.8-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/Skyeye-v2.5.8-0.png) + +This version update focuses on enhancing the core functionalities of the system. By supporting multi-tenant architecture, a flexible printing template system, a comprehensive front-end framework upgrade, and in-depth optimizations across various business modules, it provides users with a more efficient, intelligent, and professional experience. + +Project Address: https://gitee.com/dromara/skyeye + +Feature List: https://www.kdocs.cn/l/cbf2cgCLrUyz + +01# + +Core Function Updates + +CORE FUNCTIONS + +![](/assets/img/news/Skyeye-v2.5.8-1.png) + +1. Multi-Tenant Functionality + Implemented a new multi-tenant architecture support, offering a more secure and flexible data management solution for enterprise-level applications. + +• Added support for multi-tenant architecture, enabling data isolation and permission control +• Supports personalized configuration and independent management interfaces for tenants +• Optimized tenant resource allocation and performance monitoring mechanisms + +2. Printing Template System + Introduced a flexible custom printing template designer to meet diverse document output needs. + +• Added a custom printing template designer +• Supports multiple export formats (PDF/Excel/Word) +• Fine-grained template permission management and version control + +3. VUE 100% Full Coverage + Completed a comprehensive upgrade of the front-end framework and component-based refactoring, significantly improving user experience and system performance. + +• Front-end framework fully upgraded to the latest version of Vue +• Achieved component-based refactoring for all business modules +• Improved page loading speed and interactive response performance + +4. Production Management Module + Enhanced end-to-end production process management capabilities and optimized production scheduling and resource allocation efficiency. + +• Added production planning scheduling and progress tracking functionality +• Optimized material requirement calculation and inventory warning mechanisms +• Improved production quality traceability and exception handling processes + +5. Scheduling Management System + Upgraded the scheduling management system, providing intelligent scheduling and a convenient personnel management experience. + +• Optimized intelligent scheduling algorithms with support for multi-dimensional rule configuration +• Automatic detection of employee attendance and scheduling conflicts +• Mobile scheduling viewing and one-click shift change functionality + +Overall system performance optimization, with an average response speed improvement of 30%. Enhanced security protection mechanisms, refined user experience details, and simplified operational processes. + +02# + +Other Improvements + +OTHER IMPROVEMENTS + +![](/assets/img/news/Skyeye-v2.5.8-2.png) + +This version also includes enhancements and optimizations for the following key functional modules, covering multiple business scenarios and improving overall system efficiency: + +6. Scheduling Management System (New Sub-Features) + • **Workstation Management**: Visual workstation allocation and real-time status monitoring + • **Shift Management**: Customizable shift templates and rotation rule configuration + • **Scheduling Management**: Optimized intelligent scheduling algorithms with support for multi-dimensional rule adaptation + • **Temporary Worker Leave Application**: Mobile quick leave process and approval management + • **Temporary Worker Monthly Salary Details**: Automatic hours calculation and salary detail generation + +7. ERP Module Upgrade + • **Sales Exchange**: Complete exchange process tracking and inventory linkage + • **Sales Return**: Return reason classification statistics and refund time optimization + • **Loan-Out Management**: Asset loan registration and到期 reminder + • **Return Management**: Return acceptance process and status change records + +8. Intelligent Store System + • **Store Order Management**: Integrated online and offline order processing + • **Shipping Template Billing Configuration**: Multi-dimensional customizable shipping rules and intelligent calculation + • **Store Beautification**: Updated store decoration template library with support for visual editing + +9. Financial System Enhancements + • **Income and Expenditure Management / Financial Account / Ledger Management / Accounting Subjects** + • **Voucher Management / Voucher Upload / Payment and Receipt Management / Fund Statistics** + • **Accounts Receivable / Accounts Payable / Invoice Issuance / Invoice Collection** + • **Invoice Statistics / Expense Application / Expense Reimbursement / Expense Analysis** + • **Loan Management / Repayment Management / Loan and Repayment Statistics** + +10. Recruitment Module + **Recruitment Analysis**: Multi-dimensional recruitment data dashboard, including channel efficiency, resume conversion rate, and recruitment cycle analysis. + +System Version | V2.5.8 +Release Date | August 2025 +Update Scope | Full System Function Upgrade +Feedback Suggestions | WeChat Discussion Group \ No newline at end of file diff --git a/src/news/Skyeye-v3.14.17.md b/src/news/Skyeye-v3.14.17.md new file mode 100644 index 0000000000..5cee1309df --- /dev/null +++ b/src/news/Skyeye-v3.14.17.md @@ -0,0 +1,114 @@ +--- +title: Skyeye Cloud v3.14.17 Major Source Code Release +author: Resource Site +date: 2024-12-16 +cover: /assets/img/news/Skyeye-v3.14.17-0.png +head: + - - meta + - name: News +--- + +Skyeye Cloud Smart Manufacturing adopts a low-code platform built with Spring Boot and WinUI, while the mobile端 uses UNI-APP. It includes over 30 application modules and more than 50 electronic processes, covering CRM, PM, ERP, MES, ADM, EHR, notes, knowledge base, projects, stores, mall, finance, multi-shift attendance, payroll, recruitment, cloud after-sales service, forums, announcements, surveys, report design, workflows, schedules, cloud storage, and comprehensive management, achieving integrated management in the smart manufacturing industry. It enables efficient operation of management processes: "customer relationship → online/offline quotes → sales quotes → sales contracts → production planning → product design → procurement → processing and manufacturing → warehousing → delivery → after-sales service." It also manages enterprise employees and internal operational processes,完善ing functions such as employee "onboarding → training → probation → office work → resignation." + +**Skyeye Cloud [Source Code] is open-sourced for {Planet Users}. After obtaining the source code, it can be used for learning, graduation projects, enterprises, etc.** + +**Payment integration has been implemented. As the author is an individual, it has not been fully debugged yet. Customers need to perform their own debugging.** + +Skyeye Cloud Smart Manufacturing v3.14.17 is released with the following updates: + +* **Added comparison table with other software: https://docs.qq.com/sheet/DYUtPdWhTbVBITlpL?tab=000001** + +* **Skyeye Cloud has joined the Dromara community** + +* **Mall (supports PC and mobile端)** + +* **Added regional package management** + +* **Added express company management** + +* **Added express template management** + +* **Added membership management** + +* **Added advertising space management** + +* **Membership management module supports synchronization with ERP members, registration, login, etc.** + +* **Personal center supports user center, account security, location information maintenance, etc.** + +* **Order center supports placing orders, canceling orders, etc.** + +* **Order evaluation/follow-up evaluation** + +* **Added coupon management, my coupons, etc.** + +* **Added address management** + +* **Added homepage carousel images** + +* **Added brand management** + +* **Supports store search and product search** + +* Added shopping cart management + +* Skyeye Cloud + +* Added payment application management + +* Added payment channel management + +* Added support for file management + +* Added SMS channel management + +* Added SMS template management + +* Upgraded Spring Boot version, some components upgraded to corresponding versions + +* ERP + MES + +* Added product listing management for the mall + +* Pre-production planning integrates product source types (self-produced/externally purchased), can convert to purchase orders, production plan orders + +* VUE version of Skyeye Cloud has added 80+ components, expected to be released in November 2025. Users who have purchased the mobile端 and whose planet membership has not expired can obtain the VUE version. + +* Added offline document project management + +* **Source code is open to planet users** + +* Fixed several issues. + + +Skyeye features **low-code, rapid development, visual design, microservices**, etc., facilitating secondary development for customers and greatly improving development efficiency. + +Address 1: https://gitee.com/doc_wei01/skyeye +Address 2: https://gitee.com/dromara/skyeye + +**Screenshots** + +| Screenshot | Screenshot | +| ------------------------------------------- | ------------------------------------------- | +| ![](/assets/img/news/Skyeye-v3.14.17-0.png) | ![](/assets/img/news/Skyeye-v3.14.17-1.png) | +| ![](/assets/img/news/Skyeye-v3.14.17-2.png) | ![](/assets/img/news/Skyeye-v3.14.17-3.png) | +| ![](/assets/img/news/Skyeye-v3.14.17-4.png) | | +| ![](/assets/img/news/Skyeye-v3.14.17-5.png) | ![](/assets/img/news/Skyeye-v3.14.17-6.png) | +| ![](/assets/img/news/Skyeye-v3.14.17-7.png) | ![](/assets/img/news/Skyeye-v3.14.17-8.png) | + +**Screenshots** + +| Screenshot | Screenshot | Screenshot | Screenshot | +| -------------------------------------------- | -------------------------------------------- | -------------------------------------------- | -------------------------------------------- | +| ![](/assets/img/news/Skyeye-v3.14.17-9.png) | ![](/assets/img/news/Skyeye-v3.14.17-10.png) | ![](/assets/img/news/Skyeye-v3.14.17-11.png) | ![](/assets/img/news/Skyeye-v3.14.17-12.png) | +| ![](/assets/img/news/Skyeye-v3.14.17-13.png) | ![](/assets/img/news/Skyeye-v3.14.17-14.png) | ![](/assets/img/news/Skyeye-v3.14.17-15.png) | ![](/assets/img/news/Skyeye-v3.14.17-16.png) | + +About Dromara + +Dromara is an open-source community composed of top domestic open-source project authors. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, scheduling orchestration, etc. The technology stack is fully open-source and co-built, maintaining community neutrality, and is committed to providing global users with microservices cloud-native solutions. Let every participating open-source enthusiast experience the joy of open source. + +The Dromara open-source community currently has 10+ GVP projects, with a total star count of over 100,000, building an open-source community of tens of thousands of people, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to interact with me** + +![](/assets/img/news/Skyeye-v3.14.17-17.png) \ No newline at end of file diff --git a/src/news/Skyeye-v3.15.14.md b/src/news/Skyeye-v3.15.14.md new file mode 100644 index 0000000000..d1cd2fc85d --- /dev/null +++ b/src/news/Skyeye-v3.15.14.md @@ -0,0 +1,108 @@ +--- +title: Skyeye Cloud Intelligent Manufacturing Office System VUE Version v3.15.14 Released +author: Resource Site +date: 2025-03-27 +cover: /assets/img/news/Skyeye-v3.15.14-0.png +head: + - - meta + - name: News +--- + +Skyeye Cloud Intelligent Manufacturing adopts a low-code platform based on Springboot + WinUI, with the mobile端 (end) using UNI-APP. It includes over 30 application modules, more than 50 types of electronic workflows, and comprehensive management features such as CRM, PM, ERP, MES, ADM, EHR, Notes, Knowledge Base, Projects, Stores, Mall, Finance, Multi-Shift Attendance, Payroll, Recruitment, Cloud After-Sales Service, Forum, Announcements, Surveys, Report Design, Workflow, Schedule, Cloud Drive, and more. It achieves integrated management for the intelligent manufacturing industry. It enables the efficient operation of management processes: "Customer Relationship -> Online/Offline Quotation -> Sales Quotation -> Sales Contract -> Production Planning -> Product Design -> Procurement -> Processing & Manufacturing -> Warehousing -> Shipping -> After-Sales Service," while also facilitating employee management and internal operational processes,完善 (perfecting) functions for employees from "Onboarding -> Training -> Regularization -> Office Work -> Resignation." + +**FAQ**      **Development Docs**      **Video Tutorials**      **Feature List** + +**Skyeye Cloud [Source Code] is open source for {Community Members}. After obtaining the source code, it can be used for learning, graduation projects, enterprises, etc.** + +Skyeye Cloud Intelligent Manufacturing v3.15.14 has been released. The release content is as follows: + +* **Skyeye Cloud has joined the Dromara Community** +* **VUE Version of Skyeye Cloud** + +* **65** components have been refactored. VUE version refactoring progress can be referenced at: https://kdocs.cn/l/cbf2cgCLrUyz + +* The VUE version is expected to have a total of **289** features. Currently, **80%** has been refactored, meeting the standard for the April version. + +* The VUE version is expected to have a total of **289** features. **231** features have been handed over for testing, and currently, **226** features have completed testing. + +* Resolved issues existing in the Layui version. + +* School Module - Mobile端 (end) added chat and friends functionality. + +* School Module - VUE version test papers, improved editing, design (single-choice matrix, multiple-choice matrix, fill-in-the-blank matrix, rating matrix, pagination, score design). + +* School Module - Confession Wall added posts, circles, and video features. + +* **Key Point: The first version of the School Module, including the Confession Wall and Online Classroom, will be released in April.** + +* **VUE Version Skyeye Cloud** + + Completed development of **100+** basic components. + +* **Source code is open to Community Members.** +* Resolved several issues. + + +Skyeye features **low-code, rapid development, visual design, microservices**, etc., facilitating secondary development for customers and greatly improving development efficiency. + +ERP: https://gitee.com/doc_wei01/skyeye + +OA: https://gitee.com/dromara/skyeye + +Reports: https://gitee.com/doc_wei01/skyeye-report  For any issues, please contact the author. Details can be found in the development plan. + +**PC端 (End) Screenshots** + + +| Screenshot | Screenshot | +| -------------------------------------------- | -------------------------------------------- | +| ![](/assets/img/news/Skyeye-v3.15.14-0.png) | ![](/assets/img/news/Skyeye-v3.15.14-1.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-2.png) | ![](/assets/img/news/Skyeye-v3.15.14-3.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-4.png) | ![](/assets/img/news/Skyeye-v3.15.14-5.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-6.png) | ![](/assets/img/news/Skyeye-v3.15.14-7.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-8.png) | ![](/assets/img/news/Skyeye-v3.15.14-9.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-10.png) | ![](/assets/img/news/Skyeye-v3.15.14-11.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-12.png) | ![](/assets/img/news/Skyeye-v3.15.14-13.png) | + + +**Mobile End Screenshots** + + + + + + + + + + + + + + + +
+ Screenshot + + Screenshot + + Screenshot + + Screenshot +
+
+ Image +
+
+
+ Image +
+
+
+ Image +
+
+
+ Image +
+
\ No newline at end of file diff --git a/src/news/Stream-Query-0.md b/src/news/Stream-Query-0.md new file mode 100644 index 0000000000..6181ed6564 --- /dev/null +++ b/src/news/Stream-Query-0.md @@ -0,0 +1,62 @@ +--- +title: Farewell to Tedious Mappers! Stream-Query Officially Joins GitCode Platform +author: August 4, 2025, 08:54 +date: 2025-08-04 +cover: /assets/img/news/Stream-Query-0-0.gif +head: + - - meta + - name: News +--- + +![](/assets/img/news/Stream-Query-0-0.gif) + +In the Java development field, MyBatis-Plus, as an enhancement tool for MyBatis, is widely popular for simplifying CRUD operations. But have you ever been troubled by these issues? + +🤕 **Proliferation of Mapper Interfaces**: Every entity class requires a Mapper, resulting in projects cluttered with XxxMapper.java files. +😠 **Cumbersome Complex Queries**: Handling one-to-many queries requires manually querying the database multiple times in loops, leading to code that is as difficult to maintain as "spaghetti code." +😣 **Repetitive Stream Operations**: The same `stream().filter().map()` pattern is written countless times, with no elegant way to encapsulate it. + +Now, the open-source project Stream-Query has officially joined the GitCode platform as a G-Star outstanding graduated project, set to completely change your perception of MyBatis-Plus! + +![](/assets/img/news/Stream-Query-0-1.jpg) + +**🚀 Project Highlight: Operate the Database Like Using Utility Classes** + +Stream-Query’s core goal is to free developers entirely from the constraints of Mappers. With this project, you can perform database operations as easily as calling static methods of utility classes, significantly improving development efficiency. + +![](/assets/img/news/Stream-Query-0-2.png) + +**✨ Three Core Features** + +1. **Dynamic Mapper Innovation**: The project uses Byte-Buddy technology to dynamically generate Mappers at runtime, eliminating the need to write tedious Mapper interfaces for each entity class. + +2. **Stream-Style Query Processing**: For complex query scenarios, Stream-Query provides convenient handling of one-to-one, one-to-many, and other relationships, effortlessly assembling data from multiple single-table queries. + +3. **Simplified Stream Operations**: The uniquely crafted Steam class (Steam = Stream-r, where "r" stands for Repeat) is specifically designed to reduce repetitive Stream API code, making functional programming more elegant. + +**🎯 Project Vision and Mission** + +Stream-Query was born from a deep understanding of the pain points developers face when using ORMs in modern Java development. + +In daily development practices, the project’s founders observed that most developers spend too much time writing repetitive Mapper interfaces, handling complex association queries, and writing lengthy Stream operations. These mechanical tasks severely distract developers from focusing on core business logic. Based on these observations, the team was determined to create a tool that truly liberates developer productivity, making database operations as simple and intuitive as using utility classes. + +From its inception, the project established a clear technical philosophy: reduce complexity through intelligent encapsulation rather than simply adding features. This philosophy is reflected in the clever implementation of dynamic Mappers, which retain the powerful functionality of MyBatis-Plus while eliminating its most cumbersome aspects. In terms of Stream processing, the project innovatively introduced the concept of "Stream-r," freeing developers from repetitive stream operations. + +The team believes that through continuous technological innovation and community collaboration, Stream-Query has the potential to become the de facto standard for database operations in the Java ecosystem. In the future, Stream-Query will continue to optimize the performance of dynamic Mappers and further enrich the tool methods for Stream operations. Throughout this process, feedback and suggestions from every developer will serve as vital forces driving the project forward, collectively shaping the next generation of best practices for Java data access layers. + +--- + +**Visit the GitCode platform**: Experience Stream-Query now +💻 **Open Source License**: Apache License v2.0 +📮 **Project Address**: http://gitcode.com/dromara/stream-query + +![](/assets/img/news/Stream-Query-0-3.png) + +**Recommended Reading** +[![](/assets/img/news/Stream-Query-0-4.png)](https://mp.weixin.qq.com/s?__biz=MzkyNjY0MDY1Ng==&mid=2247491795&idx=1&sn=70e669c10491d17c0fda127ffc116a42&scene=21#wechat_redirect) +GitCode’s First Batch of Top 100 Open Source Projects Shortlist Announced + +[![](/assets/img/news/Stream-Query-0-5.png)](https://mp.weixin.qq.com/s?__biz=MzkyNjY0MDY1Ng==&mid=2247489603&idx=1&sn=66a5adee615b79d841558c0b54b5881f&scene=21#wechat_redirect) +【Submit and Win an iPhone 17】"My First Open Source Project" + +![](/assets/img/news/Stream-Query-0-6.png) \ No newline at end of file diff --git a/src/news/WGAI-0.md b/src/news/WGAI-0.md new file mode 100644 index 0000000000..ea44a7423b --- /dev/null +++ b/src/news/WGAI-0.md @@ -0,0 +1,66 @@ +--- +title: WGAI, a New AI Open-Source Project, Joins Dromara Community—A Lightweight and Modular AI Assistant Framework +author: WGAI +date: 2025-02-10 +cover: /assets/img/news/WGAI-0-0.jpg +head: + - - meta + - name: News +--- + +### 🚀 A New Beginning + +We are thrilled to announce that the WGAI project has officially joined the Dromara open-source community! This is a significant milestone, marking WGAI’s opportunity to share its unique capabilities with more developers and researchers, and to jointly drive progress in the field of artificial intelligence. + +About WGAI +WGAI is an open-source artificial intelligence project released under the Apache License 2.0. This means you are free to use, copy, and distribute this work under certain conditions. With WGAI, users can not only leverage pre-trained models for various applications but also customize model training according to their specific needs to meet the requirements of particular scenarios. + +![](/assets/img/news/WGAI-0-0.jpg) + +### 🚀 Feature Design + +- Execute tasks without knowing Python—no more worrying about technical barriers +- Online training, online annotation, and model lists +- Includes OCR, image-text, audio-video recognition, and more +- Local deployment of speech recognition and hotword configuration + +## 🚀 Four Core Advantages + +1. **Java Full-Stack Friendly** - Deep integration with the Spring ecosystem, offering `wgai-spring-boot-starter` +2. **Self-Training Models** - A standout feature of WGAI is its support for users to train models independently. This allows users, whether in academic research or industrial applications, to optimize model performance based on their own datasets and task objectives +3. **Industrial-Grade Deployment** - Supports ONNX export and TensorRT acceleration +4. **Simple and Easy to Use** - WGAI provides clean API interfaces and detailed documentation, enabling developers to quickly get started and integrate into their projects + +## 🛠️ Applicable Scenarios + +- **Security Monitoring** - Facial recognition (identity verification at airports, stations), behavior detection (identifying abnormal behaviors such as falls or fights, triggering real-time alerts) +- **Transportation and Logistics** - Autonomous driving (real-time recognition of road signs, pedestrians, and vehicles to assist navigation decisions), license plate recognition (automatic license plate recognition in highway ETC systems or parking lots) +- **Industrial Manufacturing** - Defect detection (AI visual inspection of product surface scratches and dimensional deviations to improve quality control efficiency), predictive maintenance (identifying equipment vibration or temperature data anomalies to predict failures) +- **Financial Security** - Fraud detection (analyzing transaction patterns to identify abnormal behaviors such as large transfers from unfamiliar locations), document verification (using OCR technology to automatically extract information from IDs and bank cards for authenticity verification) + +# 💻 Quick Start + +### One-Click Training + +![](/assets/img/news/WGAI-0-1.png) + +### One-Click Recognition + +![](/assets/img/news/WGAI-0-2.jpg) + +![](/assets/img/news/WGAI-0-3.jpg) + +![](/assets/img/news/WGAI-0-4.jpg) + +## 🚀 Open-Source Address + +WGAI is a lightweight, modular AI assistant framework designed to provide developers with easy-to-use tools for quickly building intelligent applications. Whether you are a beginner in the AI field or an experienced developer, WGAI offers powerful support. Everyone is welcome to try it out and provide feedback to help drive the project forward! + +If you find this project helpful, don’t forget to give it a Star! 🌟 + +- **Gitee**: https://gitee.com/dromara/wgai +- **GitHub**: https://github.com/dromara/wgai + +Thank you for your support! + +--- \ No newline at end of file diff --git a/src/news/WGAI-V2.0.md b/src/news/WGAI-V2.0.md new file mode 100644 index 0000000000..34692f077a --- /dev/null +++ b/src/news/WGAI-V2.0.md @@ -0,0 +1,82 @@ +--- +title: WGAI Training Platform V2.0 Major Upgrade +author: WGAI +date: 2025-03-05 +cover: /assets/img/news/WGAI-V2.0-0.png +head: + - - meta + - name: News +--- + +## **WGAI Training Platform V2.0 Grand Release: One-Click Model Distribution, Cross-Platform Sharing, and Seamless Third-Party Integration Usher in a New Era of Intelligent Collaboration!** + +**——Dromara Open-Source Community Adds Another Enterprise-Grade AI Tool** + +## **Introduction** + +In today's rapidly evolving AI landscape, how can we achieve efficient collaboration between model training and inference while lowering the barriers to enterprise intelligent transformation? The WGAI Training Platform, as a core AI development tool within the Dromara open-source ecosystem, has always aimed to "**simplify processes and empower collaboration**." With this V2.0 upgrade, we focus on three key areas: **model distribution efficiency**, **resource collaboration and sharing**, and **open ecosystem compatibility**, delivering solutions that better meet enterprise needs! + +* * * + +## **I. Analysis of Core Upgrade Highlights** + +#### **1. One-Click Model Distribution: 1 Training Machine, N Inference Machines, 300% Improvement in Resource Utilization** + +* **Feature Description**: Supports **one-click distribution of trained models to any inference platform**, automating the "training-inference" pipeline. Whether for edge devices or cloud clusters, deployment paths can be quickly configured via the platform interface, eliminating the hassle of manual export and script uploads. +* **Technical Advantages**: + * Based on dynamic resource scheduling algorithms, it automatically adapts to the interface requirements of different inference frameworks (such as TensorFlow Serving, ONNX Runtime, etc.). +* **User Value**: Enterprises can flexibly scale inference computing power, quickly respond to business peaks, and reduce idle training resource costs. + +![](/assets/img/news/WGAI-V2.0-0.png) + +#### **2. Model Sharing and Collaboration: Cross-Platform Resource Interoperability, Building an Enterprise AI Asset Pool** + +* **Feature Description**: Allows **model sharing between platforms based on permissions**, supporting distribution to specified inference machines or opening access to collaborative teams, enabling knowledge沉淀 (precipitation) and reuse. +* **Technical Highlights**: + * Provides model version tracking and dependency analysis, ensuring the reliability and consistency of shared models. +* **Use Case**: A manufacturing enterprise unified quality inspection standards and improved fault detection efficiency by 40% by sharing defect detection models across multiple production line inference platforms. + +![](/assets/img/news/WGAI-V2.0-1.png) + +#### **3. Seamless Third-Party Integration: Open Subscription Mechanism, Closing the Intelligent Business Loop** + +* **Feature Description**: New **subscription address and video stream interfaces** allow third-party systems to subscribe to model alerts via API, or push video streams directly to the platform for real-time analysis with results returned. +* **Technical Breakthrough**: + * Built-in multi-protocol parsing engine, compatible with mainstream video stream formats (RTSP/RTMP/HLS), reducing integration costs. +* **Application Scenario**: Security clients achieve millisecond-level response times by integrating video streams for real-time face recognition model calls, with anomaly event images automatically pushed to command centers. + +![](/assets/img/news/WGAI-V2.0-2.png) +![](/assets/img/news/WGAI-V2.0-3.png) + +* * * + +## **II. Technological Innovations Behind the Upgrade** + +This version leverages the powerful technical foundation of the Dromara community, deeply integrating the following capabilities: + +* **Low-Code Extension**: Provides a visual configuration interface, enabling users to complete third-party system integration without coding, aligning with the domestic low-code trend. + +* * * + +## **III. How Do Users Benefit?** + +* **Enterprise Managers**: Build cross-departmental AI collaboration networks, avoid duplicate development, and reduce model management costs by 70%. +* **Ecosystem Partners**: Open APIs and plugin mechanisms enable rapid integration of industry solutions. + +* * * + +## **IV. Experience Now** + +* **Gitee Repository**: https://gitee.com/dromara/wgai +* **GitHub Repository**: https://github.com/dromara/wgai +* **Demo Address**: http://116.198.227.105:8888 +* **Demo Video**: https://www.bilibili.com/video/BV13C9BYiEFS?t=38.4 +* **Join the Community**: + +![](/assets/img/news/WGAI-V2.0-4.jpg) + +* * * + +## **Conclusion** + +WGAI V2.0 is not just a technical upgrade but a reconstruction of the AI development paradigm. We believe that **openness, collaboration, and efficiency** will become the core keywords of future AI engineering. Upgrade and experience it now, and join the Dromara community in promoting the democratization of AI! \ No newline at end of file diff --git a/src/news/WGAI-V3.0.md b/src/news/WGAI-V3.0.md new file mode 100644 index 0000000000..43097d6a89 --- /dev/null +++ b/src/news/WGAI-V3.0.md @@ -0,0 +1,107 @@ +--- +title: "WGAI Star 800+ Training Platform V3.0 Launched: Full-Stack Digital Human Training Engine, Empowering You to Build Your Own Exclusive AI Agent!" +author: WGAI +date: 2025-05-16 +cover: /assets/img/news/WGAI-V3.0-0.png +head: + - - meta + - name: News +--- + +### 🔥 🔥 🔥 **WGAI Star 800+ Training Platform V3.0 Launched: Full-Stack Digital Human Training Engine, Empowering You to Build Your Own Exclusive AI Agent!** + +> 🚫 Solemn Promise: Permanently Free! No Commercial Version! + +> #### ❤ **Update Showcase** +> +> * ![](/assets/img/news/WGAI-V3.0-0.png) +> +> ​ +> +> * Turn an image into a dynamic digital human video / Set your own text and voice tone +> +> * https://img.nj-kj.com/zhangwei_1745562613859_1745465917540_1745567724504.mp4 +> +> * * * + +> #### ✨ **Major Updates** +> +> * ✅ Added a digital human training system – achieve a dynamic digital human with just one image +> +> * ✅ Added custom audio support with 0-179 voice tones to choose from +> +> * ✅ Added digital human management / custom voice TTS conversion +> +> * ✅ One-click training – so easy that even beginners can use it without understanding code! +> +> * * * + +> #### 📦 **Next Preview** +> +> * 🚀 Add real-time streaming digital human interaction for human-computer interaction +> +> * 🚀 Integrate digital humans with local large models for intelligent Q&A based on knowledge bases +> +> * * * + +### 🖼️ **Effect Demonstration** + +> #### **1. Digital Human Image Training: Customizable Digital Human** +> +> ![](/assets/img/news/WGAI-V3.0-1.png) +> +> +> +> * **Feature Description**: Supports **customizing digital human images** (hairstyle, clothing, expression, etc.) through the platform interface. Upload images, set names and background identities to create your exclusive digital human. +> +> * **Technical Advantages**: +> +> * Integrated AI Generative Adversarial Network (GAN) for one-click generation of high-precision faces and dynamic expressions. +> +> * Built-in lightweight rendering engine, compatible with multiple platforms including Web, mobile, and XR devices. +> + +> #### **2. Free Action Training and Choreography: Bring Digital Humans to Life** +> +> ![](/assets/img/news/WGAI-V3.0-2.png) +> +> * **Feature Description**: Provides a visual action editor supporting **custom gestures, body movements, and lip-syncing**. Achieve smooth and natural interactive performances through keyframe settings or AI motion capture technology. +> +> * **Technical Highlights**: \* Integrates bone binding and physics engine for smooth action transitions without lag. \* Supports automatic lip-syncing with voice content (based on LSTM time-series models) to enhance realism. +> + +> #### **3. Voice Cloning and Emotional Broadcasting: Hear the "Unique" AI** +> +> ![](/assets/img/news/WGAI-V3.0-3.png) +> +> ![](/assets/img/news/WGAI-V3.0-4.png) +> +> * **Feature Description**: Choose from 179 voice tones to generate personalized digital human voices; adjust emotional parameters (e.g., cheerful, serious, gentle) to suit different interactive scenarios. +> +> * **Technical Breakthroughs**: \* High-fidelity voice synthesis based on deep neural networks (Tacotron 2+WaveGlow). \* Integrated emotion recognition models (BERT+Prosody analysis) to make voice output more expressive. +> + +> #### **4. Upcoming Features Preview: Smart Q&A + Local Deployment, Unlocking Commercial Closed Loop** +> +> * **Coming Soon**: \* **Voice Model Smart Q&A**: Integrate large language models (e.g., ChatGLM, Llama 2) for multi-turn dialogues and business consultations (e.g., product inquiries, troubleshooting). \* **Localized Private Deployment**: Supports offline operation with complete data privatization, meeting high-security needs in sectors like finance and government. \* **API Ecosystem Expansion**: Open digital human driving interfaces for seamless integration into enterprise CRM, live streaming systems, and smart hardware. +> + +* * * + +### **4. Experience Now** + +* **Gitee Open Source Address**: https://gitee.com/dromara/wgai + +* **GitHub Open Source Address**: https://github.com/dromara/wgai + +* **Experience Address**: http://1.95.152.91:9999/ Password: wgai wgai@2024 + +* **Demo Video**: https://www.bilibili.com/video/BV13C9BYiEFS?t=38.4 + +* **Join the Community**: + + ![](/assets/img/news/WGAI-V3.0-5.jpg) + + + +* * * \ No newline at end of file diff --git a/src/news/WGAI-V4.0.md b/src/news/WGAI-V4.0.md new file mode 100644 index 0000000000..2c8c7201b4 --- /dev/null +++ b/src/news/WGAI-V4.0.md @@ -0,0 +1,93 @@ +--- +title: "WGAI Training and Recognition Platform V4.0 Officially Released: One-Stop Intelligent Training with Added GPU-OPENCL-CPU Processing for Training and Recognition!" +author: WGAI +date: 2025-07-18 +cover: /assets/img/news/WGAI-V4.0-0.jpg +head: + - - meta + - name: News +--- + +### 🔥 🔥 🔥 **WGAI Training and Recognition Platform V4.0 Officially Released: One-Stop Intelligent Training with Added GPU-OPENCL-CPU Processing for Training and Recognition**! + +> 🚫 Solemn Commitment: Permanently free! No commercial version! Only Knowledge Planet! + +> #### ✨**Major Updates** +> +> * ✅ Added decoding methods: GPU/NPU/CPU support for direct streaming media/camera access +> +> * ✅ Added alarm-triggered video recording - Added video subscription configuration +> +> * ✅ Added annotation memory - Quick annotation +> +> * ✅ Updated front-end with new UI +> +> * * * + +> #### 📦 **Next Version Preview** +> +> * 🚀 Reduced resource consumption with added iterative content +> +> * 🚀 Added automatic annotation functionality +> +> * * * + +### 🖼️**Effect Showcase** + +> #### **1. Added Decoding Methods** + +![](/assets/img/news/WGAI-V4.0-0.jpg) + +> * **Feature Description**: Supports configuring decoding acceleration via the platform interface - decoding methods include GPU/NPU/CPU +> +> * **Technical Advantages**: +> +> * Compatible with domestic chips, Intel, and NVIDIA GPU acceleration, decoding acceleration, and recognition acceleration +> + +> #### **2. Added Multi-Dimensional Model Recognition for a Single Video Stream** + +![](/assets/img/news/WGAI-V4.0-1.png) + +> * **Feature Description**: A single video stream supports multi-model configuration for recognition. +> +> * **Technical Highlights**: +> * Improves accuracy through multi-model judgment and establishes direct model relationships +> * Example: Intrusion detection → intruder behavior detection → intruder identification +> + +> #### **3. Added Alarm-Triggered Video Recording** + +![](/assets/img/news/WGAI-V4.0-2.jpg) + +> * **Feature Description**: Added alarm-triggered video recording, video push, and new UI content +> +> * **Technical Breakthrough**: Analysis results can be included in the recorded videos +> + +> #### **4. New UI Color Scheme** + +![](/assets/img/news/WGAI-V4.0-3.jpg) + +![](/assets/img/news/WGAI-V4.0-4.jpg) + +![](/assets/img/news/WGAI-V4.0-5.jpg) + +![](/assets/img/news/WGAI-V4.0-6.jpg) + +* * * + +### **4. Experience Now** + +* **Gitee Open Source Address**: https://gitee.com/dromara/wgai + +* **GitHub Open Source Address**: https://github.com/dromara/wgai + +* **Demo Address**: http://1.95.152.91:9999/ Password: wgai wgai@2024 + +* **Demo Video**: https://www.bilibili.com/video/BV13C9BYiEFS?t=38.4 + +* **Join the Community**: + + +![](/assets/img/news/WGAI-V4.0-7.jpg) \ No newline at end of file diff --git a/src/news/Warm-Flow-1.3.4.md b/src/news/Warm-Flow-1.3.4.md new file mode 100644 index 0000000000..3f915b1af1 --- /dev/null +++ b/src/news/Warm-Flow-1.3.4.md @@ -0,0 +1,115 @@ +--- +title: Warm-Flow Releases Version 1.3.4, Adds Support for Solon and Enhances Usability +author: Warm-Flow +date: 2024-11-27 +cover: /assets/img/news/Warm-Flow-1.3.4-0.png +head: + - - meta + - name: News +--- + +# Warm-Flow Releases 1.3.4, Adds Solon Support and Usability Improvements + +![](/assets/img/news/Warm-Flow-1.3.4-0.png) + +## 1. Old group was taken down, new group: + +![](/assets/img/news/Warm-Flow-1.3.4-1.png) + +**This version significantly improves the usability of the workflow, as follows:** + +> Introduced designer support, added support for Solon. +> Added listener SpEL expression support with extensibility. +> Added global listeners for the entire system, accessible via interface. +> Process variable expressions now support collection replacement. + +## 2. Detailed Updates: + +* Changelog + +* \[feat\] Added SpEL expression support for listeners with extensibility. + +* \[feat\] Added global listeners for the entire system, accessible via interface. + +* \[feat\] Added an API to retrieve current assignees before approval, similar to Satoken style @huangjian + +* \[feat\] Process variable expressions now support collection replacement @huangjian + +* \[feat\] Designer introduction, added support for Solon. + +* \[feat\] Added default node initialization when creating process definitions. + +* \[feat\] Added API to query process instance collections based on process definition ID sets. + +* \[update\] Adjusted the UI of the listener configuration page. + +* \[update\] Redefined listener names: original "global listeners" renamed to "process listeners", and "local listeners" renamed to "node listeners". + +* \[update\] Disabled unpublishing and deletion for approval tasks that have already been started. + +* \[update\] Added parameter validity checks for transfer, delegation, add sign, and reduce sign operations. + +* \[update\] Modified process variable passing method; subsequent assignees can now be initialized via assignee variable expressions or assignment listeners. + +* \[update\] Removed lazy loading for handler loading; refactored test project. + +* \[update\] Removed strategy prefix from assignee variable expressions; distinguished by $ and #. + +* \[update\] Process version numbers now auto-increment by default and do not accept external settings. + +* \[refactor\] Refactored condition expressions and assignee variable expressions. + +* \[remove\] Removed permission listeners. + + +## 3. Project Introduction + +> Warm-Flow is a domestic workflow engine🎉, characterized by its simplicity, lightweight design, and extensibility. It is a workflow that can integrate a designer via JAR. + +1. Simple and Easy to Use: Only 7 tables, minimal code, quick to learn and integrate. + +2. Approval Functions: Supports pass, return, arbitrary jump, transfer, termination, countersign, vote sign, delegation, add/reduce sign, mutual exclusion, and parallel gateways. + +3. Listeners and Process Variables: Supports four types of listeners with varying granularity, SpEL expressions, flexibility, extensibility, parameter passing, and dynamic permissions. + +4. Flowchart: The workflow engine includes a built-in flowchart and can be used without integrating a designer. + +5. Process Designer: Can be quickly integrated into projects via JAR, reducing tedious code migration and adaptation. Supports Solon and Spring Boot. + +6. Condition Expressions: Built-in common and SpEL condition expressions, with support for custom extensions. + +7. Assignee Variable Expressions: Built-in ${handler} and SpEL format expressions to meet various scenarios, flexible and extensible. + +8. ORM Framework Extensions: Currently supports MyBatis, MyBatis-Plus, MyBatis-Flex, and JPA. Community will provide support for others in the future; easy to extend. + +9. Database Support: Currently supports MySQL, Oracle, and PostgreSQL. Support for other or domestic databases will be added later. + +10. Multi-Tenancy and Soft Delete: The workflow engine maintains its own multi-tenancy and soft delete implementation, but can also use the corresponding ORM framework's methods. + +11. Supports both Spring and Solon. + +12. Compatible with Java 8 and Java 17 (theoretically Java 11 as well). + +13. Officially provides a practical project based on Ruoyi-Vue encapsulation. + + +## 4. Demo Address + +* admin/admin123 + + +Demo: http://www.hhzai.top + +## 7. Official Website + +http://warm-flow.cn + +About Dromara + +Dromara is an open-source community composed of top domestic open-source project authors. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, scheduling orchestration, etc. The technology stack is fully open-source and co-built, maintaining community neutrality, and is committed to providing global users with microservices cloud-native solutions. Let every participating open-source enthusiast experience the joy of open source. + +The Dromara open-source community currently has 10+ GVP projects, with a total star count of over 100,000, building an open-source community of tens of thousands of people, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to interact with me** + +![](/assets/img/news/Warm-Flow-1.3.4-2.webp) \ No newline at end of file diff --git a/src/news/Warm-Flow-1.6.6.md b/src/news/Warm-Flow-1.6.6.md new file mode 100644 index 0000000000..60d4ee98d1 --- /dev/null +++ b/src/news/Warm-Flow-1.6.6.md @@ -0,0 +1,127 @@ +--- +title: "Warm-Flow Spring Festival Edition 1.6.6 Released: A Domestic Workflow Engine" +author: Warm-Flow +date: 2025-01-24 +cover: /assets/img/news/Warm-Flow-1.6.6-0.png +head: + - - meta + - name: News +--- + +# 🧨Warm-Flow Spring Festival Edition 1.6.6: Gateway Direct Connection and Flowchart Refactoring, Adds Excellent Ruoyi-Vue-Plus Open-Source Integration Example + +* This release primarily addresses gateway direct connection and flowchart refactoring, enabling support for various complex gateway hybrids and multi-gateway direct connections. +* Added an excellent open-source integration example with Ruoyi-Vue-Plus. + +## Changelog + +* \[feat\] Added JSON format support for import, export, save, etc.: DefService.importIs/importJson/importDef/saveDef/exportJson +* \[feat\] Added method to retrieve subsequent nodes: NodeService.suffixNodeList +* \[feat\] Added gateway direct connection and test cases +* \[feat\] Added completion status color legend in the upper right corner of the flowchart +* \[feat\] Added flowchart query interface and extension interface ChartService +* \[feat\] Added synchronization of historical table data to new flowchart metadata +* \[feat\] Added full SQL Server script +* \[update\] Marked import, export, and save in XML format as deprecated; please refer to hh-vue for switching to JSON APIs +* \[update\] FlowFactory renamed to FlowEngine +* \[update\] Increased field length for target node code and name in history table to 200 +* \[update\] When passing or returning to a parallel gateway, creating multiple tasks now generates only one history record +* \[update\] When returning or completing a task, other tasks to be deleted do not need history records as the return record already exists, avoiding duplication +* \[update\] Transfer, delegation, add sign, and reduce sign operations now generate only one history record +* \[update] Changed batch save default to 1000 records per batch +* \[update] Added overlay mask during process design saving +* \[refactor] Adjusted and refactored flowchart rendering +* \[refactor] Removed mybatis-flex, easy-query, and jpa extension packages; these are now independent projects maintained by dedicated personnel +* \[refactor] Entity class and DAO retrieval switched to reflection to decouple from the orm-core package +* \[refactor] Refactored method to get previous nodes: NodeService.previousNodeList +* \[fix] Fixed issue where other pending tasks were not deleted during return +* \[fix] Fixed issue where returning to a target node before a parallel gateway prevented generating pending tasks +* \[fix] Fixed issue where the presence of `|` in conditional expressions caused incorrect splitting +* \[fix] Fixed incorrect judgment of the same line key when drawing flowcharts +* \[fix] Fixed issue where end nodes still executed listener creation +* \[remove] Removed DefService flowchart API; replaced by chartIns and chartDef in ChartService +* \[remove] Removed frontend log printing +* \[remove] Removed Oracle and PostgreSQL upgrade scripts; only MySQL upgrade scripts will be provided henceforth. Users must convert other upgrade scripts themselves from the full scripts provided. + +## Project Introduction + +**Dromara Warm-Flow is a domestic workflow engine, characterized by its simplicity, lightweight design, and extensibility. It is a workflow that can integrate a designer via JAR.** + +1. Supports common approval functions, listeners and process variables, conditional expressions, assignee variable expressions. +2. Built-in flowchart and process designer. +3. Rich and extensible ecosystem. +4. Comprehensive documentation. + +![](/assets/img/news/Warm-Flow-1.6.6-0.png) + +## Demo Address + +* admin/admin123 + +Demo: http://www.hhzai.top + +## Official Website + +https://warm-flow.dromara.org/ + +## Demo Images + + + + + + + + + + + + + + + + + + + + + + +
+ +
+

+ +

+

+ +
+ +
+ +
+ +About Dromara + +Dromara is an open-source community composed of top domestic open-source project authors. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, scheduling orchestration, etc. The technology stack is fully open-source and co-built, maintaining community neutrality, and is committed to providing global users with microservices cloud-native solutions. Let every participating open-source enthusiast experience the joy of open source. + +The Dromara open-source community currently has 10+ GVP projects, with a total star count of over 100,000, building an open-source community of tens of thousands of people, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to interact with me** + +![](/assets/img/news/Warm-Flow-1.6.6-6.webp) \ No newline at end of file diff --git a/src/news/Warm-Flow-1.6.7.md b/src/news/Warm-Flow-1.6.7.md new file mode 100644 index 0000000000..0b03c04ad7 --- /dev/null +++ b/src/news/Warm-Flow-1.6.7.md @@ -0,0 +1,82 @@ +--- +title: "Three Heads and Six Arms Show Their Powers: Warm-Flow Engine Achieves Multi-Dimensional Flexible Configuration" +author: Warm-Flow +date: 2025-03-03 +cover: /assets/img/news/Warm-Flow-v1.6.7-0.png +head: + - - meta + - name: News +--- + +# Warm-Flow Engine Achieves Multi-Dimensional Flexible Configuration + +![](/assets/img/news/Warm-Flow-v1.6.7-0.png) + +# + +## Main Updates + +- Designer now supports node extension property settings +- Flowchart extensions, with new interfaces for easily appending text +- Process status now supports customizable colors + +## Detailed Changelog + +- Upgrade Guide +- [feat] Designer supports node extension property settings +- [feat] Flowchart extensions, added interfaces for easily appending text +- [feat] Process status now supports customizable colors +- [update] Marked the version field in the node table for deletion in the next version +- [update] Jackson ignores unknown fields during deserialization +- [update] Removed some code and adjusted comments +- [update] Modified description notes for ticket-sign and countersign nodes +- [fix] Standardized Solon and API annotations to prevent inability to retrieve method parameter names in certain cases +- [fix] Fixed deletion failure when processing users do not exist during process instance deletion +- [fix] #IBP397: Fixed issue where text names were not displayed when the start node appeared at negative coordinates in the design process +- [fix] #IBP3LK: Fixed issue where the first node in the flowchart did not show the pending color when starting the process +- [fix] Handled text editing errors for gateway nodes +- [remove] Removed XML import/export method for process definitions +- [remove] Removed redundant skip_Any_Node field +- [style] Changed constants to uppercase with underscores + +## Project Introduction + +**Dromara Warm-Flow is a domestically developed workflow engine. It is characterized by its simplicity, lightweight design, and comprehensive functionality, with strong flexibility and extensibility. It is a workflow engine that can be integrated with a designer via JAR inclusion.** + +1. Supports common approval functions, listeners and process variables, conditional expressions, and assignee variable expressions. +2. Comes with built-in flowchart, process designer, and node extension properties. +3. Features a rich and extensible ecosystem. +4. Comprehensive documentation. + +![](/assets/img/news/Warm-Flow-v1.6.7-1.png) + +# + +## Demo Address + +- Username/Password: admin/admin123 + +Demo Address: http://www.hhzai.top + +## Official Website + +https://warm-flow.dromara.org + +## Screenshots of This Update + +![](/assets/img/news/Warm-Flow-v1.6.7-2.png) +![](/assets/img/news/Warm-Flow-v1.6.7-3.png) +![](/assets/img/news/Warm-Flow-v1.6.7-4.png) +![](/assets/img/news/Warm-Flow-v1.6.7-5.png) + +--- + +### About Dromara + +Dromara is an open-source community composed of top domestic open-source project authors. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, and scheduling orchestration. The community is committed to full open-source collaboration and maintains neutrality, striving to provide microservices cloud-native solutions for global users. It aims to let every participating open-source enthusiast experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total of over 100,000 stars. It has built a community of tens of thousands of members, with countless individuals and teams using Dromara's open-source projects. + +**Welcome to join the Knowledge Planet and interact with me** + +![](/assets/img/news/Warm-Flow-v1.6.7-6.webp) \ No newline at end of file diff --git a/src/news/Warm-Flow-1.6.8.md b/src/news/Warm-Flow-1.6.8.md new file mode 100644 index 0000000000..a8471c457c --- /dev/null +++ b/src/news/Warm-Flow-1.6.8.md @@ -0,0 +1,53 @@ +--- +title: "Warm-Flow Version 1.6.8 Upgrade: Partial Bug Fixes" +author: Warm-Flow +date: 2025-03-19 +cover: /assets/img/news/Warm-Flow-1.6.8-0.png +head: + - - meta + - name: News +--- + +# + +![](/assets/img/news/Warm-Flow-1.6.8-0.png) + +## **Main Updates** + +* Upgrade Guide +* [fix] After copying a process, the original configuration information for rejecting to a specified node was lost. +* [fix] The flowchart return status setting lacked null checks. +* [fix] Added a regular index to the `associated` field in the `flow_user` table to improve query and update performance. +* [fix] Fixed an issue where, when the target node had multiple nodes pointing to it and a parallel gateway was present, no pending tasks were generated. +* [fix] Fixed the issue where ascending sorting in the public API was ineffective. + +## **Project Introduction** + +**Dromara Warm-Flow is a domestically developed workflow engine. It is characterized by its simplicity, lightweight design, and comprehensive functionality, with strong flexibility and extensibility. It is a workflow engine that can be integrated with a designer via JAR inclusion.** + +1. Supports common approval functions, listeners and process variables, conditional expressions, and assignee variable expressions. +2. Comes with built-in flowchart, process designer, and node extension properties. +3. Features a rich and extensible ecosystem. +4. Comprehensive documentation. + +## **Functional Mind Map** + +![](/assets/img/news/Warm-Flow-1.6.8-1.png) + +## **Flowchart** + +![](/assets/img/news/Warm-Flow-1.6.8-2.png) + +## **Demo Address** + +http://www.hhzai.top + +Username/Password: admin/admin123 + +## **Official Website** + +https://warm-flow.dromara.org + +## **Warm-Flow Video** + +Warm-Flow First Experience: https://www.bilibili.com/video/BV1AWRGYEEVr \ No newline at end of file diff --git a/src/news/Warm-Flow-1.6.9.md b/src/news/Warm-Flow-1.6.9.md new file mode 100644 index 0000000000..1569492d77 --- /dev/null +++ b/src/news/Warm-Flow-1.6.9.md @@ -0,0 +1,56 @@ +--- +title: "Warm-Flow Version 1.6.9 Upgrade: Fixed Several Issues" +author: Warm-Flow +date: 2025-04-10 +cover: /assets/img/news/Warm-Flow-1.6.9-0.png +head: + - - meta + - name: News +--- + +# Warm-Flow Version 1.6.9 Upgrade: Fixed Several Issues + +![](/assets/img/news/Warm-Flow-1.6.9-0.png) + +## **Main Updates** + +* \[update\] Handled multi-selection and reverse display of extended nodes +* \[update\] Removed redundant comments +* \[fix\] Page did not implement Serializable, causing errors in Dubbo 3.2+ default STRICT strict checks @yuegc +* \[fix\] Fixed an issue where double-clicking a middle node and then clicking a jump line did not open the right drawer @Vittery +* \[fix\] Fixed an issue where the text of start, end, and exclusive gateway nodes did not display correctly when the graph appeared at negative coordinates +* \[remove\] Removed the abstract class for assignee expressions + +## **Project Introduction** + +**Dromara Warm-Flow is a domestic workflow engine known for its simplicity, lightweight design, and comprehensive functionality. It is highly flexible and extensible, and includes a designer that can be integrated via JAR**. + +1. Supports common approval functions, listeners, process variables, conditional expressions, and assignee expressions +2. Comes with flowchart design, process designer, and node extension properties +3. Compatible with common ORM frameworks +4. Supports various databases +5. Features a rich ecosystem, strong extensibility, and comprehensive documentation + +## **Functional Mind Map** + +![](/assets/img/news/Warm-Flow-1.6.9-1.png) + + + +## **Flowchart** + +![](/assets/img/news/Warm-Flow-1.6.9-2.png) + +## **Demo Address** + +http://www.hhzai.top + +Username and password: admin/admin123 + +## **Official Website** + +https://warm-flow.dromara.org + +## **Warm-Flow Video** + +First Experience with Warm-Flow: https://www.bilibili.com/video/BV1AWRGYEEVr/ \ No newline at end of file diff --git a/src/news/Warm-Flow-1.7.0.md b/src/news/Warm-Flow-1.7.0.md new file mode 100644 index 0000000000..2a4dbe45c3 --- /dev/null +++ b/src/news/Warm-Flow-1.7.0.md @@ -0,0 +1,77 @@ +--- +title: Domestically-Made Free Workflow Engine Reaches 5.9k Stars – Warm-Flow Upgrades to Version 1.7.0 (Adds Many Useful Features) +author: April 27, 2025 11:15 +date: 2025-04-27 +cover: /assets/img/news/Warm-Flow-1.7.0-0.png +head: + - - meta + - name: News +--- + +# 🔥 Domestically-Made Free Workflow Engine Hits 5.9k Stars – Warm-Flow Upgrades to Version 1.7.0 (Adds Many Useful Features) + +📢 Highly anticipated features are now available: + +Added **Withdraw**, **Reject to Previous Task**, and **Task Recall** functions ^_^ + +> 🎯 Since joining the Dromara open-source community in February 2024, Warm-Flow has attracted: +> +> * 👥 10+ core contributors +> * 🌟 5900+ Gitee Stars +> +> **🚫 Solemn pledge: Permanently free! No commercial version!** + +## ✨ Main Updates + +\[New Features\] + +* ✅ Added process status field to the to-do task table +* ✅ Designer node assignee now supports display in Chinese +* ✅ Added 16 new process control APIs (Withdraw/Reject/Recall, etc.) + +\[Optimizations & Fixes\] + +* 🚀 Performance boost: Optimized duplicate code in node queries +* 🔧 Fixed issue with multiple task execution in parallel gateways +* 🧹 Cleaned up deprecated interfaces (marked for deletion/alternatives provided) + +## 📦 Project Highlights + +🔨 A domestically-made lightweight workflow engine, featuring: + +1. 📌 Support for process variables/conditional expressions +2. 🎨 Built-in visual designer +3. 🔄 Compatibility with multiple databases/ORM frameworks +4. 📚 Full ecosystem extensibility + +## 🖼️ Preview + +
+ Warm-Flow-1.7.0-0 +
+ +
+ Warm-Flow-1.7.0-1 +
+ +![](/assets/img/news/Warm-Flow-1.7.0-2.png) + +![](/assets/img/news/Warm-Flow-1.7.0-3.png) + +--- + +🚀 Try it now: http://www.hhzai.top +🌐 Visit the official website: https://warm-flow.dromara.org + +--- + +### About Dromara + +Dromara is an open-source community formed by top domestic open-source project authors. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, scheduling orchestration, and more. The community is committed to full open-source collaboration, maintaining neutrality, and dedicated to providing global users with microservices cloud-native solutions. It aims to let every participating open-source enthusiast experience the joy of open source. + +The Dromara open-source community currently boasts 10+ GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built a community of tens of thousands of members, with countless individuals and teams using Dromara's open-source projects. + +**Everyone is welcome to join the Knowledge Planet to interact with me** + +![](/assets/img/news/Warm-Flow-1.7.0-4.webp) +*(Note: "Knowledge Planet" is a popular Chinese platform for community interaction, similar to a paid membership group or forum. The image likely contains a QR code or invite link to this specific community space.)* \ No newline at end of file diff --git a/src/news/Warm-Flow-1.7.3.md b/src/news/Warm-Flow-1.7.3.md new file mode 100644 index 0000000000..b91ab910dd --- /dev/null +++ b/src/news/Warm-Flow-1.7.3.md @@ -0,0 +1,55 @@ +--- +title: Warm-Flow Releases Version 1.7.3 on Dragon Boat Festival (Major Upgrade for Designer Flows and Flowcharts) +author: Warm-Flow +date: 2025-05-30 +cover: /assets/img/news/Warm-Flow-1.7.3-0.png +head: + - - meta + - name: News +--- + +## Update Content + +* \[feat\] New flowchart rendering via front-end + +* \[perf\] Improved UI for the process designer + +* \[feat\] Assignee permission processor, added assignee conversion interface (e.g., role-to-user conversion) + +* \[update\] Optimized error notifications + +* \[update\] Added Version to adapt to the current designer's behavior + +* \[update\] Built-in component access request changed from `/warm-flow-ui/token-name` to `/warm-flow-ui/config` + +**New Version Designer** +![](/assets/img/news/Warm-Flow-1.7.3-0.png) + +**New Version Flowchart** +![](/assets/img/news/Warm-Flow-1.7.3-1.png) + +## Project Introduction + +**Dromara Warm-Flow** is a domestic workflow engine known for its simplicity, lightweight design, and comprehensive functionality. It offers flexible extensibility and includes a designer that can be integrated via JAR. + +1. Supports common approval functions, listeners, process variables, conditional expressions, and assignee expressions. +2. Comes with built-in flowcharts, a process designer, and node extension properties. +3. Compatible with common ORM frameworks. +4. Supports various databases. +5. Features a rich ecosystem with strong extensibility and comprehensive documentation. + +## Functional Mind Map +
+ Warm-Flow-1.7.3-2 +
+ +## Demo Address +http://www.hhzai.top + +Username/Password: admin/admin123 + +## Official Website +https://warm-flow.dromara.org + +## Warm-Flow Video +First Impressions of Warm-Flow: https://www.bilibili.com/video/BV1AWRGYEEVr \ No newline at end of file diff --git a/src/news/Warm-Flow-1.8.0.md b/src/news/Warm-Flow-1.8.0.md new file mode 100644 index 0000000000..97e0f9f8a5 --- /dev/null +++ b/src/news/Warm-Flow-1.8.0.md @@ -0,0 +1,74 @@ +--- +title: Major Update in Warm-Flow 1.8.0 +author: Warm-Flow +date: 2025-08-14 +cover: /assets/img/news/Warm-Flow-1.8.0-0.png +head: + - - meta + - name: News +--- + +# 🚀 Warm-Flow Achieves Major Breakthrough with Self-Developed DingTalk-Style Designer! + +Dear developer friends, +We are thrilled to announce that the Warm-Flow workflow engine has received a major update to version 1.8.0! This release not only introduces new features but also brings a qualitative leap in user experience. + +## 🔥 Core Highlights + +### Self-Developed DingTalk-Style Designer + +Warm-Flow now supports both **Classic Mode** and **DingTalk-Style Mode**! We have independently developed a DingTalk-inspired designer based on Logic-Flow, avoiding the complexity of maintaining two separate codebases and achieving better consistency and maintainability. + +### Enhanced Smart Interaction Experience + +- **Automatic Jump Line Recognition**: When drawing backtracking lines, the system automatically recognizes and sets them as "return jump" types. +- **Free Node Dragging**: In Classic Mode, nodes and connecting text can be freely dragged and adjusted. +- **Smart Edit Control**: The designer now automatically determines editability based on the process's publication status. + +### Functional Enhancements and Optimizations + +- Added the [`getFirstBetweenNode`](file://D:\\IdeaProjects\\min\\RuoYi-Vue-Warm-Flow\\warm-flow-test\\warm-flow-mybatis-sb-test\\src\\test\\java\\org\\dromara\\warm\\flow\\FlowTest.java#L217-L220) interface for quickly retrieving the first intermediate node. +- Flowchart rendering now supports showing/hiding the top name display. +- Added user icons for intermediate nodes, making the interface more user-friendly. +- Improved code formatting and comments for a better development experience. + +## 📊 Visual Showcase + +The new process designer interface is more aesthetically pleasing and intuitive: + +![](/assets/img/news/Warm-Flow-1.8.0-0.png) + +--- + +![](/assets/img/news/Warm-Flow-1.8.0-1.png) + +## 🌟 Why Choose Warm-Flow? + +As a domestically developed workflow engine, Warm-Flow offers the following advantages: + +1. **Lightweight & Simple** – Fully functional with strong extensibility. +2. **Dual-Mode Support** – Natively supports both Classic and DingTalk-Style modes. +3. **Quick Integration** – Easily integrate the designer via JAR packages. +4. **Broad Compatibility** – Supports multiple ORM frameworks and databases. + +## 🎯 Full Feature Overview + +
+ Warm-Flow-1.8.0-2 +
+ +Functional Mind Map + +## 🚀 Quick Experience + +**Demo Address**: http://www.hhzai.top +**Username/Password**: admin / admin123 + +**Official Website**: https://warm-flow.dromara.org + +Want to dive deeper? Check out our video tutorial: +*Master from Scratch: Full Process Development and Source Code Interpretation* + +--- + +**Upgrade to Warm-Flow 1.8.0 now and embark on a new workflow design journey!** \ No newline at end of file diff --git a/src/news/easyAI-v1.2.6.md b/src/news/easyAI-v1.2.6.md new file mode 100644 index 0000000000..5b721618ea --- /dev/null +++ b/src/news/easyAI-v1.2.6.md @@ -0,0 +1,239 @@ +--- +title: Native Java Artificial Intelligence Algorithm Framework easyAI v1.2.6 Version Update +author: EasyAi +tag: + - EasyAI +date: 2024-10-08 +cover: /assets/img/news/easyAI-v1.2.6-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/easyAI-v1.2.6-0.png) + +## Foreword + +The significance of EasyAi for Java is equivalent to the role of Spring in the JavaWeb field—to provide an out-of-the-box solution that enables every developer to use EasyAi to develop small-scale models that meet their artificial intelligence business needs. This is its mission! + +## Introduction to EasyAi + +EasyAi has no dependencies. It is a native Java artificial intelligence algorithm framework. Firstly, **it can be seamlessly integrated into our Java projects with Maven in one step, without any additional environment configuration or dependencies, achieving out-of-the-box usability.** Secondly, it includes pre-packaged modules for image target detection and AI customer service, as well as underlying algorithm tools for deep learning, machine learning, reinforcement learning, heuristic learning, matrix operations, and more. Developers can, with minimal learning, develop small-scale models that deeply align with their business needs. + +* EasyAI Gitee download link: https://gitee.com/dromara/easyAi +* EasyAI technical documentation: https://www.myeasyai.cn/ +* EasyAI detailed video tutorials: https://www.bilibili.com/video/av89134035 +* EasyAI framework zero-based deep development and complete AI system tutorials: https://www.bilibili.com/cheese/play/ss17600 + +# v1.2.6 Update Content + +## Word Embedder and TransFormer Now Support Custom Word Granularity Training + +> **Tip:** When preparing training samples for word embedding, developers need to consider "word granularity." If the training samples are relatively few, setting a coarser word granularity can yield better results. For example, when training the SQL statement `select * from student`, if the default minimal word granularity (i.e., single characters as word granularity) is used without considering word granularity, the training and generation will be based on basic units like **`s`, `e`, `l`, `e`, `c`, `t`......**. +> +> > However, if we consider word granularity, we can use coarser units like **`select`, `*`, `from`, `student`** as the basic units for training. If certain fixed characters always appear as a whole, setting an appropriate word granularity for training will significantly improve efficiency and stability. + +### Sentence Template Entity Class Adds New Constructor Parameters and Methods to Support Custom Word Granularity + +* `SentenceModel` is a template class for word embedding, designed to load all sentences. + +> `public SentenceModel(String splitWord)` is a constructor method that accepts a delimiter parameter. If developers want to customize the coarseness of word granularity, they can use this method to set a delimiter to separate words in sentences, thereby controlling the word granularity. +> +> > For example: In the statement `select * from student`, the words are separated by a "space" delimiter. Thus, the constructor method with a delimiter parameter can be used: `SentenceModel s = new SentenceModel(" ")`. This way, during training, the word embedder will treat `select`, `*`, `from`, and `student` as whole units for training. The delimiter can be customized. If developers use the parameterless constructor, the word embedder will default to treating each single character as a whole unit for training. **Note: For small training samples, setting a coarser granularity often helps with stability. Currently, custom granularity only affects methods dependent on TransFormer.** +> > +> > > Parameter `String splitWord` is the custom word granularity delimiter. +> +> `public SentenceModel()` is a parameterless constructor. Using this constructor will default to the minimal word granularity, where each character is treated as a whole unit for word embedding training. +> +> `public void setSentence(String sentence)` inputs a sentence (training sample) into the sentence template entity class. The sample sentence is **a sentence without delimiters**. This method can be called whether using the parameterless constructor or the constructor with a delimiter parameter. +> +> `public void setSentenceBySplitWord(String sentence)` inputs a sentence (training sample) into the sentence template entity class. The sample sentence is **a sentence with delimiters**. If this method is used, the class must be constructed using `public SentenceModel(String splitWord)` with a delimiter parameter. + +## TF Configuration Class Adds Two New Parameters to Enhance Business Stability + +> `private String splitWord` sets the word granularity delimiter. **Note: This delimiter must match the delimiter set in the constructor parameter of the sentence template entity class!** If no delimiter is needed, this value can be set to `null`, but the sentence template entity class must also use the corresponding parameterless constructor. +> +> `private boolean selfTimeCode` determines whether to use an auto-incrementing time series positional encoding. If `true`, the TF positional encoding uses a uniformly auto-incrementing time series. If `false`, the TF positional encoding uses symmetric trigonometric positional encoding. +> +> > The performance of symmetric trigonometric positional encoding and auto-incrementing encoding varies under different business environments. Generally, for generative sentences, symmetric trigonometric positional encoding performs better, while for sentence semantic classification, auto-incrementing positional encoding performs better. However, this is only a reference and not absolute. It is recommended to treat this parameter as a tuning attempt and choose the best-performing option. + +## Word Embedder Updated to Support Custom Word Granularity, with API for Retrieving Word Vector Matrix + +> `public MyWordFeature getEmbedding(String word, long eventId, boolean once)` retrieves the word vector matrix entity class for a phrase (or word). +> +> > Parameter `String word` is the phrase or word for which the word vector is to be retrieved. +> +> > Parameter `boolean once` determines whether to retrieve it as a one-time operation. If `false`, this method call retrieves the word vector for each character unit in the phrase `String word`, arranging them in order from top to bottom to form a word vector matrix for the sentence. Each character's word vector occupies a row in the word vector matrix entity class. If `true`, the word parameter `String word` is treated as a whole, directly retrieving the vector corresponding to the word. This is typically used in conjunction with delimiter-based word granularity settings. +> +> > Parameter `long eventId` is the thread-safe unique ID for the execution lifecycle. + +### Below is a simple demo (Chinese automatic SQL statement generation) demonstrating the usage of this method. Note that this is only a demo with three sample data points and is intended solely for demonstrating API usage: + +```java +public class Test { + public static TfConfig config = new TfConfig(); // Outer configuration file + public static WordEmbedding wordEmbedding = new WordEmbedding(); // Word embedder + public static int wordVectorDimension = 32; // Set word vector dimension + public static TalkToTalk talk; + + public static void main(String[] args) throws Exception { + init(); // Initialization + // creat(); // Generate + study(); // Train + } + + public static void creat() throws Exception { + wordEmbedding.insertModel(readWordModel(), wordVectorDimension); // Deserialize model file into model entity class, load word vector model + talk = new TalkToTalk(wordEmbedding, config); // Create long-sentence dialogue class + talk.init(); // Initialize long-sentence dialogue class + talk.insertModel(readTalkModel()); // Load TF model into long-sentence dialogue class + // Note: The above part is initialization and model loading, executed only once during service startup. Do not load the model every time for recognition! + // TalkToTalk must be a singleton in actual use! The TalkToTalk used for actual getAnswer calls must not be newly created! + // Instead, directly use the singleton class initialized and loaded during service startup. If the model is loaded every time for recognition, the process will be very time-consuming! + String a = talk.getAnswer("Query # student information", 1); // Input question, return answer + System.out.println(a); // Final output: select * from student where id = # + } + + public static void init() { + wordEmbedding.setConfig(new SentenceConfig()); // Word embedder sets configuration class + wordEmbedding.setStudyTimes(100); // Word embedder trains for 100 cycles + config.setMaxLength(40); // Set maximum length to 40 + config.setMultiNumber(2); // Set 2 multi-heads per encoder/decoder layer + config.setFeatureDimension(wordVectorDimension); // Set word vector dimension + config.setAllDepth(1); // Set TF encoder/decoder depth + config.setSplitWord(" "); // Set space as word delimiter + config.setSelfTimeCode(false); // Set symmetric trigonometric positional encoding + config.setTimes(500); // Train for 500 cycles + config.setStudyPoint(0.01); // Set TF learning rate + config.setShowLog(true); // Print data during training + config.setRegularModel(RZ.NOT_RZ); // No regularization mode + config.setRegular(0.0); // No regularization coefficient + } + + public static void study() throws Exception { + SentenceModel sentenceModel = new SentenceModel(config.getSplitWord()); // Set delimiter training sample class + List talkBodies = data(); // Read test data + for (TalkBody value : talkBodies) { // Insert test data + sentenceModel.setSentence(value.getQuestion()); // Insert question into delimiter-free sample class + sentenceModel.setSentenceBySplitWord(value.getAnswer()); // Insert answer into delimiter sample class + } + wordEmbedding.init(sentenceModel, wordVectorDimension); // Initialize word embedding + WordTwoVectorModel wordTwoVectorModel = wordEmbedding.start(); // Word embedding training completed, return word vector model + talk = new TalkToTalk(wordEmbedding, config); // Create long-sentence dialogue class + talk.init(); // Initialize long-sentence dialogue class + TransFormerModel transFormerModel = talk.study(talkBodies); // Training completed, return TF model + Tools.writeModel(JSONObject.toJSONString(wordTwoVectorModel), "/Users/lidapeng/job/testDocument/model/testWord.json"); // Write model + Tools.writeModel(JSONObject.toJSONString(transFormerModel), "/Users/lidapeng/job/testDocument/model/testTalk.json"); // Write model + } + + private static WordTwoVectorModel readWordModel() { // Read model + File file = new File("/Users/lidapeng/job/testDocument/model/testWord.json"); + String a = Tools.readPaper(file); + return JSONObject.parseObject(a, WordTwoVectorModel.class); + } + + private static TransFormerModel readTalkModel() { // Read model + File file = new File("/Users/lidapeng/job/testDocument/model/testTalk.json"); + String a = Tools.readPaper(file); + return JSONObject.parseObject(a, TransFormerModel.class); + } + + public static List data() { // Configure three training data points for testing + List talkBodies = new ArrayList<>(); + TalkBody talkBody1 = new TalkBody(); + TalkBody talkBody2 = new TalkBody(); + TalkBody talkBody3 = new TalkBody(); + talkBody1.setQuestion("Query # student information"); // Query student information where name is # + talkBody1.setAnswer("select * from student where id = #"); + talkBody2.setQuestion("Add student information with id # and age 17"); + talkBody2.setAnswer("insert into student ( id , age ) values ( # , 17 )"); + talkBody3.setQuestion("Change age of # to 18"); + talkBody3.setAnswer("update student set age = 18 where id = #"); + talkBodies.add(talkBody1); + talkBodies.add(talkBody2); + talkBodies.add(talkBody3); + return talkBodies; + } +} +``` + +## Face Detection Effect Demonstration + +![](/assets/img/news/easyAI-v1.2.6-1.png) + +### Image Recognition FastYolo Effect Demonstration + +* Using EasyAi to implement the visual kernel of an image-based automated vending machine + +![](/assets/img/news/easyAI-v1.2.6-2.jpg) + +### sayOrder AI Customer Service + +* sayOrder is an AI customer service system built on EasyAi. +* It can analyze the semantics of user input to identify user behavior and distinguish user intent through typeID. It captures keyword categories set in the backend to extract system-relevant content from user statements, such as time and location. +* It can also autonomously interact with users through Q&A, answering questions or engaging in other intent-based conversations. +* Project link: https://gitee.com/dromara/sayOrder + +### sayOrder Interaction Basic Workflow Demonstration + +* User inputs their idea for the first time: + ![](/assets/img/news/easyAI-v1.2.6-3.png) +* SayOrder detects missing essential order information and asks a follow-up question. The user responds by providing additional information: + ![](/assets/img/news/easyAI-v1.2.6-4.png) +* The user's second input still does not meet the key information requirements for the backend's 14-category legal consultation order. After further supplementation, the order information is completed and the order is generated: + ![](/assets/img/news/easyAI-v1.2.6-5.png) +* The user inputs a question they want to consult, and SayOrder autonomously answers the user's query: + ![](/assets/img/news/easyAI-v1.2.6-6.png) + +### Architecture Design + +**Common Underlying Algorithm Modules** + +* Basic Matrix and Linear Algebra Calculation Module: + 1. Built-in matrix classes and matrix calculation classes capable of performing common matrix operations, parity checks, multiple linear regression, logistic regression, Euclidean distance, cosine similarity, im2col, inverse im2col, algebraic cofactor calculation, inversion, adjoint matrix calculation, inner product, differentiation, and a series of other APIs. + 2. RGB three-channel matrices capable of image conversion, cropping, blocking, generating image matrices, and other operations to facilitate subsequent calculations. + +* Machine Learning - Clustering: + k-means clustering, Gaussian mixture clustering, density clustering, learning vector quantization clustering, etc. + +* Machine Learning - Classification and Fitting: + Multi-layer feedforward neural networks, multi-layer recurrent neural networks, residual networks, multi-layer residual recurrent neural networks, convolutional neural networks, decision trees, random forests, k-nearest neighbors, etc. + +* Heuristic Algorithms: + Particle swarm optimization, ant colony optimization, simulated annealing. + +* Reinforcement Learning: + Dynamic programming, Monte Carlo analysis, Markov decision processes, temporal difference learning. + +**Common Upper-Level Algorithm Modules** + +* Visual Imaging: + Image recognition, image summarization, target detection. + +* Natural Language Processing: + Semantic understanding, word segmentation, sensitive and keyword inference, sentence completion, language interaction. + +* Game Bots: + Autonomous strategy, autonomous action. + +### Usage + +1. Download the project and package it into the local Maven repository. +2. Import the EasyAi pom file into the project. + +### Follow the Project + +* For ideas, suggestions, or learning related to the project, you can add my work WeChat: + ![](/assets/img/news/easyAI-v1.2.6-7.jpg) + +--- + +About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, scheduling orchestration, and more. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, and is dedicated to providing microservices cloud-native solutions for global users. It aims to allow every participating open-source enthusiast to experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of members, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to join the Knowledge Planet to interact with me** + +![](/assets/img/news/easyAI-v1.2.6-8.webp) \ No newline at end of file diff --git a/src/news/easyAI-v1.2.7.md b/src/news/easyAI-v1.2.7.md new file mode 100644 index 0000000000..eeac391e5e --- /dev/null +++ b/src/news/easyAI-v1.2.7.md @@ -0,0 +1,129 @@ +--- +title: Native Java Artificial Intelligence Algorithm Framework easyAI v1.2.7 Version Update +author: easyAI +tag: + - EasyAI +date: 2024-10-14 +cover: /assets/img/news/easyAI-v1.2.7-0.png +head: + - - meta + - name: News +--- + +![](/assets/img/news/easyAI-v1.2.7-0.png) + +## Foreword + +The significance of EasyAi for Java is equivalent to the role of Spring in the JavaWeb field—to provide an out-of-the-box solution that enables every developer to use EasyAi to develop small-scale models that meet their artificial intelligence business needs. This is its mission! + +## Introduction to EasyAi + +EasyAi has no dependencies. It is a native Java artificial intelligence algorithm framework. Firstly, **it can be seamlessly integrated into our Java projects with Maven in one step, without any additional environment configuration or dependencies, achieving out-of-the-box usability.** Secondly, it includes pre-packaged modules for image target detection and AI customer service, as well as underlying algorithm tools for deep learning, machine learning, reinforcement learning, heuristic learning, matrix operations, and more. Developers can, with minimal learning, develop small-scale models that deeply align with their business needs. + +* EasyAI Gitee download link: https://gitee.com/dromara/easyAi +* EasyAI technical documentation: https://www.myeasyai.cn/ +* EasyAI detailed video tutorials: https://www.bilibili.com/video/av89134035 +* EasyAI framework zero-based deep development and complete AI system tutorials: https://www.bilibili.com/cheese/play/ss17600 + +# v1.2.7 Update Content + +## v1.2.7 Now supports multi-core parallel computing to accelerate training when the feature dimension is large enough! + +### Object Detection Configuration Class Adds Multi-core Acceleration Parameter + +> `private int coreNumber` sets the number of threads for multi-threaded parallel computing acceleration. The theoretical maximum value for this number is your current device's core count * 2. If this value is set to less than or equal to 1, it will still use single-process computation. +> +> > **Note: It is only worth enabling parallel computation for acceleration when at least one of the moving detection window width `private int windowWidth` or height `private int windowHeight` exceeds 300. If the values are smaller than this, parallel computation efficiency may be lower than single-process computation!** + +### TF Configuration Class Adds Multi-core Acceleration Parameter + +> `private int coreNumber` sets the number of threads for multi-threaded parallel computing acceleration. The theoretical maximum value for this number is your current device's core count * 2. If this value is set to less than or equal to 1, it will still use single-process computation. +> +> > **Note: It is only worth enabling parallel computation for acceleration when the feature dimension `private int featureDimension` exceeds 280. If the value is smaller than this, parallel computation efficiency may be lower than single-process computation!** + +### easyAI Package Has Been Uploaded to Maven Central Repository + +> You can now simply add the following JAR package dependency in your pom file; manual downloading and packaging are no longer required. + +```xml + + cn.myeasyai.easyai + easyAi + 1.2.7 + +``` + +## Face Detection Effect Demonstration + +![](/assets/img/news/easyAI-v1.2.7-1.png) + +### Image Recognition FastYolo Effect Demonstration + +* Using EasyAi to implement the visual kernel for an image-based automated vending machine settlement system + +![](/assets/img/news/easyAI-v1.2.7-2.jpg) + +### sayOrder AI Customer Service + +* sayOrder is an AI customer service system built on EasyAi. +* It can analyze the semantics of user input to identify user behavior and distinguish user intent through typeID. It captures keyword categories set in the backend to extract system-relevant content from user statements, such as time, location, etc. +* It can also autonomously interact with users through Q&A, answering questions or engaging in conversations based on other intents. +* Project link: https://gitee.com/dromara/sayOrder + +### sayOrder Interaction Basic Workflow Demonstration + +* User inputs their idea for the first time: + ![](/assets/img/news/easyAI-v1.2.7-3.png) +* SayOrder detects missing essential order information and asks a follow-up question. The user responds by providing additional information: + ![](/assets/img/news/easyAI-v1.2.7-4.png) +* The user's second input still does not meet the key information requirements for the backend's 14-category legal consultation order. After further supplementation, the order information is completed and the order is generated: + ![](/assets/img/news/easyAI-v1.2.7-5.png) +* The user inputs a question they want to consult, and SayOrder autonomously answers the user's query: + ![](/assets/img/news/easyAI-v1.2.7-6.png) + +### Architecture Design + +**Common Underlying Algorithm Modules** + +* Basic Matrix and Linear Algebra Calculation Module: + 1. Built-in matrix classes and matrix calculation classes capable of performing common matrix operations, parity checks, multiple linear regression, logistic regression, Euclidean distance, cosine similarity, im2col, inverse im2col, algebraic cofactor calculation, inversion, adjoint matrix calculation, inner product, differentiation, and a series of other APIs. + 2. RGB three-channel matrices capable of image conversion, cropping, blocking, generating image matrices, and other operations to facilitate subsequent calculations. +* Machine Learning - Clustering: + k-means clustering, Gaussian mixture clustering, density clustering, learning vector quantization clustering, etc. +* Machine Learning - Classification and Fitting: + Multi-layer feedforward neural networks, multi-layer recurrent neural networks, residual networks, multi-layer residual recurrent neural networks, convolutional neural networks, decision trees, random forests, k-nearest neighbors, etc. +* Heuristic Algorithms: + Particle swarm optimization, ant colony optimization, simulated annealing. +* Reinforcement Learning: + Dynamic programming, Monte Carlo analysis, Markov decision processes, temporal difference learning. + +**Common Upper-Level Algorithm Modules** + +* Visual Imaging: + Image recognition, image summarization, target detection. +* Natural Language Processing: + Semantic understanding, word segmentation, sensitive and keyword inference, sentence completion, language interaction. +* Game Bots: + Autonomous strategy, autonomous action. + +### Usage + +1. Download the project and package it into the local Maven repository. +2. Import the easyAi pom file dependency into the project. + +### Follow the Project + +* For ideas, suggestions, or learning related to the project, you can add my work WeChat: + ![](/assets/img/news/easyAI-v1.2.7-7.jpg) + +--- + +About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a range of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, and scheduling orchestration. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, and is dedicated to providing microservices cloud-native solutions for global users. It aims to allow every participating open-source enthusiast to experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of members, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to Interact with Me** + +![](/assets/img/news/easyAI-v1.2.7-8.webp) \ No newline at end of file diff --git a/src/news/easyAI-v1.2.9.md b/src/news/easyAI-v1.2.9.md new file mode 100644 index 0000000000..d181eb509e --- /dev/null +++ b/src/news/easyAI-v1.2.9.md @@ -0,0 +1,128 @@ +--- +title: Native Java Artificial Intelligence Algorithm Framework easyAI-v1.2.9 Version Update +author: EasyAi +date: 2024-11-25 +cover: /assets/img/news/easyAI-v1.2.9-0.webp +head: + - - meta + - name: News +--- + +![](/assets/img/news/easyAI-v1.2.9-0.webp) + +## Foreword + +The significance of EasyAi for Java is equivalent to the significance of the emergence of Spring in the JavaWeb field—to be an out-of-the-box solution that enables every developer to use EasyAi to develop small and micro models that meet their own artificial intelligence business needs. This is its mission! + +## Introduction to EasyAi + +EasyAi has no dependencies. It is a native Java artificial intelligence algorithm framework. Firstly, **it can be smoothly introduced into our Java project with Maven in one step, without any additional environment configuration or dependencies, achieving out-of-the-box usability.** Furthermore, it includes pre-packaged modules for image target detection and AI customer service, as well as provides various underlying algorithm tools for deep learning, machine learning, reinforcement learning, heuristic learning, matrix operations, etc. Developers can, through simple learning, develop small and micro models that deeply align with their own business needs. + +* EasyAI Gitee download link: https://gitee.com/dromara/easyAi +* EasyAI technical documentation address: https://www.myeasyai.cn/ +* EasyAI detailed video tutorials: https://www.bilibili.com/video/av89134035 +* EasyAI framework zero-based deep development and complete artificial intelligence system tutorial: https://www.bilibili.com/cheese/play/ss17600 + +# v1.2.9 + +## Facial Recognition + +* Created a new facial recognition algorithm kernel project seeFace based on EasyAi. +* Kernel algorithm link: https://gitee.com/ldp_dpsmax/see-face +* Video tutorial address: https://www.bilibili.com/video/BV1A8UeYwEsX + +### Quick API + +> `public ErrorMessage look(ThreeChannelMatrix face, long eventID, int secondExplore)` Gets the feature vector of the image. +> +> > Parameter `ThreeChannelMatrix face` is the three-channel matrix of the parsed image. +> +> > Parameter `long eventID` is the thread-safe unique ID. +> +> > Parameter `int secondExplore` is the iteration intensity. It is recommended to use 30 for recognition and increase to 60 for入库 (storing in database). The higher this value, the more stable the face capture, but the slower the speed. Therefore, increase this value when storing faces to obtain vectors for maximum stability. Reduce it to 30 when recognizing features to achieve speed while maintaining a certain level of stability. +> +> Return entity `ErrorMessage`. +> +> > `private int errorCode = 0` is the error code. When the error code is 0, the result is obtained normally. +> +> > `private String errorMessage` is the error reason. +> +> > `FaceMessage faceMessage` is the face information result. When the error code is not 0, this return entity is `null`. +> > +> > > Parameter `private Matrix feature` is the feature matrix, with a built-in two-dimensional array storing feature values. The `getMatrix()` method can be used to extract the two-dimensional array for saving feature values into a database, facilitating comparison using a vector database. +> +> > > Parameter `private ThreeChannelMatrix channel` is the image three-channel matrix locating the facial features. The `ImageTools` method `imageTools.writeImage(faceMessage.getChannel(), "/Users/lidapeng/job/faceData/test/b1.jpg");` can be used to output the face image and observe the face positioning result. If the captured position is abnormal, it indicates that this recognition result is not ideal, and re-shooting can be done to obtain it again. + +### easyAI package has been uploaded to the Maven Central Repository + +> From now on, simply introduce the following JAR package address in the pom file. No need to download and manually package. + +``` +     +    org.dromara.easyai +    easyAi +    1.2.9 +     +``` + +## Facial Detection Effect Demonstration + +![](/assets/img/news/easyAI-v1.2.9-1.png) + +### Image Recognition FastYolo Effect Display + +* Using EasyAi to implement the visual kernel of an image-based self-service vending machine. + +![](/assets/img/news/easyAI-v1.2.9-2.jpg) + +### sayOrder Artificial Intelligence Customer Service + +* sayOrder is an artificial intelligence customer service system packaged based on EasyAi. +* It can analyze the semantics of user input to identify user behavior and distinguish user intent IDs through typeID. It captures keyword categories set in the background to extract content of interest to the system contained in the user's statement, such as time, place, etc. +* It can also autonomously interact with users through Q&A, autonomously answering questions or engaging in communication about other intents. +* Project link address: https://gitee.com/dromara/sayOrder + +### sayOrder Interaction Basic Business Process Demonstration + +* The user first inputs to express their idea. ![](/assets/img/news/easyAI-v1.2.9-3.png) +* SayOrder finds that the user's description lacks necessary order information and asks a follow-up question. The user receives SayOrder's question and further supplements their idea. ![](/assets/img/news/easyAI-v1.2.9-4.png) +* The user's second input still does not meet the key information requirements for the background 14-category legal consultation order. They continue to add information until the order information is complete and the order is generated. ![](/assets/img/news/easyAI-v1.2.9-5.png) +* The user inputs a question they want to consult, and SayOrder autonomously answers the user's consulting question. ![](/assets/img/news/easyAI-v1.2.9-6.png) + +### Architecture Design + +**Common Underlying Algorithm Modules** + +* Basic Matrix and Linear Algebra Calculation Module: + 1. Built-in matrix class, matrix calculation class, can complete common matrix arithmetic, parity, multiple linear regression, logistic regression, Euclidean distance, cosine similarity, im2col, inverse im2col, finding algebraic cofactors, finding inverse, finding adjoint matrix, inner product, differentiation, and a series of APIs. + 2. RGB three-channel matrix, which can perform image conversion, cropping, blocking, generating image matrices, and other operations to facilitate subsequent calculations. +* Machine Learning - Clustering: + k-means clustering, Gaussian mixture clustering, density clustering, learning vector quantization clustering, etc. +* Machine Learning - Classification and Fitting: Multi-layer feedforward neural network, multi-layer recurrent neural network, residual network, multi-layer residual recurrent neural network, convolutional neural network, decision tree, random forest, k-nearest neighbor, etc. +* Heuristic Algorithms: Particle swarm, ant colony, simulated annealing. +* Reinforcement Learning: Dynamic programming, Monte Carlo analysis, Markov, temporal difference. + +**Common Upper-layer Algorithm Modules** + +* Visual Image: Image recognition, image summarization, target detection. +* Natural Language: Semantic understanding, word segmentation, reasoning about sensitivity and keywords, sentence completion, language interaction. +* Game Bots: Autonomous strategy, autonomous action. + +### Usage + +1. Download the project and package it into the local Maven repository. +2. Introduce the easyAi pom file address into the project. + +### Follow the Project + +* For any ideas, suggestions, or learning related to the project, you can add my work WeChat. ![](/assets/img/news/easyAI-v1.2.9-7.jpg) + +About Dromara + +Dromara is an open-source community composed of top domestic open-source project authors. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservice RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, scheduling orchestration, etc. The technology stack is fully open-source and co-built, maintaining community neutrality, and is committed to providing microservice cloud-native solutions for global users. Let every open-source enthusiast involved experience the happiness of open source. + +The Dromara open-source community currently has 10+ GVP projects, with a total star count of over 100,000, building an open-source community of tens of thousands of people, with thousands of individuals and teams using the open-source projects of the Dromara community. + +**Everyone is welcome to come to the Knowledge Planet to interact with me.** + +![](/assets/img/news/easyAI-v1.2.9-8.webp) \ No newline at end of file diff --git a/src/news/hmily-2.0.2.md b/src/news/hmily-2.0.2.md index d3bf17d2ae..3f5057e05e 100644 --- a/src/news/hmily-2.0.2.md +++ b/src/news/hmily-2.0.2.md @@ -1,65 +1,65 @@ ---- -title: Hmily released 2.0.2-Release -author: xiaoyu -tag: - - hmily -date: 2019-04-05 -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: News ---- - -## Hmily released 2.0.2-Release - -- Resolved the issue of SpringCloud using Hystrix to configure thread pool. -- New issue with SpringCloud embedded transaction calls. - -- Added Hmily load balancing strategy. - -- Other bug fixes and code optimizations. - -- Remove unnecessary third-party JAR packages. - -- Introduction of zero intrusion mode. - -## Hmily's support for the popular RPC framework and Spring. - -- Dubbo 2.7.0 for all versions below. -- SpringCloud Dalston and above, including support for Finchley and Greenwich - -- All versions of Motan. - -- All Spring versions up to 3.0. - -## Hmily has a load-balancing policy for user RPC clusters in version 2.0.2. - -- Hmily provides its own implementation of the load-balancing strategy, only for interfaces with @Hmily added - - Dubbo cluster configuration with loadbalance="hmily" - -```xml - -``` - -Spring Cloud added to the caller's YML configuration file: - -```yml -hmily : - ribbon: - rule - enabled : true -``` - -## Documents - -- Official document: https://dromara.org/website/zh-cn/docs/hmily/index.html - -- Github: https://github.com/yu199195/hmily - -- Gitee: https://gitee.com/dromara/hmily - - Welcome to Star Fork, provide excellent code and suggestions. +--- +title: Hmily released 2.0.2-Release +author: xiaoyu +tag: + - hmily +date: 2019-04-05 +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: News +--- + +## Hmily released 2.0.2-Release + +- Resolved the issue of SpringCloud using Hystrix to configure thread pool. +- New issue with SpringCloud embedded transaction calls. + +- Added Hmily load balancing strategy. + +- Other bug fixes and code optimizations. + +- Remove unnecessary third-party JAR packages. + +- Introduction of zero intrusion mode. + +## Hmily's support for the popular RPC framework and Spring. + +- Dubbo 2.7.0 for all versions below. +- SpringCloud Dalston and above, including support for Finchley and Greenwich + +- All versions of Motan. + +- All Spring versions up to 3.0. + +## Hmily has a load-balancing policy for user RPC clusters in version 2.0.2. + +- Hmily provides its own implementation of the load-balancing strategy, only for interfaces with @Hmily added + + Dubbo cluster configuration with loadbalance="hmily" + +```xml + +``` + +Spring Cloud added to the caller's YML configuration file: + +```yml +hmily : + ribbon: + rule + enabled : true +``` + +## Documents + +- Official document: https://dromara.org/website/zh-cn/docs/hmily/index.html + +- Github: https://github.com/yu199195/hmily + +- Gitee: https://gitee.com/dromara/hmily + + Welcome to Star Fork, provide excellent code and suggestions. diff --git a/src/news/hmily-2.1.1.md b/src/news/hmily-2.1.1.md index 17acc6d141..f5f506d977 100644 --- a/src/news/hmily-2.1.1.md +++ b/src/news/hmily-2.1.1.md @@ -1,291 +1,291 @@ ---- -title: One year later, the dromara team released version 2.1.1 of the new architecture Hmily distributed transaction framework -author: xiaoyu -date: 2020-09-28 -tag: - - hmily -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: News ---- - -Thank you guys for your support all the way, and keep everyone waiting. In this version, our team refactored the entire project, reasonably divided functional modules, added configuration centers, adjusted the underlying storage structure, solved difficult bugs, and supported other new features, and absorbed more outstanding open source community members to join in. - -## Architecture - -![](/assets/img/architecture/hutool-framework.png) - -### Features - -- High availability·: Supports abnormal transaction rollback and overtime transaction recovery in distributed scenarios to prevent transaction suspension. -- Ease of use: Provide zero-invasive Spring-Boot, Spring-Namespace to quickly integrate with business systems. -- High performance: Decentralized design, fully integrated with business systems, naturally supports cluster deployment. -- Observability: Performance monitoring of multiple metrics by Metrics, as well as admin management UI . -- Multiple RPCs: support Dubbo, SpringCloud, Motan, Sofa-rpc and other well-known RPC frameworks. -- Log storage: Support Mysql, Oracle, Mongodb, Redis, Zookeeper, etc. -- Complex scenarios: Support RPC nested call transactions. - -### Refactoring part - -- **Module division:** - - - Extract the SPI custom module and It's open-the-box. - - * SPI module that defines multiple storage methods for transaction logs. - * SPI module that defines multiple serialization methods for transaction logs. - * Add configuration center, support various mainstream configuration centers (Nacos, Apollo, Zookeeper, etc.), and support dynamic refresh of configuration. - * Add metrics module to monitor various information at runtime. - * Remove the core transaction execution module. - * Extract multiple RPC support modules. - * Extract the Spring and Spring Boot support modules. - -- **On the dependent package version:** - - - Guava upgraded to 2.9.0. - - Curator upgraded to 5.1.0. - -- **Code quality:** - - - Strict check-style code inspection, adhering to the principle of elegance and simplicity (talk is cheap, show you code). - -- **openness :** - - - The community pursues the basic principles of simplicity, happiness, and harmony. - -- **Goal:** - - - Create a high-availability, high-performance, easy-to-use financial-level distributed transaction solution. - -### Solve bugs: - -- The Dubbo framework does not support the use of annotations (spring-boot-starter-dubbo). -- The Motan framework does not support the use of annotations. -- If Spring Cloud users use Feign and Hystrix to integrate Hmily, the thread switching problem occurs. -- In extreme cases, the transaction log serialization is abnormal. -- If timeout happen in `try `, It will cause the transaction suspension bug. -- When the `confirm` and `cancel` phases are abnormal, the transaction fails to rollback. -- In the transaction log storage, two modes of synchronous and asynchronous are supported for users to choose. - -### User guide - -For Hmily users, it only takes three steps to achieve the BASE transaction between RPC service calls - -- Add the maven dependencies supported by Hmily for various RPC. -- Add Hmily configuration. -- Add `@Hmily` annotation to RPC interface method. - -#### Dependency changes - -There is no change to the dependencies, only the version needs to be upgraded to 2.1.0. Here are examples of Dubbo microservices. - -#### Dubbo RPC microservices - -- Dubbo interface service dependency. - -```xml - - org.dromara - hmily-annotation - 2.1.0 - -``` - -- Dubbo service provider depends on version<2.7. - -```xml - - org.dromara - hmily-dubbo - 2.1.0 - - - or - - - org.dromara - hmily-spring-boot-starter-dubbo - 2.1.0 - -``` - -#### Hmily configuration changes - -In the new version 2.1.0, the hmily-config module has been added to support local and registry modes. The user first needs to create a new file named `hmily.yml` under the project `resouce` file. The default path is the project's `resource` directory, it can also be specified with `-Dhmily.conf`, or the configuration can be placed in the `user.dir` directory. Priority level `-Dhmily.conf`> `user.dir`> `resource`. The file format is as follows (The local mode of configuration): - -```yml - server: - configMode: local - appName: account-dubbo - config: - appName: account-dubbo - serializer: kryo - contextTransmittalMode: threadLocal - scheduledThreadMax: 16 - scheduledRecoveryDelay: 60 - scheduledCleanDelay: 60 - scheduledPhyDeletedDelay: 600 - scheduledInitDelay: 30 - recoverDelayTime: 60 - cleanDelayTime: 180 - limit: 200 - retryMax: 10 - bufferSize: 8192 - consumerThreads: 16 - asyncRepository: true - autoSql: true - phyDeleted: true - storeDays: 3 - repository: mysql - -repository: - database: - driverClassName: com.mysql.jdbc.Driver - url : jdbc:mysql://127.0.0.1:3306/hmily?useUnicode=true&characterEncoding=utf8 - username: root - password: - maxActive: 20 - minIdle: 10 - connectionTimeout: 30000 - idleTimeout: 600000 - maxLifetime: 1800000 -``` - -**If you want to use Nacos as configuration center:** - -```yml -hmily: - server: - configMode: nacos - appName: xxxxx -remote: - nacos: - server: 192.168.3.22:8848 - dataId: hmily.properties - group: DEFAULT_GROUP - timeoutMs: 6000 - fileExtension: yml - passive: true -``` - -**If you want use Apollo as configuration center:** - -```yml -hmily: - server: - configMode: apollo - appName: xxxx -remote: - apollo: - appId: hmily-xxxxx - configService: http://192.168.3.22:8080 - namespace: byin_hmily - secret: - fileExtension: yml - passive: true - env: dev - meta: http://192.168.3.22:808 -``` - -If you want to know more configuration methods and detailed explanations of configuration content, please refer to: https://dromara.org/zh-cn/docs/hmily/config.html . - -**Changes in the use of annotation methods** - -In the previous version, RPC interface and implementation only need to add `@Hmily` annotation, but now It need to be changed, you need to add `@Hmily` in the RPC interface method, which is used to identify this is a Hmily distributed transaction interface method , besides, you need to add `@HmilyTCC` to the implementation of the interface, and then specify the method names of `confirm` and `cancel`. - -**Example (say method in Dubbo needs to participate in distributed transactions):** - -```java -public interface HelloService { - - @Hmily - void say(String hello); -} - -public class HelloServiceImpl implements HelloService { - - @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel") - public void say(String hello) { - System.out.println("hello world"); - } - - public void sayConfrim(String hello) { - System.out.println(" confirm hello world"); - } - - public void sayCancel(String hello) { - System.out.println(" cancel hello world"); - } -} -``` - -**Example (say method in springcloud needs to participate in distributed transactions):** - -- Spring-cloud service caller FeignClient. - -```java -@FeignClient(value = "helle-service") -public interface HelloService { - - @Hmily - @RequestMapping("/helle-service/sayHello") - void say(String hello); -} -``` - -- Spring-cloud provider. - -```java -@RestController -public class HelloController { - - private final HelloService helloService ; - - @Autowired - public AccountController(HelloService helloService) { - this.helloService= helloService; - } - - @RequestMapping("/sayHello") - public void payment(String hello) { - return helloService.say(hello); - } -} -public interface HelloService { - - void say(String hello); -} -public class HelloServiceImpl implements HelloService { - - @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel") - public void say(String hello) { - System.out.println("hello world"); - } - - public void sayConfrim(String hello) { - System.out.println(" confirm hello world"); - } - - public void sayCancel(String hello) { - System.out.println(" cancel hello world"); - } -} -``` - -- **Changes in transaction log storage structure** - - Users don't need to care about using or upgrading, the framework will be initialized by default. - -## Next version - -- Because of the adjustment of the architecture, it will be easier to support other modes. In the next version, TAC mode (try-auto-cancel) will be released, which will greatly simplify the use of the framework. You need only to care about the development of `confirm` and `cancel` methods, and It's provide better compatibility with the transformation of the old system. Don't worry about additional development tasks, just leave everything to Hmily! -- It will support Brpc. -- It will support Tars-rpc. - -## Community - -We uphold the principle of harmony and happiness. If you have ideas and want to contribute to community, come and join us! - -- Github: https://github.com/dromara/hmily -- Gitee: https://gitee.com/dromara/hmily -- QQ group: 162614487 +--- +title: One year later, the dromara team released version 2.1.1 of the new architecture Hmily distributed transaction framework +author: xiaoyu +date: 2020-09-28 +tag: + - hmily +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: News +--- + +Thank you guys for your support all the way, and keep everyone waiting. In this version, our team refactored the entire project, reasonably divided functional modules, added configuration centers, adjusted the underlying storage structure, solved difficult bugs, and supported other new features, and absorbed more outstanding open source community members to join in. + +## Architecture + +![](/assets/img/architecture/hutool-framework.png) + +### Features + +- High availability·: Supports abnormal transaction rollback and overtime transaction recovery in distributed scenarios to prevent transaction suspension. +- Ease of use: Provide zero-invasive Spring-Boot, Spring-Namespace to quickly integrate with business systems. +- High performance: Decentralized design, fully integrated with business systems, naturally supports cluster deployment. +- Observability: Performance monitoring of multiple metrics by Metrics, as well as admin management UI . +- Multiple RPCs: support Dubbo, SpringCloud, Motan, Sofa-rpc and other well-known RPC frameworks. +- Log storage: Support Mysql, Oracle, Mongodb, Redis, Zookeeper, etc. +- Complex scenarios: Support RPC nested call transactions. + +### Refactoring part + +- **Module division:** + + - Extract the SPI custom module and It's open-the-box. + + * SPI module that defines multiple storage methods for transaction logs. + * SPI module that defines multiple serialization methods for transaction logs. + * Add configuration center, support various mainstream configuration centers (Nacos, Apollo, Zookeeper, etc.), and support dynamic refresh of configuration. + * Add metrics module to monitor various information at runtime. + * Remove the core transaction execution module. + * Extract multiple RPC support modules. + * Extract the Spring and Spring Boot support modules. + +- **On the dependent package version:** + + - Guava upgraded to 2.9.0. + - Curator upgraded to 5.1.0. + +- **Code quality:** + + - Strict check-style code inspection, adhering to the principle of elegance and simplicity (talk is cheap, show you code). + +- **openness :** + + - The community pursues the basic principles of simplicity, happiness, and harmony. + +- **Goal:** + + - Create a high-availability, high-performance, easy-to-use financial-level distributed transaction solution. + +### Solve bugs: + +- The Dubbo framework does not support the use of annotations (spring-boot-starter-dubbo). +- The Motan framework does not support the use of annotations. +- If Spring Cloud users use Feign and Hystrix to integrate Hmily, the thread switching problem occurs. +- In extreme cases, the transaction log serialization is abnormal. +- If timeout happen in `try `, It will cause the transaction suspension bug. +- When the `confirm` and `cancel` phases are abnormal, the transaction fails to rollback. +- In the transaction log storage, two modes of synchronous and asynchronous are supported for users to choose. + +### User guide + +For Hmily users, it only takes three steps to achieve the BASE transaction between RPC service calls + +- Add the maven dependencies supported by Hmily for various RPC. +- Add Hmily configuration. +- Add `@Hmily` annotation to RPC interface method. + +#### Dependency changes + +There is no change to the dependencies, only the version needs to be upgraded to 2.1.0. Here are examples of Dubbo microservices. + +#### Dubbo RPC microservices + +- Dubbo interface service dependency. + +```xml + + org.dromara + hmily-annotation + 2.1.0 + +``` + +- Dubbo service provider depends on version<2.7. + +```xml + + org.dromara + hmily-dubbo + 2.1.0 + + + or + + + org.dromara + hmily-spring-boot-starter-dubbo + 2.1.0 + +``` + +#### Hmily configuration changes + +In the new version 2.1.0, the hmily-config module has been added to support local and registry modes. The user first needs to create a new file named `hmily.yml` under the project `resouce` file. The default path is the project's `resource` directory, it can also be specified with `-Dhmily.conf`, or the configuration can be placed in the `user.dir` directory. Priority level `-Dhmily.conf`> `user.dir`> `resource`. The file format is as follows (The local mode of configuration): + +```yml + server: + configMode: local + appName: account-dubbo + config: + appName: account-dubbo + serializer: kryo + contextTransmittalMode: threadLocal + scheduledThreadMax: 16 + scheduledRecoveryDelay: 60 + scheduledCleanDelay: 60 + scheduledPhyDeletedDelay: 600 + scheduledInitDelay: 30 + recoverDelayTime: 60 + cleanDelayTime: 180 + limit: 200 + retryMax: 10 + bufferSize: 8192 + consumerThreads: 16 + asyncRepository: true + autoSql: true + phyDeleted: true + storeDays: 3 + repository: mysql + +repository: + database: + driverClassName: com.mysql.jdbc.Driver + url : jdbc:mysql://127.0.0.1:3306/hmily?useUnicode=true&characterEncoding=utf8 + username: root + password: + maxActive: 20 + minIdle: 10 + connectionTimeout: 30000 + idleTimeout: 600000 + maxLifetime: 1800000 +``` + +**If you want to use Nacos as configuration center:** + +```yml +hmily: + server: + configMode: nacos + appName: xxxxx +remote: + nacos: + server: 192.168.3.22:8848 + dataId: hmily.properties + group: DEFAULT_GROUP + timeoutMs: 6000 + fileExtension: yml + passive: true +``` + +**If you want use Apollo as configuration center:** + +```yml +hmily: + server: + configMode: apollo + appName: xxxx +remote: + apollo: + appId: hmily-xxxxx + configService: http://192.168.3.22:8080 + namespace: byin_hmily + secret: + fileExtension: yml + passive: true + env: dev + meta: http://192.168.3.22:808 +``` + +If you want to know more configuration methods and detailed explanations of configuration content, please refer to: https://dromara.org/zh-cn/docs/hmily/config.html . + +**Changes in the use of annotation methods** + +In the previous version, RPC interface and implementation only need to add `@Hmily` annotation, but now It need to be changed, you need to add `@Hmily` in the RPC interface method, which is used to identify this is a Hmily distributed transaction interface method , besides, you need to add `@HmilyTCC` to the implementation of the interface, and then specify the method names of `confirm` and `cancel`. + +**Example (say method in Dubbo needs to participate in distributed transactions):** + +```java +public interface HelloService { + + @Hmily + void say(String hello); +} + +public class HelloServiceImpl implements HelloService { + + @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel") + public void say(String hello) { + System.out.println("hello world"); + } + + public void sayConfrim(String hello) { + System.out.println(" confirm hello world"); + } + + public void sayCancel(String hello) { + System.out.println(" cancel hello world"); + } +} +``` + +**Example (say method in springcloud needs to participate in distributed transactions):** + +- Spring-cloud service caller FeignClient. + +```java +@FeignClient(value = "helle-service") +public interface HelloService { + + @Hmily + @RequestMapping("/helle-service/sayHello") + void say(String hello); +} +``` + +- Spring-cloud provider. + +```java +@RestController +public class HelloController { + + private final HelloService helloService ; + + @Autowired + public AccountController(HelloService helloService) { + this.helloService= helloService; + } + + @RequestMapping("/sayHello") + public void payment(String hello) { + return helloService.say(hello); + } +} +public interface HelloService { + + void say(String hello); +} +public class HelloServiceImpl implements HelloService { + + @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel") + public void say(String hello) { + System.out.println("hello world"); + } + + public void sayConfrim(String hello) { + System.out.println(" confirm hello world"); + } + + public void sayCancel(String hello) { + System.out.println(" cancel hello world"); + } +} +``` + +- **Changes in transaction log storage structure** + + Users don't need to care about using or upgrading, the framework will be initialized by default. + +## Next version + +- Because of the adjustment of the architecture, it will be easier to support other modes. In the next version, TAC mode (try-auto-cancel) will be released, which will greatly simplify the use of the framework. You need only to care about the development of `confirm` and `cancel` methods, and It's provide better compatibility with the transformation of the old system. Don't worry about additional development tasks, just leave everything to Hmily! +- It will support Brpc. +- It will support Tars-rpc. + +## Community + +We uphold the principle of harmony and happiness. If you have ideas and want to contribute to community, come and join us! + +- Github: https://github.com/dromara/hmily +- Gitee: https://gitee.com/dromara/hmily +- QQ group: 162614487 diff --git a/src/news/hmily-restart.md b/src/news/hmily-restart.md index 3775cd8cb2..9cbed284e1 100644 --- a/src/news/hmily-restart.md +++ b/src/news/hmily-restart.md @@ -1,95 +1,95 @@ ---- -title: Hmily distributed transaction restart monthly report -author: xiaoyu -date: 2020-09-08 -tag: - - hmily -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: News ---- - -Hmily is a flexible distributed transaction architecture with high performance, high avalibility and ease to use. At present, it provides support for Dubbo, Spring-Cloud, Motan, GRPC and other RPC frameworks. In terms of ease of use, it provides zero-intrusive rapid integration of Spring-Boot and Spring-Namespace, with the goal of building a distributed transaction solution of financial level. - -## Adjust Hmily architecture with more reasonable module partition - -**Architecture:** - -![全景图](/assets/img/architecture/hmily-framework.png) - -**Architecture adjustment:** - -- Pull out the core execution module, support a variety of transaction mode and mixed use of TCC mode, TAC mode. -- The core module removes dependencies on Spring. -- Define implementations of various SPI interfaces. -- New `hmily-rpc` : aggregates support for various RPC frameworks. -- Added `hmily-spi` : Hmily framework custom SPI mechanism implementation. -- New `hmily-bom` : resolves version dependency management conflicts. -- Added `hmily-metrics`: monitoring JVM, thread, transaction health, time, etc. -- New `hmily-TCC` : Core implementation of TCC pattern. -- Added `hmily-TCC` : Core implementation of TAC mode. - -**SPI module partition: ** - -- Added `hmily-repository`: transaction log storage module with support (MySQL, Oracle, PostgreSQL, SQL Server, ZooKeeper, Redis, MongoDB, File). -- Added `hmily-serializer`: transaction log serializer module, support (Hessian, JDK, Kryo, Protobuf) -- Added `hmily-config`: config module to support (local mode, Zookeeper, Nacos, Apollo, Etcd). -- Added `hmily-tac-SQLParser`: SQL parsing module under TAC mode - -### Gather the Hmily Community Issue and solve bugs. - -![hmily-bug](/assets/img/architecture/hmily-bug.png) - -For example, in the community, it is gather the problems reported by the community, as well as to cooperate with the community for developing new version. - -**Solve bug: ** - -- Dubbo framework does not support annotation (spring-boot-starter-dubbo) -- The Motan framework does not support the use of annotations -- Exceptions in Spring-Cloud users when integrating Hmily with Hystrix using Feign. -- Transaction log serialization exception. -- Timeout exception transaction suspension bug. -- Transaction timing recovers bugs. - -**Added function: ** - -- `build`: Added travis-ci feature -- Transaction log support: Oracle, PostgreSQL, Sqlsever, Mongo, Zookeeper, File, Redis. -- Configuration module: new configuration center support for Apollo, ETCD, and Nacos -- Demo: Added Motan-RPC to use Hmily distributed transaction. - -### Community building - -- The community adheres to the principles of simplicity, pleasure, elegance, and harmony. - - - Code guidelines: The code follows the HMILY-CHECKSTYLE standard, and there is plenty of room for flexibility. Talk is cheap,show you code. - - Open rule: I hope everyone here can offer good ideas, we can discuss together, review code repeatedly, think about solving bugs, grow happily. - -### Recently - -Hmily-2.1.0 of the latest architecture will be released (TCC mode only will be supported). - -**Configuration module** - -- Configuration dynamic refresh function, support all configuration centers. - -**TAC mode:** - -- `sql-parser`: accessing Apache-Shardingsphere, Apache-Calcite. -- `SQL-revert`: Under development. - -### At last - -Good to be here for the season, at this point in time, Hmily-2.2.0 will be released, which will fully support TAC, TCC modes. -TAC(Transaction Auto Rollback): With this mode, users no longer have to worry about writing reverse cancel methods like TCC. Greatly reduce the use cost and learning cost. -TCC : Stability, reliability has been greatly strengthened, completely solve the problem of transaction suspension. - -- More RPC framework support: BRPC and more. -- Support XA mode. - -Github: https://github.com/dromara/hmily - -Gitee: https://github.com/shuaiqiyu/hmily - -QQ group: 162614487 +--- +title: Hmily distributed transaction restart monthly report +author: xiaoyu +date: 2020-09-08 +tag: + - hmily +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: News +--- + +Hmily is a flexible distributed transaction architecture with high performance, high avalibility and ease to use. At present, it provides support for Dubbo, Spring-Cloud, Motan, GRPC and other RPC frameworks. In terms of ease of use, it provides zero-intrusive rapid integration of Spring-Boot and Spring-Namespace, with the goal of building a distributed transaction solution of financial level. + +## Adjust Hmily architecture with more reasonable module partition + +**Architecture:** + +![全景图](/assets/img/architecture/hmily-framework.png) + +**Architecture adjustment:** + +- Pull out the core execution module, support a variety of transaction mode and mixed use of TCC mode, TAC mode. +- The core module removes dependencies on Spring. +- Define implementations of various SPI interfaces. +- New `hmily-rpc` : aggregates support for various RPC frameworks. +- Added `hmily-spi` : Hmily framework custom SPI mechanism implementation. +- New `hmily-bom` : resolves version dependency management conflicts. +- Added `hmily-metrics`: monitoring JVM, thread, transaction health, time, etc. +- New `hmily-TCC` : Core implementation of TCC pattern. +- Added `hmily-TCC` : Core implementation of TAC mode. + +**SPI module partition: ** + +- Added `hmily-repository`: transaction log storage module with support (MySQL, Oracle, PostgreSQL, SQL Server, ZooKeeper, Redis, MongoDB, File). +- Added `hmily-serializer`: transaction log serializer module, support (Hessian, JDK, Kryo, Protobuf) +- Added `hmily-config`: config module to support (local mode, Zookeeper, Nacos, Apollo, Etcd). +- Added `hmily-tac-SQLParser`: SQL parsing module under TAC mode + +### Gather the Hmily Community Issue and solve bugs. + +![hmily-bug](/assets/img/architecture/hmily-bug.png) + +For example, in the community, it is gather the problems reported by the community, as well as to cooperate with the community for developing new version. + +**Solve bug: ** + +- Dubbo framework does not support annotation (spring-boot-starter-dubbo) +- The Motan framework does not support the use of annotations +- Exceptions in Spring-Cloud users when integrating Hmily with Hystrix using Feign. +- Transaction log serialization exception. +- Timeout exception transaction suspension bug. +- Transaction timing recovers bugs. + +**Added function: ** + +- `build`: Added travis-ci feature +- Transaction log support: Oracle, PostgreSQL, Sqlsever, Mongo, Zookeeper, File, Redis. +- Configuration module: new configuration center support for Apollo, ETCD, and Nacos +- Demo: Added Motan-RPC to use Hmily distributed transaction. + +### Community building + +- The community adheres to the principles of simplicity, pleasure, elegance, and harmony. + + - Code guidelines: The code follows the HMILY-CHECKSTYLE standard, and there is plenty of room for flexibility. Talk is cheap,show you code. + - Open rule: I hope everyone here can offer good ideas, we can discuss together, review code repeatedly, think about solving bugs, grow happily. + +### Recently + +Hmily-2.1.0 of the latest architecture will be released (TCC mode only will be supported). + +**Configuration module** + +- Configuration dynamic refresh function, support all configuration centers. + +**TAC mode:** + +- `sql-parser`: accessing Apache-Shardingsphere, Apache-Calcite. +- `SQL-revert`: Under development. + +### At last + +Good to be here for the season, at this point in time, Hmily-2.2.0 will be released, which will fully support TAC, TCC modes. +TAC(Transaction Auto Rollback): With this mode, users no longer have to worry about writing reverse cancel methods like TCC. Greatly reduce the use cost and learning cost. +TCC : Stability, reliability has been greatly strengthened, completely solve the problem of transaction suspension. + +- More RPC framework support: BRPC and more. +- Support XA mode. + +Github: https://github.com/dromara/hmily + +Gitee: https://github.com/shuaiqiyu/hmily + +QQ group: 162614487 diff --git a/src/news/hutool-5.8.0.md b/src/news/hutool-5.8.0.md index 277736f9cd..14980f5d51 100644 --- a/src/news/hutool-5.8.0.md +++ b/src/news/hutool-5.8.0.md @@ -1,106 +1,106 @@ ---- -title: Hutool-5.8.0.M1 released, attempting milestone release -author: hutool -tag: - - hutool -date: 2022-03-30 -cover: /assets/img/architecture/hutool-framework.png -head: - - - meta - - name: News ---- - -> Hutool is a small but complete Java utility class library that provides elegant, efficient and convenient tool methods. - -## What is Hutool - -! [architecture panorama] (/ assets/img/architecture/hutool - framework. PNG) - -Originally, this version should have been 5.7.23, but the user raised some issues, which must be resolved by modifying the original code structure: - -1. For example, when the MongoDB client is packaged, the packaged tool class has to be modified due to incompatible modifications to its driver. -2. The part of the code involved in Bean copy (BeanCopier), due to a parameter failure, thought it was just a simple bug, and later found that there was a problem with the whole design... You can imagine the extent of the crash, liver spent two nights refactoring this part of the code. -3. When I modified the code, I found that there were many design problems in many parts, and I made small refactoring by the way. -4. In order to solve the possible impact of each major version upgrade on old users, this version is released in a milestone way, the version is M1 (feel to send a CPU to the user), and it is also to solve the problem of each "radical" upgrade of Hutool (after all, old age, to be stable). -5. Purchase instead of donation, if you want to support Hutool, you can go to the Hutool home page and click -> click into the surrounding stores to buy Hutool peripheral to support Hutool. Oh, this is more affordable than donation (after all, I do not know how to thank the donor, it is a moral burden...) - -Thank you to the Hutool members who discussed and solved a lot of issues together in this release: -@阿超 @Cherryrum @Husky - ---- - -## 5.8.0.M1 - -### ❌ Incompatible feature - -• 【db 】 【 Not backward compatible 】 Added MongoDB4.x support Return MongoClient change (pr#568@Gitee) -• 【json】【Possible compatibility issues】 Modify JSONObject structure, inherited from MapWrapper -• 【core】【Possible compatibility issues】 Rebuild BeanCopier, create XXXCopier, and delete XXXValueProvider -• 【core】【Possible compatibility issues】URLEncoder is deprecated, URLEncoderUtil uses RFC3986 -• 【core】【Possible compatibility issues】 Base32 separate encoding and decoding in order to reduce data load, support Hex mode -• 【core】【Possible compatibility issues】 Base58 separate encoding and decoding -• 【core】【Possible compatibility issues】 Base62 separate encoding and decoding, added inverted mode support -• 【core】【compatibility issues】 PunyCode parameter changed from String to Charsequence -• 【cron 】 【 Possible compatibility issues 】SimpleValueParser was renamed AbsValueParser and changed to abstract -• 【poi 】 【 Possible compatibility problem 】ExcelUtil.getBigWriter Change the return value to BigExcelWriter -• 【core】【Possible compatibility issues】 Opt.ofEmptyAble argument changed from List to Collection subclass (pr#580@Gitee) -• 【json】【Possible compatibility issues】 When converting JSON to Bean, use JSON's own Settings instead of the default (issue#2212@Github) -• 【json】【Possible compatibility issues】isOrder is discarded in JSONConfig, and is all ordered by default - -### 🐣 New features - -• 【http 】 HttpRequest.form is in TableMap mode (issue#I4W427@Gitee) -• 【core 】 AnnotationUtil adds getAnnotationAlias (pr#554@Gitee) -• 【core 】 FileUtil.extName added special handling for tar.gz (issue#I4W5FS@Gitee) -• 【crypto 】 Add XXTEA implementation (issue#I4WH2X@Gitee) -• 【core 】 Add Table implementation (issue#2179@Github) -• 【core 】 Add UniqueKeySet (issue#I4WUWR@Gitee) -• 【core 】 Extension of conversion from Arabic numerals to Chinese to par value of invoice (pr#570@Gitee) -• [core] ArrayUtil adds replace method (pr#570@Gitee) -• 【core 】 CsvReadConfig Add the custom title line number (issue#2180@Github) -• 【core 】 FileAppender Optimizes initial List size (pr#2197@Github) -• 【core 】 Base32 adds pad support (pr#2195@Github) -• 【core 】 Adds setFields methods to Dict (pr#578@Gitee) -• 【db 】 New index related interface to db.meta (pr#563@Gitee) -• 【db 】 The length of Column#typeName is removed from the column of Oracle (pr#563@Gitee). -• 【poi 】 Optimize ExcelReader for read-only mode (pr#2204@Gitee) -• 【poi 】 Optimizes ExcelBase and puts alias in -• 【core 】 Improved StrUtil#startWith and endWith performance -• 【cron 】 Add CronPatternParser and MatcherTable -• 【http 】 GlobalHeaders add system attributes allowUnsafeServerCertChange、allowUnsafeRenegotiation -• 【http 】 UserAgentUtil parsing, add MiUI/XiaoMi browser judgment logic (pr#581@Gitee) -• 【core 】 FileAppender Add lock structure (pr#2211@Github) -• 【poi 】 ExcelReader added construction (pr#2213@Github) -• 【core 】 MapUtil provides change function, EnumUtil provides getBy function, enumeration field mapping through lambda (pr#583@Gitee) -• 【core 】 CompareUtil adds comparingIndexed (pr#585@Gitee). -• 【db 】 DruidDataSource build with custom parameters (issue#I4ZKCW@Gitee) -• 【poi 】 ExcelWriter adds addImg overload (issue#2218@Github) -• 【bloomFilter】 Added FuncFilter -• 【http 】 Added GlobalInterceptor(issue#2217) - -### 🐞Bug fixed - -• 【core 】 Fix ObjectUtil.hasNull passing null returns true (pr#555@Gitee) -• 【core 】 Fix NumberConverter number conversion issue (issue#I4WPF4@Gitee) -• 【core 】 Fixing problems with ReflectUtil.getMethods Obtaining interface methods (issue#I4WUWR@Gitee) -• 【core 】 Fix uppercase conversion issue in NamingCase (pr#572@Gitee) -• 【http 】 Fix to GET parameter carrying issue (issue#2189@Github) -• 【core 】 Fix the parent path error of FileUtil and FileCopier relative paths (pr#2188@Github) -• 【core 】 Fixed invalid fieldNameEditor in CopyOptions (issue#2202@Github) -• 【json 】 Fix JSON parsing of Map.Entry -• 【core 】 Fix MapConverter map and map conversion compatibility issues -• 【poi 】 Solves POI-5.2.x compatibility issues when sax reads -• 【core 】 Fixed the intersection problem of judging two time intervals (pr#2210@Github) -• 【http 】 Fix tag deletion issue (issue#I4Z7BV@Gitee) -• 【core 】 Fix file name with \* in Win (pr#584@Gitee) -• 【core 】 FileUtil.getMimeType added rar and 7z support (issue#I4ZBN0@Gitee) -• 【json 】 JSON fixed invalid transient Settings (issue#2212@Github) -• 【core 】 Fix IterUtil.getElementType getting null (issue#2222@Github) -• 【core 】 Fix lunar calendar to Gregorian calendar in leap month error (issue#I4ZSGJ@Gitee) - -## Community co-construction - -We uphold the principle of `harmony and happiness`, `code first`, if you have ideas, are willing to grow with us, contribute together, come and join us! - -- github:https://github.com/dromara/hutool -- gitee:https://gitee.com/dromara/hutool +--- +title: Hutool-5.8.0.M1 released, attempting milestone release +author: hutool +tag: + - hutool +date: 2022-03-30 +cover: /assets/img/architecture/hutool-framework.png +head: + - - meta + - name: News +--- + +> Hutool is a small but complete Java utility class library that provides elegant, efficient and convenient tool methods. + +## What is Hutool + +! [architecture panorama] (/ assets/img/architecture/hutool - framework. PNG) + +Originally, this version should have been 5.7.23, but the user raised some issues, which must be resolved by modifying the original code structure: + +1. For example, when the MongoDB client is packaged, the packaged tool class has to be modified due to incompatible modifications to its driver. +2. The part of the code involved in Bean copy (BeanCopier), due to a parameter failure, thought it was just a simple bug, and later found that there was a problem with the whole design... You can imagine the extent of the crash, liver spent two nights refactoring this part of the code. +3. When I modified the code, I found that there were many design problems in many parts, and I made small refactoring by the way. +4. In order to solve the possible impact of each major version upgrade on old users, this version is released in a milestone way, the version is M1 (feel to send a CPU to the user), and it is also to solve the problem of each "radical" upgrade of Hutool (after all, old age, to be stable). +5. Purchase instead of donation, if you want to support Hutool, you can go to the Hutool home page and click -> click into the surrounding stores to buy Hutool peripheral to support Hutool. Oh, this is more affordable than donation (after all, I do not know how to thank the donor, it is a moral burden...) + +Thank you to the Hutool members who discussed and solved a lot of issues together in this release: +@阿超 @Cherryrum @Husky + +--- + +## 5.8.0.M1 + +### ❌ Incompatible feature + +• 【db 】 【 Not backward compatible 】 Added MongoDB4.x support Return MongoClient change (pr#568@Gitee) +• 【json】【Possible compatibility issues】 Modify JSONObject structure, inherited from MapWrapper +• 【core】【Possible compatibility issues】 Rebuild BeanCopier, create XXXCopier, and delete XXXValueProvider +• 【core】【Possible compatibility issues】URLEncoder is deprecated, URLEncoderUtil uses RFC3986 +• 【core】【Possible compatibility issues】 Base32 separate encoding and decoding in order to reduce data load, support Hex mode +• 【core】【Possible compatibility issues】 Base58 separate encoding and decoding +• 【core】【Possible compatibility issues】 Base62 separate encoding and decoding, added inverted mode support +• 【core】【compatibility issues】 PunyCode parameter changed from String to Charsequence +• 【cron 】 【 Possible compatibility issues 】SimpleValueParser was renamed AbsValueParser and changed to abstract +• 【poi 】 【 Possible compatibility problem 】ExcelUtil.getBigWriter Change the return value to BigExcelWriter +• 【core】【Possible compatibility issues】 Opt.ofEmptyAble argument changed from List to Collection subclass (pr#580@Gitee) +• 【json】【Possible compatibility issues】 When converting JSON to Bean, use JSON's own Settings instead of the default (issue#2212@Github) +• 【json】【Possible compatibility issues】isOrder is discarded in JSONConfig, and is all ordered by default + +### 🐣 New features + +• 【http 】 HttpRequest.form is in TableMap mode (issue#I4W427@Gitee) +• 【core 】 AnnotationUtil adds getAnnotationAlias (pr#554@Gitee) +• 【core 】 FileUtil.extName added special handling for tar.gz (issue#I4W5FS@Gitee) +• 【crypto 】 Add XXTEA implementation (issue#I4WH2X@Gitee) +• 【core 】 Add Table implementation (issue#2179@Github) +• 【core 】 Add UniqueKeySet (issue#I4WUWR@Gitee) +• 【core 】 Extension of conversion from Arabic numerals to Chinese to par value of invoice (pr#570@Gitee) +• [core] ArrayUtil adds replace method (pr#570@Gitee) +• 【core 】 CsvReadConfig Add the custom title line number (issue#2180@Github) +• 【core 】 FileAppender Optimizes initial List size (pr#2197@Github) +• 【core 】 Base32 adds pad support (pr#2195@Github) +• 【core 】 Adds setFields methods to Dict (pr#578@Gitee) +• 【db 】 New index related interface to db.meta (pr#563@Gitee) +• 【db 】 The length of Column#typeName is removed from the column of Oracle (pr#563@Gitee). +• 【poi 】 Optimize ExcelReader for read-only mode (pr#2204@Gitee) +• 【poi 】 Optimizes ExcelBase and puts alias in +• 【core 】 Improved StrUtil#startWith and endWith performance +• 【cron 】 Add CronPatternParser and MatcherTable +• 【http 】 GlobalHeaders add system attributes allowUnsafeServerCertChange、allowUnsafeRenegotiation +• 【http 】 UserAgentUtil parsing, add MiUI/XiaoMi browser judgment logic (pr#581@Gitee) +• 【core 】 FileAppender Add lock structure (pr#2211@Github) +• 【poi 】 ExcelReader added construction (pr#2213@Github) +• 【core 】 MapUtil provides change function, EnumUtil provides getBy function, enumeration field mapping through lambda (pr#583@Gitee) +• 【core 】 CompareUtil adds comparingIndexed (pr#585@Gitee). +• 【db 】 DruidDataSource build with custom parameters (issue#I4ZKCW@Gitee) +• 【poi 】 ExcelWriter adds addImg overload (issue#2218@Github) +• 【bloomFilter】 Added FuncFilter +• 【http 】 Added GlobalInterceptor(issue#2217) + +### 🐞Bug fixed + +• 【core 】 Fix ObjectUtil.hasNull passing null returns true (pr#555@Gitee) +• 【core 】 Fix NumberConverter number conversion issue (issue#I4WPF4@Gitee) +• 【core 】 Fixing problems with ReflectUtil.getMethods Obtaining interface methods (issue#I4WUWR@Gitee) +• 【core 】 Fix uppercase conversion issue in NamingCase (pr#572@Gitee) +• 【http 】 Fix to GET parameter carrying issue (issue#2189@Github) +• 【core 】 Fix the parent path error of FileUtil and FileCopier relative paths (pr#2188@Github) +• 【core 】 Fixed invalid fieldNameEditor in CopyOptions (issue#2202@Github) +• 【json 】 Fix JSON parsing of Map.Entry +• 【core 】 Fix MapConverter map and map conversion compatibility issues +• 【poi 】 Solves POI-5.2.x compatibility issues when sax reads +• 【core 】 Fixed the intersection problem of judging two time intervals (pr#2210@Github) +• 【http 】 Fix tag deletion issue (issue#I4Z7BV@Gitee) +• 【core 】 Fix file name with \* in Win (pr#584@Gitee) +• 【core 】 FileUtil.getMimeType added rar and 7z support (issue#I4ZBN0@Gitee) +• 【json 】 JSON fixed invalid transient Settings (issue#2212@Github) +• 【core 】 Fix IterUtil.getElementType getting null (issue#2222@Github) +• 【core 】 Fix lunar calendar to Gregorian calendar in leap month error (issue#I4ZSGJ@Gitee) + +## Community co-construction + +We uphold the principle of `harmony and happiness`, `code first`, if you have ideas, are willing to grow with us, contribute together, come and join us! + +- github:https://github.com/dromara/hutool +- gitee:https://gitee.com/dromara/hutool diff --git a/src/news/mica-mqtt-2.4.0.md b/src/news/mica-mqtt-2.4.0.md new file mode 100644 index 0000000000..4ae3ddb1c5 --- /dev/null +++ b/src/news/mica-mqtt-2.4.0.md @@ -0,0 +1,178 @@ +--- +title: Dromara mica-mqtt 2.4.0 Released +author: mica-mqtt +date: 2024-12-19 +cover: /assets/img/news/mica-mqtt-2.4.0-0.png +head: + - - meta + - name: News +--- + +# Dromara mica-mqtt 2.4.0 Released: Enhanced Server Interfaces, Recommended Upgrade + +![](/assets/img/news/mica-mqtt-2.4.0-0.png) + +## 1. Preface + +The official release of Dromara mica-mqtt **2.4.0** is now available. Please note that starting from version `2.4.x`, the Maven groupId has been migrated to `org.dromara.mica-mqtt`, the package name has been switched to `org.dromara`, and the project has moved to Central Sonatype (snapshots are not supported). All other usage remains consistent with the previous versions. The `2.4.x` series will continue to improve and enrich server functionality. Future versions may also introduce some **major** adjustments and optimizations, though we will strive to **minimize interface changes** to facilitate upgrades. **It is highly recommended** to upgrade to the new `2.4.x` version as soon as possible. + +Additionally, since the old documentation was scattered across various modules and inconvenient to navigate, we have added a new documentation site: https://mica-mqtt.dromara.org + +## 2. Features + +* Supports MQTT v3.1, v3.1.1, and v5.0 protocols. +* Supports WebSocket MQTT sub-protocol (compatible with mqtt.js). +* Supports HTTP REST API. `HTTP API documentation:` https://gitee.com/dromara/mica-mqtt/blob/master/docs/http-api.md +* Supports MQTT client. +* Supports MQTT server. +* Supports MQTT Will messages. +* Supports MQTT Retained messages. +* Supports custom message (MQ) processing and forwarding for cluster implementation. +* MQTT client demo for connecting to Alibaba Cloud MQTT. +* Supports compilation into native executables via GraalVM. +* Supports quick integration with Spring Boot, Solon, and JFinal projects. +* mica-mqtt-spring-boot-starter supports integration with Prometheus + Grafana. +* Cluster implementation based on Redis Stream. See the `mica-mqtt-broker module:` https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-broker. + +## 3. Use Cases + +* Internet of Things (IoT) (cloud MQTT broker) +* IoT (edge device message communication) +* Group-based IM +* Message push +* Simple and easy-to-use MQTT client + +## 4. Changelog + +* ✨ HTTP API adds `stats`, `clients` list, and `client details` interfaces. +* ✨ MqttServer and MqttServerTemplate add `getClientInfo` and `getClients` series of client information interfaces. +* ✨ MqttServer and MqttServerTemplate add `getSubscriptions` interface to retrieve client subscription lists. +* ✨ MqttServer and MqttServerTemplate add `getStat` statistics interface. +* 🚚 Maven groupId changed from `net.dreamlu` to `org.dromara.mica-mqtt`. +* 🚚 Package name changed from `net.dreamlu.iot.mqtt` to `org.dromara.mica.mqtt`. All other aspects remain unchanged. +* 🚚 Moved to Central Sonatype. Central Sonatype does not support snapshots, so mica-mqtt will no longer publish snapshot versions. +* 🐛 Fixed subscription sending timing issue. Gitee #IB72L6. Thanks to `@江上烽` for reporting. + +## 5. Quick Start + +### 5.1 Spring Boot Project + +**Client**: + +```xml + + org.dromara.mica-mqtt + mica-mqtt-client-spring-boot-starter + ${mica-mqtt.version} + +``` + +**mica-mqtt-client-spring-boot-starter Usage Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-spring-boot-starter/README.md + +**Server**: + +```xml + + org.dromara.mica-mqtt + mica-mqtt-server-spring-boot-starter + ${mica-mqtt.version} + +``` + +**mica-mqtt-server-spring-boot-starter Usage Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-spring-boot-starter/README.md + +### 5.2 Solon Project + +**Client**: + +```xml + + org.dromara.mica-mqtt + mica-mqtt-client-solon-plugin + ${mica-mqtt.version} + +``` + +**mica-mqtt-client-solon-plugin Usage Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-solon-plugin/README.md + +**Server**: + +```xml + + org.dromara.mica-mqtt + mica-mqtt-server-solon-plugin + ${mica-mqtt.version} + +``` + +**mica-mqtt-server-solon-plugin Usage Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-solon-plugin/README.md + +### 5.3 JFinal Project + +**Client**: + +```xml + + org.dromara.mica-mqtt + mica-mqtt-client-jfinal-plugin + ${mica-mqtt.version} + +``` + +**mica-mqtt-client-jfinal-plugin Usage Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-jfinal-plugin/README.md + +**Server**: + +```xml + + org.dromara.mica-mqtt + mica-mqtt-server-jfinal-plugin + ${mica-mqtt.version} + +``` + +**mica-mqtt-server-jfinal-plugin Usage Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-jfinal-plugin/README.md + +### 5.4 Other Java Projects + +**Client**: + +```xml + + org.dromara.mica-mqtt + mica-mqtt-client + ${mica-mqtt.version} + +``` + +**mica-mqtt-client Usage Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-client/README.md + +**Server**: + +```xml + + org.dromara.mica-mqtt + mica-mqtt-server + ${mica-mqtt.version} + +``` + +**mica-mqtt-server Usage Documentation:** https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-server/README.md + +## 6. Open Source Addresses + +* Gitee: https://gitee.com/dromara/mica-mqtt +* GitHub: https://github.com/dromara/mica-mqtt +* GitCode: https://gitcode.com/dromara/mica-mqtt + +--- + +About Dromara + +Dromara is an open-source community composed of top open-source project authors in China. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, and scheduling orchestration. The technology stack is fully open-source and collaboratively built, maintaining community neutrality, and is dedicated to providing microservices cloud-native solutions for global users. It aims to allow every participating open-source enthusiast to experience the joy of open-source. + +The Dromara open-source community currently boasts over 10 GVP (Gitee Most Valuable Project) projects, with a total star count exceeding 100,000. It has built an open-source community of tens of thousands of members, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to interact with me** + +![](/assets/img/news/mica-mqtt-2.4.0-1.png) \ No newline at end of file diff --git a/src/news/soul-1.0.4.md b/src/news/soul-1.0.4.md index 44a3fa4ef6..b0bf326c5d 100644 --- a/src/news/soul-1.0.4.md +++ b/src/news/soul-1.0.4.md @@ -1,49 +1,49 @@ ---- -title: Soul Gateway released version 1.0.4-RELEASE -author: xiaoyu -date: 2019-04-09 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: News ---- - -### Soul Gateway released version 1.0.4-RELEASE - -- Fix the bug that appeared in the Soul-admin of version 1.0.3. -- The serialization method supports custom extensions. The default serialization method has been changed from Kroy to Java serialization method. -- Dubbo support. - -### Changes Dubbo usage - -- In the previous version (1.0.2 or 1.0.3), the parameters of Dubbo are passed through the header, and in the 1.0.4 version it is passed through the body. - -- Relevant document information has been updated. - -### Recommendations on using version 1.0.4 - -- Version 1.0.4 supports user-defined plug-in, and supports regular expression matching. - -- The change of Dubbo parameter transfer and it would be more friendly to use. - -### If you used version 1.0.2 before and want to update to version 1.0.4. - -- Add role field in the plug-in table. - -- Restart the Soul-admin of version 1.0.4. - -- Perform synchronization of all plug-ins (because of serialization changes) - -- Start the soul-web service of version 1.0.4. - -### For more information - -- QQ group: 429951241 - -- Official website document: https://dromara.org/website/zh-cn/docs/soul/soul.html - -- Github: https://github.com/Dromara/soul - -- Gitee: https://gitee.com/dromara/soul +--- +title: Soul Gateway released version 1.0.4-RELEASE +author: xiaoyu +date: 2019-04-09 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: News +--- + +### Soul Gateway released version 1.0.4-RELEASE + +- Fix the bug that appeared in the Soul-admin of version 1.0.3. +- The serialization method supports custom extensions. The default serialization method has been changed from Kroy to Java serialization method. +- Dubbo support. + +### Changes Dubbo usage + +- In the previous version (1.0.2 or 1.0.3), the parameters of Dubbo are passed through the header, and in the 1.0.4 version it is passed through the body. + +- Relevant document information has been updated. + +### Recommendations on using version 1.0.4 + +- Version 1.0.4 supports user-defined plug-in, and supports regular expression matching. + +- The change of Dubbo parameter transfer and it would be more friendly to use. + +### If you used version 1.0.2 before and want to update to version 1.0.4. + +- Add role field in the plug-in table. + +- Restart the Soul-admin of version 1.0.4. + +- Perform synchronization of all plug-ins (because of serialization changes) + +- Start the soul-web service of version 1.0.4. + +### For more information + +- QQ group: 429951241 + +- Official website document: https://dromara.org/website/zh-cn/docs/soul/soul.html + +- Github: https://github.com/Dromara/soul + +- Gitee: https://gitee.com/dromara/soul diff --git a/src/news/soul-2.1.x.md b/src/news/soul-2.1.x.md index 0d97964761..9d82994902 100644 --- a/src/news/soul-2.1.x.md +++ b/src/news/soul-2.1.x.md @@ -1,117 +1,117 @@ ---- -title: How convenient is the 2.1.X version of Soul Gateway? -author: xiaoyu -tag: - - Soul -date: 2019-12-12 -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: News ---- - -It has been a year since I open sourced Soul gateway in October last year, and received many suggestions from you guys in community. It has provided very rich functions after optimization, many of functions are highly cusmized, visualized, and highly extensible, now let's make a summary. - -### Plugin - -- Provides various plug-ins, such as signature, monitoring, rate limiting, circuit breaker, Http proxy, Dubbo proxy, Websocket, etc. - -- Support users to quickly develop plug-ins. - -- All plug-in data and switch state support dynamic changes. - -### Data Synchronization - -- Provides three different data synchronization strategies: Http long polling, Zookeeper, and Websocket, allowing users to choose freely. -- It is recommended to use Websocket, which is the lightest and more efficient in a cluster environment. - -### For Users - -- First of all, we provide a client package that is convenient for users to access. Users can quickly register their projects to the Soul gateway. -- By default, users don't need to care about Soul Gateway's selectors, rules and other configurations. -- The user's previous interface is completely zero intrusion, It is only need to change to the domain name of the Soul gateway. -- For Dubbo users, the conversion from Http protocol to Dubbo protocol is almost completed by Http. -- Soul gataway uses the Http protocol, so it is destined to be cross-language, It is feasible for .Net programmers, PHP programmers to interact with Java program. - -For example, if you have a Dubbo interface, the parameter definition is a java bean, - -```java -public void insert(final DubboTest dubboTest) { -} -public class DubboTest implements Serializable { - private String id; - private String name; -} -``` - -If you use the Soul gateway to call this method, your Http parameter is to pass a json string in the body, which is no different from a normal http call. - -```json -{ "id": "123", "name": "xiaoyu" } -``` - -### For Developers - -- With more and more users, the situation of each company is different. Soul gateway in 2.1.X version are more extensible, making it convenient for developers . -- For example, there are may things could be extensible, such as Plug-ins, Filters, Dubbo parameter parser, iphost parser, return results, etc. We know that the default return result of the soul gateway is: - -```json -{ "code": 200, "message ": "成功!", "data": "helloWorld!" } -``` - -However, when using the Soul gateway to call your business system, your business system may define the result that is not confirm the above structure. Maybe your field is called `msg`, which will cause a different structure and bring confusion to the front-end processing. We have noticed this thing: https://github.com/Dromara/soul/issues/109, now it has been optimized, users can customize the return results to define, the specifics depend on the Soul document. - -### What scenarios of Soul gateway are suitable, and what should you pay attention to? - -First of all, I think we should follow pragmatism, when you need to use it , then you have monmentum to know it. Thus, where are you need Soul? - -#### Scenario1: Adimistration back-end - -- First of all, as rising popularity of microservices, our back-end is divided into many micro-services. I believe that your companies has a back-end management system. I guess they generally have the following architecture. - -![soul-rpc](/assets/img/architecture/soul-rpc.png) - -- It may cause some troubles as follows: - - - The developers of every microservice are developing based on this, which will become more and more cumbersome. - - - How to publish without downtime? If you want to publish the commodity module, all other modules will not be able to work at this time. - - - If a certain module interface requires a lot of requests (multiple deployments are required), and another module does not need it, how can you split it? - -- Some people may say I can disassemble them into a few web projects. But this will bring new troubles, where to do load balance? Where to do unified certification? -- Soul gateway solves all the above problems very well, just register your microservice to Soul gateway. You can do whatever you want. For example, the order module has 2 nodes, and you want to release a new version, you can send request to one of them in the gateway, and update the version in the other node. When the update complete, let the request go though both two nodes. So Java programmer can also do the jod of system operation engineer. -- If you need unified authentication, you only need to add an authentication plug-in suitable for your business to the gateway. - -![soul-admin](/assets/img/architecture/soul-admin-1.png) - -### Scenario2: Company entrance gateway (open platform) - -- If a company wants to do open-platform or an entrance gateway, authentication, rate limiting, circuit breaker, monitoring are indispensable. - -- If your company is in Dubbo system, when developers have written the Dubbo service, there is no need to add a new web project to provide an interface. - -- If an interface attacked by a large amount of request, how do you deal with it? - -- Soul is here to solve the trouble above, this is the purpose of the design. Let’s take a look at the overall architecture diagram. - -![soul-framework](/assets/img/architecture/soul-framework.png) - -- Soul gateway is implemented using reactive programming. Just look at the weather vane Spring, responsive programming is definitely an important direction in the future. When I was in 2014, I wrote for loop every day. The leader told me to use lambda expressions, which would be the trend of the future. Nowadays, if you are a java programmer but don't know lambda expression, you are out. - -![soul-framework](/assets/img/architecture/soul-framework.png) - -### There are many other functions - -- Support websocket proxy. -- Support file upload and download. - -- You can customize your plug-in development. - -### At last - -- Github: https://github.com/Dromara/soul -- Gitee: https://gitee.com/dromara/soul -- Document: https://dromara.org/zh-cn/docs/soul/soul.html -- QQ group: 429951241 -- Finally, Soul3.0 has been open sourced, which has been verified in the double 11 concurrency scene for 2 years. I hope It will help you guys. +--- +title: How convenient is the 2.1.X version of Soul Gateway? +author: xiaoyu +tag: + - Soul +date: 2019-12-12 +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: News +--- + +It has been a year since I open sourced Soul gateway in October last year, and received many suggestions from you guys in community. It has provided very rich functions after optimization, many of functions are highly cusmized, visualized, and highly extensible, now let's make a summary. + +### Plugin + +- Provides various plug-ins, such as signature, monitoring, rate limiting, circuit breaker, Http proxy, Dubbo proxy, Websocket, etc. + +- Support users to quickly develop plug-ins. + +- All plug-in data and switch state support dynamic changes. + +### Data Synchronization + +- Provides three different data synchronization strategies: Http long polling, Zookeeper, and Websocket, allowing users to choose freely. +- It is recommended to use Websocket, which is the lightest and more efficient in a cluster environment. + +### For Users + +- First of all, we provide a client package that is convenient for users to access. Users can quickly register their projects to the Soul gateway. +- By default, users don't need to care about Soul Gateway's selectors, rules and other configurations. +- The user's previous interface is completely zero intrusion, It is only need to change to the domain name of the Soul gateway. +- For Dubbo users, the conversion from Http protocol to Dubbo protocol is almost completed by Http. +- Soul gataway uses the Http protocol, so it is destined to be cross-language, It is feasible for .Net programmers, PHP programmers to interact with Java program. + +For example, if you have a Dubbo interface, the parameter definition is a java bean, + +```java +public void insert(final DubboTest dubboTest) { +} +public class DubboTest implements Serializable { + private String id; + private String name; +} +``` + +If you use the Soul gateway to call this method, your Http parameter is to pass a json string in the body, which is no different from a normal http call. + +```json +{ "id": "123", "name": "xiaoyu" } +``` + +### For Developers + +- With more and more users, the situation of each company is different. Soul gateway in 2.1.X version are more extensible, making it convenient for developers . +- For example, there are may things could be extensible, such as Plug-ins, Filters, Dubbo parameter parser, iphost parser, return results, etc. We know that the default return result of the soul gateway is: + +```json +{ "code": 200, "message ": "成功!", "data": "helloWorld!" } +``` + +However, when using the Soul gateway to call your business system, your business system may define the result that is not confirm the above structure. Maybe your field is called `msg`, which will cause a different structure and bring confusion to the front-end processing. We have noticed this thing: https://github.com/Dromara/soul/issues/109, now it has been optimized, users can customize the return results to define, the specifics depend on the Soul document. + +### What scenarios of Soul gateway are suitable, and what should you pay attention to? + +First of all, I think we should follow pragmatism, when you need to use it , then you have monmentum to know it. Thus, where are you need Soul? + +#### Scenario1: Adimistration back-end + +- First of all, as rising popularity of microservices, our back-end is divided into many micro-services. I believe that your companies has a back-end management system. I guess they generally have the following architecture. + +![soul-rpc](/assets/img/architecture/soul-rpc.png) + +- It may cause some troubles as follows: + + - The developers of every microservice are developing based on this, which will become more and more cumbersome. + + - How to publish without downtime? If you want to publish the commodity module, all other modules will not be able to work at this time. + + - If a certain module interface requires a lot of requests (multiple deployments are required), and another module does not need it, how can you split it? + +- Some people may say I can disassemble them into a few web projects. But this will bring new troubles, where to do load balance? Where to do unified certification? +- Soul gateway solves all the above problems very well, just register your microservice to Soul gateway. You can do whatever you want. For example, the order module has 2 nodes, and you want to release a new version, you can send request to one of them in the gateway, and update the version in the other node. When the update complete, let the request go though both two nodes. So Java programmer can also do the jod of system operation engineer. +- If you need unified authentication, you only need to add an authentication plug-in suitable for your business to the gateway. + +![soul-admin](/assets/img/architecture/soul-admin-1.png) + +### Scenario2: Company entrance gateway (open platform) + +- If a company wants to do open-platform or an entrance gateway, authentication, rate limiting, circuit breaker, monitoring are indispensable. + +- If your company is in Dubbo system, when developers have written the Dubbo service, there is no need to add a new web project to provide an interface. + +- If an interface attacked by a large amount of request, how do you deal with it? + +- Soul is here to solve the trouble above, this is the purpose of the design. Let’s take a look at the overall architecture diagram. + +![soul-framework](/assets/img/architecture/soul-framework.png) + +- Soul gateway is implemented using reactive programming. Just look at the weather vane Spring, responsive programming is definitely an important direction in the future. When I was in 2014, I wrote for loop every day. The leader told me to use lambda expressions, which would be the trend of the future. Nowadays, if you are a java programmer but don't know lambda expression, you are out. + +![soul-framework](/assets/img/architecture/soul-framework.png) + +### There are many other functions + +- Support websocket proxy. +- Support file upload and download. + +- You can customize your plug-in development. + +### At last + +- Github: https://github.com/Dromara/soul +- Gitee: https://gitee.com/dromara/soul +- Document: https://dromara.org/zh-cn/docs/soul/soul.html +- QQ group: 429951241 +- Finally, Soul3.0 has been open sourced, which has been verified in the double 11 concurrency scene for 2 years. I hope It will help you guys. diff --git a/src/news/soul-2.2.0.md b/src/news/soul-2.2.0.md index ef57f27944..40b656f052 100644 --- a/src/news/soul-2.2.0.md +++ b/src/news/soul-2.2.0.md @@ -1,224 +1,224 @@ ---- -title: 【Soul gateway version2.2.0 release】Make high-performance gateways so easy! -author: xiaoyu -tag: - - Soul -date: 2020-06-17 -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: News ---- - -Let's take a look at the new features first, and then I would like to share my story. - -- Completely pluggable architecture design, plug-in hot swap. -- Fully supports all versions of Dubbo, Alibaba-Dubbo, Apache-Dubbo. -- Support Dubbo generalization call, multi-parameter, complex parameter interface. -- Enhance the monitor plug-in and remove the Influxdb, add metrics such as memory, CPU, QPS, TPS, response delay, and support access to Prometheus. -- The SpringCloud plugin supports Eureka and Nacos two registration centers. -- The waf plugin is enhanced to support black or white lists and mixed modes. -- Remove the Hystrix circuit breaker to be an independent plug-in. -- Fix the Zookeeper data synchronization bug, and add the data synchronization method implemented by Nacos. -- Support multiple kinds of soul-client, such as traditional Spring and Springboot. -- Optimize the soul-admin user interface. -- Fix load balancing algorithm bug. -- Fix uploading large files bug. -- etc. - -## Experience the new architecture and get a high-availability and high-performance gateway in 10 minutes! - -### Bootstrap soul-admin - -- Please download soul-admin.jar, and bootstrap it. -- Please access http://localhost:9095/index.html , and the default user name is `admin`, password is `123456`。 - -```shell -> wget https://yu199195.github.io/jar/soul-admin.jar -> java -jar soul-admin.jar --spring.datasource.url="jdbc:mysql://你的url:3306/soul?useUnicode=true&characterEncoding=utf-8&useSSL=false" - --spring.datasource.username='you username' --spring.datasource.password='you password' -``` - -### Build your own gateway - -- Firstly, you should create an empty Springboot project, please refer to `soul-bootstrap`. You can also visit the Spring official website :[https://spring.io/quickstart] -- Secondly, please add Maven dependency like follows: - -```xml - - org.springframework.boot - spring-boot-starter-webflux - 2.2.2-RELEASE - - - - org.springframework.boot - spring-boot-starter-actuator - 2.2.2-RELEASE - - - - - org.dromara - soul-spring-boot-starter-gateway - 2.2.0 - - - - - org.dromara - soul-spring-boot-starter-sync-data-websocket - 2.2.0 - -``` - -- Please add the following configuration to your `application.yaml`: - -``` -spring: - main: - allow-bean-definition-overriding: true -management: - health: - defaults: - enabled: false -soul : - sync: - websocket : - urls: ws://localhost:9095/websocket //Set to youe soul-admin address. -``` - -## Experience plug-in hot swap under the new architecture - -- Q: If I want to use circuit breaker, what should I do? - -- A: You can add the following dependencies in `pom.xml`, please access https://dromara.org/zh-cn/docs/soul/soul.html for details. - -```xml - - - org.dromara - soul-spring-boot-starter-plugin-hystrix - 2.2.0 - - -``` - -- Q: How can I use Dubbo service? -- A: If you are using Alibaba-Dubbo, then you should add the following dependencies in `pom.xml`. - -```xml - - - org.dromara - soul-spring-boot-starter-plugin-alibaba-dubbo - 2.2.0 - - -``` - -If you are using Apache-Dubbo, then you should add the following dependencies in `pom.xml`. - -```xml - - - org.dromara - soul-spring-boot-starter-plugin-apache-dubbo - 2.2.0 - - -``` - -For your inference: https://dromara.org/zh-cn/docs/soul/user-dubbo.html. - -- Q: What if I want to use the rate limiter function? - -- A: You can add the following dependencies, for your inference: https://dromara.org/zh-cn/docs/soul/plugin-rateLimiter.html - -```xml - - - org.dromara - soul-spring-boot-starter-plugin-ratelimiter - 2.2.0 - - - -``` - -- Q: What should I do if I don’t want to use some plug-ins? - -* A: You can disable or enable the plug-in in the soul-admin. That is hot pluggable. - -All in all, If you want to use some plug-ins, then you can add the Maven dependency in `pom.xml`, but this is not called hot pluggable. - -### Features of Soul Gateway - -- I think the biggest feature is traffic screening and control. No matter how complex the request is, traffic can be filtered, and processed according to various selectors, rules, and matching methods. This process is completely visualized, customized and effective immediately, without any changes to the program. -- Configurations is configured in the soul-admin and will be synchronized to the JVM memory of each Soul gateway node. This is also one of the keypoints to the high performance of the Soul gateway cluster. By the way, Http long polling, websocket, and Zookeeper are used to implement cache synchronization between Soul admin and Soul gateway. -- Soul gateway uses Reactor code to achieve the independent thread scheduling with low consumption. When we open 10 plug-ins, the delay of all traffic passing through the gateway is 1~2ms. -- The plug-in mechanism provides functions such as rate limiting, circuit breaker, black and white list, authentication, etc. -- Soul gateway supports A/B test, blue and green release (because all traffic is controlled, this is easy to do). - -## What scenarios of Soul gateway are suitable, and what should you pay attention to? - -First of all, I think we should follow pragmatism, when you need to use it , then you have monmentum to know it. Thus, where are you need Soul? - -### Scenario1: Adimistration back-end - -- First of all, as rising popularity of microservices, our back-end is divided into many micro-services. I believe that your companies has a back-end management system. I guess they generally have the following architecture. - - ![soul-admin](/assets/img/architecture/soul-admin.png) - -- It may cause some troubles as follows: - - - The developers of every microservice are developing based on this, which will become more and more cumbersome. - - - How to publish without downtime? If you want to publish the commodity module, all other modules will not be able to work at this time. - - - If a certain module interface requires a lot of requests (multiple deployments are required), and another module does not need it, how can you split it? - -- Some people may say I can disassemble them into a few web projects. But this will bring new troubles, where to do load balance? Where to do unified certification? - -- Soul gateway solves all the above problems very well, just register your microservice to Soul gateway. You can do whatever you want. For example, the order module has 2 nodes, and you want to release a new version, you can send request to one of them in the gateway, and update the version in the other node. When the update complete, let the request go though both two nodes. So Java programmer can also do the jod of system operation engineer. - -- If you need unified authentication, you only need to add an authentication plug-in suitable for your business to the gateway. - -### Scenario2: Company entrance gateway (open platform) - -- If a company wants to do open-platform or an entrance gateway, authentication, rate limiting, circuit breaker, monitoring are indispensable. -- If your company is in Dubbo system, when developers have written the Dubbo service, there is no need to add a new web project to provide an interface. -- If an interface attacked by a large amount of request, how do you deal with it? -- Soul is here to solve the trouble above, this is the purpose of the design. Let’s take a look at the overall architecture diagram. - -![soul-framework](/assets/img/architecture/soul-framework.png) - -- Soul gateway is implemented using reactive programming. Just look at the weather vane Spring, responsive programming is definitely an important direction in the future. When I was in 2014, I wrote for loop every day. The leader told me to use lambda expressions, which would be the trend of the future. Nowadays, if you are a java programmer but don't know lambda expression, you are out. - -## My open source story - -I started writing open source projects when I was in 2017. At first, I discussed distributed transactions "LCN" with Wang Liang. Later, I wrote distributed transaction middleware such as Hmily, Raincat, Myth, etc., and then wrote the Soul gateway, I have encountered many interesting things along the way, but also suffered from many novice users. The general feeling is that high extensiable, and pluggable design are really important for a good open source project. Here are some cases. - -- Case 1: Soul gateway only supports Zookeeper at the beginning of data synchronization. Some users have reported that we do not have Zookeeper, What should we do? -- Case 2: Soul gateway supports Dubbo, but some users are Alibaba-Dubbo and some users are Apache-Dubbo, What should we do? -- Case 3: The clients provided by soul at the beginning are all based on Springboot. Some users are traditional Spring. What should we do? - -So plug-in design and SPI pluggable design is imperative. - -**SPI VS pluggability** - -It is true that the SPI expansion is the cornerstone of pluggability, but they are not completely equivalent. Give an example: If we store a piece of data first, then you have set the SPI interface, and there are many ways to achieve it, such as Mysql, Mongodb, Elasticseach, Zookeeper, etc. Now you have to consider whether to combine it in the project or put it in different Projects, packaged and loaded on demand? These are all things to be considered, so the SPI method cannot be a sure card. - -**checkStyle** - -- Strict code specification is not only a respect for framework users in source code learning but alos an open source attitude. - -- Strict code specifications make people look comfortable and make it easier for people to understand the entire code. - -- I do hope that when you submit a PR, the local installation should be successful. - -**Participate in open source** - -- At present, I mainly focus on Apache ShardingSphere, which is the first top-level project on database sub-database and sub-table organized by Chinese in Apache. Welcome everyone to participate. https://github.com/apache/shardingsphere. - -- The Chinese people lag far behind in terms of open source and technology as large as chips and as small as MATLAB. I also hope that everyone has an open source mentality, participates in open source, learns technology, propagates ideas, and continues to learn for the future! +--- +title: 【Soul gateway version2.2.0 release】Make high-performance gateways so easy! +author: xiaoyu +tag: + - Soul +date: 2020-06-17 +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: News +--- + +Let's take a look at the new features first, and then I would like to share my story. + +- Completely pluggable architecture design, plug-in hot swap. +- Fully supports all versions of Dubbo, Alibaba-Dubbo, Apache-Dubbo. +- Support Dubbo generalization call, multi-parameter, complex parameter interface. +- Enhance the monitor plug-in and remove the Influxdb, add metrics such as memory, CPU, QPS, TPS, response delay, and support access to Prometheus. +- The SpringCloud plugin supports Eureka and Nacos two registration centers. +- The waf plugin is enhanced to support black or white lists and mixed modes. +- Remove the Hystrix circuit breaker to be an independent plug-in. +- Fix the Zookeeper data synchronization bug, and add the data synchronization method implemented by Nacos. +- Support multiple kinds of soul-client, such as traditional Spring and Springboot. +- Optimize the soul-admin user interface. +- Fix load balancing algorithm bug. +- Fix uploading large files bug. +- etc. + +## Experience the new architecture and get a high-availability and high-performance gateway in 10 minutes! + +### Bootstrap soul-admin + +- Please download soul-admin.jar, and bootstrap it. +- Please access http://localhost:9095/index.html , and the default user name is `admin`, password is `123456`。 + +```shell +> wget https://yu199195.github.io/jar/soul-admin.jar +> java -jar soul-admin.jar --spring.datasource.url="jdbc:mysql://你的url:3306/soul?useUnicode=true&characterEncoding=utf-8&useSSL=false" + --spring.datasource.username='you username' --spring.datasource.password='you password' +``` + +### Build your own gateway + +- Firstly, you should create an empty Springboot project, please refer to `soul-bootstrap`. You can also visit the Spring official website :[https://spring.io/quickstart] +- Secondly, please add Maven dependency like follows: + +```xml + + org.springframework.boot + spring-boot-starter-webflux + 2.2.2-RELEASE + + + + org.springframework.boot + spring-boot-starter-actuator + 2.2.2-RELEASE + + + + + org.dromara + soul-spring-boot-starter-gateway + 2.2.0 + + + + + org.dromara + soul-spring-boot-starter-sync-data-websocket + 2.2.0 + +``` + +- Please add the following configuration to your `application.yaml`: + +``` +spring: + main: + allow-bean-definition-overriding: true +management: + health: + defaults: + enabled: false +soul : + sync: + websocket : + urls: ws://localhost:9095/websocket //Set to youe soul-admin address. +``` + +## Experience plug-in hot swap under the new architecture + +- Q: If I want to use circuit breaker, what should I do? + +- A: You can add the following dependencies in `pom.xml`, please access https://dromara.org/zh-cn/docs/soul/soul.html for details. + +```xml + + + org.dromara + soul-spring-boot-starter-plugin-hystrix + 2.2.0 + + +``` + +- Q: How can I use Dubbo service? +- A: If you are using Alibaba-Dubbo, then you should add the following dependencies in `pom.xml`. + +```xml + + + org.dromara + soul-spring-boot-starter-plugin-alibaba-dubbo + 2.2.0 + + +``` + +If you are using Apache-Dubbo, then you should add the following dependencies in `pom.xml`. + +```xml + + + org.dromara + soul-spring-boot-starter-plugin-apache-dubbo + 2.2.0 + + +``` + +For your inference: https://dromara.org/zh-cn/docs/soul/user-dubbo.html. + +- Q: What if I want to use the rate limiter function? + +- A: You can add the following dependencies, for your inference: https://dromara.org/zh-cn/docs/soul/plugin-rateLimiter.html + +```xml + + + org.dromara + soul-spring-boot-starter-plugin-ratelimiter + 2.2.0 + + + +``` + +- Q: What should I do if I don’t want to use some plug-ins? + +* A: You can disable or enable the plug-in in the soul-admin. That is hot pluggable. + +All in all, If you want to use some plug-ins, then you can add the Maven dependency in `pom.xml`, but this is not called hot pluggable. + +### Features of Soul Gateway + +- I think the biggest feature is traffic screening and control. No matter how complex the request is, traffic can be filtered, and processed according to various selectors, rules, and matching methods. This process is completely visualized, customized and effective immediately, without any changes to the program. +- Configurations is configured in the soul-admin and will be synchronized to the JVM memory of each Soul gateway node. This is also one of the keypoints to the high performance of the Soul gateway cluster. By the way, Http long polling, websocket, and Zookeeper are used to implement cache synchronization between Soul admin and Soul gateway. +- Soul gateway uses Reactor code to achieve the independent thread scheduling with low consumption. When we open 10 plug-ins, the delay of all traffic passing through the gateway is 1~2ms. +- The plug-in mechanism provides functions such as rate limiting, circuit breaker, black and white list, authentication, etc. +- Soul gateway supports A/B test, blue and green release (because all traffic is controlled, this is easy to do). + +## What scenarios of Soul gateway are suitable, and what should you pay attention to? + +First of all, I think we should follow pragmatism, when you need to use it , then you have monmentum to know it. Thus, where are you need Soul? + +### Scenario1: Adimistration back-end + +- First of all, as rising popularity of microservices, our back-end is divided into many micro-services. I believe that your companies has a back-end management system. I guess they generally have the following architecture. + + ![soul-admin](/assets/img/architecture/soul-admin.png) + +- It may cause some troubles as follows: + + - The developers of every microservice are developing based on this, which will become more and more cumbersome. + + - How to publish without downtime? If you want to publish the commodity module, all other modules will not be able to work at this time. + + - If a certain module interface requires a lot of requests (multiple deployments are required), and another module does not need it, how can you split it? + +- Some people may say I can disassemble them into a few web projects. But this will bring new troubles, where to do load balance? Where to do unified certification? + +- Soul gateway solves all the above problems very well, just register your microservice to Soul gateway. You can do whatever you want. For example, the order module has 2 nodes, and you want to release a new version, you can send request to one of them in the gateway, and update the version in the other node. When the update complete, let the request go though both two nodes. So Java programmer can also do the jod of system operation engineer. + +- If you need unified authentication, you only need to add an authentication plug-in suitable for your business to the gateway. + +### Scenario2: Company entrance gateway (open platform) + +- If a company wants to do open-platform or an entrance gateway, authentication, rate limiting, circuit breaker, monitoring are indispensable. +- If your company is in Dubbo system, when developers have written the Dubbo service, there is no need to add a new web project to provide an interface. +- If an interface attacked by a large amount of request, how do you deal with it? +- Soul is here to solve the trouble above, this is the purpose of the design. Let’s take a look at the overall architecture diagram. + +![soul-framework](/assets/img/architecture/soul-framework.png) + +- Soul gateway is implemented using reactive programming. Just look at the weather vane Spring, responsive programming is definitely an important direction in the future. When I was in 2014, I wrote for loop every day. The leader told me to use lambda expressions, which would be the trend of the future. Nowadays, if you are a java programmer but don't know lambda expression, you are out. + +## My open source story + +I started writing open source projects when I was in 2017. At first, I discussed distributed transactions "LCN" with Wang Liang. Later, I wrote distributed transaction middleware such as Hmily, Raincat, Myth, etc., and then wrote the Soul gateway, I have encountered many interesting things along the way, but also suffered from many novice users. The general feeling is that high extensiable, and pluggable design are really important for a good open source project. Here are some cases. + +- Case 1: Soul gateway only supports Zookeeper at the beginning of data synchronization. Some users have reported that we do not have Zookeeper, What should we do? +- Case 2: Soul gateway supports Dubbo, but some users are Alibaba-Dubbo and some users are Apache-Dubbo, What should we do? +- Case 3: The clients provided by soul at the beginning are all based on Springboot. Some users are traditional Spring. What should we do? + +So plug-in design and SPI pluggable design is imperative. + +**SPI VS pluggability** + +It is true that the SPI expansion is the cornerstone of pluggability, but they are not completely equivalent. Give an example: If we store a piece of data first, then you have set the SPI interface, and there are many ways to achieve it, such as Mysql, Mongodb, Elasticseach, Zookeeper, etc. Now you have to consider whether to combine it in the project or put it in different Projects, packaged and loaded on demand? These are all things to be considered, so the SPI method cannot be a sure card. + +**checkStyle** + +- Strict code specification is not only a respect for framework users in source code learning but alos an open source attitude. + +- Strict code specifications make people look comfortable and make it easier for people to understand the entire code. + +- I do hope that when you submit a PR, the local installation should be successful. + +**Participate in open source** + +- At present, I mainly focus on Apache ShardingSphere, which is the first top-level project on database sub-database and sub-table organized by Chinese in Apache. Welcome everyone to participate. https://github.com/apache/shardingsphere. + +- The Chinese people lag far behind in terms of open source and technology as large as chips and as small as MATLAB. I also hope that everyone has an open source mentality, participates in open source, learns technology, propagates ideas, and continues to learn for the future! diff --git a/src/news/warm-flow-1.2.8.md b/src/news/warm-flow-1.2.8.md new file mode 100644 index 0000000000..af985f9137 --- /dev/null +++ b/src/news/warm-flow-1.2.8.md @@ -0,0 +1,263 @@ +--- +title: warm-flow v1.2.8 Update-Added Handler Variable Expressions and Conditional Expressions to support spel +author: warm-flow +date: 2024-09-25 +cover: /assets/img/news/warm-flow-v1.2.8-0.png +head: + - - meta + - name: News +--- + +# The warm-flow version 1.2.8 is updated, and the new variable expression and conditional expression support spel are added. + +![](/assets/img/news/warm-flow-v1.2.8-0.png) + +* [Upgrade Notes]] + +* In this upgrade, the built-in json library is loaded in snack3 mode instead of spi mode. The implementation of which json exists in the business project will be used. The 1 types of json are supported to be loaded in sequence: snack3, jackson, fastjson and gson. Currently, only these 4 types are implemented and can be extended. + +* If the snack3 library is not integrated, the snack3 library needs to be used separately (the original component uses the snack3 library). + + ``` + + org.noear + snack3 + 3.2.88 + + ``` + +* Update log + +* \[feat\] json library supports snack3, jackson, fastjson, and gson, and supports extension + +* \[feat\] adds the variable expression of the handling person, supports ${xxx} substitution and spel, and supports extension + +* \[feat\] Add a FlowParams field to ListenerVariable the listener variable to facilitate the global transmission of parameters to the listener. + +* \[feat\] End new support for start and finish listeners + +* \[update\] the conditional expression of springboot project supports spel by default + +* \[update\] Change the historical record to a single record and delete duplicate codes + +* \[update\] Modify the FlowUserDao bean name + +* \[update\] The middle node is split into or, countersign, and ticket sign + +* \[fix\] The creation time of the repair history is equal, resulting in abnormal rendering of the flowchart + +* \[fix\] fix the defect that Mybatis logical deletion becomes real deletion @ xiarigang + +* \[refactor\] Reconstruct the ID generator to support the default ORM policy. Delete the data and fill the default implementation class, and change it to an anonymous class. + + +# Partial update content introduction + +## 1, add the variable expression of the handling person + +### 1.1. Default Handler Variable Policy + +#### Front-end page setting variable + +* For example: '@ @ @ |${ brush},role:1,1' + +* '@ @ @ @ |${'} @ @ @ @ @ @ @ indicates the default supervisor variable strategy, which is the identifier that needs to be replaced in the process variable. + +* 'role:1,1 'indicates the role of the person in charge and the specific person in charge + + +![](/assets/img/news/warm-flow-v1.2.8-1.png) + +#### Backend Code Setting Variables + +``` + +// 流程变量 +Map variable = new HashMap<>(); +variable.put("handler1", "100"); +flowParams.variable(variable); + +Instance instance = insService.skipByInsId(testLeave.getInstanceId(), flowParams); +``` + +### 1.2、spel Handle Variable Strategy + +#### Front End Page Setup Variables + +* For example: '@ @ spel @ |#{ @ user. (#) resp.}' + +* '#{@ user.evalVar(#handler2)}' is a spel expression, '#handler2' is a method input variable, which may not be set + + +![](/assets/img/news/warm-flow-v1.2.8-2.png) + +#### Backend Code Setting Variables + +``` +/** + * 用户类 + */ +@Component("user") +public class User { + +    /** +     * spel办理人变量表达式 +     * @param handler2 办理人 +     * @return String +     */ +    public String evalVar(String handler2) { +        return handler2; +    } +} + +// 流程变量 +Map variable = new HashMap<>(); +variable.put("handler2", "101"); +flowParams.variable(variable); + +Instance instance = insService.skipByInsId(testLeave.getInstanceId(), flowParams); +``` + +## 2、Listener Variables Add FlowParams Fields + +> Add a FlowParams field to ListenerVariable the listener variable to facilitate the global transfer of parameters to the listener. + +``` +@Component +public class GlobalStartListener implements Listener { + + +  private static final Logger log = LoggerFactory.getLogger(GlobalStartListener.class); + +  /** +   * 设置办理人id、所拥有的权限等操作,也可以放到业务代码中办理前设置,或者局部监听器 +   * @param listenerVariable 监听器变量 +   */ +  @Override +  public void notify(ListenerVariable listenerVariable) { +    log.info("全局开始监听器"); + +    FlowParams flowParams = listenerVariable.getFlowParams(); +    LoginUser user = SecurityUtils.getLoginUser(); +    // 设置当前办理人id +    flowParams.setHandler(user.getUser().getUserId().toString()); + +    // 设置办理人所拥有的权限,比如角色、部门、用户等 +    List permissionList = flowParams.getPermissionFlag(); +    if (StringUtils.isEmpty(permissionList)) { +      permissionList = new ArrayList<>(); +    } + +    List roles = user.getUser().getRoles(); +    if (Objects.nonNull(roles)) { +      permissionList.addAll(roles.stream().map(role -> "role:" + role.getRoleId()).collect(Collectors.toList())); +    } +    permissionList.add("dept:" + SecurityUtils.getLoginUser().getUser().getDeptId()); +    permissionList.add(user.getUser().getUserId().toString()); +    flowParams.setPermissionFlag(permissionList); + +    log.info("全局开始监听器结束;{}", "开启流程完成"); +  } +} +``` + +## 3、conditional expressions support spel by default + +> The conditional expression of the springboot project supports spel by default. + +* The front-end configuration is, for example, '#{@ user.eval(#flag)}' expression. Before warehousing, the prefix should be spliced to facilitate the differentiation of expression types. The final value is '@ @ spel @@|#{@ user.eval(#flag)}' + +* '#flag' indicates that the variable is named the same as the following method input parameters, but the input parameters may not be set. + + +![](/assets/img/news/warm-flow-v1.2.8-3.png) + +``` +@Component("user") +public class User { + +  /** +   * spel条件表达:判断大于等4 +   * @param flag 待判断的字符串 +   * @return boolean +   */ +  public boolean eval(String flag) { +    BigDecimal a = new BigDecimal(flag); +    BigDecimal b = new BigDecimal("4"); +    return a.compareTo(b) > 0; +  } +} + +/** + * 新增OA 请假申请 + * + * @param testLeave OA 请假申请 + * @return 结果 + */ +@Override +public int insertTestLeave(TestLeave testLeave, String flowStatus) +{ +  FlowParams flowParams = FlowParams.build().flowCode(getFlowType(testLeave)); +  // 流程变量 +  Map variable = new HashMap<>(); +  variable.put("flag", String.valueOf(testLeave.getDay())); +  flowParams.variable(variable); + +  Instance instance = insService.start(id, flowParams); +  return instance != null? 1 : 0; +} +``` + +# warm-flow introduction + +> \[!IMPORTANT\] Warm-Flow Domestic Workflow Engine🎉, Its characteristics are simple and lightweight but not simple, complete with five internal organs, independent components, expandable, and can meet the components of small and medium-sized projects. + +1. Simple and easy to use: only 7 tables, less code, can be quickly started and integrated + +2. Approval function: supports pass, return, arbitrary jump, transfer, termination, countersign, ticket sign, delegation, addition and subtraction, mutual exclusion and parallel gateway + +3. Listener and process variables: supports five kinds of listeners, can cope with different scenarios, flexible and scalable, parameter transfer, dynamic permissions + +4. Flowchart: The process engine comes with a flowchart that can be used without integrating the process designer. + +5. Conditional expressions: Built-in common and spel conditional expressions, and support for custom extensions. + +6. Handler variable expression: built-in ${handler} and spel format expression, can meet different scenarios, flexible and scalable + +7. Orm framework extension: currently supports MyBatis, Mybatis-Plus, Mybatis-Flex and Jpa, and will be supported by the community in the future, which is convenient for expansion. + +8. Database support: currently supports MySQL, Oracle and PostgreSQL, and will continue to support other databases or domestic databases in the future. + +9. Multi-tenancy and soft deletion: The process engine itself maintains the implementation of multi-tenancy and soft deletion, and can also use the implementation of the corresponding orm framework. + +10. Support role, department and user permissions configuration + +Supports both Spring and Solon + +12. Compatible with java8 and java17, theory 11 can also be + +13. The official provides practical projects based on ruoyi-vue packaging, which is very practical. + + +## Demo Address + +* admin/admin123 + + +Demo Address:http://www.hhzai.top + +## official website + +http://warm-flow.cn + +About Dromara + +Dromara is an open source community composed of top open source project authors in China. It provides a series of open source products, solutions and consulting, technical support and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservice RPC, operation and maintenance monitoring, Agent monitoring, distributed logging, scheduling and orchestration. The technology stack is fully open-source and community-neutral, and is committed to providing microservice cloud-native solutions for global users. Let every 1 open source enthusiasts involved experience the joy of open source. + + + +Dromara open source community currently has 10 GVP projects, with a total number of star exceeding 100,000. It has built an open source community of tens of thousands of people, and thousands of individuals and teams are using the open source projects of Dromara community. + +**Welcome to the knowledge planet and I interact * * + +![](/assets/img/qrcode_zsxq.webp) \ No newline at end of file diff --git a/src/news/x-easypdf-v3.3.0.md b/src/news/x-easypdf-v3.3.0.md new file mode 100644 index 0000000000..85f9d467f1 --- /dev/null +++ b/src/news/x-easypdf-v3.3.0.md @@ -0,0 +1,77 @@ +--- +title: "x-easypdf v3.3.0 Released: An AI-Powered PDF Framework" +author: x-easypdf +date: 2025-02-17 +cover: /assets/img/news/x-easypdf-v3.3.0-0.png +head: + - - meta + - name: News +--- + +##### + +![](/assets/img/news/x-easypdf-v3.3.0-0.png) + + + +x-easypdf is a Java-based framework designed to simplify PDF processing. It includes both an FOP module and a PDFBox module. The FOP module focuses on creation, generating PDF documents based on XSL-FO templates and rendering templates through data sources. The PDFBox module focuses on editing, extending the standard PDFBox with a wealth of additional features. + +The updates in this release are as follows: + +###### New Features: + +* 【PDFBox】Added support for JPEG2000 format images. +* 【PDFBox】Added support for large language model (LLM) document parsing. +* 【PDFBox】Added Gitee (Open Source China) AI parser. +* 【PDFBox】Added Zhipu (GLM) AI parser. +* 【PDFBox】Added Tencent (Hunyuan) AI parser. +* 【PDFBox】Added Alibaba (Qwen) AI parser. +* 【PDFBox】Added DeepSeek AI parser. +* 【PDFBox】Added ByteDance (Doubao) AI parser. +* 【PDFBox】Added Kunlun Wanwei (Tiangong) AI parser. +* 【PDFBox】Added Moonshot AI (Kimi) AI parser. +* 【PDFBox】Added iFlytek (Spark) AI parser. +* 【PDFBox】Added linearization support. +* 【PDFBox】Added support for converting Office files to PDF (requires Office services). +* 【PDFBox】Added Word converter. +* 【PDFBox】Added Excel converter. +* 【PDFBox】Added PowerPoint converter. +* 【PDFBox】Added HTML converter. +* 【PDFBox】Added RTF converter. +* 【PDFBox】Added attachment processor. +* 【PDFBox】Added support for loading AWT fonts. +* 【FOP】Added configuration for barcodes without white edges. +* 【FOP】Added method to set barcode cache. +* 【FOP】Added permission configuration. +* 【FOP】Added support for loading AWT fonts from resource paths. + +###### Existing Changes: + +The Maven coordinates have changed. The original `groupId "org.dromara.x-easypdf"` has been updated to `org.dromara`. + +###### Bug Fixes: + +* 【PDFBox Module】Fixed line break errors when adding multiple components to a table cell. +* 【PDFBox Module】Fixed table overlapping issues. +* 【PDFBox Module】Fixed empty text error issues. + + +Gitee: https://gitee.com/dromara/x-easypdf  + +AI Parsing Feature Demo:  + +![](/assets/img/news/x-easypdf-v3.3.0-1.jpg) + + + + + +About Dromara + +Dromara is an open-source community composed of top domestic open-source project authors. It provides a series of open-source products, solutions, consulting, technical support, and training certification services, including distributed transactions, popular tools, enterprise-level authentication, microservices RPC, operation and maintenance monitoring, Agent monitoring, distributed logs, scheduling orchestration, etc. The technology stack is fully open-source and co-built, maintaining community neutrality, and is committed to providing global users with microservices cloud-native solutions. Let every participating open-source enthusiast experience the joy of open source. + +The Dromara open-source community currently has 10+ GVP projects, with a total star count of over 100,000, building an open-source community of tens of thousands of people, with thousands of individuals and teams using Dromara's open-source projects. + +**Welcome to the Knowledge Planet to interact with me** + +![](/assets/img/news/x-easypdf-v3.3.0-2.webp) \ No newline at end of file diff --git a/src/news/x-file-storage-2.0.0.md b/src/news/x-file-storage-2.0.0.md index 113576f670..1633cff9eb 100644 --- a/src/news/x-file-storage-2.0.0.md +++ b/src/news/x-file-storage-2.0.0.md @@ -1,264 +1,264 @@ ---- -title: Welcome X File Storage to join dromara open source community, one-stop file storage -author: XuYanwu -tag: - - X-File-Storage -date: 2023-10-19 -cover: /assets/img/news/X-File-Storage-Cover.svg -head: - - - meta - - name: News ---- - -

- logo
- Originally known as X Spring File Storage, it has been donated to the dromara open source organization -

- -# 📚Summary - -One line of code stores files locally, FTP, SFTP, WebDAV, Alibaba Cloud OSS, Huawei Cloud OBS, Qiniu Cloud Kodo, Tencent Cloud COS, Baidu Cloud BOS, Youpai Cloud USS, MinIO, -Amazon S3, Google Cloud Storage, Kingsoft Cloud KS3, Meituan Cloud MSS, JD Cloud OSS, Tianyi Cloud OOS, Mobile Cloud EOS, Woyun OSS, -NetEase Shufan NOS, Ucloud US3, Qingyun QingStor, Ping An Cloud OBS, Shouyun OSS, IBM COS, and other storage platforms compatible with the S3 protocol. View [all supported storage platforms](https://x-file-storage.xuyanwu.cn/#/存储平台) - -💡 After connecting to Alist through WebDAV, you can use common storage services such as Baidu Cloud Disk, Tianyi Cloud Disk, Alibaba Cloud Disk, and Thunder Cloud Disk. Check [Storage Platforms Supported by Alist](https://alist-doc.nn.ci/docs/webdav) - -GitHub:https://github.com/dromara/x-file-storage -Gitee:https://gitee.com/dromara/x-file-storage - -Document1:https://x-file-storage.dromara.org -Document2:https://x-file-storage.xuyanwu.cn -Document3:https://spring-file-storage.xuyanwu.cn - -------- - -# 📜UpdateContent - -- Change project name, change package name, optimize project structure -- Added the ability to directly read the HttpServletRequest stream for uploading, so the file does not fall to disk and is faster -- Added support for Metadata -- Optimize ACL exception handling -- Optimize file deletion logic -- Fixed the issue of ResetException occasionally occurring when uploading files to Amazon S3 -- Donate to [dromara](https://dromara.org/zh) open source community - -#### Changes in project dependencies - -Versions before 2.0.0 - -```xml - - cn.xuyanwu - spring-file-storage - 1.0.3 - -``` - -2.0.0 and later versions - -```xml - - org.dromara.x-file-storage - x-file-storage-spring - 2.0.0 - -``` - -#### Changes in configuration parameters - -Versions before 2.0.0 - -```yaml -spring: - file-storage: #File storage configuration - default-platform: huawei-obs-1 #Default storage platform used - thumbnail-suffix: ".min.jpg" #Thumbnail suffix, such as [.min.jpg] [.png] - #The configuration of the corresponding platform is written here, pay attention to the indentation to be aligned -``` - -2.0.0 and later versions - -```yaml -dromara: - x-file-storage: #File storage configuration - default-platform: huawei-obs-1 #Default storage platform used - thumbnail-suffix: ".min.jpg" #Thumbnail suffix, such as [.min.jpg] [.png] - #The configuration of the corresponding platform is written here, pay attention to the indentation to be aligned -``` - -#### Changes in package name - -Versions before 2.0.0 - -```java -cn.xuyanwu.spring.file.storage -cn.xuyanwu.spring.file.storage.spring -``` - -2.0.0 and later versions - -```java -org.dromara.x.file.storage.core -org.dromara.x.file.storage.spring -``` - -------- - -# 📦Use - -Click [Quick Start](https://x-file-storage.xuyanwu.cn/#/快速入门) to view how to use all storage platforms! - -#### 🔧 Configuration - -Here we take Alibaba Cloud OSS as an example. `pom.xml` is introduced into this project. The default here is the `SpringBoot` environment. For other environments, please refer to [Use alone without SpringBoot](https://x-file-storage.xuyanwu.cn/#/脱离SpringBoot单独使用) -```xml - - - org.dromara.x-file-storage - x-file-storage-spring - 2.0.0 - - - - com.aliyun.oss - aliyun-sdk-oss - 3.16.1 - -``` - -Add the following basic configuration to the `application.yml` configuration file - -```yaml -dromara: - x-file-storage: #File storage configuration - default-platform: aliyun-oss-1 #Default storage platform - aliyun-oss: - - platform: aliyun-oss-1 # Storage platform identification - enable-storage: true # Enable storage - access-key: ?? - secret-key: ?? - end-point: ?? - bucket-name: ?? - domain: ?? # Access the domain name, pay attention to the "/" ending, for example: https://abc.oss-cn-shanghai.aliyuncs.com/ - base-path: test/ # base path -``` - -#### 🔨Encoding - -Add the `@EnableFileStorage` annotation to the startup class - -```java -@EnableFileStorage -@SpringBootApplication -public class SpringFileStorageTestApplication { - - public static void main(String[] args) { - SpringApplication.run(SpringFileStorageTestApplication.class,args); - } - -} -``` -#### ✨Start uploading - -Supports File, MultipartFile, byte[], InputStream, URL, URI, String, HttpServletRequest, and large files will be automatically uploaded in parts. If you want to support more methods, please read the [File Adapter](https://x-file-storage.xuyanwu.cn/#/文件适配器) chapter - -```java -@RestController -public class FileDetailController { - - @Autowired - private FileStorageService fileStorageService;//注入实列 - - /** - * upload files - */ - @PostMapping("/upload") - public FileInfo upload(MultipartFile file) { - //Only this line of code is needed to upload successfully - return fileStorageService.of(file).upload(); - } - - /** - * Upload the file and return the file url successfully - */ - @PostMapping("/upload2") - public String upload2(MultipartFile file) { - FileInfo fileInfo = fileStorageService.of(file) - .setPath("upload/") //Save to a relative path. For the convenience of management, you don’t need to write it. - .setObjectId("0") //Associated object id. For the convenience of management, you don’t need to write it. - .setObjectType("0") //Associated object type. For convenience of management, you don’t need to write it. - .putAttr("role","admin") //Save some attributes, which can be obtained and used in aspects, saved upload records, customized storage platforms, etc. You don't need to write them - .upload(); //Upload the file to the corresponding place - return fileInfo == null ? "Upload failed!" : fileInfo.getUrl(); - } - - /** - * Upload pictures and return file information successfully - *Image processing uses https://github.com/coobird/thumbnailator - */ - @PostMapping("/upload-image") - public FileInfo uploadImage(MultipartFile file) { - return fileStorageService.of(file) - .image(img -> img.size(1000,1000)) //Adjust the image size to 1000*1000 - .thumbnail(th -> th.size(200,200)) //Generate a 200*200 thumbnail - .upload(); - } - - /** - * Upload files to the designated storage platform and return file information successfully - */ - @PostMapping("/upload-platform") - public FileInfo uploadPlatform(MultipartFile file) { - return fileStorageService.of(file) - .setPlatform("aliyun-oss-1") //Use the specified storage platform - .upload(); - } - - /** - * Directly read the file in HttpServletRequest for upload, and successfully return the file information - * There are some precautions when using this method. Please check the Basic Functions-Upload chapter of the document. - */ - @PostMapping("/upload-request") - public FileInfo uploadPlatform(HttpServletRequest request) { - return fileStorageService.of(request).upload(); - } -} -``` - -#### 🎨Other operations - -```java -//Manually construct file information, which can be used for other operations -FileInfo fileInfo = new FileInfo() - .setPlatform("huawei-obs-1") - .setBasePath("test/") - .setPath("aa/") - .setFilename("image.png") - .setThFilename("image.png.min.jpg"); - -//Does the file exist? -boolean exists = fileStorageService.exists(fileInfo); -//download -byte[] bytes = fileStorageService.download(fileInfo).bytes(); -//delete -fileStorageService.delete(fileInfo); -//Other more operations - -``` - -If you save file records to the database, you can also operate based on the URL more conveniently. For details, please read [Save Upload Records](https://x-file-storage.xuyanwu.cn/#/基础功能?id=保存上传记录) Chapter - -```java -//Get the FileInfo object directly from the database, making it easier to perform other operations -FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://abc.def.com/test/aa/image.png"); - -//Does the file exist? -boolean exists = fileStorageService.exists("https://abc.def.com/test/aa/image.png"); -//download -byte[] bytes = fileStorageService.download("https://abc.def.com/test/aa/image.png").bytes(); -//delete -fileStorageService.delete("https://abc.def.com/test/aa/image.png"); -//Other more operations -``` - -Click [Quick Start](https://x-file-storage.xuyanwu.cn/#/快速入门) to view how to use all storage platforms! +--- +title: Welcome X File Storage to join dromara open source community, one-stop file storage +author: XuYanwu +tag: + - X-File-Storage +date: 2023-10-19 +cover: /assets/img/news/X-File-Storage-Cover.svg +head: + - - meta + - name: News +--- + +

+ logo
+ Originally known as X Spring File Storage, it has been donated to the dromara open source organization +

+ +# 📚Summary + +One line of code stores files locally, FTP, SFTP, WebDAV, Alibaba Cloud OSS, Huawei Cloud OBS, Qiniu Cloud Kodo, Tencent Cloud COS, Baidu Cloud BOS, Youpai Cloud USS, MinIO, +Amazon S3, Google Cloud Storage, Kingsoft Cloud KS3, Meituan Cloud MSS, JD Cloud OSS, Tianyi Cloud OOS, Mobile Cloud EOS, Woyun OSS, +NetEase Shufan NOS, Ucloud US3, Qingyun QingStor, Ping An Cloud OBS, Shouyun OSS, IBM COS, and other storage platforms compatible with the S3 protocol. View [all supported storage platforms](https://x-file-storage.xuyanwu.cn/#/存储平台) + +💡 After connecting to Alist through WebDAV, you can use common storage services such as Baidu Cloud Disk, Tianyi Cloud Disk, Alibaba Cloud Disk, and Thunder Cloud Disk. Check [Storage Platforms Supported by Alist](https://alist-doc.nn.ci/docs/webdav) + +GitHub:https://github.com/dromara/x-file-storage +Gitee:https://gitee.com/dromara/x-file-storage + +Document1:https://x-file-storage.dromara.org +Document2:https://x-file-storage.xuyanwu.cn +Document3:https://spring-file-storage.xuyanwu.cn + +------- + +# 📜UpdateContent + +- Change project name, change package name, optimize project structure +- Added the ability to directly read the HttpServletRequest stream for uploading, so the file does not fall to disk and is faster +- Added support for Metadata +- Optimize ACL exception handling +- Optimize file deletion logic +- Fixed the issue of ResetException occasionally occurring when uploading files to Amazon S3 +- Donate to [dromara](https://dromara.org/zh) open source community + +#### Changes in project dependencies + +Versions before 2.0.0 + +```xml + + cn.xuyanwu + spring-file-storage + 1.0.3 + +``` + +2.0.0 and later versions + +```xml + + org.dromara.x-file-storage + x-file-storage-spring + 2.0.0 + +``` + +#### Changes in configuration parameters + +Versions before 2.0.0 + +```yaml +spring: + file-storage: #File storage configuration + default-platform: huawei-obs-1 #Default storage platform used + thumbnail-suffix: ".min.jpg" #Thumbnail suffix, such as [.min.jpg] [.png] + #The configuration of the corresponding platform is written here, pay attention to the indentation to be aligned +``` + +2.0.0 and later versions + +```yaml +dromara: + x-file-storage: #File storage configuration + default-platform: huawei-obs-1 #Default storage platform used + thumbnail-suffix: ".min.jpg" #Thumbnail suffix, such as [.min.jpg] [.png] + #The configuration of the corresponding platform is written here, pay attention to the indentation to be aligned +``` + +#### Changes in package name + +Versions before 2.0.0 + +```java +cn.xuyanwu.spring.file.storage +cn.xuyanwu.spring.file.storage.spring +``` + +2.0.0 and later versions + +```java +org.dromara.x.file.storage.core +org.dromara.x.file.storage.spring +``` + +------- + +# 📦Use + +Click [Quick Start](https://x-file-storage.xuyanwu.cn/#/快速入门) to view how to use all storage platforms! + +#### 🔧 Configuration + +Here we take Alibaba Cloud OSS as an example. `pom.xml` is introduced into this project. The default here is the `SpringBoot` environment. For other environments, please refer to [Use alone without SpringBoot](https://x-file-storage.xuyanwu.cn/#/脱离SpringBoot单独使用) +```xml + + + org.dromara.x-file-storage + x-file-storage-spring + 2.0.0 + + + + com.aliyun.oss + aliyun-sdk-oss + 3.16.1 + +``` + +Add the following basic configuration to the `application.yml` configuration file + +```yaml +dromara: + x-file-storage: #File storage configuration + default-platform: aliyun-oss-1 #Default storage platform + aliyun-oss: + - platform: aliyun-oss-1 # Storage platform identification + enable-storage: true # Enable storage + access-key: ?? + secret-key: ?? + end-point: ?? + bucket-name: ?? + domain: ?? # Access the domain name, pay attention to the "/" ending, for example: https://abc.oss-cn-shanghai.aliyuncs.com/ + base-path: test/ # base path +``` + +#### 🔨Encoding + +Add the `@EnableFileStorage` annotation to the startup class + +```java +@EnableFileStorage +@SpringBootApplication +public class SpringFileStorageTestApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringFileStorageTestApplication.class,args); + } + +} +``` +#### ✨Start uploading + +Supports File, MultipartFile, byte[], InputStream, URL, URI, String, HttpServletRequest, and large files will be automatically uploaded in parts. If you want to support more methods, please read the [File Adapter](https://x-file-storage.xuyanwu.cn/#/文件适配器) chapter + +```java +@RestController +public class FileDetailController { + + @Autowired + private FileStorageService fileStorageService;//注入实列 + + /** + * upload files + */ + @PostMapping("/upload") + public FileInfo upload(MultipartFile file) { + //Only this line of code is needed to upload successfully + return fileStorageService.of(file).upload(); + } + + /** + * Upload the file and return the file url successfully + */ + @PostMapping("/upload2") + public String upload2(MultipartFile file) { + FileInfo fileInfo = fileStorageService.of(file) + .setPath("upload/") //Save to a relative path. For the convenience of management, you don’t need to write it. + .setObjectId("0") //Associated object id. For the convenience of management, you don’t need to write it. + .setObjectType("0") //Associated object type. For convenience of management, you don’t need to write it. + .putAttr("role","admin") //Save some attributes, which can be obtained and used in aspects, saved upload records, customized storage platforms, etc. You don't need to write them + .upload(); //Upload the file to the corresponding place + return fileInfo == null ? "Upload failed!" : fileInfo.getUrl(); + } + + /** + * Upload pictures and return file information successfully + *Image processing uses https://github.com/coobird/thumbnailator + */ + @PostMapping("/upload-image") + public FileInfo uploadImage(MultipartFile file) { + return fileStorageService.of(file) + .image(img -> img.size(1000,1000)) //Adjust the image size to 1000*1000 + .thumbnail(th -> th.size(200,200)) //Generate a 200*200 thumbnail + .upload(); + } + + /** + * Upload files to the designated storage platform and return file information successfully + */ + @PostMapping("/upload-platform") + public FileInfo uploadPlatform(MultipartFile file) { + return fileStorageService.of(file) + .setPlatform("aliyun-oss-1") //Use the specified storage platform + .upload(); + } + + /** + * Directly read the file in HttpServletRequest for upload, and successfully return the file information + * There are some precautions when using this method. Please check the Basic Functions-Upload chapter of the document. + */ + @PostMapping("/upload-request") + public FileInfo uploadPlatform(HttpServletRequest request) { + return fileStorageService.of(request).upload(); + } +} +``` + +#### 🎨Other operations + +```java +//Manually construct file information, which can be used for other operations +FileInfo fileInfo = new FileInfo() + .setPlatform("huawei-obs-1") + .setBasePath("test/") + .setPath("aa/") + .setFilename("image.png") + .setThFilename("image.png.min.jpg"); + +//Does the file exist? +boolean exists = fileStorageService.exists(fileInfo); +//download +byte[] bytes = fileStorageService.download(fileInfo).bytes(); +//delete +fileStorageService.delete(fileInfo); +//Other more operations + +``` + +If you save file records to the database, you can also operate based on the URL more conveniently. For details, please read [Save Upload Records](https://x-file-storage.xuyanwu.cn/#/基础功能?id=保存上传记录) Chapter + +```java +//Get the FileInfo object directly from the database, making it easier to perform other operations +FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://abc.def.com/test/aa/image.png"); + +//Does the file exist? +boolean exists = fileStorageService.exists("https://abc.def.com/test/aa/image.png"); +//download +byte[] bytes = fileStorageService.download("https://abc.def.com/test/aa/image.png").bytes(); +//delete +fileStorageService.delete("https://abc.def.com/test/aa/image.png"); +//Other more operations +``` + +Click [Quick Start](https://x-file-storage.xuyanwu.cn/#/快速入门) to view how to use all storage platforms! diff --git a/src/projects/README.md b/src/projects/README.md index 03c48e82e2..3f7bbd2263 100644 --- a/src/projects/README.md +++ b/src/projects/README.md @@ -1,27 +1,9 @@ ---- -title: Projects -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - - - - - - +--- +title: Projects +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +sidebar: false +layout: projects +--- diff --git a/src/zh/about/README.md b/src/zh/about/README.md index f57d9692c9..02887c3915 100644 --- a/src/zh/about/README.md +++ b/src/zh/about/README.md @@ -1,27 +1,28 @@ ---- -title: 关于 -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - -## 我们的愿景 - -为往圣继绝学,一个人或许能走的更快,但一群人会走的更远,让每一位开源爱好者,体会到开源的快乐。 - -## 社区口号 - -技术栈全面开源共建、保持社区中立、和谐快乐做开源 。 - -## 官网 - -**[https://dromara.org](https://dromara.org)** 是 **Dromara** 开源社区官方网站。 - -## 荣誉 - - - - +--- +title: 关于 +pageInfo: false +contributors: false +editLink: false +sidebar: false +lastUpdated: false +--- + +## 我们的愿景 + +为往圣继绝学,一个人或许能走的更快,但一群人会走的更远,让每一位开源爱好者,体会到开源的快乐。 + +## 社区口号 + +技术栈全面开源共建、保持社区中立、和谐快乐做开源 。 + +## 官网 + +**[https://dromara.org](https://dromara.org)** 是 **Dromara** 开源社区官方网站。 + +## 荣誉 + + + + diff --git a/src/zh/activity/2024-summary.md b/src/zh/activity/2024-summary.md index 126f679bcb..f70ab0a897 100644 --- a/src/zh/activity/2024-summary.md +++ b/src/zh/activity/2024-summary.md @@ -1,83 +1,83 @@ ---- -title: 超 200K Star 开源社区的年终总结,请查收! -author: Dromara -date: 2024-01-09 -cover: /assets/img/activity/2024-summary-0.png -head: - - - meta - - name: 活动 ---- - -> 过去一年大家见证了 Dromara 开源社区的飞速发展,社区的进步离不开社区成员们的,社区下开源项目贡献者们的辛勤劳动贡献和触达用户们,开源爱好者们的大力支持,对此我们感激不尽。 - -接下来我们看看社区这 2023 一年的成长吧! - -## 新增捐赠孵化 20+ 非常棒的开源项目,目前社区下的总项目数量超 50 个。 - -![](/assets/img/activity/2024-summary-0.png) - -## 社区下孵化项目 HertzBeat, Dynamic-Tp, Easy-Es 发展迅速,成功毕业成为社区顶级开源项目。云原生大数据平台 CloudEon 成为被评为 Gitee GVP (Gitee 最具价值开源), 目前 Dromara 社区下的 GVP 项目数量已达到 16 个。社区项目 HertzBeat 被 CNCF 全景图收录。 - -![](/assets/img/activity/2024-summary-1.png) - -![](/assets/img/activity/2024-summary-2.png) - -## 在 Gitee 和 Github 平台上,Dromara 社区项目总共获得超 200K star 小星星 🌟,和 数不清的 Fork Watch 次数(偷懒了 😂)。 - -![](/assets/img/activity/2024-summary-3.png) - -![](/assets/img/activity/2024-summary-4.png) - -**2023 年这一年社区下开源项目在 Github 平台上新增 2 万 颗小星星,PR 数量 1000+,Issues 数量 1400+** - -## 社区下开源项目作为课题项目成功参与中科院的 OSPP 开源之夏和计算机学会的 GLCC 编程夏令营活动,申请的同学们热情很高,最终中选的同学在导师的指导下完成课题项目并顺利结项。祝贺他们㊗️。 - -![](/assets/img/activity/2024-summary-5.png) - -![](/assets/img/activity/2024-summary-6.png) - -![](/assets/img/activity/2024-summary-7.png) - -![](/assets/img/activity/2024-summary-8.png) - -## 5 月 Dromara 参展全球开源技术峰会 GOTC,社区小伙伴的在上海线下面基成功,+薅了很多羊毛。 - -![](/assets/img/activity/2024-summary-9.jpg) - -![](/assets/img/activity/2024-summary-10.jpg) - -## 7 月 Dromara 参加华为云开发者大会,社区小伙伴们又东莞线下面基一波,三位小伙伴做了 Dromara 与华为云开源主题分享,收获满满。 - -![](/assets/img/activity/2024-summary-11.png) - -## 10 月 Dromara 参加了 COSCon'23 中国开源年会,社区小伙伴们又成都线下面基一波,我们准备的小礼品很受欢迎,社区小伙伴做了关于社区项目的主题分享。 - -![](/assets/img/activity/2024-summary-12.png) - -![](/assets/img/activity/2024-summary-13.png) - -## 12 月 Dromara 参加了 2023 开放原子开发者大会,虽然没有面基成功(下次一定),但是社区大佬明哥在会上给开源同行们分享介绍了我们社区,收获满满+1。 - -![](/assets/img/activity/2024-summary-14.jpg) - -![](/assets/img/activity/2024-summary-15.jpg) - -## 关于荣誉 - -> 荣获中国开源创新大赛优秀奖, 2023 年度最受关注喜爱的开源组织和最活跃组织之一, 掘金 2023 年度人气团队等。 - -![](/assets/img/activity/2024-summary-16.png) - -![](/assets/img/activity/2024-summary-17.png) - -![](/assets/img/activity/2024-summary-18.png) - -#### 还有更多事件在 2023 年的 Dromara 发生。。。就先列举到这里了 - -> 社区下各个项目团队在 2023 年也发布了无数个版本,维护着上万人的开源社群,回答并帮助解决了数不清的用户问题。安装量,下载量,被引用量都是以万计新增。这一切此时此刻依然正在发生。 - -**2023 已然结束,期待 2024 更加美好!** - -**船新官网: https://dromara.org/** -**Github: https://github.com/dromara** -**Gitee: https://gitee.com/dromara** +--- +title: 超 200K Star 开源社区的年终总结,请查收! +author: Dromara +date: 2024-01-09 +cover: /assets/img/activity/2024-summary-0.png +head: + - - meta + - name: 活动 +--- + +> 过去一年大家见证了 Dromara 开源社区的飞速发展,社区的进步离不开社区成员们的,社区下开源项目贡献者们的辛勤劳动贡献和触达用户们,开源爱好者们的大力支持,对此我们感激不尽。 + +接下来我们看看社区这 2023 一年的成长吧! + +## 新增捐赠孵化 20+ 非常棒的开源项目,目前社区下的总项目数量超 50 个。 + +![](/assets/img/activity/2024-summary-0.png) + +## 社区下孵化项目 HertzBeat, Dynamic-Tp, Easy-Es 发展迅速,成功毕业成为社区顶级开源项目。云原生大数据平台 CloudEon 成为被评为 Gitee GVP (Gitee 最具价值开源), 目前 Dromara 社区下的 GVP 项目数量已达到 16 个。社区项目 HertzBeat 被 CNCF 全景图收录。 + +![](/assets/img/activity/2024-summary-1.png) + +![](/assets/img/activity/2024-summary-2.png) + +## 在 Gitee 和 Github 平台上,Dromara 社区项目总共获得超 200K star 小星星 🌟,和 数不清的 Fork Watch 次数(偷懒了 😂)。 + +![](/assets/img/activity/2024-summary-3.png) + +![](/assets/img/activity/2024-summary-4.png) + +**2023 年这一年社区下开源项目在 Github 平台上新增 2 万 颗小星星,PR 数量 1000+,Issues 数量 1400+** + +## 社区下开源项目作为课题项目成功参与中科院的 OSPP 开源之夏和计算机学会的 GLCC 编程夏令营活动,申请的同学们热情很高,最终中选的同学在导师的指导下完成课题项目并顺利结项。祝贺他们㊗️。 + +![](/assets/img/activity/2024-summary-5.png) + +![](/assets/img/activity/2024-summary-6.png) + +![](/assets/img/activity/2024-summary-7.png) + +![](/assets/img/activity/2024-summary-8.png) + +## 5 月 Dromara 参展全球开源技术峰会 GOTC,社区小伙伴的在上海线下面基成功,+薅了很多羊毛。 + +![](/assets/img/activity/2024-summary-9.jpg) + +![](/assets/img/activity/2024-summary-10.jpg) + +## 7 月 Dromara 参加华为云开发者大会,社区小伙伴们又东莞线下面基一波,三位小伙伴做了 Dromara 与华为云开源主题分享,收获满满。 + +![](/assets/img/activity/2024-summary-11.png) + +## 10 月 Dromara 参加了 COSCon'23 中国开源年会,社区小伙伴们又成都线下面基一波,我们准备的小礼品很受欢迎,社区小伙伴做了关于社区项目的主题分享。 + +![](/assets/img/activity/2024-summary-12.png) + +![](/assets/img/activity/2024-summary-13.png) + +## 12 月 Dromara 参加了 2023 开放原子开发者大会,虽然没有面基成功(下次一定),但是社区大佬明哥在会上给开源同行们分享介绍了我们社区,收获满满+1。 + +![](/assets/img/activity/2024-summary-14.jpg) + +![](/assets/img/activity/2024-summary-15.jpg) + +## 关于荣誉 + +> 荣获中国开源创新大赛优秀奖, 2023 年度最受关注喜爱的开源组织和最活跃组织之一, 掘金 2023 年度人气团队等。 + +![](/assets/img/activity/2024-summary-16.png) + +![](/assets/img/activity/2024-summary-17.png) + +![](/assets/img/activity/2024-summary-18.png) + +#### 还有更多事件在 2023 年的 Dromara 发生。。。就先列举到这里了 + +> 社区下各个项目团队在 2023 年也发布了无数个版本,维护着上万人的开源社群,回答并帮助解决了数不清的用户问题。安装量,下载量,被引用量都是以万计新增。这一切此时此刻依然正在发生。 + +**2023 已然结束,期待 2024 更加美好!** + +**船新官网: https://dromara.org/** +**Github: https://github.com/dromara** +**Gitee: https://gitee.com/dromara** diff --git a/src/zh/activity/CommunityOverCode-Asia-2023.md b/src/zh/activity/CommunityOverCode-Asia-2023.md index dd45fa7612..f7b90620c2 100644 --- a/src/zh/activity/CommunityOverCode-Asia-2023.md +++ b/src/zh/activity/CommunityOverCode-Asia-2023.md @@ -1,62 +1,62 @@ ---- -title: CommunityOverCode Asia 2023 参会攻略出炉,共享开源盛宴 -author: ALC Beijing -date: 2023-08-17 -cover: /assets/img/activity/CommunityOverCode-Asia-2023-cover.png -head: - - - meta - - name: 活动 ---- - -从一个项目成长到今天,Apache 软件基金会(ASF)已经成为目前为止全球最大的开源软件基金会,维护着包括 350 多个顶级项目以及数十个孵化器项目,为全球提供着几十亿甚至上百亿美元市值的开源软件,是推动全球开源软件发展的重要力量。 - -伴随着全球开源项目数量的迅速增长,我们很高兴看到越来越多中国的开发者与开源项目加入,以及越来越多高质量项目的涌现。种种迹象表明,中国在国际基金会的影响力愈发重要。 - -今年,Apache 软件基金会的官方全球系列大会 CommunityOverCode Asia(原 ApacheCon Asia)将首次在中国线下举办,囊括 17 个论坛方向、上百个前沿议题,期待和各个层次的参与者,共赴开源盛宴,探索“今天的明天技术”。 - -如今大会已经进入最后的倒计时,我们也为大家制作了一份详尽的参会攻略,赶紧收藏分享起来吧! - -![](/assets/img/activity/CommunityOverCode-Asia-2023-cover.png) - -### **时间地点** - -**大会时间:** - -2023 年 8 月 18 日 - 2023 年 8 月 20 日,早 09:30 - 18:00 - -请参会者根据所选票种安排好自己的行程,并**在早上 08:30 - 9:30 间至大会签到处签到入场** - -**大会地址:** - -北京市海淀区知春路 25 号 北京丽亭华苑酒店 - -**路线(推荐绿色出行):** - -- **公交**:至知春路站下车,步行约 81 米,约 2 分钟到达北京丽亭华苑酒店; -- **地铁**:地铁 10 号线至知春路站下车,F 东北口出步行 348 米,约 5 分钟到达北京丽亭华苑酒店; -- **打车/自驾**:可直接定位导航至北京丽亭华苑酒店,酒店停车位有限,推荐绿色出行。 - -**会议在酒店** **3 楼宴会厅**,到达酒店后会有指引牌和引导人员哦~ - -### **签到入场** - -CommunityOverCode Asia 2023 采用售票制,所有参会者(含讲师及工作人员)皆需凭票扫码入场,请您确保在大会开始前已完成购票注册。 - -今年的大会为期 3 天,从 8 月 18 日至 8 月 20 日,各票种价格从 199 元至 6999 元不等,您可  [点击此处](https://mp.weixin.qq.com/s?__biz=MzI4ODI1NjI2NA==&mid=2247488496&idx=1&sn=219faea3ac92191584d2e17305e7c014&scene=21#wechat_redirect)  查看大会各票种介绍。 - -所有成功购票的用户皆会收到大会官方的报名成功邮件,如您未收到邮件,请尝试在垃圾邮件中寻找。报名成功的邮件中,附有您的专属电子门票二维码。您也可以在百格活动微信公众号找到您的电子门票。 - -### **微信端门票二维码获取方式:** - -1. 微信搜索并关注微信公众号“百格活动” -2. 点击底部菜单栏,并选择【我的电子票】绑定您在报名系统中输入的手机号码,即可查看大会门票 - - - -8 月 18 日至 8 月 20 日,北京丽亭华苑 - -CommunityOverCode Asia,论道开源 - -我们不见不散 - - +--- +title: CommunityOverCode Asia 2023 参会攻略出炉,共享开源盛宴 +author: ALC Beijing +date: 2023-08-17 +cover: /assets/img/activity/CommunityOverCode-Asia-2023-cover.png +head: + - - meta + - name: 活动 +--- + +从一个项目成长到今天,Apache 软件基金会(ASF)已经成为目前为止全球最大的开源软件基金会,维护着包括 350 多个顶级项目以及数十个孵化器项目,为全球提供着几十亿甚至上百亿美元市值的开源软件,是推动全球开源软件发展的重要力量。 + +伴随着全球开源项目数量的迅速增长,我们很高兴看到越来越多中国的开发者与开源项目加入,以及越来越多高质量项目的涌现。种种迹象表明,中国在国际基金会的影响力愈发重要。 + +今年,Apache 软件基金会的官方全球系列大会 CommunityOverCode Asia(原 ApacheCon Asia)将首次在中国线下举办,囊括 17 个论坛方向、上百个前沿议题,期待和各个层次的参与者,共赴开源盛宴,探索“今天的明天技术”。 + +如今大会已经进入最后的倒计时,我们也为大家制作了一份详尽的参会攻略,赶紧收藏分享起来吧! + +![](/assets/img/activity/CommunityOverCode-Asia-2023-cover.png) + +### **时间地点** + +**大会时间:** + +2023 年 8 月 18 日 - 2023 年 8 月 20 日,早 09:30 - 18:00 + +请参会者根据所选票种安排好自己的行程,并**在早上 08:30 - 9:30 间至大会签到处签到入场** + +**大会地址:** + +北京市海淀区知春路 25 号 北京丽亭华苑酒店 + +**路线(推荐绿色出行):** + +- **公交**:至知春路站下车,步行约 81 米,约 2 分钟到达北京丽亭华苑酒店; +- **地铁**:地铁 10 号线至知春路站下车,F 东北口出步行 348 米,约 5 分钟到达北京丽亭华苑酒店; +- **打车/自驾**:可直接定位导航至北京丽亭华苑酒店,酒店停车位有限,推荐绿色出行。 + +**会议在酒店** **3 楼宴会厅**,到达酒店后会有指引牌和引导人员哦~ + +### **签到入场** + +CommunityOverCode Asia 2023 采用售票制,所有参会者(含讲师及工作人员)皆需凭票扫码入场,请您确保在大会开始前已完成购票注册。 + +今年的大会为期 3 天,从 8 月 18 日至 8 月 20 日,各票种价格从 199 元至 6999 元不等,您可  [点击此处](https://mp.weixin.qq.com/s?__biz=MzI4ODI1NjI2NA==&mid=2247488496&idx=1&sn=219faea3ac92191584d2e17305e7c014&scene=21#wechat_redirect)  查看大会各票种介绍。 + +所有成功购票的用户皆会收到大会官方的报名成功邮件,如您未收到邮件,请尝试在垃圾邮件中寻找。报名成功的邮件中,附有您的专属电子门票二维码。您也可以在百格活动微信公众号找到您的电子门票。 + +### **微信端门票二维码获取方式:** + +1. 微信搜索并关注微信公众号“百格活动” +2. 点击底部菜单栏,并选择【我的电子票】绑定您在报名系统中输入的手机号码,即可查看大会门票 + + + +8 月 18 日至 8 月 20 日,北京丽亭华苑 + +CommunityOverCode Asia,论道开源 + +我们不见不散 + + diff --git a/src/zh/activity/Dromara-OSPP-2025.md b/src/zh/activity/Dromara-OSPP-2025.md new file mode 100644 index 0000000000..65057ec0f8 --- /dev/null +++ b/src/zh/activity/Dromara-OSPP-2025.md @@ -0,0 +1,141 @@ +--- +title: 开源之夏学生招募中 | 认领 Dromara 项目赢取丰厚奖金。 +author: Dromara +date: 2025-05-12 +cover: /assets/img/activity/Dromara-OSPP-2025-0.png +head: + - - meta + - name: 活动 +--- + + + +# 开源之夏 2025 + +开源之夏是由中科院软件所 开源软件供应链点亮计划 发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,培养和发掘更多优秀的开发者,促进优秀开源软件社区的蓬勃发展,助力开源软件供应链建设。 + +![](/assets/img/activity/Dromara-OSPP-2025-0.png) + +## 学生开启报名 + +开源之夏 2025 学生报名正式开启啦!同学们就可以在开源之夏官网 https://summer-ospp.ac.cn/ 挑选项目,与导师沟通并准备项目申请材料、提交申请。 + +> 这么多项目任务,是不是已经跃跃欲试了?不管你是新手还是老手,无论你想要贡献代码,还是学习开源技术和进行开发实践,或是想为自己的履历增添优势,在开源之夏,你都能找到自己的机会。 +> +> 带着对开源的探索和求知之心,欢迎在校的你报名加入开源之夏 2025,一起探索今夏的无限可能!  +> +> 在开源之夏,参与学生有机会与众多开源社区和资深开发者导师进行交流和互动,提升自己的技术水平和实践能力,掌握更多开源知识和技能。成功结项的你还将获得丰厚项目奖金——基础难度项目:8000 元、进阶难度项目:12000 元(税前),以及有机会成为 Dromara 社区成员享受福利多多! + +## Dromara 社区介绍 + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。 + +提供包括分布式事务,流行工具,企业级认证,微服务 RPC,运维监控,Agent 监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、保持社区中立,致力于为全球用户提供微服务云原生解决方案。 + +让参与的每一位开源爱好者,体会到开源的快乐。 Dromara 开源社区目前拥有 10+GVP 项目,总 star 数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用 Dromara 社区的开源项目。 + +## Dromara 社区课题项目 + +给大家带来的是 **Dromara 开源社区一体化管理系统** 和 **Dromara 孵化器网站优化建设和主网站优化**。 + +> 课题任务门槛不会太高的哦,且全程由 Dromara 社区导师倾心指导。 + +### 一. Dromara 开源社区一体化管理系统 + +在开源蓬勃发展的当下,Dromara 开源社区规模持续扩大,项目数量不断增多,参与人员愈发复杂。现有的管理方式相对分散,缺乏一个集中、高效的人员与项目管理系统,导致项目孵化过程难以追踪,人员与项目关联不清晰,权限管理混乱等问题,严重影响社区运作效率与协同效果。 + +**项目需求** + +构建一个功能完善、操作便捷的开源社区人员与项目管理系统,提升社区管理效率,促进社区成员更好地协作与交流,推动开源项目的顺利开展。 + +**主要产出如下**: + +1. 完成系统的前后端架构设计,绘制详细的架构图,包括模块划分、接口设计等。 + +2. 开发开源项目管理模块,实现项目孵化流程全生命周期管理,包括项目创建、审核、进度跟踪、文档管理等功能。 + +3. 开发社区人员管理模块,实现人员信息录入、编辑,人员与项目关联关系管理,展示人员在项目中的贡献信息。 + +4. 完成系统测试,包括功能测试、性能测试、安全测试等,并提交测试报告。 + +5. 撰写系统使用文档,包括管理员使用手册、普通用户使用手册,方便社区成员使用。 + + +* 地址:https://dromara.org + +* 项目源码:https://github.com/dromara/droer + + +**项目技术要求**: + +1. 熟悉 Java 开发语言,掌握常用的 Java 开发框架,如 Spring Boot、MyBatis 等。 + +2. 了解前端开发技术,包括 HTML、CSS、JavaScript,具备使用 Vue.js 等前端框架开发的能力。 + +3. 掌握数据库设计与开发,熟练使用 MySQL 等关系型数据库。 + +4. 有一定的软件系统设计能力,能够根据需求进行合理的模块划分与接口设计。 + +5. 具备良好的代码编写习惯,遵循代码规范,有一定的代码注释能力。 + + + + +* 导师:唐振超 tangzc2@gmail.com + +* 项目成果仓库:https://github.com/dromara/droer + +* 申请地址:https://summer-ospp.ac.cn/org/prodetail/25ee40202?list=org&navpage=org + + +### 二. Dromara 孵化器网站优化建设和主网站优化 + +**项目简介** + +Dromara 是由顶尖开源项目维护者自发组织的开源社区。 + +Dromara 开源社区目前拥有 10+ 顶级项目和 50+ 孵化器项目,我们的主服务和孵化器都有对应的官方网站。  + +此课题需要对两个官方网站(孵化器官网和主官网)进行 UI 风格统一设计,架构统一,页面完善优化,官网功能特性新增等,突出主官网和孵化器项目的展示,优化博客提交流程,编写官网使用文档等。 + +**项目产出要求** + +1. 优化完善孵化器网站 UI 页面和官网 UI 页面 + +2. 设计和完善网站内容展示 + + +* 2.1:顶级项目群和孵化器项目群的展示 UI 优化 + + +4. 统一孵化器网站和官网的技术架构,升级使用最新对应的技术版本,提供相应的构建文档和本地部署文档等 + +5. 其它网站页面开发 + + +**项目技术要求**: + +   1. 有一定的开源经验,热爱开源者优先 + +   2. Vue Html Css + +   3. 熟悉 Git 常用命令 + + + +* 导师:猫大人 549477611@qq.com + +* 项目成果仓库:https://github.com/dromara/incubatorhttps://github.com/dromara/dromara.github.io + +* 申请地址:https://summer-ospp.ac.cn/org/prodetail/25ee40277?list=org&navpage=org + + +## 快速参与开源之夏 2025 + +开源之夏 2025 Dromara 社区各项目课题将从 5 月 09 日开始接受学生参与项目申请,欢迎通过上方联系方式,与各导师沟通并准备项目申请材料。 + +![](/assets/img/activity/Dromara-OSPP-2025-1.png) + + + +![](/assets/img/activity/Dromara-OSPP-2025-2.png) \ No newline at end of file diff --git a/src/zh/activity/GOTC-2023.md b/src/zh/activity/GOTC-2023.md index 83db204cb0..e46c861a94 100644 --- a/src/zh/activity/GOTC-2023.md +++ b/src/zh/activity/GOTC-2023.md @@ -1,32 +1,32 @@ ---- -title: 摆摊啦,Dromara 与您在全球开源技术峰会(GOTC 2023)不见不散 -author: 周六见 -date: 2023-05-26 -cover: /assets/img/activity/GOTC-2023-1.jpg -head: - - - meta - - name: 活动 ---- - -全球开源技术峰会(Global Open-source Technology Conference) GOTC 2023  由开放原子开源基金会、 上海浦东软件园、Linux 基金会亚太区和开源中国联合发起, 将于  **5 月 27 日至 28 日在上海张江科学会堂隆重举行,Dromara 开源社区将出席本次大会。** - -为期 2 天的开源行业盛会,将以行业展览、主题发言、论坛、圆桌、快闪演讲、开源市集等形式来诠释此次大会主题 ——“Open Source, Into the Future”。与会者将一起探讨元宇宙、安全、3D 引擎、eBPF、Web3.0、区块链等热门技术主题,以及开源治理、汽车软件、AIGC、AI 编程、开源教育培训、云原生等热门话题,探讨开源未来,助力开源发展。大会主论坛将邀请国内外多位重磅嘉宾发表致辞、主题演讲,并设置两大圆桌讨论,从多个角度探讨开源与各项技术、各个行业的结合与落地实践,探讨未来可能的发展方向。敬请关注! - -大会报名通道:https://www.bagevent.com/event/8387611 - -大会官网: https://gotc.oschina.net - -![](/assets/img/activity/GOTC-2023-1.jpg) -**GOTC 2023 由一个主论坛、十余个分论坛组成。** - -本次大会将邀请 LF AI & Data 基金会执行董事、PyTorch 基金会执行董事 Ibrahim Haddad、OpenSSF 总经理 Brian Behlendorf、Open 3D 基金会执行董事 Royal O'Brien、Hyperledger 基金会执行董事 Daniela Barbosa,以及来自百度、华为、腾讯、火山引擎、红帽、Intel、VMWare、F5、微软、开源中国等企业的全球开源重磅嘉宾出席,为开发者、为行业分享开源的观点、经验借鉴与未来探索。 - -● **开源市集** - -在大会展厅,三十多家开源技术社区将以创意摆摊的方式,打造一场身临其境的沉浸式市集体验,展示开源项目、周边商品,交流开源技术、开源理念、开放协作方式。**Dromara** **开源社区将在开源集市展位等待大家,我们给大家准备精美礼品~ Hutool 阿超,LiteFlow 铂赛东,Forest 子骏,DataCompare 子房等众多大佬等你线下网友面基** - -张江科学会堂 - -![](/assets/img/activity/GOTC-2023-2.png) - -![](/assets/img/activity/GOTC-2023-3.jpg) +--- +title: 摆摊啦,Dromara 与您在全球开源技术峰会(GOTC 2023)不见不散 +author: 周六见 +date: 2023-05-26 +cover: /assets/img/activity/GOTC-2023-1.jpg +head: + - - meta + - name: 活动 +--- + +全球开源技术峰会(Global Open-source Technology Conference) GOTC 2023  由开放原子开源基金会、 上海浦东软件园、Linux 基金会亚太区和开源中国联合发起, 将于  **5 月 27 日至 28 日在上海张江科学会堂隆重举行,Dromara 开源社区将出席本次大会。** + +为期 2 天的开源行业盛会,将以行业展览、主题发言、论坛、圆桌、快闪演讲、开源市集等形式来诠释此次大会主题 ——“Open Source, Into the Future”。与会者将一起探讨元宇宙、安全、3D 引擎、eBPF、Web3.0、区块链等热门技术主题,以及开源治理、汽车软件、AIGC、AI 编程、开源教育培训、云原生等热门话题,探讨开源未来,助力开源发展。大会主论坛将邀请国内外多位重磅嘉宾发表致辞、主题演讲,并设置两大圆桌讨论,从多个角度探讨开源与各项技术、各个行业的结合与落地实践,探讨未来可能的发展方向。敬请关注! + +大会报名通道:https://www.bagevent.com/event/8387611 + +大会官网: https://gotc.oschina.net + +![](/assets/img/activity/GOTC-2023-1.jpg) +**GOTC 2023 由一个主论坛、十余个分论坛组成。** + +本次大会将邀请 LF AI & Data 基金会执行董事、PyTorch 基金会执行董事 Ibrahim Haddad、OpenSSF 总经理 Brian Behlendorf、Open 3D 基金会执行董事 Royal O'Brien、Hyperledger 基金会执行董事 Daniela Barbosa,以及来自百度、华为、腾讯、火山引擎、红帽、Intel、VMWare、F5、微软、开源中国等企业的全球开源重磅嘉宾出席,为开发者、为行业分享开源的观点、经验借鉴与未来探索。 + +● **开源市集** + +在大会展厅,三十多家开源技术社区将以创意摆摊的方式,打造一场身临其境的沉浸式市集体验,展示开源项目、周边商品,交流开源技术、开源理念、开放协作方式。**Dromara** **开源社区将在开源集市展位等待大家,我们给大家准备精美礼品~ Hutool 阿超,LiteFlow 铂赛东,Forest 子骏,DataCompare 子房等众多大佬等你线下网友面基** + +张江科学会堂 + +![](/assets/img/activity/GOTC-2023-2.png) + +![](/assets/img/activity/GOTC-2023-3.jpg) diff --git a/src/zh/activity/HDC.Cloud-2023.md b/src/zh/activity/HDC.Cloud-2023.md index 1b1c6b90fc..b07a6c917e 100644 --- a/src/zh/activity/HDC.Cloud-2023.md +++ b/src/zh/activity/HDC.Cloud-2023.md @@ -1,40 +1,40 @@ ---- -title: HDC.Cloud 2023 | Dromara社区在华为云开发者大会精彩花絮 -author: 星期二 -date: 2023-08-08 -cover: /assets/img/activity/HDC.Cloud-2023-4.jpg -head: - - - meta - - name: 活动 ---- - -> 华为开发者大会 2023(Cloud)在东莞圆满落幕,会上众多 AI 黑科技与精彩演讲,Dromara 社区和 Apache Shenyu 社区的小伙伴也应邀参加,和开发者们分享了开源的故事。 - -![](/assets/img/activity/HDC.Cloud-2023-1.jpg) - -## - -**Dromara 开源社区加入 HuaweiCloud 开源业务全景图** - -开源是开发者生态中不可或缺的环节,华为云一直在倡导和推动开源生态发展,推出了**开源 For 华为云业务生态全景图**,Dromara 开源社区也作为开源社区组织加入其中,未来更多与华为云开源生态的结合。 - -![](/assets/img/activity/HDC.Cloud-2023-2.jpg) - -## - -**开源生态合作圆桌讨论会** - -各个领域的专家齐聚一堂,围绕着华为云如何帮助开源开发者实现商业化价值各抒己见,其中不乏尖锐的开源白嫖等话题,加深了开源企业,开源组织,开源个人和开源高校的之间的交流认知,Dromara 猫大也作为开源组织代表发表了自己的见解。 - -![](/assets/img/activity/HDC.Cloud-2023-3.jpg) - -## - -**开源演讲-让每一位开源爱好者享受开源红利** - -Dromara 社区的小伙伴 猫大 tom 永伦分别给大家介绍了我们的 Dromara 开源社区,社区与华为云开源解决方案合作进展,HertzBeat 开源项目介绍和与华为云的结合,Apache ShenYu 助力中小型企业应对高并发洪流,线下和开发者与开源爱好者们接触交流,收获良多。 - -![](/assets/img/activity/HDC.Cloud-2023-4.jpg) -![](/assets/img/activity/HDC.Cloud-2023-5.jpg) - -Done!  期待下一次再与开源小伙伴们和开发者们线下交流学习,一起进步。 +--- +title: HDC.Cloud 2023 | Dromara社区在华为云开发者大会精彩花絮 +author: 星期二 +date: 2023-08-08 +cover: /assets/img/activity/HDC.Cloud-2023-4.jpg +head: + - - meta + - name: 活动 +--- + +> 华为开发者大会 2023(Cloud)在东莞圆满落幕,会上众多 AI 黑科技与精彩演讲,Dromara 社区和 Apache Shenyu 社区的小伙伴也应邀参加,和开发者们分享了开源的故事。 + +![](/assets/img/activity/HDC.Cloud-2023-1.jpg) + +## + +**Dromara 开源社区加入 HuaweiCloud 开源业务全景图** + +开源是开发者生态中不可或缺的环节,华为云一直在倡导和推动开源生态发展,推出了**开源 For 华为云业务生态全景图**,Dromara 开源社区也作为开源社区组织加入其中,未来更多与华为云开源生态的结合。 + +![](/assets/img/activity/HDC.Cloud-2023-2.jpg) + +## + +**开源生态合作圆桌讨论会** + +各个领域的专家齐聚一堂,围绕着华为云如何帮助开源开发者实现商业化价值各抒己见,其中不乏尖锐的开源白嫖等话题,加深了开源企业,开源组织,开源个人和开源高校的之间的交流认知,Dromara 猫大也作为开源组织代表发表了自己的见解。 + +![](/assets/img/activity/HDC.Cloud-2023-3.jpg) + +## + +**开源演讲-让每一位开源爱好者享受开源红利** + +Dromara 社区的小伙伴 猫大 tom 永伦分别给大家介绍了我们的 Dromara 开源社区,社区与华为云开源解决方案合作进展,HertzBeat 开源项目介绍和与华为云的结合,Apache ShenYu 助力中小型企业应对高并发洪流,线下和开发者与开源爱好者们接触交流,收获良多。 + +![](/assets/img/activity/HDC.Cloud-2023-4.jpg) +![](/assets/img/activity/HDC.Cloud-2023-5.jpg) + +Done!  期待下一次再与开源小伙伴们和开发者们线下交流学习,一起进步。 diff --git a/src/zh/activity/PowerData-AI-shanghai-4.19.md b/src/zh/activity/PowerData-AI-shanghai-4.19.md new file mode 100644 index 0000000000..fbcfc9ec4e --- /dev/null +++ b/src/zh/activity/PowerData-AI-shanghai-4.19.md @@ -0,0 +1,137 @@ +--- +title: PowerData 数字AI-上海开源行 4月19日 邀您报名~! +author: 2025年04月07日 14:02 +date: 2025-04-07 +cover: /assets/img/activity/PowerData-AI-shanghai-4.19-3.jpg +head: + - - meta + - name: 活动 +--- + + + +**数字AI 携手未来** + +**2025-PowerData 上海开源行** + + + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-2.gif) + + + +2025年4月19日13:30,上海徐汇区模速空间路演厅,一场聚焦AI与数据技术融合的盛宴即将开启!✨ + +活动汇聚国内顶尖数字AI技术团队,由 SelectDB苏奕嘉、Chat2DB姬朋飞、DataFocus王碧波 等重磅嘉宾领衔,深度解析实时数仓AI化、自然语言交互数据库、15分钟搭建ChatBI等前沿议题。✈️ + +大咖零距离对话,助你洞见AI驱动下的数据基建革新、工业决策优化与智能搜索未来!活动免费报名,扫码抢占席位,与百位开发者共探数字AI技术边界! 🚀 + +现场更设三重抽奖,周边超多,参与活动均有机会领取,快来报名吧~ 🎁 + + + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-3.jpg) + + + + + +**01** + +**活动信息** + + + +活动主办方 + +PowerData社区 + + +**活动时间** + +2025年4月19日13:30 - 17:30 + + +**活动地点** + +上海徐汇区龙台路180号F2幢1层·模速空间路演厅 + + + + + +**02** + +**四大活动亮点** + + +**【顶流技术实战】** + +Doris、Chat2DB、OceanBase、KWDB、IoTDB、DataFocus,聆听数据库厂商在AI时代下的理念融合,探索nl2sql、chatbi的技术落地,揭秘顶尖企业的AI落地实践。 + +**【零距离交流】** + +与各个技术大咖**零距离**面对面交流,自由碰撞思维。 + +**【好礼多多】** + +签到/中场/闭幕三次抽奖,开源厂商赞助,周边礼品多多。且只要参与互动就有礼品送出。 + +本次活动各合作伙伴赞助了 60+ 份周边礼品,包含 马克杯、贴纸、笔记本、捏捏乐、徽章、衣服、背包、帽子等,等你来领!  + +**【场地精美,视野宽阔】** + +本次活动由全国**首个**大模型孵化和加速载体,上海模速空间提供场地支持。 + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-9.jpg) + + + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-10.jpg) + + + +**03** + +**参与方式** + + + + + + + + + + +活动报名 + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-12.png) + +🔥 扫码报名,席位有限,即刻锁定!报名后会有志愿者邀您加入活动群,同步最新活动信息 + +**活动直播** + +🔥本次活动全程直播,点击下方视频号预约。 + + + + + +**04** + +**最佳参会者** + + +![](/assets/img/activity/PowerData-AI-shanghai-4.19-14.png) + +为了打造一个真正活跃、开放、交流的开源活动,本次 PowerData 特地准备了最佳参会者奖状,将在活动当天针对存在突出表现的参会者,例如积极提问、主动讨论、提出见解、闪电演讲的同学,进行即时发放。 + + + +<<<  END >>> + +PowerData是由一群数据从业人员,因为热爱凝聚在一起,以开源精神为基础,组成的数据开源社区。 + +社区群内会定期组织模拟面试、线上分享、行业研讨、线下Meetup、城市聚会、求职内推等活动,同时在社区群内你可以进行技术讨论、问题请教,结实更多志同道合的数据朋友。 + diff --git a/src/zh/activity/README.md b/src/zh/activity/README.md index e556827ee8..bce7c51c80 100644 --- a/src/zh/activity/README.md +++ b/src/zh/activity/README.md @@ -1,33 +1,9 @@ ---- -title: 活动 -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - - - - - - +--- +title: 活动 +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +sidebar: false +layout: siteLayout +--- diff --git a/src/zh/activity/apache-iceberg-meetup2025.md b/src/zh/activity/apache-iceberg-meetup2025.md new file mode 100644 index 0000000000..09552174a0 --- /dev/null +++ b/src/zh/activity/apache-iceberg-meetup2025.md @@ -0,0 +1,38 @@ +--- +title: 国内首次 | Apache Iceberg Meetup 2025深圳站活动预告 +author: 2025年01月10日 08:33 +date: 2025-01-10 +cover: /assets/img/activity/apache-iceberg-meetup2025-0.png +head: + - - meta + - name: 活动 +--- + +# + +**活动简介** + + + +2024年是湖仓一体(Lakehouse)数据架构蓬勃发展的一年,越来越多的企业选择将数据湖作为统一的存储层,并在之上构建包括BI、AI在内的丰富的数据应用。Apache Iceberg 作为数据湖领域内炙手可热的开源项目在2024年同样取得显著的进步,社区主导的 Rest Catalog 变得越来越完善,Iceberg V3 格式也趋向成熟,包括 Snowflake, AWS,Apple,Databricks在内的公司都将 Iceberg 作为其首选的数据湖表格式,这无疑推动着 Iceberg 逐步成为数据湖表格式的标准。 + +在2025年的开年之际,我们将在美丽的深圳举办国内首场线下 Apache Iceberg Meetup。本次 meetup 由Apache Iceberg社区,腾讯云大数和AutoMQ 联合主办,以 Iceberg 上的最佳用户实践为主题,邀请了国内最资深的一批 Iceberg 用户&贡献者:腾讯云大数据、AutoMQ、Bilibili、微信、华为终端云来分享他们使用 Iceberg 构建湖仓一体数据架构的经验。Apache Iceberg 社区和合作伙伴诚邀 Iceberg 和各大社区的小伙伴、广大数据爱好者、架构师和企业代表参与。 + +● 主办单位:腾讯云大数据、AutoMQ + +● 活动时间:2025年1月18日 13:30-17:00 + +● 活动地址:深圳市腾讯滨海大厦3611 + +● 活动形式:线下为主,线上同步直播和转播 + + + +**活动议程** + + + +![](/assets/img/activity/apache-iceberg-meetup2025-0.png) + +# +**\- END -** \ No newline at end of file diff --git a/src/zh/activity/coscon23.md b/src/zh/activity/coscon23.md index b94545d7ef..c4face8377 100644 --- a/src/zh/activity/coscon23.md +++ b/src/zh/activity/coscon23.md @@ -1,45 +1,45 @@ ---- -title: COSCon'23媒体和社区合作伙伴正式公布!百川相聚,潮汇大海,邀您天府之城共话开源! -author: COSCon 组委会 -date: 2023-10-27 -cover: /assets/img/activity/coscon23-cover.jpg -head: - - - meta - - name: 活动 ---- - -一年一度的开源盛会,COSCon'23 第八届中国开源年会,将于 10 月 28~29 日,在四川成都市高新区菁蓉汇召开!本次大会的主题是:“开源:川流不息、山海相映”! - -如往年一样,作为中国最大的非营利、厂商中立、公益性质的社区联盟,开源社在每一届中国开源年会中,都希望尽可能地连接更多的社区合作伙伴,今年,我们也诚挚地邀请所有在中国活跃的开源技术社区伙伴们加入我们的年会社区和媒体的合作方。 - -自 9 月份启动媒体和社区合作招募以来,我们很高兴看到中国开源社区依然保持着高强度的活跃,非常愿意参与到第八届中国开源年会中,截止 2023 年 10 月 25 日,我们共有**82 家开源技术社区**、**组织和 9 家合作媒体**正式确认参与到中国开源年会的合作中。 - -其中,参与 COSCon'23 第八届中国开源年的合作媒体有: - - - -我们要衷心地感谢各位媒体朋友,你们是我们的宣传推手,正是因为你们的专业报道和宣传,历届开源年会得以广泛传播,吸引了更多人的关注和热情参与。你们的支持,让我们的本次年会得以更广泛地传播! - -参与本次 COSCon'23 第八届中国开源年的合作社区有: -![](/assets/img/activity/coscon23-2.png) -![](/assets/img/activity/coscon23-3.png) -![](/assets/img/activity/coscon23-4.png) -![](/assets/img/activity/coscon23-5.png) - -开源社作为中国最大的非营利的开源社区联盟组织,从“社区的社区”再到“开源人的家”,我们一直希望有机会来感谢那些一直支持、贡献和积极参与中国开源社区建设的各位社区伙伴。你们是中国开源运动的中流砥柱,是创新和进步的推动者。你们无私分享知识、提供支持、编写代码,以及与社区分享宝贵的反馈,这一切都构成了开源社区的精髓。正是有了你们的坚韧奋斗,我们才能够见证开源项目不断成长和繁荣。**特别的,我们要向本次参与中国开源年会合作的社区合作伙伴们致以最诚挚的谢意:感谢各位社区伙伴,感谢你们的付出和支持!在第八届中国开源年会,让我们一起庆祝中国开源人的盛宴!** - -COSCon'23 第八届中国开源年会正在火热报名中,如果您也想与各位社区伙伴一同探讨最前沿的开源技术趋势,分享最佳实践,共同助力开源理念的传播和深入发展,欢迎大家报名参与!10 月 28 日至 10 月 29 日,我们在成都菁蓉汇不见不散! - -作者丨李明康(小明) - -出品 | COSCon'23 组委会 - -编辑丨乌日力嘎 - -**开源社/KAIYUANSHE** - -开源社(英文名称为“KAIYUANSHE”)成立于 2014 年,是由志愿贡献于开源事业的个人志愿者,依 “贡献、共识、共治” **原则**所组成的开源社区。开源社始终维持 “厂商中立、公益、非营利” 的**理念**,以 “立足中国、贡献全球,推动开源成为新时代的生活方式” 为**愿景**,以 “开源治理、国际接轨、社区发展、项目孵化” 为**使命**,旨在共创健康可持续发展的开源生态体系。 - -开源社积极与支持开源的社区、高校、企业以及政府相关单位紧密合作,同时也是全球开源协议认证组织 - OSI 在中国的首个成员。 - -自 2016 年起连续举办中国开源年会(COSCon),持续发布《中国开源年度报告》,联合发起了“中国开源先锋榜”、“中国开源码力榜”等,在海内外产生了广泛的影响力。 +--- +title: COSCon'23媒体和社区合作伙伴正式公布!百川相聚,潮汇大海,邀您天府之城共话开源! +author: COSCon 组委会 +date: 2023-10-27 +cover: /assets/img/activity/coscon23-cover.jpg +head: + - - meta + - name: 活动 +--- + +一年一度的开源盛会,COSCon'23 第八届中国开源年会,将于 10 月 28~29 日,在四川成都市高新区菁蓉汇召开!本次大会的主题是:“开源:川流不息、山海相映”! + +如往年一样,作为中国最大的非营利、厂商中立、公益性质的社区联盟,开源社在每一届中国开源年会中,都希望尽可能地连接更多的社区合作伙伴,今年,我们也诚挚地邀请所有在中国活跃的开源技术社区伙伴们加入我们的年会社区和媒体的合作方。 + +自 9 月份启动媒体和社区合作招募以来,我们很高兴看到中国开源社区依然保持着高强度的活跃,非常愿意参与到第八届中国开源年会中,截止 2023 年 10 月 25 日,我们共有**82 家开源技术社区**、**组织和 9 家合作媒体**正式确认参与到中国开源年会的合作中。 + +其中,参与 COSCon'23 第八届中国开源年的合作媒体有: + + + +我们要衷心地感谢各位媒体朋友,你们是我们的宣传推手,正是因为你们的专业报道和宣传,历届开源年会得以广泛传播,吸引了更多人的关注和热情参与。你们的支持,让我们的本次年会得以更广泛地传播! + +参与本次 COSCon'23 第八届中国开源年的合作社区有: +![](/assets/img/activity/coscon23-2.png) +![](/assets/img/activity/coscon23-3.png) +![](/assets/img/activity/coscon23-4.png) +![](/assets/img/activity/coscon23-5.png) + +开源社作为中国最大的非营利的开源社区联盟组织,从“社区的社区”再到“开源人的家”,我们一直希望有机会来感谢那些一直支持、贡献和积极参与中国开源社区建设的各位社区伙伴。你们是中国开源运动的中流砥柱,是创新和进步的推动者。你们无私分享知识、提供支持、编写代码,以及与社区分享宝贵的反馈,这一切都构成了开源社区的精髓。正是有了你们的坚韧奋斗,我们才能够见证开源项目不断成长和繁荣。**特别的,我们要向本次参与中国开源年会合作的社区合作伙伴们致以最诚挚的谢意:感谢各位社区伙伴,感谢你们的付出和支持!在第八届中国开源年会,让我们一起庆祝中国开源人的盛宴!** + +COSCon'23 第八届中国开源年会正在火热报名中,如果您也想与各位社区伙伴一同探讨最前沿的开源技术趋势,分享最佳实践,共同助力开源理念的传播和深入发展,欢迎大家报名参与!10 月 28 日至 10 月 29 日,我们在成都菁蓉汇不见不散! + +作者丨李明康(小明) + +出品 | COSCon'23 组委会 + +编辑丨乌日力嘎 + +**开源社/KAIYUANSHE** + +开源社(英文名称为“KAIYUANSHE”)成立于 2014 年,是由志愿贡献于开源事业的个人志愿者,依 “贡献、共识、共治” **原则**所组成的开源社区。开源社始终维持 “厂商中立、公益、非营利” 的**理念**,以 “立足中国、贡献全球,推动开源成为新时代的生活方式” 为**愿景**,以 “开源治理、国际接轨、社区发展、项目孵化” 为**使命**,旨在共创健康可持续发展的开源生态体系。 + +开源社积极与支持开源的社区、高校、企业以及政府相关单位紧密合作,同时也是全球开源协议认证组织 - OSI 在中国的首个成员。 + +自 2016 年起连续举办中国开源年会(COSCon),持续发布《中国开源年度报告》,联合发起了“中国开源先锋榜”、“中国开源码力榜”等,在海内外产生了广泛的影响力。 diff --git a/src/zh/activity/coscon24-forum-intro.md b/src/zh/activity/coscon24-forum-intro.md new file mode 100644 index 0000000000..bca91407ef --- /dev/null +++ b/src/zh/activity/coscon24-forum-intro.md @@ -0,0 +1,209 @@ +--- +title: 主论坛抢鲜看!Open Source, Open Life 开源新生活尽在 COSCon'24 +author: 2024年10月29日 12:36 +date: 2024-10-29 +cover: /assets/img/activity/coscon24-forum-intro-1.webp +head: + - - meta + - name: 活动 +--- + + + +![](/assets/img/activity/coscon24-forum-intro-0.webp) + +![](/assets/img/activity/coscon24-forum-intro-1.webp) + + + +我们诚挚邀请您来参加**第九届中国开源年会** **COSCon'24**,共襄盛举,见证开源如何改变生活、塑造未来!今年的主论坛以**“Open Source, Open Life****/ 开源新生活****”**为主题,通过丰富多彩的分享与讨论,展示开源技术如何融入到我们日常的方方面面。 + + + +**报名方式:**扫描下方二维码或复制以下链接到浏览器报名 + + + + +**报名链接:** + +**https://www.bagevent.com/event/coscon24** + + + +**1** + + +**第一天:** + +**开源新生活 - 分享故事的舞台** + + + +大会第一天的主题是“开源新生活”,我们的主题将围绕个人的开源故事展开,在分享一个一个的开源故事的过程中,也希望能够激发更多的朋友——“我也可以成为开源人!” + + + +**开幕式与欢迎致辞(9:00~9:20)** + + + +让我们一起揭开 COSCon 2024 的序幕,开源社理事长江波,开源社联合创始人、理事刘天栋 Ted 将带来对开源新生活的前瞻性分享。开源不仅仅是代码,它改变了我们的思维方式、协作方式,也深刻影响了生活的方方面面。 + + + +**特邀嘉宾演讲 (9:20~9:40)** + + + +作为本次大会的白金赞助伙伴,来自浪潮 KaiwuDB 的 CTO 魏可伟,将为我们带来《KWDB:踏上全新的开源之旅》。 + + + +**主题演讲+对谈:** + +**开源改变生活的故事(9:40~11:40)** + + + +四位来自不同行业的嘉宾将分享他们与开源的不解之缘:从退休后成为 B 站知名 UP 主,到开源自己的运动数据,再到通过开源改变医疗健康,甚至与开源共同塑造的科技新生活——每一个故事都闪耀着开源的力量。演讲过程的嘉宾现场对谈,碰撞思想,也是本次大会的创新点之一。 + + + +**闪电演讲:** + +**开源改变个人生活的瞬间(11:40~12:20)** + + + +8位来自不同背景的开源爱好者将用5分钟的闪电演讲,分享开源如何改变了他们的职业发展或生活方式。无论你是学生、职场人,还是业余爱好者,这些精彩的开源故事都将激发你对未来的无限想象!【名额有限,报名从速!详见今日次条推送】 + + + +**2** + + +**第二天:** + +**开放新未来 - 探索与愿景****的碰撞** + +大会第二天的主题是“开放新未来”,开源已经并且还将不断的改变着整个世界,我们的主题将围绕着开源技术的发展,以及由此引发的诸多变化展开。另一方面,在开源社成立十周年的生日之际,我们也将和所有的新老朋友一起,回顾与庆祝这了不起的十年,大家一起畅想未来。 + + + +**开源社新十年展望(9:00~9:20)** + + + +开源社理事长江波将为我们展望开源社的未来十年,探讨开源在中国乃至全球的最新趋势。一起迈入开源的下一个黄金十年! + + + +**重磅发布:** + +**中国开源年度报告(9:20~9:40)** + + + +年度最受期待的《开源年度报告》即将在现场发布!华东师范大学的王伟教授将为大家解读中国开源生态的最新数据与发展趋势。 + + + +**圆桌讨论:** + +**开源与人工智能的未来(9:40~10:20)** + + + +人工智能的浪潮下,开源的力量如何推动技术变革?顶尖 AI 开源项目负责人齐聚一堂,探讨开源与AI技术的共赢未来。 + + + +**圆桌讨论:** + +**开源教育的力量(10:20~10:50)** + + + +开源不仅在技术领域引领潮流,也在教育领域带来变革。让我们一起聆听专家们分享开源教育的创新模式,共同探讨如何培养下一代开源人才。 + + + +**圆桌讨论:** + +**开源的社会责任(10:50~11:20)** + + + +开源不仅是一种技术,更是一种推动社会进步的力量。由开源社发起征集的《OpenGood 开源公益案例集》将于本次大会上进行发布。随后,来自企业、社区、公益组织的代表将分享开源在解决社会问题中的实际案例,探讨如何更好地履行开源社区的社会责任。 + + + +**圆桌讨论:** + +**我与开源社的十年之旅(11:20~12:00)** + + + +回顾过去十年,开源社如何从一个初创社区成长为如今的开源标杆?创始人、核心成员和资深粉丝将讲述他们与开源社共同成长的故事。 + + + +**特别环节:** + +**开源社十年故事墙(12:00~12:30)** + + + +在这个特别的环节,您将有机会上台分享与开源社的故事,见证“开源社十年故事墙”的诞生。让我们一起见证开源对个人和社区的深远影响!当然,现场还将有蛋糕和大合影,留住这历史性的时刻! + + + +**COSCon'24 不仅是一场技术交流的盛宴,更是一场开源人的狂欢!**无论你是开源的资深玩家,还是刚刚入门的新人,都能在这里找到共鸣和灵感。快来现场参与这场开源界的年度盛会,与志同道合的朋友们一同畅想开源的未来,体验开源新生活带来的无穷魅力! + + + +**您可以点击文末阅读原文购票报名。11月2-3日,北京见!我们不见不散!** + + + +出品丨COSCon'24 组委会 + +编辑丨月饼 + +设计丨王军 + + + + +**相关阅读 | Related Reading** + +[论坛介绍 | COSCon'24 AI for Science 方向的开源 AI 论坛](http://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247539499&idx=1&sn=9f703834ad629cf93cab40f87aafc6fc&chksm=9fdad015a8ad59034d5c27731fb83ff1d9d4c11b89d085a8b6a93e084f6d97e591a5043529e2&scene=21#wechat_redirect) + +[COSCon'24 志愿者招募令:共创开源新生活!](http://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247539207&idx=1&sn=a9b09c66f440506fc898cc78113aa0da&chksm=9fdad139a8ad582f3d2a190e9ae65d9c73041483191065c2b069161a7d249217ef4b137889be&scene=21#wechat_redirect) + + + + + +**开源社** + +_**KAIYUANSHE**_ + + + +![](/assets/img/activity/coscon24-forum-intro-9.webp) + +开源社(英文名称为“KAIYUANSHE”)成立于 2014 年,是由志愿贡献于开源事业的个人志愿者,依 “贡献、共识、共治” **原则**所组成的开源社区。开源社始终维持 “厂商中立、公益、非营利” 的**理念**,以 “立足中国、贡献全球,推动开源成为新时代的生活方式” 为**愿景**,以 “开源治理、国际接轨、社区发展、项目孵化” 为**使命**,旨在共创健康可持续发展的开源生态体系。 + + + +开源社积极与支持开源的社区、高校、企业以及政府相关单位紧密合作,同时也是全球开源协议认证组织 - OSI 在中国的首个成员。 + + + +自2016年起连续举办中国开源年会(COSCon),持续发布《中国开源年度报告》,联合发起了“中国开源先锋榜”、“中国开源码力榜”等,在海内外产生了广泛的影响力。 + + + +COSCon'2429 \ No newline at end of file diff --git a/src/zh/activity/dev-ai-frontier-meet.md b/src/zh/activity/dev-ai-frontier-meet.md new file mode 100644 index 0000000000..dbb395825f --- /dev/null +++ b/src/zh/activity/dev-ai-frontier-meet.md @@ -0,0 +1,25 @@ +--- +title: '@程序员们|GitCode AI探索日,与行业大咖面对面的机会来了...' +author: GitCode开源探索 +tag: + - Dromara +date: 2024-10-10 +cover: /assets/img/activity/dev-ai-frontier-meet-0.gif +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/dev-ai-frontier-meet-0.gif) + +AI技术正引领科技革命,推动各行各业转型升级。为深入探讨AI前沿,促进开发者交流,在1024程序员节即将到来之际,GitCode将携手松山湖开发者村,在风景如画的松山湖欧洲小镇共同举办一场特别的活动。**10月20日**,AI未来探索日,正式开放! + + + +活动特别邀请了多位在AI领域具有深厚造诣的嘉宾,他们将带来关于大模型应用、AGI新纪元破局、AI辅助编码等前沿话题的精彩分享,更有GitCode AI算法工程师,带您深度探索**GitCode AI社区**,活动特别安排了抽奖环节,让开发者们有机会在交流中获得意外的惊喜和收获。 + + + +欢迎加入我们,共同开启一段关于AI未来的深度对话和探索之旅~ + +![](/assets/img/activity/dev-ai-frontier-meet-1.webp)![](/assets/img/activity/dev-ai-frontier-meet-2.webp) \ No newline at end of file diff --git a/src/zh/activity/dromara-COSCon-letter.md b/src/zh/activity/dromara-COSCon-letter.md new file mode 100644 index 0000000000..b04d897f27 --- /dev/null +++ b/src/zh/activity/dromara-COSCon-letter.md @@ -0,0 +1,59 @@ +--- +title: Dromara 开源组织参与 COSCon 的一封信 +author: dromara秘书处 +tag: + - Dromara + - COSCon +date: 2024-09-27 +cover: /assets/img/activity/dromara-COSCon-letter-0.png +head: + - - meta + - name: 活动 +--- + + +**关于** + + COSCon + +![](/assets/img/activity/dromara-COSCon-letter-1.png) + +中国开源年会(COSCon)是业界最具影响力的开源盛会之一,自2015年由开源社首次发起以来,今年即将迎来第九届。凭借其独特定位及不断增长的影响力,COSCon 不仅获得了越来越多国内外企业、高校以及开源组织和社区的鼎力支持,而且与一般由企业、IT 媒体或行业协会举办的行业大会相比,它拥有更广泛的跨组织、跨项目、跨社区的覆盖范围,因此成功吸引了大量来自全球的开源开发者、贡献者和提交者的高度关注与积极参与。 + + + +**地点** + + +![](/assets/img/activity/dromara-COSCon-letter-3.png) + + +北京中关村国家自主创新示范区会议中心 + + + + +**开源的热爱在深秋** + +dromara + +![](/assets/img/activity/dromara-COSCon-letter-6.png) + +Dromara 开源组织数十位开源作者将参与此次活动,有 80,90 后这种开源的老家伙,也有 00,10 后的开源新星作者参与,希望和关注 dromara 开源组织的各位开发者,企业单位的领导,一起在线下进行面基,充分的沟通和交流,擦出中国开源氛围的火花。 + + + +**信息收集** + + +![](/assets/img/activity/dromara-COSCon-letter-8.png) + +![](/assets/img/activity/dromara-COSCon-letter-9.png) + +希望能和大家在线下见面,有参与的小伙伴可以填写下个人信息,我们将根据人数多少为大家选定会场,定制dromara 开源周边礼物。 + + + +信息填写地址:https://docs.qq.com/sheet/DSmNjU1dma3JEVFRk?is\_no\_hook\_redirect=1&tab=BB08J2 + +![](/assets/img/activity/dromara-COSCon-letter-10.png) \ No newline at end of file diff --git a/src/zh/activity/dromara-activites-introduce.md b/src/zh/activity/dromara-activites-introduce.md index 80d76fb819..c34806b28b 100644 --- a/src/zh/activity/dromara-activites-introduce.md +++ b/src/zh/activity/dromara-activites-introduce.md @@ -1,47 +1,47 @@ ---- -title: Dromara 梦码读书会介绍 -author: xiaoyu -date: 2020-12-27 -tag: - - DreamCode - - Dromara - - GateWay -cover: /assets/img/activity/dromara-open-soul-01.jpg -head: - - - meta - - name: 活动 ---- - -![Dromara 梦码读书会介绍](/assets/img/activite/soul-xmind.png) - -### Dromara 梦码读书会(Dromara 2020 活动介绍) - -- 日期:2020 年 12 月 27 日,星期日 - -### 活动背景 - -- 为了提高社区参与者的积极性, 促进 Dromara 社区的建设, 锻炼大家的表达能力和提升技术核心力量, 社区以源码阅读形式自发行的组织本次活动 - -### 活动目的,意义和目标 - -- 提高积极性 -- 提升技术力量,扩展大家视野 -- 锻炼语言表达能力 -- 促进社区的和谐、团结、共进 -- 将 Dromara 社区做的越来越大 - -### 活动开展 - -- 活动分多期,先是挑选十二位组员进行为期 12 天的源码阅读,期间进行两次线上分享 -- 为了提高大家的自觉性我们设立了惩罚制度,先交出 500 元/人给管理员,隔天的早上 8 点作业未提交,分享迟到者扣 100 元/次,提前请假者无需惩罚 -- 每人根据每天阅读的内容,以文字形式写到各自的作业提交区 - -### 活动负责人以及主要参与者 - -#### 负责人 - -- 崔,kimming,猫大人 - -#### 主要参与者 - -- Dromara 社区组员 +--- +title: Dromara 梦码读书会介绍 +author: xiaoyu +date: 2020-12-27 +tag: + - DreamCode + - Dromara + - GateWay +cover: /assets/img/activity/dromara-open-soul-01.jpg +head: + - - meta + - name: 活动 +--- + +![Dromara 梦码读书会介绍](/assets/img/activite/soul-xmind.png) + +### Dromara 梦码读书会(Dromara 2020 活动介绍) + +- 日期:2020 年 12 月 27 日,星期日 + +### 活动背景 + +- 为了提高社区参与者的积极性, 促进 Dromara 社区的建设, 锻炼大家的表达能力和提升技术核心力量, 社区以源码阅读形式自发行的组织本次活动 + +### 活动目的,意义和目标 + +- 提高积极性 +- 提升技术力量,扩展大家视野 +- 锻炼语言表达能力 +- 促进社区的和谐、团结、共进 +- 将 Dromara 社区做的越来越大 + +### 活动开展 + +- 活动分多期,先是挑选十二位组员进行为期 12 天的源码阅读,期间进行两次线上分享 +- 为了提高大家的自觉性我们设立了惩罚制度,先交出 500 元/人给管理员,隔天的早上 8 点作业未提交,分享迟到者扣 100 元/次,提前请假者无需惩罚 +- 每人根据每天阅读的内容,以文字形式写到各自的作业提交区 + +### 活动负责人以及主要参与者 + +#### 负责人 + +- 崔,kimming,猫大人 + +#### 主要参与者 + +- Dromara 社区组员 diff --git a/src/zh/activity/dromara-cloud-native-meet-02.md b/src/zh/activity/dromara-cloud-native-meet-02.md index 2395076315..c10a4e967c 100644 --- a/src/zh/activity/dromara-cloud-native-meet-02.md +++ b/src/zh/activity/dromara-cloud-native-meet-02.md @@ -1,43 +1,43 @@ ---- -title: Dromara Soul 源码01期阅读分享会02 -author: xiaoyu -date: 2021-02-06 -tag: - - Soul - - Dromara - - Reactor -cover: /assets/img/activity/dromara-open-soul-03.jpg -head: - - - meta - - name: 活动 ---- - -![Dromara 线上活动](/assets/img/activite/soul-xmind.png) - -### Dromara 源码阅读(Soul 2021 首次活动) - -- 日期:2021 年 2 月 6 日,星期日 -- 时间:20:00 – 23:00 -- 地点:线上腾讯会议室 - -### 活动详情 - -**20:00 - 20:10 开场介绍近期梦码分享情况 by kimming & 崔** - -**20:10 - 20:25 [SPI 介绍以及 Soul SPI 如何增强实现](https://blog.csdn.net/zm469568595/article/details/113362044) by 朱明** - -**20:25 - 20:50 [响应式编程介绍](https://zhoutzzz.com/archives/xiang-ying-shi-bian-cheng-reactiveprogramming) by Ztzzz** - -**20:50 - 21:10 [Soul 单测小结](https://www.yuque.com/docs/share/27992671-8d47-4bba-b2dc-c0e39074d649?#) by 阿行** - -**21:10 - 21:25 [容错设计](http://icyfenix.cn/distribution/traffic-management/failure.html) by 蒋文博** - -**21:25 - 21:40 [Soul WebFlux 加载流程以及处理请求分析](https://blog.csdn.net/u012180773?t=1) by rwby** - -**21:40 - 21:55 [Soul 限流和熔断分析](https://redick01.github.io/redick.github.io/#/blog/sourcecode/soul/soul_19) by 刘鹏辉** - -**21:55 - 22:05 Java 常见问题总结 by 木偶** - -**22:05 - 22:20 如何打开社交面 by 伟楷** - -**22:20 - 22:30 Soul 作者 猫大人 总结与 展望 by 猫大人** +--- +title: Dromara Soul 源码01期阅读分享会02 +author: xiaoyu +date: 2021-02-06 +tag: + - Soul + - Dromara + - Reactor +cover: /assets/img/activity/dromara-open-soul-03.jpg +head: + - - meta + - name: 活动 +--- + +![Dromara 线上活动](/assets/img/activite/soul-xmind.png) + +### Dromara 源码阅读(Soul 2021 首次活动) + +- 日期:2021 年 2 月 6 日,星期日 +- 时间:20:00 – 23:00 +- 地点:线上腾讯会议室 + +### 活动详情 + +**20:00 - 20:10 开场介绍近期梦码分享情况 by kimming & 崔** + +**20:10 - 20:25 [SPI 介绍以及 Soul SPI 如何增强实现](https://blog.csdn.net/zm469568595/article/details/113362044) by 朱明** + +**20:25 - 20:50 [响应式编程介绍](https://zhoutzzz.com/archives/xiang-ying-shi-bian-cheng-reactiveprogramming) by Ztzzz** + +**20:50 - 21:10 [Soul 单测小结](https://www.yuque.com/docs/share/27992671-8d47-4bba-b2dc-c0e39074d649?#) by 阿行** + +**21:10 - 21:25 [容错设计](http://icyfenix.cn/distribution/traffic-management/failure.html) by 蒋文博** + +**21:25 - 21:40 [Soul WebFlux 加载流程以及处理请求分析](https://blog.csdn.net/u012180773?t=1) by rwby** + +**21:40 - 21:55 [Soul 限流和熔断分析](https://redick01.github.io/redick.github.io/#/blog/sourcecode/soul/soul_19) by 刘鹏辉** + +**21:55 - 22:05 Java 常见问题总结 by 木偶** + +**22:05 - 22:20 如何打开社交面 by 伟楷** + +**22:20 - 22:30 Soul 作者 猫大人 总结与 展望 by 猫大人** diff --git a/src/zh/activity/dromara-cloud-native-meet.md b/src/zh/activity/dromara-cloud-native-meet.md index 8b9211ab94..381877ea44 100644 --- a/src/zh/activity/dromara-cloud-native-meet.md +++ b/src/zh/activity/dromara-cloud-native-meet.md @@ -1,43 +1,43 @@ ---- -title: Dromara Soul源码01期阅读分享会01 -author: xiaoyu -date: 2021-01-21 -tag: - - Soul - - Dromara - - Reactor -cover: /assets/img/activity/dromara-open-soul-02.jpg -head: - - - meta - - name: 活动 ---- - -![Dromara 线上活动](/assets/img/activite/soul-xmind.png) - -### Dromara 源码阅读(Soul 2021 首次活动) - -- 日期:2021 年 1 月 24 日,星期日 -- 时间:15:00 – 17:00 -- 地点:线上腾讯会议室 - -### 活动详情 - -**15:00 - 15:10 开场介绍梦码分享流程 by kimming & 崔** - -**15:10 - 15:25 Soul 数据同步之 websocket by 婷** - -**15:25 - 15:50 Http 探活分享 by 朱明** - -**15:50 - 16:10 基于 Sofa-Rpc 协议的分析 by 东东** - -**16:10 - 16:25 Metrics 监控 by 葛天野** - -**16:25 - 16:40 Http 长轮询分享 by 杜宇航** - -**16:40 - 16:55 数据同步整体架构分享与介绍 by 夏文涛** - -**16:55 - 17:05 微内核架构分享 by 沈祥俊** - -**17:05 - 17:20 分享读源码的心得与感悟 by 金泽** - -**17:20 - 17:30 Soul 作者 猫大人 总结与 社区发展 展望 by 猫大人** +--- +title: Dromara Soul源码01期阅读分享会01 +author: xiaoyu +date: 2021-01-21 +tag: + - Soul + - Dromara + - Reactor +cover: /assets/img/activity/dromara-open-soul-02.jpg +head: + - - meta + - name: 活动 +--- + +![Dromara 线上活动](/assets/img/activite/soul-xmind.png) + +### Dromara 源码阅读(Soul 2021 首次活动) + +- 日期:2021 年 1 月 24 日,星期日 +- 时间:15:00 – 17:00 +- 地点:线上腾讯会议室 + +### 活动详情 + +**15:00 - 15:10 开场介绍梦码分享流程 by kimming & 崔** + +**15:10 - 15:25 Soul 数据同步之 websocket by 婷** + +**15:25 - 15:50 Http 探活分享 by 朱明** + +**15:50 - 16:10 基于 Sofa-Rpc 协议的分析 by 东东** + +**16:10 - 16:25 Metrics 监控 by 葛天野** + +**16:25 - 16:40 Http 长轮询分享 by 杜宇航** + +**16:40 - 16:55 数据同步整体架构分享与介绍 by 夏文涛** + +**16:55 - 17:05 微内核架构分享 by 沈祥俊** + +**17:05 - 17:20 分享读源码的心得与感悟 by 金泽** + +**17:20 - 17:30 Soul 作者 猫大人 总结与 社区发展 展望 by 猫大人** diff --git a/src/zh/activity/dromara-coscon-market.md b/src/zh/activity/dromara-coscon-market.md new file mode 100644 index 0000000000..9e2125c3a2 --- /dev/null +++ b/src/zh/activity/dromara-coscon-market.md @@ -0,0 +1,519 @@ +--- +title: COSCon'24开源集市正式亮相,喊你一起来集市摊位玩耍辣~ +author: 2024年11月02日 08:02 +date: 2024-11-02 +cover: /assets/img/activity/dromara-coscon-market-0.webp +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/dromara-coscon-market-0.webp) + + + +开源集市是本届年会的亮点环节,旨在搭建一个展示开源项目、促进开源社区、开源组织流合作的平台。在这里,您可以: + + + +* 展示优秀开源项目/活动,提升项目/社区/组织知名度; + + + +* 拓展合作伙伴,促进社区/项目/组织发展; + + + +* 交流开源心得,共谋开源事业发展; + + + +* 感受开源文化,共享开源盛宴。 + + + + + +**开源集市摊位** + + + +**KCC开源社城市社区** + + + +![](/assets/img/activity/dromara-coscon-market-3.webp) + + + +KCC 是开源社城市社区(Kaiyuanshe City Community)的简称,是由海内外各城市或区域认同开源社理念的开源爱好者组成,称为 “**KCC**”。每个城市或区域都可以申请成立 KCC。目前在**深圳、南京、上海、北京、成都、杭州、大连、广州**、新加坡、硅谷已经成立,预计未来会有更多的 KCC 成立。 + + + +**SegmentFault 思否 & Apache Answer** + + + +![](/assets/img/activity/dromara-coscon-market-4.webp) + +![](/assets/img/activity/dromara-coscon-market-5.webp) + + + +SegmentFault 思否,国内领先的技术问答社区。 + +Apache Answer,思否团队发起的开源问答软件。 + + + +**摊位活动**:互动周边发放。 + + + +**HyperAI 超神经** + + + +![](/assets/img/activity/dromara-coscon-market-6.webp) + + + +HyperAI 超神经 (hyper.ai) 是国内领先的开源人工智能及高性能计算社区,旨在通过提供数据集加速下载、在线教程演示、论文深度解读、顶会日历集成等多个服务,助力数据科学及⼈⼯智能⾏业的开发者、爱好者学习、理解、实践,与社区⼀起构建⼈⼯智能的未来。 + + + +**摊位活动**:互动抽奖。 + + + +**异步社区嘉年华** + + + +![](/assets/img/activity/dromara-coscon-market-7.webp) + + + +“异步社区”(www.epubit.com)是由人民邮电出版社创办的 IT 专业图书社区,于 2015 年 8 月上线运营,致力于优质内容的出版和分享,为读者提供高品质的学习内容,为作译者提供专业的出版服务,实现作者与读者在线交流互动,以及传统出版与数字出版的融合发展。 + + + +**摊位活动**: + +1. 抽奖赠书; + + + +2. 拼魔方复原赠书; + + + +3. 新书签售会 + + + + +**COSCUP开源人年会** + + + +![](/assets/img/activity/dromara-coscon-market-8.webp) + + + +2014 年,多個台灣開源社群共同發起開放文化基金會(Open Culture Foundation,簡稱 OCF),期望以法人組織的力量推廣開放文化,在產、官、學、民之間激起開放協作的火花。 + + + +**摊位活动**:介紹技術開源技術的講題趨勢及遊戲互動 + + + +**Apache IoTDB** + + + +![](/assets/img/activity/dromara-coscon-market-9.webp) + + + +Apache IoTDB(物联网数据库)是一体化收集、存储、管理与分析物联网时序数据的软件系统。Apache IoTDB 采用轻量式架构,具有高性能和丰富的功能。 + + + +**摊位互动**:互动抽奖。 + + + +**SQLE(爱可生开源社区)** + + + +![](/assets/img/activity/dromara-coscon-market-10.webp) + + + +一个有深度的 MySQL 开源社区。社区成立于 2017 年,以开源高质量的运维工具、日常分享技术干货内容、数据库技术布道为己任;目前开源的产品有:SQL 审核工具 SQLE、分布式中间件 DBLE 和数据传输组件 DTLE。 + + + +**摊位活动:**互动抽奖 + + + +**PowerData数据之力社区** + + + +![](/assets/img/activity/dromara-coscon-market-11.png) + +我们是由一群数据从业人员,因为热爱凝聚在一起,以开源精神为基础,组成的PowerData数据之力社区。 + + + +**摊位活动:**互动抽奖 + + + +**Dromara开源社区面对面** + + + +![](/assets/img/activity/dromara-coscon-market-12.png) + + + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +**摊位活动**: + +1. 互动答题:参展者到摊位,答对题目可领取Dromara周边 + + + +2. 一起动手操作:参展者到展,可选择动手写代码,体验动手操作的乐趣 + + + + +**魔搭ModelScope开源社区** + + + +![](/assets/img/activity/dromara-coscon-market-13.png) + + + +ModelScope 社区成立于 2022 年 6 月,是一个模型开源社区及创新平台。 + + + +**摊位活动**: + +展现ModelScope的开源历程,合作伙伴墙,以及社区体验互动 + + + +**deepin** + + + +![](/assets/img/activity/dromara-coscon-market-14.png) + + + +深度开源社区是一个桌面操作系统根社区,也是中国活跃的桌面操作系统社区——致力于通过社区开发与协作,为所有人提供自由、开放的交流平台以及最好的开源操作系统。目前国内论坛注册用户12.7万,总发帖量已破100万,代码贡献参与者已达2000人。 + + + +**摊位互动**: + +设置相应闯关任务,用户完成任务后可集章,按章数领取不同周边礼物。 + + + +关卡1:基础开源通识知识问答 + +描述:如 Linux 操作系统是由谁开发、列举一些流行的Linux发行版、列举几种常见的开源许可证等 + + + +关卡2:deepin-IDE 基础代码挑战 + +描述:完成简单的编程任务,如排序算法、基本的数据处理 + + + +关卡3:deepin 系统互动体验 + +描述:现场体验 deepin 23,并通过体验留下您的产品反馈及建议 + + + +**Rainbond** + + + +![](/assets/img/activity/dromara-coscon-market-15.png) + + + +Rainbond 是一个云原生应用管理平台,核心100%开源,使用简单,不需要懂容器和Kubernetes,支持管理多种Kubernetes集群,提供企业级应用的全生命周期管理。 + + + +**摊位活动**: + +1\. 互动赠送社区贴纸,例如关注微信公众号、Github Star等 + +2\. 深入互动赠送帆布袋或者笔记本 + + + +**班夫中国** + + + +![](/assets/img/activity/dromara-coscon-market-16.webp) + + + +班夫山地电影节始于1976年,是全球最具权威、规模最大的户外探险、山地文化及环境主题的电影节。它被誉为户外“奥斯卡”。而我们公司,班夫(北京)文化传播有限公司成立于2010年,2019年引进《徒手攀岩》,该片同年获得奥斯卡最佳长纪录片奖,成就了中国纪录片票房纪录的第一。今年班夫中国携手开源社共创真·黑客马拉松环节,为推进开源成为一种生活方式贡献一份力量。 + + + +**摊位活动**:运动知识科普,周边互动。 + + + +**智能机器人互动体验** + + + +![](/assets/img/activity/dromara-coscon-market-17.png) + +在COSCon'24开源集市的展示摊位,我们将带来基于智能机器人技术的互动体验,参会者可以亲身感受智能机器人如何通过手势控制和人体跟随技术来实现对实体和虚拟小车的智能控制。展示内容包括完全开源的实体小车项目,从电路设计到硬件实现,参会者可以深入了解其工作原理。同时,虚拟小车的展示将同步开放源码,方便参会者探索和调整控制方案。 + + + +**摊位互动**: + +1. 手势控制与人体跟随竞速:参会者可以通过手势或人体跟随控制两台实体小车进行竞速比拼,体验智能控制技术的乐趣。 + + + +2. 路径绘制互动:利用手势控制虚拟小车绘制路径,提供源码接口,可以现场调整,完成任务的参会者将获得精美礼物。 + + + +3. 算法问答环节:关于智能机器人技术和算法的问答挑战,答对者同样有机会赢取礼品。 + + + +4. 通过这些有趣的互动活动,参会者不仅能体验智能控制,还可以进一步了解背后的技术原理。我们也提供了详细的源码和学习资料,供参会者在活动后参考。 + + + + +**Apache Doris 快问快答** + +![](/assets/img/activity/dromara-coscon-market-18.png) + + + +Apache Doris 是一款基于 MPP 的现代化、高性能、实时的分析型数据库。以极速易用的特点被人们所熟知,仅需亚秒级响应时间即可返回海量数据下的查询结果,不仅可以支持高并发的点查询场景,也能支持高吞吐的复杂分析场景。 + + + +**摊位互动**: + +自带 Apache Doris 宣传资料,包括项目介绍、核心特性、应用场景等,以随机提问五个问题的形式向观众提问,答对1、2-3、4-5个分别有对应周边礼品:贴纸、电脑支架、T恤等,每天数量有限,先到先得。 + + + +**openInula** + + + +![](/assets/img/activity/dromara-coscon-market-19.png) + + + +openInula是一款基于编译的响应式前端开发框架,开发者可使用熟悉的JSX语法,享受原生JavaScript的灵活性,同时有更高页面性能、更少内存占用和包体积。 + + + +**摊位互动**:展区可结合分论坛议题体验上机操作openInula API2.0,准备小礼品奖励开发者。 + + + +**Sermant开源社区** + + + +![](/assets/img/activity/dromara-coscon-market-20.png) + + + +Sermant是基于Java字节码增强技术的无代理的服务网格,其利用Java字节码增强技术为宿主应用程序提供服务治理功能,以解决大规模微服务场景中的服务治理问题。 + + + +**摊位互动:** + +* Sermant插件开发体验:基于Sermant官方提供的模板文件进行插件开发,动态实现Java微服务的字节码增强。 + + + +* 朋友圈分享Sermant技术布道文章或打卡的现场照片 + + + +* 现场项目介绍和答疑 + + + +* 现场参会者通过参与以上活动,可以获得Sermant社区提供的奖品,比如帆布包等 + + + + +**OpenFDE开源linux桌面** + + + +![](/assets/img/activity/dromara-coscon-market-21.png) + + + +OpenFDE是一款Linux开源桌面。不同的是,它基于AOSP图形栈,为安卓与Linux应用的提供一致的窗口图形环境。 + + + +**摊位互动**:开源知识问答:准备几个开源&社区相关的问题,答对有礼品。 + + + +**禅道项目管理软件开源版** + + + +![](/assets/img/activity/dromara-coscon-market-22.png) + + + +禅道是一款开源的全生命周期项目管理软件,基于敏捷和CMMI管理理念进行设计,集产品管理、项目管理、质量管理、文档管理、组织管理和事务管理于一体,完整覆盖研发项目管理的核心流程。 + + + +**摊位互动**:准备开源题目,通过参与者回答题目正确率来给予相应小礼物。 + + + +**OpenBuild** + + + +![](/assets/img/activity/dromara-coscon-market-23.png) + + + +OpenBuild 是一个面向 Web3 开发者的开源社区,致力于连接 Web2 和 Web3,帮助开发者过渡到去中心化网络。并通过开源协作的方式提供系统性的教育内容、开源工具和社区资源,帮助开发者构建声誉体系,创造商业机会。 + + + +**摊位互动**:互动抽奖。 + + + +**NebulaGraph** + +![](/assets/img/activity/dromara-coscon-market-24.webp) + + + +NebulaGraph 是一款开源的、分布式的、易扩展的原生图数据库,能够承载包含数千亿个点和数万亿条边的超大规模数据集,并且提供毫秒级查询。 + + + +**摊位互动**:根据易拉宝和公众号内容,回答关于NebulaGraph的基础问题,正确可获得任意周边,若不正确,可获得小黄鸭一只。 + + + +**AIGCOPEN 社区 x 微软 Reactor** + + + +![](/assets/img/activity/dromara-coscon-market-25.png) + +![](/assets/img/activity/dromara-coscon-market-26.png) + + + +AIGC旨在创造中国AIGC生态落地。 + + + +**摊位互动**:在现场展示微软 Reactor 开发者社区活动,以及微软 AI 开发者认证的指引宣传单页,最新的 OpenAI 中文版白皮书引导下载,AIGCOPEN 社区的免费学习资料发放 + + + +**隐语SecretFlow** + +![](/assets/img/activity/dromara-coscon-market-27.webp) + + + +SecretFlow 是一个隐私保护数据分析和机器学习的开源统一框架。 + + + +**摊位互动**:隐私计算快问快答——隐语开源社区发起的「隐语·隐私计算快问快答」活动,围绕隐私计算基础知识,展开快问快答,旨在对数据安全、隐私计算等相关知识的布道推广。 + + + +**IvorySQL开源社区** + + + + + +![](/assets/img/activity/dromara-coscon-market-28.webp) + + + +IvorySQL是由瀚高股份主导研发的一款开源的兼容Oracle的PostgreSQL,致力于为企业和开发者提供一个高性能、可扩展和安全的Oracle迁移最佳解决方案。作为国际性开源项目,目前IvorySQL中国社区成员增加至2000+,完成提交超过270个Commit,开源代码贡献量超900000+行。 + + + +**摊位互动**: + +参与者只需参与抽奖飞镖和社区互动,便有机会参与激动人心的抽奖活动,赢取丰厚的奖品。 + + + +**Apollo开发者社区** + + + +![](/assets/img/activity/dromara-coscon-market-29.png) + + + +Apollo开发者社区致力于为全球自动驾驶开发者和合作伙伴提供的一个学习、交流的平台,助力开发者快速了解并使用自动驾驶技术。 + + + +**摊位互动**:自动驾驶技术科普互动;周边互动。 + + + +以上就是本次开源集市的摊位,欢迎您的到来~ + + + +![](/assets/img/activity/dromara-coscon-market-30.png) + +![](/assets/img/activity/dromara-coscon-market-31.webp) \ No newline at end of file diff --git a/src/zh/activity/dromara-coscon-showcase.md b/src/zh/activity/dromara-coscon-showcase.md new file mode 100644 index 0000000000..8faad91069 --- /dev/null +++ b/src/zh/activity/dromara-coscon-showcase.md @@ -0,0 +1,103 @@ +--- +title: Dromara 亮相 COSCon'24 中国开源年会,共话国产开源未来 +author: achao +tag: + - Dromara + - COSCon +date: 2024-10-17 +cover: /assets/img/activity/dromara-coscon-showcase-0.jpg +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/dromara-coscon-showcase-0.jpg) + +**时间**:2024 年 11 月 2-3 日 + +**地点**:北京·中关村国家自主创新示范区展示中心 + +由开源社主办的 COSCon'24 中国开源年会暨开源社 10 周年嘉年华将在北京隆重举办。作为国内开源界的年度盛会,本次大会汇聚了大量开源爱好者、技术专家和行业领袖,共同探讨开源技术的未来发展。此次大会设有多个分论坛,其中中间件 / 微服务分论坛由 Dromara 开源组织出品,Dromara 社区将携多位成员带来精彩的技术分享,展示国产开源力量。 + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括流行工具,企业级认证,微服务,运维监控,调度编排,分布式等一系列开源产品。技术栈全面开源共建、 保持社区中立。让参与的每一位开源爱好者,体会到开源的快乐。社区目前拥有15+GVP项目,总star数量超过300K,构建了上万人的开源社区,成千上万的个人及团队在使用Dromara社区的开源项目。 + +本次 Dromara 社区至少有三位成员将在论坛上进行演讲,覆盖 AI、数据操作以及前端动画引擎等多个热门技术领域。与此同时,Dromara 社区还有许多其他开源项目的核心作者也将亲临现场,Dromara社区的线下聚会,与大家面对面交流,分享他们在各自领域的见解与经验,推动开源技术的发展。 + +### 1\. 李大鹏:国产影响力天花板的开源 Java AI 框架 EasyAi + +**演讲嘉宾**:李大鹏,Easy-Ai 作者,拥有多项 AI 算法发明专利,Dromara 开源社区项目委员会成员,山东威尔数据有限公司算法工程师 +李大鹏作为国产 AI 框架的推动者,致力于用 Java 构建开箱即用的 AI 解决方案,赋能企业级开发团队。他的工作不仅填补了 Java 在 AI 领域的空白,还为开发者提供了极大的便利与灵活性。本次他将带来精彩分享: + +**演讲主题**:《100% JAVA 代码的国产开源 AI 框架 EasyAi - 使用 Easy 框架,开发完全 JAVA 体系下的人工智能小微模型在企业业务中的巨大优势与作用》 + +**演讲时间**:2024 年 11 月 3 日 + +**简介**:Java 作为企业级开发的主力语言,一直以丰富的生态而著称,是移动互联网时代造就了它。但随着人工智能的时代即将到来,它却丧失了人工智能领域的生态。所以为了解决这个问题,EasyAi 应运而生,它的出现对于 Java 的意义,等同于在 JavaWeb 领域 spring 出现的意义一样 —— 做一个开箱即用,让每一个开发者都可以使用 EasyAi,来开发符合自己人工智能业务需求的小微模型,这就是它的使命!EasyAi 无任何依赖,它是一个原生 Java 人工智能算法框架。首先,它可以 Maven 一键丝滑引入我们的 Java 项目,无需任何额外的环境配置与依赖,做到开箱即用。再者,它既有一些我们已经封装好的图像目标检测及人工智能客服的模块,也提供各种深度学习,机器学习,强化学习,启发式学习,矩阵运算,求导函数,求偏导函数等底层算法工具。开发者可以通过简单的学习,就能完成根据自身业务,深度开发符合自己业务的小微模型。 + +**开源项目链接**: +Gitee 地址: +https://gitee.com/dromara/easyAi +项目荣誉:Gitee GVP项目、GitCode G-Star项目 + +![](/assets/img/activity/dromara-coscon-showcase-1.jpg) + +* * * + +### 2\. 阿超:Stream-Query 为 Javaer 解锁数据操作新方式 + +**演讲嘉宾**:阿超,00 后开源爱好者,Dromara 开源组织副秘书长,Gitee封面人物第33期,Stream-Query 作者,Apache StreamPark Committer、Apache ShenYu Committer、Dromara Hutool 成员、Baomidou Mybatis-Plus 成员,合十思维(北京)科技有限公司技术副总监 +阿超是一位极具创新精神的年轻开发者,他的贡献在多个开源项目中得到了广泛认可,尤其是在简化 Java 数据操作的领域。Stream-Query 这一工具的推出,使得 Java 开发者能够以更高效的方式进行数据查询,降低开发成本。本次他将分享: + +**演讲主题**:《从繁杂到简洁:Stream-Query 为 Javaer 解锁数据操作新方式》 + +**演讲时间**:2024 年 11 月 3 日  + +**简介**:Dromara Stream-Query 作为一款创新的工具类库,旨在简化数据查询流程,提升开发效率。它包括两个核心模块:Stream-Core 和 Stream-Plugin,现已增强了 MybatisPlus 功能,支持动态 Mapper 的实现,不再需要手动创建 Mapper 类,即可轻松完成 CRUD 操作。本次演讲将详细解读 Stream-Query 的核心功能、技术架构及其在实际项目中的应用,帮助 Java 开发者充分利用这一工具,提高工作效率。 + +**开源项目链接**: +Gitee 地址: +https://gitee.com/dromara/stream-query +项目荣誉:Gitee 推荐项目、GitCode G-Star项目、中国信通院可信开源社区共同体(TWOS)可信开源项目 + +![](/assets/img/activity/dromara-coscon-showcase-2.jpg) + +* * * + +### 3\. 刘陈阳:程序化动画与开源前端引擎的探索 + +**演讲嘉宾**:刘陈阳,05 后休学高一学生,BugDuck 团队创始人之一,Dromara 开源社区项目委员会成员,Newcar 动画引擎作者 + +刘陈阳作为年轻一代开源贡献者,凭借他在前端动画引擎领域的创造性开发工作,已成为开源社区中的耀眼新星。无论是 Newcar 还是 VueMotion,他都展现了深厚的技术功底与独到的见解。本次他将带来三个议题的分享: + +* • **议题 1:《从 Newcar 到 VueMotion 前端动画引擎的探索》** **简介**:介绍 Newcar 和 VueMotion 均是现代化的动画引擎。VueMotion 是一个基于 VueJs 的动画引擎,提供了多种多样的组件,类似于 Manim,不过比 Manim 应用范围更广。这个演讲将会讲述理念和开发故事以及未来展望。 + +* • **议题 2:《LLMVision: 程序化动画与大模型能力的结合》** + **简介**:介绍 LLMVision 是一个基于 VueMotion 的数学和统计学演示动画生成器。 + +* • **议题 3:《一个高中生与开源的四年》** + **简介**:我将会讲述我开源的经验以及我用开源和不公命运斗争的故事。 + + +**开源项目链接**: +Gitee 地址: +https://gitee.com/dromara/newcar +项目荣誉:Gitee 推荐项目 + +![](/assets/img/activity/dromara-coscon-showcase-3.jpg) + + + +* * * + +此次 COSCon'24 中国开源年会,Dromara 社区的分享将为 Java 开发者、AI 技术探索者及前端爱好者带来前沿的技术思考与实践经验。 + +更值得期待的是,Dromara 社区还有多个项目的核心作者将到场,现场与大家面对面交流,分享他们的开发历程与心得。这是一次难得的机会,开源爱好者们可以与这些优秀的开源贡献者深入探讨,共同推动国产开源生态的繁荣与发展! + +**活动详情链接**: +https://kaiyuanshe.cn/activity/COSCon-2024 + +**参会报名链接**: +https://www.bagevent.com/event/coscon24 + +**Dromara COSCon**: +https://coscon.dromara.org/ \ No newline at end of file diff --git a/src/zh/activity/dromara-coscon24-countdown.md b/src/zh/activity/dromara-coscon24-countdown.md new file mode 100644 index 0000000000..eb4c4ccadb --- /dev/null +++ b/src/zh/activity/dromara-coscon24-countdown.md @@ -0,0 +1,11 @@ +--- +title: 倒计时3天!我们在第九届中国开源年会暨开源社10周年嘉年华现场等你! +author: +date: 2024-10-31 +cover: /assets/img/activity/dromara-coscon24-countdown-0.jpg +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/dromara-coscon24-countdown-0.jpg) \ No newline at end of file diff --git a/src/zh/activity/dromara-coscon24-forum.md b/src/zh/activity/dromara-coscon24-forum.md new file mode 100644 index 0000000000..9cbbe90525 --- /dev/null +++ b/src/zh/activity/dromara-coscon24-forum.md @@ -0,0 +1,118 @@ +--- +title: 议程介绍|开源中间件/微服务分论坛 +author: COSCon'24 组委会 +date: 2024-10-31 +cover: /assets/img/activity/dromara-coscon24-forum-13.webp +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/dromara-coscon24-forum-0.webp) + +COSCon'24 第九届中国开源年会暨开源社 10 周年嘉年华,将于 2024 年 11 月 2-3 日在中关村国家自主创新示范区会议中心举办。作为业界最具影响力的开源盛会之一,COSCon 由开源社在 2016 年首次发起,至今已经是第九届。 + + + +以其独特定位及日益增加的影响力,COSCon 吸引了越来越多的国内外企业、高校、开源组织/社区的大力支持,具有跨组织、跨项目、跨社区的广泛覆盖面,也吸引了众多国内外开源开发者和开源爱好者的关注及参与。2024 年,恰逢开源社 10 周年,众多社区将共襄盛举,联合带来不同主题方向的专题论坛,涵盖 Web 应用开发、云计算、大数据、人工智能、Web3.0 等技术,及开源评价标准、开源治理、开源人才教育等方向。 + + + +**1** + +**大会及论坛信息** + + + +**⏰ 大会时间**:2024 年 11 月 2-3 日,上午为主论坛环节,下午为各个平行的专题论坛 + + + +**📍 地点**:北京•中关村国家自主创新示范区会议中心 + + + +**🙌🏻 报名**:扫描下方二维码报名 + +http://coscon24.bagevent.com + +![](/assets/img/activity/dromara-coscon24-forum-3.webp) + +在 COSCon'24 第九届中国开源年会,Dromara 开源社区将作为联合出品社区,在大会期间带来开源中间件/微服务的专题论坛。 + + + +**2** + + + +**论坛联合出品人** + +
+dromara-coscon24-forum +
+
+肖宇 面壁智能 +
+ +
+dromara 开源组织创始人 +
+ +**3** + + +**论坛议程介绍** + +![](/assets/img/activity/dromara-coscon24-forum-10.webp) + + + +**4** + + +**关于活动** + +COSCon'24 第九届中国开源年会暨开源社十周年嘉年华是一年一度的开源盛会,在为期 2 天的大会中,不仅有精彩纷呈的 Keynote 主题演讲,还有百花齐放的专题论坛 / 动手实验室 / 社区团聚,预期会有超过 2,000 人次现场参与这本次盛会,还会有超过 1 万名观众以线上直播的形式「云参会」。 + + + +![](/assets/img/activity/dromara-coscon24-forum-13.webp) + + + +出品丨COSCon'24 组委会 + +编辑丨王军 + + + +**相关阅读 | Related Reading** + +[主论坛抢鲜看!Open Source, Open Life 开源新生活尽在 COSCon'24](http://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247539577&idx=1&sn=12c6cf318e39bf583b8c746a4cdc8123&chksm=9fdad047a8ad595125aab0b3fb10da55e1d3bfff18b7c3f9f97c0e88242fd52342556ad60dca&scene=21#wechat_redirect) + +[论坛介绍 | 开源教育分论坛(开源之夏与开源人才培养)](http://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247539807&idx=1&sn=fa057571874df6208346e394f2a3cbed&chksm=9fdad761a8ad5e779b8467ba4cc5df3119c211b831148503fe9e3a3e0880a33aed9f8e94cfd8&scene=21#wechat_redirect) + + + +**开源社** + +_**KAIYUANSHE**_ + + + +![](/assets/img/activity/dromara-coscon24-forum-16.webp) + +开源社(英文名称为“KAIYUANSHE”)成立于 2014 年,是由志愿贡献于开源事业的个人志愿者,依 “贡献、共识、共治” **原则**所组成的开源社区。开源社始终维持 “厂商中立、公益、非营利” 的**理念**,以 “立足中国、贡献全球,推动开源成为新时代的生活方式” 为**愿景**,以 “开源治理、国际接轨、社区发展、项目孵化” 为**使命**,旨在共创健康可持续发展的开源生态体系。 + + + +开源社积极与支持开源的社区、高校、企业以及政府相关单位紧密合作,同时也是全球开源协议认证组织 - OSI 在中国的首个成员。 + + + +自2016年起连续举办中国开源年会(COSCon),持续发布《中国开源年度报告》,联合发起了“中国开源先锋榜”、“中国开源码力榜”等,在海内外产生了广泛的影响力。 + + + +我们期待 11 月 2-3 日,在 COSCon'24 第九届中国开源年会与您不见不散! \ No newline at end of file diff --git a/src/zh/activity/dromara-milvusplus-live-preview.md b/src/zh/activity/dromara-milvusplus-live-preview.md new file mode 100644 index 0000000000..d15c0cc795 --- /dev/null +++ b/src/zh/activity/dromara-milvusplus-live-preview.md @@ -0,0 +1,50 @@ +--- +title: "直播预告丨Dromara MilvusPlus: 专为Java开发者打造的向量数据库操作库" +author: 2024年12月11日 08:41 +date: 2024-12-11 +cover: /assets/img/activity/dromara-milvusplus-live-preview-0.png +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/dromara-milvusplus-live-preview-0.png) + +![](/assets/img/activity/dromara-milvusplus-live-preview-1.png) + + + +本期【User Tech】直播将在12月12日晚上20点 Zilliz直播间开启。我们荣幸邀请到**Dromara社区委员**熊国超,他将带来《Dromara MilvusPlus: 专为Java开发者打造的向量数据库操作库》分享。MilvusPlus 极大地简化了在 Java 应用中集成向量数据库的过程。该项目提供了类似 MybatisPlus 的调用风格,使得开发者能够以更高效、更熟悉的方式进行向量数据的管理和查询。 + + + +![](/assets/img/activity/dromara-milvusplus-live-preview-2.png) + + + +点击下方立即预约,敬请锁定我们的直播,与我们一同开启这段探索之旅! + + + + + +**推荐阅读** + +如何用 RAG 技术玩转文档问答?Milvus × 网易有道 QAnything 为你揭秘! + + + + +User Tech|新年第一趴!Milvus 与携程的向量探索大公开 + + + + + + + + + +![](/assets/img/activity/dromara-milvusplus-live-preview-5.png) + +![](/assets/img/activity/dromara-milvusplus-live-preview-6.png) \ No newline at end of file diff --git a/src/zh/activity/dromara-wuzhen-summit-2024.md b/src/zh/activity/dromara-wuzhen-summit-2024.md new file mode 100644 index 0000000000..62cd9f28f0 --- /dev/null +++ b/src/zh/activity/dromara-wuzhen-summit-2024.md @@ -0,0 +1,57 @@ +--- +title: Dromara在列!我国30个开源成果亮相2024世界互联网大会乌镇峰会 +author: 中国经济网 +date: 2024-11-22 +cover: /assets/img/activity/dromara-wuzhen-summit-2024-0.jpg +head: + - - meta + - name: 活动 +--- + +      **喜报!**Dromara 开源社区在由中央网信办信息化发展局指导,中国互联网发展基金会,中国网络空间研究院,中国互联网投资基金联合主办,北京初心开源技术有限公司承办的“2024中国互联网发展创新与投资大赛(开源)”大赛中荣获**二等奖**,社区多位核心成员受到表彰! + +      感谢社区所有的小伙伴们的支持,感谢大赛组委会对我们的认可! + +![](/assets/img/activity/dromara-wuzhen-summit-2024-0.jpg) + +下面是来自中国经济网的报道, + +**大赛组委会负责人宋老师评价Dromara: 一些由草根开发者自发形成的开源社区如Dromara,其‘为爱发电’的开源文化着实令人动容;** + +  2024年11月21日,在2024世界互联网大会乌镇峰会互联网公益慈善论坛上,2024中国互联网发展创新与投资大赛(开源)圆满落幕。此次大赛是“中国互联网发展创新与投资大赛”品牌公益项目的系列赛事,是在中央网信办信息化发展局指导下,由中国互联网发展基金会、中国网络空间研究院、中国互联网投资基金联合举办的第二届全国开源公益性赛事。大赛历经初赛遴选、代码成分分析和决赛现场评审等多个环节,最终从97个参选项目中评选出一等奖10名、二等奖20名,获奖项目覆盖Risc-V、操作系统、数据库、云计算、大数据、人工智能、供应链安全等领域,部分参赛项目达到国际先进水平。 + +![](/assets/img/activity/dromara-wuzhen-summit-2024-1.jpg) + +  2024中国互联网发展创新与投资大赛(开源)自2024年4月正式启动以来,受到开源领域从业人士的广泛关注。本届赛事由北京初心开源技术有限公司承办,协办单位包括:中国开源软件推进联盟、科创中国开源创新联合体、中国软件评测中心、中国工程师联合体、中国电子科技集团公司、中国移动通信集团公司、中国联合通信集团公司、国家智能网联汽车创新中心、北京开源创新委员会、上海开源技术信息协会、华为、中兴、腾讯、百度、阿里达摩院、微众银行、蚂蚁集团、字节跳动、京东、小米、奇安信等几十家行业机构,以及中国科学院计算所、中国科学院软件所、中国科学院战略咨询研究院、清华大学、北京大学、浙江大学、国防科技大学、北京航空航天大学、北京理工大学、北京邮电大学、电子科技大学、四川大学、华东师范大学、南方科技大学、西南交通大学等十几家科研院所。作为大赛的宣传工作主阵地,中国经济网为本届大赛开设了专题,全程播报赛事新闻动态。Linux基金会AI&DATA社区、Apache软件基金会以及国内的CSDN开发者社区、InfoQ开发者社区、思否开发者社区,对本届大赛线上的宣传工作和在北京、上海、成都、广州、杭州、深圳和大湾区的各地开展线下推广活动做出了积极贡献。 + +  中国开源行业泰斗、中国开源软件推进联盟名誉主席陆首群教授认为:“我国网络安全和信息化的基础设施和底层技术得益于开源技术的全球普及,以Linux、Kubernetes和RISC-V为代表的全球著名开源项目为我国培养了一批世界顶尖的开源技术人才。人才是实现科技强国、民族振兴,赢得国际竞争主动权的基础性支撑。中央网信办相关单位连续举办的开源大赛意义格外重大。开源竞赛是我们发现、选拔和培养开源青年才俊的重要渠道。开源生态中的企事业单位、高校、研究机构均应视其为己任,积极支持并参与其中,为中国培养开源人才、提升科技创新力贡献一份力量。” + +  据悉,此次大赛建立了一只由60位技术专家和知识产权专家组成的评审委员会,通过“技术创新、开源合规、社区运营、商业发展”四个维度设定了全面的指标评估体系。在决赛阶段,所有参选项目均通过了中国软件评测中心、奇安信集团和苏州棱镜七彩信息科技有限公司三方的专业代码成分分析,为决赛专家评审提供了客观依据。 + +  本届大赛评审委员会主席、北京大学计算机学院陈钟教授认为:“以赛事促发展在许多创新领域都发挥了及其重要的作用,开源领域毫不例外,甚至具有更加广泛的示范性意义。既可以鼓励开发者和相关项目脱颖而出、又能够弘扬开源精神和开源文化,一举两得。” + +  **大赛组织委员会负责人、北京初心开源技术有限公司总经理宋可为表示:“参赛项目多样化是本届大赛一个显著特点。与往届大赛相比,本届大赛上涌现出许多个人开源项目,其影响力虽然不大,但其项目的成熟度和社区活跃度很高;一些由草根开发者自发形成的开源社区如Dromara,其‘为爱发电’的开源文化着实令人动容;**还有一些起源于头部科技企业(腾讯)捐赠给开放原子开源基金会的项目,如OpenTenbase,也格外引人瞩目。” + +  自2021年开源被纳入国家《“十四五”规划和2035年远景目标纲要》以来,中国开源正式进入了“加速”发展阶段。中国已经成为全球开源开发者数量增速第一,保有量第二的国家,开源创新已经成为我国核心技术和前沿技术的重要发展路径。 + +  党的二十届三中全会《决定》提出“健全因地制宜发展新质生产力体制机制。推动技术革命性突破、生产要素创新性配置、产业深度转型升级,推动劳动者、劳动资料、劳动对象优化组合和更新跃升,催生新产业、新模式、新动能,发展以高技术、高效能、高质量为特征的生产力。” + +  大赛主办方中国互联网发展基金会深入实施网络强国战略部署,充分发挥公益基金“四两拨千斤”作用,围绕关键技术人才和项目创新,开展了系列创投大赛。先后于2022年和2024年在中国互联网发展创新与投资大赛中开设了开源赛道。通过举办开源系列创投大赛,持续关注开源产业发展,协助孵化我国优秀开源项目,为促进我国信息化发展,夯实数字经济发展贡献力量。 + +  “与2022年大赛相比,组委会扩充了评审委员会队伍,并将开源项目适配国产芯片和国产操作系统作为考核项之一,充分体现出大赛对我国信创产业生态的有力支撑。此外,与往届对获奖项目进行投融资培训不同,本届大赛已经增设了优质开源项目的融资路演推介活动,进一步的体现出‘创投大赛’的属性。”宋可为补充道,“2024年以来,全国各地政府纷纷出台开源产业扶植政策,对开源企业、开源项目、开源社区进行定向支持。通过这两届开源大赛,我们已经遴选出的120个中国优秀开源项目和500多位优秀开源人才,分布在国内20多个城市。后续我们将继续着手开源人才培养和开源项目的孵化工作。” + +  详细获奖名单,参见:https://bs.bjos.club/hjgg-n183.html + +  (中国互联网发展基金会资金支持项目) + +![](/assets/img/activity/dromara-wuzhen-summit-2024-2.jpg) + +![](/assets/img/activity/dromara-wuzhen-summit-2024-3.jpg) + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 \ No newline at end of file diff --git a/src/zh/activity/gstar-reward-submit.md b/src/zh/activity/gstar-reward-submit.md new file mode 100644 index 0000000000..c516364161 --- /dev/null +++ b/src/zh/activity/gstar-reward-submit.md @@ -0,0 +1,29 @@ +--- +title: G-Star光引计划启动|投稿GitCode项目故事赢AirPods Pro,通过必得京东卡! +author: GitCode +date: 2024-11-12 +cover: /assets/img/activity/gstar-reward-submit-0.gif +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/gstar-reward-submit-0.gif) + + + + + +![](/assets/img/activity/gstar-reward-submit-1.webp) + +![](/assets/img/activity/gstar-reward-submit-2.webp) + +![](/assets/img/activity/gstar-reward-submit-3.webp) + +![](/assets/img/activity/gstar-reward-submit-4.webp) + +![](/assets/img/activity/gstar-reward-submit-5.webp) + +![](/assets/img/activity/gstar-reward-submit-6.webp) + +![](/assets/img/activity/gstar-reward-submit-7.webp) \ No newline at end of file diff --git a/src/zh/activity/gstar-shenzhen-developers.md b/src/zh/activity/gstar-shenzhen-developers.md new file mode 100644 index 0000000000..adb7b521b3 --- /dev/null +++ b/src/zh/activity/gstar-shenzhen-developers.md @@ -0,0 +1,55 @@ +--- +title: 【活动报名】G-Star Gathering Day深圳站|开发者的专属派对,来深圳了! +author: 2024年11月13日 08:30 +date: 2024-11-13 +cover: /assets/img/activity/gstar-shenzhen-developers-0.gif +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/gstar-shenzhen-developers-0.gif) + + + +**G-Star Gathering Day** 是 GitCode 为开源项目开发者打造的面对面聚会。我们准备了View超棒的场地和美味的披萨、啤酒,畅谈开源开发的激情与乐趣,分享经验,激发灵感。这不仅是技术交流盛会,更是开发者们友谊的美好见证,共同为开源生态繁荣贡献力量。 + + + +**G-Star Gathering Day 深圳站** 即将拉开帷幕!我们诚邀每一位热爱开源的开发者,于 **11 月 30 日**齐聚深圳,一同探索开源技术的无限可能。继续阅读了解活动详情,一起来参加这场属于开发者的专属Party吧! + + + + + +![](/assets/img/activity/gstar-shenzhen-developers-1.png) + +**G-Star Gathering Day 深圳站** + +📅 活动时间:11月30日 14:00 + +📍 活动地点:深圳南山区 + + + +“ + +**活动详情及报名方式** + +![](/assets/img/activity/gstar-shenzhen-developers-2.png) + + + +本次活动不仅邀请了多位行业大牛,带来丰富多样的技术分享,还设有互动与抽奖环节!这是一场难得的技术交流和人脉拓展机会,新手老手都能收获满满。扫描上方二维码或点击下方小程序报名,踏上开源技术探索之旅!名额有限,不要错过! + + + + + +作为中国的科技创新之都,深圳一直以来都是技术创新和交流的前沿阵地,也诞生了不少优质的G-Star项目。相信本次 G-Star Gathering Day 深圳站,必将激发更多创新灵感! + + + +**11月30日,我们深圳见!** + +![](/assets/img/activity/gstar-shenzhen-developers-3.jpg) \ No newline at end of file diff --git a/src/zh/activity/harmonyos-tool-gift.md b/src/zh/activity/harmonyos-tool-gift.md new file mode 100644 index 0000000000..47b36046ac --- /dev/null +++ b/src/zh/activity/harmonyos-tool-gift.md @@ -0,0 +1,49 @@ +--- +title: 鸿蒙 er 紧急集合!贡献你的鸿蒙开发工具,赢取大赶集好礼 +author: 2025年01月23日 10:24 +date: 2025-01-23 +cover: /assets/img/activity/harmonyos-tool-gift-0.gif +head: + - - meta + - name: 活动 +--- + +![](/assets/img/activity/harmonyos-tool-gift-0.gif) + + + +在科技浪潮中,鸿蒙生态正以破竹之势迅猛前行,蓬勃发展。鸿蒙开发工具广场社区作为鸿蒙开发实用工具的汇聚高地,一直致力于为开发者们打造一个资源丰富、高效便捷的交流平台。 + + + +2025年开春之际,我们诚邀广大开发者参与“**鸿蒙开发工具大赶集**”活动,将平时鸿蒙开发过程中的经验工具化,助力更多开发者高效地开发鸿蒙应用。欢迎**扫描下方海报中的二维码加入此次活动**,一同为鸿蒙生态的繁荣添砖加瓦! + + + +![](/assets/img/activity/harmonyos-tool-gift-1.png) + + + + + +![](/assets/img/activity/harmonyos-tool-gift-2.svg) + +**推荐阅读** + +![](/assets/img/activity/harmonyos-tool-gift-3.svg) + +[![](/assets/img/activity/harmonyos-tool-gift-4.png)](https://mp.weixin.qq.com/s?__biz=MzkyNjY0MDY1Ng==&mid=2247488718&idx=1&sn=5da5632275a58f1b743e875e2ad9ffc3&scene=21#wechat_redirect) + +仓颉编程语言有奖征文获奖公示 + + + +[![](/assets/img/activity/harmonyos-tool-gift-5.png)](https://mp.weixin.qq.com/s?__biz=MzkyNjY0MDY1Ng==&mid=2247488488&idx=1&sn=d5cb681cc3eec11f0e56785dbab75f3e&scene=21#wechat_redirect) + +CodeMaster 专栏人物第一期 + + + + + +![](/assets/img/activity/harmonyos-tool-gift-6.jpg) \ No newline at end of file diff --git a/src/zh/activity/ospp-2023.md b/src/zh/activity/ospp-2023.md index f6f254825e..058926db57 100644 --- a/src/zh/activity/ospp-2023.md +++ b/src/zh/activity/ospp-2023.md @@ -1,113 +1,113 @@ ---- -title: 开源之夏学生招募中 | 认领项目赢取丰厚奖金。Dromara课题项目介绍之系列一 -author: 夏天来玩 -date: 2023-05-02 -cover: /assets/img/news/ospp-2023-2.png -head: - - - meta - - name: 活动 ---- - -## 开源之夏 2023 - -开源之夏是由中科院软件所`开源软件供应链点亮计划`发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,培养和发掘更多优秀的开发者,促进优秀开源软件社区的蓬勃发展,助力开源软件供应链建设。 - -## 学生开启报名 - -开源之夏 2023 学生报名 4 月 29 日正式开启啦!明天开始,同学们就可以在开源之夏官网 https://summer-ospp.ac.cn/ 挑选项目,与导师沟通并准备项目申请材料、提交申请。 - -![](/assets/img/news/ospp-2023-1.png) -这么多项目任务,是不是已经跃跃欲试了?不管你是新手还是老手,无论你想要贡献代码,还是学习开源技术和进行开发实践,或是想为自己的履历增添优势,在开源之夏,你都能找到自己的机会。带着对开源的探索和求知之心,欢迎在校的你报名加入开源之夏 2023,一起探索今夏的无限可能! - -在开源之夏,参与学生有机会与众多开源社区和资深开发者导师进行交流和互动,提升自己的技术水平和实践能力,掌握更多开源知识和技能。成功结项的你还将获得丰厚项目奖金——基础难度项目:`8000` 元、进阶难度项目:`12000` 元(税前),以及有机会成为 Dromara 社区成员享受福利多多! - -![](/assets/img/news/ospp-2023-2.png) - -## Dromara 社区介绍 - -Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务 RPC,运维监控,Agent 监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 - -Dromara 开源社区目前拥有 10+GVP 项目,总 star 数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用 Dromara 社区的开源项目。 - -## Dromara 社区课题项目系列一 - -> 我们在接下来的几天陆续推送 Dromara 社区下课题项目介绍,欢迎大家关注了解。选择自己感兴趣的课题申请。 -> 今天给大家带来的是  **Dromara 开源组织官网设计开发**  和  **为 Easy-Es 提供自定义注解,支持字段与索引一对多映射关系** -> **课题任务门槛不会太高的哦,且全程由 Dromara 社区导师倾心指导。** - -### 一. Dromara 开源组织官网设计开发 - -> 重新设计 dromara 官网。目前 dromara 组织有 30+的项目,超过 20W+star。是开源中国最受欢迎的组织。 - -##### 项目需求 1: - -新官网除了要表现出大气,美观。还要梳理出目前社区组织结构,成员列表,贡献者文档等。 - -最重要的由于我们的项目比较多,新闻,活动,版本发布比较多。所有需要按照项目维度去设计项目,博客,社区等分类。学生需要会前端开发,自定义开发官网,包含不限于 node.js.vue.js 等。 - -##### 项目需求 2: - -需要搬运已经存在的新闻,发版活动,并且翻译成英文。(这也是个挑战) - -##### 主要产出如下: - -1. 包括主页的重新设计 -2. 项目的重新分类,设计,更新项目 -3. Blog 按照项目分类,更新博客 -4. 社区按照项目分类,并且更新文档 -5. 新增社区组织架构与成员文档 -6. 新增社区捐赠流程等文档 -7. 其他(可以自己提一些好的想法和建议,与导师沟通) - -地址:https://dromara.org - -项目源码:https://github.com/dromara/dromara.github.io - -导师联系邮箱:549477611@qq.com - -##### 项目技术要求: - -- 熟练的前端开发,会 JavaScrpt,Node.js, react.js, vue.js 中的其中之一 -- 熟悉 Github action -- 熟悉 hugo 这种静态网站框架 -- 较强的英文表达能力 - -##### 项目成果仓库: - -- https://github.com/dromara/dromara.github.io - -**申请地址:https://summer-ospp.ac.cn/org/prodetail/23ee40016?lang=zh&list=pro** - -### 二. 为 Easy-Es 提供自定义注解,支持字段与索引一对多映射关系 - -##### 项目简介 - -Easy-Es 是一款由国内开发者打造并完全开源的 ElasticSearch 搜索引擎 ORM 框架. 兼具轻量,智能,高效,低码,易用,易拓展等特性,全自动索引托管及数据迁移,平均可节省 3-8 倍代码量,并显著减少开发和运维负担。上线仅一年多时间,收获 STAR 总计 4K+,社区 Q 及微信群成员近 2000 人,OSC 搜索引擎类目排名第一并长期占据该类目热搜榜首, 社区活跃,处理 issue 200+,以及解答 Easy-Es 共计 9 个社区群内的各种问题,长期坚持用爱发电。 - -##### 项目产出要求 - -1.通过提供自定义注解,能够支持对单个类成员变量配置生成索引逻辑,能够生成一对多的索引关系,以此来支持实际使用中针对单字段检索时可以既使用中文分词器,又使用拼音分词器和英文分词器的场景,比如在实际使用 ES 的业务场景中,有用户需要检索药品库中某种药品,针对同一个字段,无论用户输入的是该药品的中文名称或是汉语拼音甚至英文学名等均可以被检索到对应药品,此时就可以通过此功能来解决。其底层实现可以参考目前已有的`@IndexField`注解,目前该注解仅支持一对一映射,若要实现上述功能需要用户创建多个冗余字段才可以,因此需要学生新增`@IndexMultiField`注解,并将其拓展为支持一对多的映射关系,通过一对多映射可以更加优雅解决此类需求,不需要额外创建冗余字段,并且占用的磁盘存储空间也相对更少。 - -2.该自定义注解需要在框架已有的索引托管-“自动挡模式”下自动创建索引,并在项目启动时感知索引变化,自动完成索引的创建/更新以及数据的自动迁移,需要在现有的自动挡模式下兼容此一对多索引关系,其实现原理仍然可以参考当前已有的单字段注解`@IndexField`注解。 - -##### 项目技术要求: - -- 掌握 Java 编程语言及 Git 和 Elasticsearch -- 熟悉 Java 及 lambda 语法 -- 熟悉 Git 常用命令 -- 熟悉 Elasticsearch 搜索引擎 -- 了解 Springboot - -##### 项目成果仓库: - -- https://gitee.com/dromara/easy-es - -**申请地址:https://summer-ospp.ac.cn/org/prodetail/23ee40017?list=org&navpage=org** - -## 快速参与开源之夏 2023 - -开源之夏 2023 Dromara 社区各项目课题将从 4 月 29 日开始接受学生参与项目申请,欢迎通过上方联系方式,与各导师沟通并准备项目申请材料。 - -我们 Dromara 社区会在接下来几天陆续介绍项目课题系列,欢迎大家关注了解选择你感兴趣的课题申请。 - -![](/assets/img/news/ospp-2023-3.png) +--- +title: 开源之夏学生招募中 | 认领项目赢取丰厚奖金。Dromara课题项目介绍之系列一 +author: 夏天来玩 +date: 2023-05-02 +cover: /assets/img/news/ospp-2023-2.png +head: + - - meta + - name: 活动 +--- + +## 开源之夏 2023 + +开源之夏是由中科院软件所`开源软件供应链点亮计划`发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,培养和发掘更多优秀的开发者,促进优秀开源软件社区的蓬勃发展,助力开源软件供应链建设。 + +## 学生开启报名 + +开源之夏 2023 学生报名 4 月 29 日正式开启啦!明天开始,同学们就可以在开源之夏官网 https://summer-ospp.ac.cn/ 挑选项目,与导师沟通并准备项目申请材料、提交申请。 + +![](/assets/img/news/ospp-2023-1.png) +这么多项目任务,是不是已经跃跃欲试了?不管你是新手还是老手,无论你想要贡献代码,还是学习开源技术和进行开发实践,或是想为自己的履历增添优势,在开源之夏,你都能找到自己的机会。带着对开源的探索和求知之心,欢迎在校的你报名加入开源之夏 2023,一起探索今夏的无限可能! + +在开源之夏,参与学生有机会与众多开源社区和资深开发者导师进行交流和互动,提升自己的技术水平和实践能力,掌握更多开源知识和技能。成功结项的你还将获得丰厚项目奖金——基础难度项目:`8000` 元、进阶难度项目:`12000` 元(税前),以及有机会成为 Dromara 社区成员享受福利多多! + +![](/assets/img/news/ospp-2023-2.png) + +## Dromara 社区介绍 + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务 RPC,运维监控,Agent 监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + +Dromara 开源社区目前拥有 10+GVP 项目,总 star 数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用 Dromara 社区的开源项目。 + +## Dromara 社区课题项目系列一 + +> 我们在接下来的几天陆续推送 Dromara 社区下课题项目介绍,欢迎大家关注了解。选择自己感兴趣的课题申请。 +> 今天给大家带来的是  **Dromara 开源组织官网设计开发**  和  **为 Easy-Es 提供自定义注解,支持字段与索引一对多映射关系** +> **课题任务门槛不会太高的哦,且全程由 Dromara 社区导师倾心指导。** + +### 一. Dromara 开源组织官网设计开发 + +> 重新设计 dromara 官网。目前 dromara 组织有 30+的项目,超过 20W+star。是开源中国最受欢迎的组织。 + +##### 项目需求 1: + +新官网除了要表现出大气,美观。还要梳理出目前社区组织结构,成员列表,贡献者文档等。 + +最重要的由于我们的项目比较多,新闻,活动,版本发布比较多。所有需要按照项目维度去设计项目,博客,社区等分类。学生需要会前端开发,自定义开发官网,包含不限于 node.js.vue.js 等。 + +##### 项目需求 2: + +需要搬运已经存在的新闻,发版活动,并且翻译成英文。(这也是个挑战) + +##### 主要产出如下: + +1. 包括主页的重新设计 +2. 项目的重新分类,设计,更新项目 +3. Blog 按照项目分类,更新博客 +4. 社区按照项目分类,并且更新文档 +5. 新增社区组织架构与成员文档 +6. 新增社区捐赠流程等文档 +7. 其他(可以自己提一些好的想法和建议,与导师沟通) + +地址:https://dromara.org + +项目源码:https://github.com/dromara/dromara.github.io + +导师联系邮箱:549477611@qq.com + +##### 项目技术要求: + +- 熟练的前端开发,会 JavaScrpt,Node.js, react.js, vue.js 中的其中之一 +- 熟悉 Github action +- 熟悉 hugo 这种静态网站框架 +- 较强的英文表达能力 + +##### 项目成果仓库: + +- https://github.com/dromara/dromara.github.io + +**申请地址:https://summer-ospp.ac.cn/org/prodetail/23ee40016?lang=zh&list=pro** + +### 二. 为 Easy-Es 提供自定义注解,支持字段与索引一对多映射关系 + +##### 项目简介 + +Easy-Es 是一款由国内开发者打造并完全开源的 ElasticSearch 搜索引擎 ORM 框架. 兼具轻量,智能,高效,低码,易用,易拓展等特性,全自动索引托管及数据迁移,平均可节省 3-8 倍代码量,并显著减少开发和运维负担。上线仅一年多时间,收获 STAR 总计 4K+,社区 Q 及微信群成员近 2000 人,OSC 搜索引擎类目排名第一并长期占据该类目热搜榜首, 社区活跃,处理 issue 200+,以及解答 Easy-Es 共计 9 个社区群内的各种问题,长期坚持用爱发电。 + +##### 项目产出要求 + +1.通过提供自定义注解,能够支持对单个类成员变量配置生成索引逻辑,能够生成一对多的索引关系,以此来支持实际使用中针对单字段检索时可以既使用中文分词器,又使用拼音分词器和英文分词器的场景,比如在实际使用 ES 的业务场景中,有用户需要检索药品库中某种药品,针对同一个字段,无论用户输入的是该药品的中文名称或是汉语拼音甚至英文学名等均可以被检索到对应药品,此时就可以通过此功能来解决。其底层实现可以参考目前已有的`@IndexField`注解,目前该注解仅支持一对一映射,若要实现上述功能需要用户创建多个冗余字段才可以,因此需要学生新增`@IndexMultiField`注解,并将其拓展为支持一对多的映射关系,通过一对多映射可以更加优雅解决此类需求,不需要额外创建冗余字段,并且占用的磁盘存储空间也相对更少。 + +2.该自定义注解需要在框架已有的索引托管-“自动挡模式”下自动创建索引,并在项目启动时感知索引变化,自动完成索引的创建/更新以及数据的自动迁移,需要在现有的自动挡模式下兼容此一对多索引关系,其实现原理仍然可以参考当前已有的单字段注解`@IndexField`注解。 + +##### 项目技术要求: + +- 掌握 Java 编程语言及 Git 和 Elasticsearch +- 熟悉 Java 及 lambda 语法 +- 熟悉 Git 常用命令 +- 熟悉 Elasticsearch 搜索引擎 +- 了解 Springboot + +##### 项目成果仓库: + +- https://gitee.com/dromara/easy-es + +**申请地址:https://summer-ospp.ac.cn/org/prodetail/23ee40017?list=org&navpage=org** + +## 快速参与开源之夏 2023 + +开源之夏 2023 Dromara 社区各项目课题将从 4 月 29 日开始接受学生参与项目申请,欢迎通过上方联系方式,与各导师沟通并准备项目申请材料。 + +我们 Dromara 社区会在接下来几天陆续介绍项目课题系列,欢迎大家关注了解选择你感兴趣的课题申请。 + +![](/assets/img/news/ospp-2023-3.png) diff --git a/src/zh/activity/secretflow-3rd-upgrade-showcase.md b/src/zh/activity/secretflow-3rd-upgrade-showcase.md new file mode 100644 index 0000000000..6a591536d2 --- /dev/null +++ b/src/zh/activity/secretflow-3rd-upgrade-showcase.md @@ -0,0 +1,137 @@ +--- +title: 三年之约,相聚北京!邀您见证迈向“数据流通全栈技术社区”的全新隐语 +author: 隐语开源社区 +date: 2025-07-29 +cover: /assets/img/activity/secretflow-3rd-upgrade-showcase-0.webp +head: + - - meta + - name: 活动 +--- + +在数字经济纵深发展的关键阶段,国家数据基础设施是数据基础制度和先进技术落地的重要载体,需要技术生态与产业生态打破互通壁垒实现融合与共创。值此加速发展的机遇期,隐语开源社区也迎来了成立三周年的重要里程碑! + +三年前,隐语开源社区以隐私计算技术为起点,开启护航数据安全可信流通的探索与实践之路;三年后的今天,社区已汇聚超 20000+ 关注者与开发者,60+ 高校学研合作机构,70+ 产业合作伙伴,共同推动数据要素流通关键技术演进与行业应用落地。 + +为响应国家数据基建政策指引,隐语开源社区既可顺势而生,亦将与时俱进!期待您与我们共同见证隐语从“隐私计算技术社区”迈向“数据流通全栈技术社区”。 + + + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-0.webp) + +2025年8月14日,“技术互联·价值无限——隐语三周年暨社区升级仪式”即将到来!本次活动由蚂蚁密算科技有限公司、区块链技术与数据安全工业和信息化部重点实验室、浙江大学区块链与数据安全全国重点实验室、中国电子数据产业集团有限公司联合主办,北京国际大数据交易所、中国电子信息行业联合会数字经济专委会协办。 + +以期协同产学研生态伙伴共同推进数据要素流通基础设施建设,加速数据要素应用场景创新与产业价值释放。 + +微信扫码预约【现场席位】 + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-1.webp) + +三年之约,隐语与您相聚北京! + + + +**🌟 活动亮点  抢先了解** + +**一、隐语社区全新升级,技术生态聚力而行** + +独木不成林,隐语社区将“破圈”跨越隐私计算范畴,凝聚技术产业生态力量,构建覆盖可信数据空间、数据元件、数联网、区块链、隐私计算的融合技术生态,实现从隐私计算技术社区向数据要素流通全栈技术社区的演进,打造国家数据基础设施协作生态。 + +同时,推动数据要素行业互联互通,依托隐语开源社区的开放基因,构建产学研用多方参与的生态共同体,打破行业孤岛,促进跨领域技术协同与标准共建,实现数据要素跨域流通的高效衔接,激发产业数据创新活力。首发阵容,敬请期待! + +**二、技术+产业,双轮驱动的思维碰撞盛宴** + +我们相信,前沿技术并非“独角戏”,让硬核代码遇见真实业务场景,本次活动将通过观点分享与深度圆桌等形式展开交流碰撞,围绕数据流通基础设施建设技术演进&产业数据流通标杆场景经验,共同探讨开源如何驱动创新、技术如何赋能业务、产业如何加速孵化等关键话题。 + +当上层建筑遇到基层建设,当技术极客遇到产业实干家,我们将打破认知与实践的壁垒,碰撞出驱动数据价值挖掘与产业应用落地的切实路径。 + +**三、数据x千行百业,洞察产业应用真命题** + +产业是技术最好的练兵场,应用场景是数据价值产生的根本动力。本次活动,我们将邀请到来自政务、金融、保险、交通等不同领域的行业先行者,现场分享数据要素流通及价值释放的产业实践案例。 + +期待产业机构能够发挥主体作用,携手探索行业数据应用场景创新,推动数据要素实现价值挖掘、跨主体复用,通过生态交流与合作实现优势互补、经验共享、知识扩散与价值倍增。 + + + +**🎓 活动议程 首次公开** + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-2.webp) + +**🌟 活动亮点  抢先了解** + +立刻报名「技术互联 价值无限·隐语三周年特别活动」,锁定您的专属席位!深入洞悉数据流通基建前沿技术与最新实践,与行业专家面对面,更有限量社区周边好礼等你来解锁! + +🔍 【报名方式】 + +微信扫描上方海报/下方二维码参与报名;添加隐语小助手 Calor(微信号:SecretFlow04),回复「三周年」可查询报名结果。 + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-3.webp) + +🎁 【助力福利】 + +**1**. **参与时间:即日起至****2025****年****8****月****13****日****23****点****59****分** + +**2\. 参与规则及奖励机制**:    + +①邀请好友线上助力: + +Step1:通过微信扫描报名二维码,进入“为隐语助力” + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-4.png) + +Step2:向好友分享助力链接,受邀好友线上点击助力 + +Step3:邀请人于活动现场签到后,可根据累计邀请好友人数领取指定周边 + +奖励机制: + +10≤累计邀请好友人数<20,可领取隐语定制晴雨伞一把;累计邀请好友人数≥20,可领取隐语定制随行杯一个。 + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-5.webp) + +②邀请好友报名且好友亲临现场: + +Step1:通过微信扫描报名二维码,进入“为隐语助力” + +Step2:向好友分享助力链接,受邀好友报名活动且到活动现场签到 + +Step3:邀请人可根据好友到场签到数领取指定周边(P.S:邀请人无需到现场,被邀请人到场即可累计邀约人数) + +奖励机制: + +a)3≤好友到场签到数<5,可领取隐语三周年定制T恤一件; + +b)5≤好友到场签到数<8,可领取隐语定制晴雨伞一把及午睡毯一件; + +c)好友到场签到数≥8,可领取隐语三周年定制T恤一件及眼部按摩仪一部。 + +![](/assets/img/activity/secretflow-3rd-upgrade-showcase-6.webp) + +说明:本次助力活动中,同一身份证号、同一手机号码、同一微信账号,满足以上任一条件均视为同一用户。请勿使用任何破坏活动公平性行为的不正当手段参与活动,违者将被取消奖励资格。 + + + +开源可信隐私计算框架——隐语 SecretFlow + +支持MPC、FL、TEE等主流隐私计算技术,融合产学研生态共创能力,助力隐私计算更广泛应用到AI、数据分析等场景中,解决千行百业隐私保护和数据孤岛等应用痛点。 + +**官网**:https://www.secretflow.org.cn + +**查看源码**:https://github.com/secretflow/secretflow + + + +**开源安全可信系统软件栈——星绽 Asterinas** + +聚焦安全可信技术软件栈的开源社区,面向通用计算和机密计算两大场景提供坚实的安全底座,为不断涌现的安全攸关和隐私敏感的应用保驾护航。 + +**官网**:https://asterinas.github.io/ + +**查看源码**:https://github.com/asterinas + + + +**联系我们** + +**公众号**:隐语的小剧场 + +**小助手**:SecretFlow04 \ No newline at end of file diff --git a/src/zh/blog/DTSE-Tech-Talk-29.md b/src/zh/blog/DTSE-Tech-Talk-29.md index 343e85e5e1..16438901d1 100644 --- a/src/zh/blog/DTSE-Tech-Talk-29.md +++ b/src/zh/blog/DTSE-Tech-Talk-29.md @@ -1,78 +1,78 @@ ---- -title: DTSE Tech Talk | 第29期:理解读与用户一起“跳动”的开源实时监控工具 HertzBeat -author: 华为云开发者联盟 -date: 2023-05-30 -cover: /assets/img/blog/DTSE-Tech-Talk-29-3.png -head: - - - meta - - name: 活动 ---- - -![](/assets/img/blog/DTSE-Tech-Talk-29-1.png) - -## **本期直播详解** - -在本期《**开源实时监控工具 HertzBeat 如何与用户一起“跳动?**》的主题直播中,HertzBeat & TanCloud 创始人巩超与开发者和伙伴朋友们交流当前主流指标监控方案,解读 HertzBeat 及能力特点,并为大家演示了如何通过华为云商店安装部署 HertzBeat 方法。 - -### \***\*HertzBeat 是什么?\*\*** - -可以把它定义为一款指标监控工具,然后也是一个开源项目:https://github.com/dromara/hertzbeat 。 - -HertzBeat 中文名称为赫兹跳动  ,是一个拥有强大自定义监控能力,无需 Agent 的开源实时监控告警工具。HertzBeat 集   监控+告警+通知   为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控,阈值告警通知一步到位。具有更自由化的阈值规则(计算表达式),邮件、Discord Slack Telegram、钉钉、微信、飞书、短信、Webhook 等方式及时送达。而且,HertzBeat 可将 Http、Jmx、Ssh、Snmp、Jdbc 等协议规范可配置化,用户只需在浏览器配置 YML 就能使用这些协议去自定义采集任何您想要的指标。 - -你相信,只需配置下就能立刻适配一款 K8s 或 Docker 等新的监控类型吗?HertzBeat 的强大自定义,多类型支持,易扩展,低耦合,能帮助开发者和中小团队快速搭建自有监控系统。 - -### \***\*HertzBeat 的强大自定义功能\*\*** - -作为一款新出的监控工具,Hertzbeat 并没有去创建自有的协议让对端监控去适配,因为这需要大量的生态适配和自生产品强大的影响力才能做到让别人来改造适配,这对一个新晋开源项目来说是不现实的。针对市面上已有的标准协议,Hertzbeat 只需使用这些协议并把它们做好,就可以满足绝大部分需求。 - -![](/assets/img/blog/DTSE-Tech-Talk-29-2.png) - -在网管协议 snmp 中,主流数据库的 sdk 中要属 java 的最完整规范。java jdbc 规范可以让我们去直连所有的主流数据库。java mbean 指标信息通过 jmx 方式暴露,很多应用都提供 api json 接口供我们获取,当然还有 ssh 直连 linux unix 系统运行脚本获取回显,ipmi 去监控服务器指标,现有的 promethues exporter,openmetrics 规范等等,这些协议规范可以满足我们绝大部分场景。 - -![](/assets/img/blog/DTSE-Tech-Talk-29-3.png) - -而 Hertzbeat 就是将 http、jmx、snmp、jdbc、ssh 等等协议或格式抽象规范,使其可统一配置化。用户通过配置 YML 格式的监控模版定义, 就能使用这些协议来采集想要的指标。 - -![](/assets/img/blog/DTSE-Tech-Talk-29-4.png) - -一个监控类型对应一个监控模版,监控模版定义的配置也是在页面可操作的,目前官方支持的所有监控类型,其实就是一个个监控模版。用户可以在页面新增自定义的监控模版,也可以对内置模版修改更新。 - -### \***\*HertzBeat 的易用来自于诸多模板支持\*\*** - -目前 HertzBeat 已支持应用服务、数据库、操作系统、云原生等领域的监控模板。主要原理是它将监控采集类型(mysql、jvm、k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控。 - -![](/assets/img/blog/DTSE-Tech-Talk-29-5.png) - -不仅如此,也支持多平台告警消息渠道,标签&级别过滤,支持告警静默,恢复,通知时段等等。 - -![](/assets/img/blog/DTSE-Tech-Talk-29-6.png) - -### \***\*开源项目离不开小伙伴协助\*\*** - -Hertzbeat 目前在 github gitee 都有同步仓库,其是开源社区 Dromara 下的开源项目。 - -https://github.com/dromara/hertzbeat github star 3k, - -https://gitee.com/dromara/hertzbeat gitee star 1.6k , gitee gvp - -Dockerhub 下载量 10K+。作为一个开源项目,除了项目本身的 star 数量,下载数量等活跃度之外,项目的开发者生态也是及其重要的。目前有超过 121 位小伙伴给 hertzbeat 代码仓库贡献过 PR,hertzbeat 的现在离不开开发者们的每一行代码。我们作为程序员可能大家都会有这样一个想法,就是自己写的代码可以部署到成千上万的服务器跑起来中给他人使用,这样的想法或者说是小梦想我们正在慢慢做到,想想有点小激动哈哈。 - -### \***\*HertzBeat for Huawei Cloud\*\*** - -关于 HuaweiCloud 开源活动,华为云面向开源软件工具链与环境、开源应用构建和开源生态组件构建这三大重点场景,提供技术支持、奖金支持、活动支持,邀请更多的开发者,携手构建开源 for HuaweiCloud。 - -开发者将开源软件工具、开源应用和开源组件与华为云对象存储 OBS、数仓 DWS、云容器 CCE 等云服务对接,同时基于 Terraform 模板,上架到华为云云商店,支持其他开发者一键部署使用开源组件 ,称为“**开源 xxx for HuaweiCloud**”。 - -Hertzbeat 与 Huaweicloud 的开源合作需求已经作为 feature issue 发布到 github 仓库了,已经实现了三个,欢迎大家了解。 - -- \[Task\] support using Huawei Cloud OBS to store custom define yml file #841 -- \[Task\] support Huawei Cloud CCE metrics monitoring #839 -- \[Task\] support EulerOS metrics monitoring #838 -- \[Task\] support using Huawei Cloud SMN send alarm notification message #837 -- \[Task\] support using GaussDB For Influx store history metrics data #836 - -为了更好的方便用户基于华为云部署 Hertzbeat,大家可前往华为云商店参看,只需几分钟就可**一键部署 Hertzbeat**。通过华为云商店部署 Hertzbeat,除了对用户的方便快捷之外,对开源团队也是有意义。Hertzbeat 本身在云商店完全免费,用户在云商店部署只收取本来的云服务器费用。当用户通过这样方式部署到云服务器时,华为云会给开源团队一定的云服务器费用分成来资助开源团队的发展。 - -如果你刚好需要云服务器来部署 hertzbeat,可以通过华为云商店免费使用。 -![](/assets/img/blog/DTSE-Tech-Talk-29-7.png) +--- +title: DTSE Tech Talk | 第29期:理解读与用户一起“跳动”的开源实时监控工具 HertzBeat +author: 华为云开发者联盟 +date: 2023-05-30 +cover: /assets/img/blog/DTSE-Tech-Talk-29-3.png +head: + - - meta + - name: 活动 +--- + +![](/assets/img/blog/DTSE-Tech-Talk-29-1.png) + +## **本期直播详解** + +在本期《**开源实时监控工具 HertzBeat 如何与用户一起“跳动?**》的主题直播中,HertzBeat & TanCloud 创始人巩超与开发者和伙伴朋友们交流当前主流指标监控方案,解读 HertzBeat 及能力特点,并为大家演示了如何通过华为云商店安装部署 HertzBeat 方法。 + +### \***\*HertzBeat 是什么?\*\*** + +可以把它定义为一款指标监控工具,然后也是一个开源项目:https://github.com/dromara/hertzbeat 。 + +HertzBeat 中文名称为赫兹跳动  ,是一个拥有强大自定义监控能力,无需 Agent 的开源实时监控告警工具。HertzBeat 集   监控+告警+通知   为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控,阈值告警通知一步到位。具有更自由化的阈值规则(计算表达式),邮件、Discord Slack Telegram、钉钉、微信、飞书、短信、Webhook 等方式及时送达。而且,HertzBeat 可将 Http、Jmx、Ssh、Snmp、Jdbc 等协议规范可配置化,用户只需在浏览器配置 YML 就能使用这些协议去自定义采集任何您想要的指标。 + +你相信,只需配置下就能立刻适配一款 K8s 或 Docker 等新的监控类型吗?HertzBeat 的强大自定义,多类型支持,易扩展,低耦合,能帮助开发者和中小团队快速搭建自有监控系统。 + +### \***\*HertzBeat 的强大自定义功能\*\*** + +作为一款新出的监控工具,Hertzbeat 并没有去创建自有的协议让对端监控去适配,因为这需要大量的生态适配和自生产品强大的影响力才能做到让别人来改造适配,这对一个新晋开源项目来说是不现实的。针对市面上已有的标准协议,Hertzbeat 只需使用这些协议并把它们做好,就可以满足绝大部分需求。 + +![](/assets/img/blog/DTSE-Tech-Talk-29-2.png) + +在网管协议 snmp 中,主流数据库的 sdk 中要属 java 的最完整规范。java jdbc 规范可以让我们去直连所有的主流数据库。java mbean 指标信息通过 jmx 方式暴露,很多应用都提供 api json 接口供我们获取,当然还有 ssh 直连 linux unix 系统运行脚本获取回显,ipmi 去监控服务器指标,现有的 promethues exporter,openmetrics 规范等等,这些协议规范可以满足我们绝大部分场景。 + +![](/assets/img/blog/DTSE-Tech-Talk-29-3.png) + +而 Hertzbeat 就是将 http、jmx、snmp、jdbc、ssh 等等协议或格式抽象规范,使其可统一配置化。用户通过配置 YML 格式的监控模版定义, 就能使用这些协议来采集想要的指标。 + +![](/assets/img/blog/DTSE-Tech-Talk-29-4.png) + +一个监控类型对应一个监控模版,监控模版定义的配置也是在页面可操作的,目前官方支持的所有监控类型,其实就是一个个监控模版。用户可以在页面新增自定义的监控模版,也可以对内置模版修改更新。 + +### \***\*HertzBeat 的易用来自于诸多模板支持\*\*** + +目前 HertzBeat 已支持应用服务、数据库、操作系统、云原生等领域的监控模板。主要原理是它将监控采集类型(mysql、jvm、k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控。 + +![](/assets/img/blog/DTSE-Tech-Talk-29-5.png) + +不仅如此,也支持多平台告警消息渠道,标签&级别过滤,支持告警静默,恢复,通知时段等等。 + +![](/assets/img/blog/DTSE-Tech-Talk-29-6.png) + +### \***\*开源项目离不开小伙伴协助\*\*** + +Hertzbeat 目前在 github gitee 都有同步仓库,其是开源社区 Dromara 下的开源项目。 + +https://github.com/dromara/hertzbeat github star 3k, + +https://gitee.com/dromara/hertzbeat gitee star 1.6k , gitee gvp + +Dockerhub 下载量 10K+。作为一个开源项目,除了项目本身的 star 数量,下载数量等活跃度之外,项目的开发者生态也是及其重要的。目前有超过 121 位小伙伴给 hertzbeat 代码仓库贡献过 PR,hertzbeat 的现在离不开开发者们的每一行代码。我们作为程序员可能大家都会有这样一个想法,就是自己写的代码可以部署到成千上万的服务器跑起来中给他人使用,这样的想法或者说是小梦想我们正在慢慢做到,想想有点小激动哈哈。 + +### \***\*HertzBeat for Huawei Cloud\*\*** + +关于 HuaweiCloud 开源活动,华为云面向开源软件工具链与环境、开源应用构建和开源生态组件构建这三大重点场景,提供技术支持、奖金支持、活动支持,邀请更多的开发者,携手构建开源 for HuaweiCloud。 + +开发者将开源软件工具、开源应用和开源组件与华为云对象存储 OBS、数仓 DWS、云容器 CCE 等云服务对接,同时基于 Terraform 模板,上架到华为云云商店,支持其他开发者一键部署使用开源组件 ,称为“**开源 xxx for HuaweiCloud**”。 + +Hertzbeat 与 Huaweicloud 的开源合作需求已经作为 feature issue 发布到 github 仓库了,已经实现了三个,欢迎大家了解。 + +- \[Task\] support using Huawei Cloud OBS to store custom define yml file #841 +- \[Task\] support Huawei Cloud CCE metrics monitoring #839 +- \[Task\] support EulerOS metrics monitoring #838 +- \[Task\] support using Huawei Cloud SMN send alarm notification message #837 +- \[Task\] support using GaussDB For Influx store history metrics data #836 + +为了更好的方便用户基于华为云部署 Hertzbeat,大家可前往华为云商店参看,只需几分钟就可**一键部署 Hertzbeat**。通过华为云商店部署 Hertzbeat,除了对用户的方便快捷之外,对开源团队也是有意义。Hertzbeat 本身在云商店完全免费,用户在云商店部署只收取本来的云服务器费用。当用户通过这样方式部署到云服务器时,华为云会给开源团队一定的云服务器费用分成来资助开源团队的发展。 + +如果你刚好需要云服务器来部署 hertzbeat,可以通过华为云商店免费使用。 +![](/assets/img/blog/DTSE-Tech-Talk-29-7.png) diff --git a/src/zh/blog/Fast Request-0.md b/src/zh/blog/Fast Request-0.md index 57bc837a50..38b75debd9 100644 --- a/src/zh/blog/Fast Request-0.md +++ b/src/zh/blog/Fast Request-0.md @@ -5,7 +5,7 @@ date: 2024-05-15 cover: /assets/img/blog/Fast Request-0-1.webp head: - - meta - - name: 新闻ç + - name: 新闻 --- **Fast Request** 是一个类似于 Postman 的 IDEA 插件。它是一个强大的 Restful API 工具包插件,可以根据已有的方法帮助您快速、自动生成 URL 和 Params。 diff --git a/src/zh/blog/HertzBea-collection-works.md b/src/zh/blog/HertzBea-collection-works.md new file mode 100644 index 0000000000..f424c4a772 --- /dev/null +++ b/src/zh/blog/HertzBea-collection-works.md @@ -0,0 +1,130 @@ +--- +title: HertzBeat 幕后花絮:公制集合的工作原理 +author: 2025年04月03日 11:19 +date: 2025-04-03 +cover: /assets/img/blog/HertzBea-collection-works-0.png +head: + - - meta + - name: 博客 +--- + + + +> 来自Apache HertzBeat 社区韩国朋友 @JuJinPark 的文章,写的很棒,这里就直接贴原文不翻译了。 + +> HertzBeat 是一个开源的实时监控系统,旨在实现灵活性和易用性。但它究竟是如何收集、处理和存储来自各种系统的指标的呢? + +在这篇文章中,我们将借助高级系统图来了解 **HertzBeat 指标收集管道**背后的内部架构——从作业分配到警报和存储。 + +* * * + +### HertzBeat 的公制集合架构 + + + +![](/assets/img/blog/HertzBea-collection-works-0.png) + +> **图**:HertzBeat 指标收集系统的高级架构。Manager 处理作业调度、警报和存储,而 Collector(外部或内部)执行实际的指标收集。Manager 和 Collectors 之间的通信使用自定义的 Netty TCP 协议。 + +* * * + +### 1\. 作业分配:分配要监控的内容 + +当 **Manager** 组件启动时,它会从数据库加载监视目标。这些目标定义主机、收集间隔和其他参数。 + +为了分配工作负载,Manager 通过**基于 Netty 的自定义 TCP 协议**将作业发送到**外部收集器**。该模块使用**一致的哈希**处理此逻辑,确保作业在收集器之间均匀分布。`CollectJobScheduling` + +> 💡 HertzBeat 还包括一个内置的**主收集器**(标识为 ),它直接在管理器内运行。这使得 HertzBeat 可以在**独立模式下**运行,而无需任何外部收集器。`MAIN_COLLECTOR_NODE` + +* * * + +### 2\. 任务调度:何时监控 + +收集器收到作业后,它会将其注册到 **`TimerDispatch`** 系统。 + +* 对于**外部收集器**,Manager 通过 TCP 连接发送任务。 + +* 对于**主收集器**,Manager 在同一进程中直接调用。`CollectJobService` + + +每个收集器在后台线程中运行一个**`计时器`**,该计时器根据其配置的时间间隔计划任务。时间到时,计时器会触发开始 指标收集。`TimerTask` + +* * * + +### 3\. 任务执行:如何收集指标 + +触发 a 时 ,它会创建一个 任务并将其传递给 ,后者将其置于 **`MetricsCollectorQueue`** 中。`TimerTask``MetricsCollect``MetricsTaskDispatch` + +* 专用线程 () 持续轮询此队列。`CommonDispatcher` + +* 任务由**工作线程池**执行,允许多个指标集合并发运行。 + +* 每个任务都使用特定的**收集器策略**(例如 HTTP、JDBC、SSH)从目标系统获取指标。 + + +* * * + +### 4\. 结果处理:收集的数据会发生什么 + +收集指标后,结果将由 **`CollectDataDispatch`** 模块处理。 + +* 如果任务是重复的,则通过 重新安排。`TimerDispatch` + +* 结果将添加到 **`CommonDataQueue`** 中以供进一步处理。 + + +对于外部收集器,结果通过 Netty TCP 连接发送回 Manager。对于主收集器,结果直接转发 到下一个处理阶段,没有网络开销。 + +* * * + +### 5\. 警报和存储:使指标有用 + +Manager 接收指标数据并将其推送到 中,通过两个主要管道进行处理:`MetricsDataToAlertQueue` + +#### 🔔 提醒 + +* 使用 警报队列中的指标。`RealTimeAlertCalculator` + +* 它根据用户定义的警报规则检查每个指标,并在满足条件时触发警报。 + + +#### 🧠 存储 + +* 警报评估后,指标将添加到 .`MetricsDataToStorageQueue` + +* 后台线程 () 处理此队列并将指标存储在数据库中,以便进行长期分析和仪表板可视化。`DataStorageDispatch` + + +* * * + +### 独立模式:无需外部集电极 + +得益于内置**的主集电器**,HertzBeat 可以完全在独立模式下运行。这对于测试、小型部署或快速设置特别有用。所有核心组件(作业调度、收集、警报和存储)都在单个进程中运行。 + +* * * + +### 🧠 结论 + +HertzBeat 的公制收集系统专为**性能、可扩展性和灵活性**而设计。凭借它: + +* **基于队列的多线程架构** + +* **持久的 TCP 连接**,实现可靠的作业/结果流 + +* **内置主集电器**,可独立作 + + +它以最小的开销和高效率处理大规模监控工作负载。 + +* * * + +### 🙌 下一步是什么? + +如果您想了解更多信息: + +* ⭐️ 在 GitHub 上为项目加星标 + +* 🤝 贡献或打开问题 + + +https://github.com/apache/hertzbeat \ No newline at end of file diff --git a/src/zh/blog/HertzBeat-intro.md b/src/zh/blog/HertzBeat-intro.md index 5edf4a71e9..262d916a21 100644 --- a/src/zh/blog/HertzBeat-intro.md +++ b/src/zh/blog/HertzBeat-intro.md @@ -1,258 +1,258 @@ ---- -title: 详细介绍下我们做的开源项目-赫兹跳动 -author: tom -tag: - - HertzBeat -date: 2024-01-18 -cover: /assets/img/blog/HertzBeat-intro-0.png -head: - - - meta - - name: 博客 ---- - -> 做了这么久,好像都没有出文章详细介绍了我们做的这个开源项目,在这里给大家介绍一波。 - -## 🎡 介绍 - -HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,兼容 Prometheus,无需 Agent 的开源实时监控告警系统。 - -### 特点 - -- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。 -- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 -- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? -- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。 -- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 -- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。 - -> `HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助开发者和团队快速搭建自有监控系统。 -> 当然我们也提供了对应的 \*\*SAAS 版本监控云服务**,中小团队和个人无需再为监控自有资源而去部署一套监控系统,**登录即可免费开始\*\*。 - ---- - -### 强大的监控模版 - -> 开始我们就说 HertzBeat 的特点是自定义监控能力,无需 Agent。在讨论这两点之前,我们先介绍下 HertzBeat 的不一样的监控模版。而正是因为这样的监控模版设计,才会有了后面的高级特性。 - -HertzBeat 自身并没有去创造一种采集数据协议让监控对端来适配它。而是充分使用了现有的生态,`SNMP协议`采集网络交换机路由器信息,`JMX规范`采集 JAVA 应用信息,`JDBC规范`采集数据集信息,`SSH`直连执行脚本获取回显信息,`HTTP+(JsonPath | prometheus等)`解析 API 接口信息,`IPMI协议`采集服务器信息等等。 -HertzBeat 使用这些已有的标准协议或规范,将他们抽象规范可配置化,最后使其都可以通过编写 YML 格式监控模版的形式,来制定模版使用这些协议来采集任何想要的指标数据。 - -![](/assets/img/blog/HertzBeat-intro-0.png) - -你相信用户只需在 UI 页面编写一个监控模版,点击保存后,就能立刻适配一款`K8s`或`Docker`等新的监控类型吗? - -![](/assets/img/blog/HertzBeat-intro-1.png) - -### 内置监控类型 - -**官方内置了大量的监控模版类型,方便用户直接在页面添加使用,一款监控类型对应一个 YML 监控模版** - -- Website, Port Telnet,Http Api, Ping Connect,Jvm, SiteMap,Ssl Certificate, SpringBoot2,FTP Server, SpringBoot3,Udp Port, Dns,Pop3, Ntp,Api Code, Smtp,Nginx -- Mysql, PostgreSQL,MariaDB, Redis,ElasticSearch, SqlServer,Oracle, MongoDB,DM, OpenGauss,ClickHouse, IoTDB,Redis Cluster, Redis SentinelDoris BE, Doris FE,Memcached, NebulaGraph -- Linux, Ubuntu,CentOS, Windows,EulerOS, Fedora CoreOS,OpenSUSE, Rocky Linux,Red Hat, FreeBSD,AlmaLinux, Debian Linux -- Tomcat, Nacos,Zookeeper, RabbitMQ,Flink, Kafka,ShenYu, DynamicTp,Jetty, ActiveMQ,Spring Gateway, EMQX MQTT,AirFlow, Hive,Spark, Hadoop -- Kubernetes, Docker -- CiscoSwitch, HpeSwitch,HuaweiSwitch, TpLinkSwitch,H3cSwitch -- 和更多自定义监控模版。 -- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。 - -### 强大自定义功能 - -> 由前面的**监控模版**介绍,大概清楚了 `HertzBeat` 拥有的强大自定义功能。 -> 我们将每个监控类型都视为一个监控模版,不管是官方内置的还是后期用户自定义新增的。用户都可以方便的通过修改监控模版来新增修改删除监控指标。 -> 模版里面包含各个协议的使用配置,环境变量,指标转换,指标计算,单位转换,指标采集等一系列功能,帮助用户能采集到自己想要的监控指标。 - -![](/assets/img/blog/HertzBeat-intro-2.png) - -### 无需 Agent - -> 对于使用过各种系统的用户来说,可能最麻烦头大的不过就是各种 `agent` 的安装部署调试升级了。 -> 每台主机得装个 `agent`,为了监控不同应用中间件可能还得装几个对应的 `agent`,监控数量上来了轻轻松松上千个,写个批量脚本可能会减轻点负担。 -> `agent` 的版本是否与主应用兼容, `agent` 与主应用的通讯调试, `agent` 的同步升级等等等等,这些全是头大的点。 - -`HertzBeat` 的原理就是使用不同的协议去直连对端系统,采用 `PULL` 的形式去拉取采集数据,无需用户在对端主机上部署安装 `Agent` | `Exporter` 等。 - -- 比如监控 `linux操作系统`, 在 `HertzBeat` 端输入 IP 端口账户密码或密钥即可。 -- 比如监控 `mysql数据库`, 在 `HertzBeat` 端输入 IP 端口账户密码即可。 - **密码等敏感信息全链路加密** - -### 高性能集群 - -> 当监控数量指数级上升,采集性能下降或者环境不稳定容易造成采集器单点故障时,这时我们的采集器集群就出场了。 - -- `HertzBeat` 支持部署采集器集群,多采集器集群横向扩展,指数级提高可监控数量与采集性能。 -- 监控任务在采集器集群中自调度,单采集器挂掉无感知故障迁移采集任务,新加入采集器节点自动调度分担采集压力。 -- 单机模式与集群模式相互切换部署非常方便,无需额外组件部署。 - -![](/assets/img/blog/HertzBeat-intro-3.png) - -### 云边协同 - -> 两地三中心,多云环境,多隔离网络,这些场景名词可能大家略有耳闻。当需要用一套监控系统统一监控不同隔离网络的 IT 资源时,这时我们的云边协同就来啦。 - -- `HertzBeat` 支持部署边缘采集器集群,与主 `HertzBeat` 服务云边协同提升采集能力。 - -在多个网络不相通的隔离网络中,在以往方案中我们需要在每个网络都部署一套监控系统,这导致数据不互通,管理部署维护都不方便。 -`HertzBeat` 提供的云边协同能力,可以在多个隔离网络部署边缘采集器,采集器在隔离网络内部进行监控任务采集,采集数据上报,由主服务统一调度管理展示。 - -![](/assets/img/blog/HertzBeat-intro-4.png) - -### 易用友好 - -- 集 **监控+告警+通知** All in one, 无需单独部署多个组件服务。 -- 全 UI 界面操作,不管是新增监控,修改监控模版,还是告警阈值通知,都可在 WEB 界面操作完成,无需要修改文件或脚本或重启。 -- 无需 Agent, 监控对端我们只需在 WEB 界面填写所需 IP 端口账户密码等参数即可。 -- 自定义友好,只需一个监控模版 YML,自动生成对应监控类型的监控管理页面,数据图表页面,阈值配置等。 -- 阈值告警通知友好,基于表达式阈值配置,多种告警通知渠道,支持告警静默,时段标签告警级别过滤等。 - -### 完全开源 - -- Dromara 开源社区顶级项目,Gitee GVP,使用`Apache2`协议,由自由开放的开源社区主导维护的开源协作产品。 -- 无监控数量`License`,监控类型限制等伪开源限制。 -- 基于`Java+SpringBoot+TypeScript+Angular`主流技术栈构建,方便的二次开发。 -- 开源不等同于免费,不能基于 HertzBeat 二次开发修改 logo,名称,版权等。 - -**HertzBeat 已被 CNCF 云原生全景图 收录** - -![](/assets/img/blog/HertzBeat-intro-5.png) - ---- - -**`HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助开发者和团队快速搭建自有监控系统。** - ---- - -## 即刻体验一波 - -Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` -浏览器访问 `http://localhost:1157` 默认账户密码 `admin/hertzbeat` - -### 登陆页面 - -- HertzBeat 的用户管理统一由配置文件 `sureness.yml` 维护,用户可以通过修改此文件来新增删除修改用户信息,用户角色权限等。默认账户密码 admin/hertzbeat - -![](/assets/img/blog/HertzBeat-intro-6.png) - -### 概览页面 - -- 全局概览页面,分类展示了当前监控大类别数量分布,用户可直观查看当前的监控类型与数量并点击跳转至对应监控类型进行维护管理。 -- 展示当前注册的采集器集群状态,包括采集器的上线状态,监控任务,启动时间,IP 地址,名称等。 -- 下发展示了最近告警信息列表,告警级别分布情况,告警处理率情况。 - -![](/assets/img/blog/HertzBeat-intro-7.png) - -### 监控中心 - -- 监控入口,支持对应用服务,数据库,操作系统,中间件,网络,自定义等监控的管理。 -- 以列表的形式展示当前已添加的监控,支持对监控的新增,修改,删除,取消监控,导入导出,批量管理等。 -- 支持标签分组,查询过滤,查看监控详情入口等。 - -内置支持的监控类型包括: - -- Website, Port Telnet,Http Api, Ping Connect,Jvm, SiteMap,Ssl Certificate, SpringBoot2,FTP Server, SpringBoot3,Udp Port, Dns,Pop3, Ntp,Api Code, Smtp,Nginx -- Mysql, PostgreSQL,MariaDB, Redis,ElasticSearch, SqlServer,Oracle, MongoDB,DM, OpenGauss,ClickHouse, IoTDB,Redis Cluster, Redis SentinelDoris BE, Doris FE,Memcached, NebulaGraph -- Linux, Ubuntu,CentOS, Windows,EulerOS, Fedora CoreOS,OpenSUSE, Rocky Linux,Red Hat, FreeBSD,AlmaLinux, Debian Linux -- Tomcat, Nacos,Zookeeper, RabbitMQ,Flink, Kafka,ShenYu, DynamicTp,Jetty, ActiveMQ,Spring Gateway, EMQX MQTT,AirFlow, Hive,Spark, Hadoop -- Kubernetes, Docker -- CiscoSwitch, HpeSwitch,HuaweiSwitch, TpLinkSwitch,H3cSwitch - -![](/assets/img/blog/HertzBeat-intro-8.png) - -### 新增修改监控 - -- 新增或修改指定监控类型的监控实例,配置对端监控的 IP,端口等参数,设置采集周期,采集任务调度方式,支持提前探测可用性等。 -- 页面上配置的监控参数由对应监控类型的监控模版所定义,用户可以通过修改监控模版来修改页面配置参数。 -- 支持关联标签,用标签来管理监控分组,告警匹配等。 - -![](/assets/img/blog/HertzBeat-intro-9.png) - -### 监控详情 - -- 监控的数据详情页面,展示了当前监控的基本参数信息,监控指标数据信息。 -- 监控实时数据报告,以小卡片列表的形式展示了当前监控的所有指标实时值,用户可根据实时值参考配置告警阈值规则。 -- 监控历史数据报告,以趋势图表的形式展示了当前监控数值类型的指标的历史值,支持查询小时,天,月的历史数据,支持配置页面刷新时间。 -- ⚠️ 注意监控历史图表需配置外置时序数据库才能获取完整功能,时序数据库支持: IOTDB, TDengine, InfluxDB, GreptimeDB - -![](/assets/img/blog/HertzBeat-intro-10.png) - -![](/assets/img/blog/HertzBeat-intro-11.png) - -### 告警中心 - -- 已触发告警消息的管理展示页面,使用户有直观的展示当前告警情况。 -- 支持告警处理,告警标记未处理,告警删除清空等批量操作。 - -![](/assets/img/blog/HertzBeat-intro-12.png) - -hertzbeat - -### 阈值规则 - -- 对于监控的可用性状态设置阈值规则,特定指标的值超过我们预期范围时发出告警,这些都可以在阈值规则这里配置。 -- 告警级别分为三级:通知告警,严重告警,紧急告警。 -- 阈值规则支持可视化页面配置或表达式规则配置,灵活性更高。 -- 支持配置触发次数,告警级别,通知模版,关联指定监控等。 - -![](/assets/img/blog/HertzBeat-intro-13.png) - -hertzbeat - -![](/assets/img/blog/HertzBeat-intro-14.png) - -hertzbeat - -### 告警收敛 - -- 当通过阈值规则判断触发告警后,会进入到告警收敛,告警收敛会根据规则对特定时间段的重复告警消息去重收敛,已避免大量重复性告警导致接收人告警麻木。 -- 告警收敛规则支持重复告警生效时间段,标签匹配和告警级别匹配过滤。 - -![](/assets/img/blog/HertzBeat-intro-15.png) - -hertzbeat - -![](/assets/img/blog/HertzBeat-intro-16.png) - -hertzbeat - -### 告警静默 - -- 当通过阈值规则判断触发告警后,会进入到告警静默,告警静默会根据规则对特定一次性时间段或周期性时候段的告警消息屏蔽静默,此时间段不发送告警消息。 -- 此应用场景如用户在系统维护中,无需发已知告警。用户在工作日时间才会接收告警消息,用户在晚上需避免打扰等。 -- 告警静默规则支持一次性时间段或周期性时间段,支持标签匹配和告警级别匹配。 - -![](/assets/img/blog/HertzBeat-intro-17.png) - -hertzbeat - -![](/assets/img/blog/HertzBeat-intro-18.png) - -hertzbeat - -### 消息通知 - -- 消息通知功能是把告警消息通过不同媒体渠道通知给指定的接收人,告警消息及时触达。 -- 功能包含接收人信息管理和通知策略管理,接收人管理维护接收人信息以其通知方式信息,通知策略管理维护把哪些告警信息通知给哪些接收人的策略规则。 -- 通知方式支持 `邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式。 -- 通知策略支持标签匹配和告警级别匹配,方便的使不同标签的告警和告警级别分派给不同的接收处理人。 -- 支持通知模版,用户可以自定义通过模版内容格式来满足自己的个性化通知展示需求。 - -![](/assets/img/blog/HertzBeat-intro-19.png) - -![](/assets/img/blog/HertzBeat-intro-20.png) - -### 监控模版 - -- HertzBeat 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? -- 同理我们内置的所有监控类型(mysql,website,jvm,k8s)也一一映射为对应的监控模版,用户可以新增修改监控模版来自定义监控功能。 - -![](/assets/img/blog/HertzBeat-intro-21.png) - ---- - -**还有更多强大的功能快去探索呀。Have Fun!** - ---- - -**官网: https://hertzbeat.com/** -**Github: https://github.com/dromara/hertzbeat** -**Gitee: https://gitee.com/dromara/hertzbeat** +--- +title: 详细介绍下我们做的开源项目-赫兹跳动 +author: tom +tag: + - HertzBeat +date: 2024-01-18 +cover: /assets/img/blog/HertzBeat-intro-0.png +head: + - - meta + - name: 博客 +--- + +> 做了这么久,好像都没有出文章详细介绍了我们做的这个开源项目,在这里给大家介绍一波。 + +## 🎡 介绍 + +HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,兼容 Prometheus,无需 Agent 的开源实时监控告警系统。 + +### 特点 + +- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。 +- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 +- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? +- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。 +- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 +- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。 + +> `HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助开发者和团队快速搭建自有监控系统。 +> 当然我们也提供了对应的 \*\*SAAS 版本监控云服务**,中小团队和个人无需再为监控自有资源而去部署一套监控系统,**登录即可免费开始\*\*。 + +--- + +### 强大的监控模版 + +> 开始我们就说 HertzBeat 的特点是自定义监控能力,无需 Agent。在讨论这两点之前,我们先介绍下 HertzBeat 的不一样的监控模版。而正是因为这样的监控模版设计,才会有了后面的高级特性。 + +HertzBeat 自身并没有去创造一种采集数据协议让监控对端来适配它。而是充分使用了现有的生态,`SNMP协议`采集网络交换机路由器信息,`JMX规范`采集 JAVA 应用信息,`JDBC规范`采集数据集信息,`SSH`直连执行脚本获取回显信息,`HTTP+(JsonPath | prometheus等)`解析 API 接口信息,`IPMI协议`采集服务器信息等等。 +HertzBeat 使用这些已有的标准协议或规范,将他们抽象规范可配置化,最后使其都可以通过编写 YML 格式监控模版的形式,来制定模版使用这些协议来采集任何想要的指标数据。 + +![](/assets/img/blog/HertzBeat-intro-0.png) + +你相信用户只需在 UI 页面编写一个监控模版,点击保存后,就能立刻适配一款`K8s`或`Docker`等新的监控类型吗? + +![](/assets/img/blog/HertzBeat-intro-1.png) + +### 内置监控类型 + +**官方内置了大量的监控模版类型,方便用户直接在页面添加使用,一款监控类型对应一个 YML 监控模版** + +- Website, Port Telnet,Http Api, Ping Connect,Jvm, SiteMap,Ssl Certificate, SpringBoot2,FTP Server, SpringBoot3,Udp Port, Dns,Pop3, Ntp,Api Code, Smtp,Nginx +- Mysql, PostgreSQL,MariaDB, Redis,ElasticSearch, SqlServer,Oracle, MongoDB,DM, OpenGauss,ClickHouse, IoTDB,Redis Cluster, Redis SentinelDoris BE, Doris FE,Memcached, NebulaGraph +- Linux, Ubuntu,CentOS, Windows,EulerOS, Fedora CoreOS,OpenSUSE, Rocky Linux,Red Hat, FreeBSD,AlmaLinux, Debian Linux +- Tomcat, Nacos,Zookeeper, RabbitMQ,Flink, Kafka,ShenYu, DynamicTp,Jetty, ActiveMQ,Spring Gateway, EMQX MQTT,AirFlow, Hive,Spark, Hadoop +- Kubernetes, Docker +- CiscoSwitch, HpeSwitch,HuaweiSwitch, TpLinkSwitch,H3cSwitch +- 和更多自定义监控模版。 +- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。 + +### 强大自定义功能 + +> 由前面的**监控模版**介绍,大概清楚了 `HertzBeat` 拥有的强大自定义功能。 +> 我们将每个监控类型都视为一个监控模版,不管是官方内置的还是后期用户自定义新增的。用户都可以方便的通过修改监控模版来新增修改删除监控指标。 +> 模版里面包含各个协议的使用配置,环境变量,指标转换,指标计算,单位转换,指标采集等一系列功能,帮助用户能采集到自己想要的监控指标。 + +![](/assets/img/blog/HertzBeat-intro-2.png) + +### 无需 Agent + +> 对于使用过各种系统的用户来说,可能最麻烦头大的不过就是各种 `agent` 的安装部署调试升级了。 +> 每台主机得装个 `agent`,为了监控不同应用中间件可能还得装几个对应的 `agent`,监控数量上来了轻轻松松上千个,写个批量脚本可能会减轻点负担。 +> `agent` 的版本是否与主应用兼容, `agent` 与主应用的通讯调试, `agent` 的同步升级等等等等,这些全是头大的点。 + +`HertzBeat` 的原理就是使用不同的协议去直连对端系统,采用 `PULL` 的形式去拉取采集数据,无需用户在对端主机上部署安装 `Agent` | `Exporter` 等。 + +- 比如监控 `linux操作系统`, 在 `HertzBeat` 端输入 IP 端口账户密码或密钥即可。 +- 比如监控 `mysql数据库`, 在 `HertzBeat` 端输入 IP 端口账户密码即可。 + **密码等敏感信息全链路加密** + +### 高性能集群 + +> 当监控数量指数级上升,采集性能下降或者环境不稳定容易造成采集器单点故障时,这时我们的采集器集群就出场了。 + +- `HertzBeat` 支持部署采集器集群,多采集器集群横向扩展,指数级提高可监控数量与采集性能。 +- 监控任务在采集器集群中自调度,单采集器挂掉无感知故障迁移采集任务,新加入采集器节点自动调度分担采集压力。 +- 单机模式与集群模式相互切换部署非常方便,无需额外组件部署。 + +![](/assets/img/blog/HertzBeat-intro-3.png) + +### 云边协同 + +> 两地三中心,多云环境,多隔离网络,这些场景名词可能大家略有耳闻。当需要用一套监控系统统一监控不同隔离网络的 IT 资源时,这时我们的云边协同就来啦。 + +- `HertzBeat` 支持部署边缘采集器集群,与主 `HertzBeat` 服务云边协同提升采集能力。 + +在多个网络不相通的隔离网络中,在以往方案中我们需要在每个网络都部署一套监控系统,这导致数据不互通,管理部署维护都不方便。 +`HertzBeat` 提供的云边协同能力,可以在多个隔离网络部署边缘采集器,采集器在隔离网络内部进行监控任务采集,采集数据上报,由主服务统一调度管理展示。 + +![](/assets/img/blog/HertzBeat-intro-4.png) + +### 易用友好 + +- 集 **监控+告警+通知** All in one, 无需单独部署多个组件服务。 +- 全 UI 界面操作,不管是新增监控,修改监控模版,还是告警阈值通知,都可在 WEB 界面操作完成,无需要修改文件或脚本或重启。 +- 无需 Agent, 监控对端我们只需在 WEB 界面填写所需 IP 端口账户密码等参数即可。 +- 自定义友好,只需一个监控模版 YML,自动生成对应监控类型的监控管理页面,数据图表页面,阈值配置等。 +- 阈值告警通知友好,基于表达式阈值配置,多种告警通知渠道,支持告警静默,时段标签告警级别过滤等。 + +### 完全开源 + +- Dromara 开源社区顶级项目,Gitee GVP,使用`Apache2`协议,由自由开放的开源社区主导维护的开源协作产品。 +- 无监控数量`License`,监控类型限制等伪开源限制。 +- 基于`Java+SpringBoot+TypeScript+Angular`主流技术栈构建,方便的二次开发。 +- 开源不等同于免费,不能基于 HertzBeat 二次开发修改 logo,名称,版权等。 + +**HertzBeat 已被 CNCF 云原生全景图 收录** + +![](/assets/img/blog/HertzBeat-intro-5.png) + +--- + +**`HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助开发者和团队快速搭建自有监控系统。** + +--- + +## 即刻体验一波 + +Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` +浏览器访问 `http://localhost:1157` 默认账户密码 `admin/hertzbeat` + +### 登陆页面 + +- HertzBeat 的用户管理统一由配置文件 `sureness.yml` 维护,用户可以通过修改此文件来新增删除修改用户信息,用户角色权限等。默认账户密码 admin/hertzbeat + +![](/assets/img/blog/HertzBeat-intro-6.png) + +### 概览页面 + +- 全局概览页面,分类展示了当前监控大类别数量分布,用户可直观查看当前的监控类型与数量并点击跳转至对应监控类型进行维护管理。 +- 展示当前注册的采集器集群状态,包括采集器的上线状态,监控任务,启动时间,IP 地址,名称等。 +- 下发展示了最近告警信息列表,告警级别分布情况,告警处理率情况。 + +![](/assets/img/blog/HertzBeat-intro-7.png) + +### 监控中心 + +- 监控入口,支持对应用服务,数据库,操作系统,中间件,网络,自定义等监控的管理。 +- 以列表的形式展示当前已添加的监控,支持对监控的新增,修改,删除,取消监控,导入导出,批量管理等。 +- 支持标签分组,查询过滤,查看监控详情入口等。 + +内置支持的监控类型包括: + +- Website, Port Telnet,Http Api, Ping Connect,Jvm, SiteMap,Ssl Certificate, SpringBoot2,FTP Server, SpringBoot3,Udp Port, Dns,Pop3, Ntp,Api Code, Smtp,Nginx +- Mysql, PostgreSQL,MariaDB, Redis,ElasticSearch, SqlServer,Oracle, MongoDB,DM, OpenGauss,ClickHouse, IoTDB,Redis Cluster, Redis SentinelDoris BE, Doris FE,Memcached, NebulaGraph +- Linux, Ubuntu,CentOS, Windows,EulerOS, Fedora CoreOS,OpenSUSE, Rocky Linux,Red Hat, FreeBSD,AlmaLinux, Debian Linux +- Tomcat, Nacos,Zookeeper, RabbitMQ,Flink, Kafka,ShenYu, DynamicTp,Jetty, ActiveMQ,Spring Gateway, EMQX MQTT,AirFlow, Hive,Spark, Hadoop +- Kubernetes, Docker +- CiscoSwitch, HpeSwitch,HuaweiSwitch, TpLinkSwitch,H3cSwitch + +![](/assets/img/blog/HertzBeat-intro-8.png) + +### 新增修改监控 + +- 新增或修改指定监控类型的监控实例,配置对端监控的 IP,端口等参数,设置采集周期,采集任务调度方式,支持提前探测可用性等。 +- 页面上配置的监控参数由对应监控类型的监控模版所定义,用户可以通过修改监控模版来修改页面配置参数。 +- 支持关联标签,用标签来管理监控分组,告警匹配等。 + +![](/assets/img/blog/HertzBeat-intro-9.png) + +### 监控详情 + +- 监控的数据详情页面,展示了当前监控的基本参数信息,监控指标数据信息。 +- 监控实时数据报告,以小卡片列表的形式展示了当前监控的所有指标实时值,用户可根据实时值参考配置告警阈值规则。 +- 监控历史数据报告,以趋势图表的形式展示了当前监控数值类型的指标的历史值,支持查询小时,天,月的历史数据,支持配置页面刷新时间。 +- ⚠️ 注意监控历史图表需配置外置时序数据库才能获取完整功能,时序数据库支持: IOTDB, TDengine, InfluxDB, GreptimeDB + +![](/assets/img/blog/HertzBeat-intro-10.png) + +![](/assets/img/blog/HertzBeat-intro-11.png) + +### 告警中心 + +- 已触发告警消息的管理展示页面,使用户有直观的展示当前告警情况。 +- 支持告警处理,告警标记未处理,告警删除清空等批量操作。 + +![](/assets/img/blog/HertzBeat-intro-12.png) + +hertzbeat + +### 阈值规则 + +- 对于监控的可用性状态设置阈值规则,特定指标的值超过我们预期范围时发出告警,这些都可以在阈值规则这里配置。 +- 告警级别分为三级:通知告警,严重告警,紧急告警。 +- 阈值规则支持可视化页面配置或表达式规则配置,灵活性更高。 +- 支持配置触发次数,告警级别,通知模版,关联指定监控等。 + +![](/assets/img/blog/HertzBeat-intro-13.png) + +hertzbeat + +![](/assets/img/blog/HertzBeat-intro-14.png) + +hertzbeat + +### 告警收敛 + +- 当通过阈值规则判断触发告警后,会进入到告警收敛,告警收敛会根据规则对特定时间段的重复告警消息去重收敛,已避免大量重复性告警导致接收人告警麻木。 +- 告警收敛规则支持重复告警生效时间段,标签匹配和告警级别匹配过滤。 + +![](/assets/img/blog/HertzBeat-intro-15.png) + +hertzbeat + +![](/assets/img/blog/HertzBeat-intro-16.png) + +hertzbeat + +### 告警静默 + +- 当通过阈值规则判断触发告警后,会进入到告警静默,告警静默会根据规则对特定一次性时间段或周期性时候段的告警消息屏蔽静默,此时间段不发送告警消息。 +- 此应用场景如用户在系统维护中,无需发已知告警。用户在工作日时间才会接收告警消息,用户在晚上需避免打扰等。 +- 告警静默规则支持一次性时间段或周期性时间段,支持标签匹配和告警级别匹配。 + +![](/assets/img/blog/HertzBeat-intro-17.png) + +hertzbeat + +![](/assets/img/blog/HertzBeat-intro-18.png) + +hertzbeat + +### 消息通知 + +- 消息通知功能是把告警消息通过不同媒体渠道通知给指定的接收人,告警消息及时触达。 +- 功能包含接收人信息管理和通知策略管理,接收人管理维护接收人信息以其通知方式信息,通知策略管理维护把哪些告警信息通知给哪些接收人的策略规则。 +- 通知方式支持 `邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式。 +- 通知策略支持标签匹配和告警级别匹配,方便的使不同标签的告警和告警级别分派给不同的接收处理人。 +- 支持通知模版,用户可以自定义通过模版内容格式来满足自己的个性化通知展示需求。 + +![](/assets/img/blog/HertzBeat-intro-19.png) + +![](/assets/img/blog/HertzBeat-intro-20.png) + +### 监控模版 + +- HertzBeat 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? +- 同理我们内置的所有监控类型(mysql,website,jvm,k8s)也一一映射为对应的监控模版,用户可以新增修改监控模版来自定义监控功能。 + +![](/assets/img/blog/HertzBeat-intro-21.png) + +--- + +**还有更多强大的功能快去探索呀。Have Fun!** + +--- + +**官网: https://hertzbeat.com/** +**Github: https://github.com/dromara/hertzbeat** +**Gitee: https://gitee.com/dromara/hertzbeat** diff --git a/src/zh/blog/LiteFlow-concept.md b/src/zh/blog/LiteFlow-concept.md index e02ff88bcc..c781b0198d 100644 --- a/src/zh/blog/LiteFlow-concept.md +++ b/src/zh/blog/LiteFlow-concept.md @@ -1,205 +1,205 @@ ---- -title: 白话文解析LiteFlow的理念是什么?什么时候用该怎么用?干货满满 -author: 元人部落 -tag: - - LiteFlow -date: 2024-01-19 -cover: /assets/img/blog/LiteFlow-concept-0.png -head: - - - meta - - name: 博客 ---- - -官网:https://liteflow.cc/ - -Gitee:https://gitee.com/dromara/liteFlow - -Github:https://github.com/dromara/liteflow - -LiteFlow 一个现代化的开源规则引擎框架,以下文中简称 LF。 - -## 前言 - -时常在社区里看到有的小伙伴在那提问: - -LF 在一个流程中如何暂停,等待操作员完成后,进行下一步该怎么做? - -LF 流程失败后,下一次能否继续上次的执行? - -LF 流程适不适合某个我的业务? - -LF 流程如何定时执行我的某个流程? - -还有的同学表示即便全部看完文档,也不知道 LF 该用在何种业务场景。能够带来什么好处。 - -究其原因是错误理解了流程的概念和不知道规则引擎的概念。 - -## 流程 - -我们先说流程。 - -LiteFlow 定位是一个规则引擎,而不是流程引擎。它并不完成流程所要做的事,其实压根 LF 和流程一点关系也没有。 - -那什么是流程呢,标准的定义是,流程由**流程定义**,**节点要做的事**和**角色**组成。每一个角色做一件事,根据定义的流程定义串起来就叫流程。最典型的例子就是审批流:采购员提交了一张采购申请,部门领导审核,审核通过则到了财务这里,财务专员根据预算进行审核,审核通过到了总监这里,总监审核通过,再到 CEO 这里签字批准。整个采购单状态变成待采购。然后进行采购流程。 - -以上就是标准的一个流程。3 大要素,**流程定义**,**事**和**角色**一个都不能少。通常在实际落地过程中,流程引擎负责流程的流转和角色的分派。开发人员只需要定义流程,和开发每个角色需要做的逻辑即可。 - -流程引擎重点强调 2 点: - -**流程的定义,下一步是什么,整体的流向,有多少分支。** - -**角色的分派,即下一步该由谁完成**。 - -大部分流程引擎为了灵活性,也提供了流程定义的热更新以及添加角色,修改流程节点更改绑定角色的功能。 - -**虽然 LF 从 EL 规则上来看,似乎也是一个个小模块的流转,但是 LF 并不涉及角色分派这件事。** - -## 规则 - -我们再来看规则引擎的概念。 - -规则引擎主要强调一件事,把业务中最主要的决策逻辑从程序中抽离出来,用预定义的 DSL 来实现。并且可以实时改变这些最主要的决策逻辑。 - -说的再白话点,就是决定逻辑走向的最关键的决策逻辑,不是在代码中的,可以放在外面的任意地方(文件里,数据库里,其他存储,远程获取)。并且这些决策逻辑并不是用你应用的代码语义来实现的。规则引擎提供一套语言,来书写这些决策逻辑。规则引擎也应该提供热更新这些决策代码的功能。 - -可以看出,规则引擎根本不涉及角色的概念,它更多的适用于一个相对比较复杂的逻辑块。把最核心的部分抽出来用规则引擎定义。 - -但是整个逻辑块基本要做的还是一件事情。只是部分抽出来而已。 - -标准的规则引擎处理的例子: - -如果有人 v 我 50 块,我就去家门口的 KFC 吃一顿 - -如果有人 v 我 200 块,我就坐地铁去港式餐厅吃一顿 - -如果有人 v 我 500 块,我就打个车,去吃顿日式烤肉 - -如果有人 v 我 2000 块,我就去买身衣服,去吃顿惠林顿牛排,再整瓶酒。 - -如果有人 v 我 100w,赶紧抽自己一巴掌,看自己醒了没。 - -有同学看到这,可能会说,那我搞个文件,存 groovy 代码,我应用每次执行到关键决策的时候,去读取这个文件里的 groovy 代码,然后解析执行。这不就是规则引擎吗,我要改变决策的时候,每次改那个文件里的 groovy 代码就行了。 - -还真是这样!这就是规则引擎! - -简单来说,规则引擎就强调 3 个点: - -**决策代码不在你的应用程序里** - -**拥有独特的 DSL 语义书写** - -**实时更新,不用改变应用程序** - -所以,一些 DSL 项目也被称作为规则引擎,如 Aviator,QLExpress。这些框架提供了热更的接口,稍作包装,就可以开发出一套最基本的规则引擎。 - -但是我更愿意把这些项目归类为**表达式引擎**,业界还有著名的 SpEL(spring 的 EL),springframwork expression language,其实从全程就可以看出,官方定义了就是**表达式语言**。 - -**LF 满足决策代码可以不写在应用程序里,也拥有独特的 DSL,也支持实时更新。但是 LF 怎么还拥有流转的功能?LF 看起来怎么有点四不像啊?** - -## 那 LF 是什么呢 - -LF 也是作用于一个大的逻辑块的,和角色没关系,满足规则引擎的需要的 3 个关键点。从这点来说,LF 是规则引擎。 - -LiteFlow 中的脚本节点已经满足了规则引擎的全部的定义了。那么 LF 只做脚本节点就可以了。可以热更,拥有独特的 DSL,可以保存在任意地方。 - -但是 LF 也可以流转,从一个节点流转到另外一个节点,但是这里的节点是一个个小的拆分的逻辑块。其实这是 LF 独特的地方,这部分并不是标准规则引擎定义所必须的。我称之为:编排。 - -编排如何理解呢,说的专业点,这就是**逻辑关系反转**,看到这里,有些同学可能又开始看不懂了。你丫的怎么还自己造词呢 。 - -且听我慢慢道来。 - -spring 的出现解决了依赖反转,我们不用 spring 的时候,对象的装载是自己 new 的,spring 出现后,只需要在 xml 里声明后,各个代码块都可以注入了。就不用自己 new 了。这就是依赖反转。相信这个大家学习八股时已经背了无数遍了。 - -那么逻辑关系的进行,我们大部分代码还是 A 类调用 B 类的方法,B 类调用 C 类的方法。这就是逻辑关系的绑死。如果 A,B,C 三个类在代码层面互不相干。但是我在 xml 里去规定,这三个的进行顺序。这就是**逻辑关系反转**。 - -逻辑关系反转有什么好处呢? - -逻辑关系反转后,好处很多,首先每个类不再强依赖,所有的代码都讲究松耦合理论。那么逻辑关系反转后,就完美实现了松耦合。并且 LF 规定了每个类的声明方式,就显得所有的逻辑类都长的差不多。更加统一。 - -其次,逻辑关系反转后,你只需要看 xml 定义就可以大致理解这个大的逻辑块都干了什么事。非常直观。 - -再次,逻辑关系都可以热更,那么这是一件多么优雅的事情啊。 - -那么做逻辑关系反转的引擎我称之为:**编排引擎**。 - -所以,LiteFlow 是**规则引擎**+**编排引擎**的结合体。 - -**它既可以热更新最关键的决策逻辑,又可以热更新逻辑关系的组成。** - -**所以 LiteFlow 做的是更现代化的规则引擎,它在做好规则引擎的基础上,超越了原本规则引擎所规定的范畴。** - -## 如何用好 LF - -其实如果对 LF 理解够深刻,它几乎是一个应用最核心的驱动器。妥妥神器,无论重构,要灵活,要解耦,它都不在话下。 - -如果对 LF 理解不够,那你可能感觉 LF 没什么用。也不知道该怎么用。 - -所以这就是我一开头提到的。理解流程和规则逻辑流的概念很重要。 - -LiteFlow 提供的脚本多达 7 种,这些脚本可以充分书写你的关键决策逻辑。并且 LF 打通了所有脚本和 Java 的互通,用脚本写不了的。也可以调用 java 方法来完成。 - -LiteFlow 提供的独特 EL 适用于做逻辑反转,让你的巨大复杂的逻辑变成一个个小的积木块,通过 DSL 来进行组搭,形成你业务的逻辑流。积木块可以是你的 java 组件,也可以是脚本写的决策逻辑。 - -**所以一般项目推荐的做法,就是首先解耦你的复杂逻辑,按照业务边界拆成一个个小的组件,然后把最经常变动的决策逻辑用脚本组件来实现。加上 DSL 编写的逻辑关系反转表达式。这样形成的系统,其优雅度是非常高的。** - -你想想,你的关键决策代码,和逻辑关系均可以进行热改变,你的系统中耦合性几乎降低到了最低。改一个小组件不会牵扯到其他的组件的改变。 - -## 再回答开头的问题 - -如果你看到这,全都能理解的话,那么最开始的几个问题也就能轻而易举的能回答了。 - -LF 因为没有角色的概念,全部流转的只是小逻辑块。所以没法暂停的。因为就相当于你普通写的瀑布式代码调用。只是换了一种方式来写而已。 - -LF 不保存状态,是一个无状态的东西。一般来说,规则引擎都是无状态的。状态这回事其实还是要业务自己做的。况且有状态这回事情非常危险,一般框架不会去保证这个。 - -LF 一般来说,所有用普通瀑布流代码一层层去调用都可以用 LF 去改造,LF 并不针对于某个特殊的领域,都可以用的。除非你的定义是属于标准的流程引擎干的事。那么的确不应该选型 LF。 - -关于定时,也不是 LF 该干的事。LF 提供接口层面的执行器,外面套层定时框架即可。很简单。 - -## LF 对标的框架是什么? - -LF 的目标始一直没变过,那就是:超越 Drools。 - -LF 独特的理念是国产自研,全部的代码也是国产自研。非常适合拿来做信创项目。 - -Drools 是国外老牌且一向作为行业标杆的规则引擎。相信了解过这行的都有听说过。 - -其实 Drools 只提供了标准的规则引擎,并没有提供逻辑关系反转的特性,而且它的 DSL 学习成本还是比较大的。而 LF 则用现成的语言来提供作为脚本,比如 js,groovy,aviator,python,lua,甚至 java 本身也可以拿来做写脚本,这些根本不用再次学习。 - -LF 除了规则引擎之外,更加拓展了规则引擎的范畴,使得 LF 能做的事情更多。 - -并且 LF 支持的存储中间件之丰富的,也绝非 Drools 能比拟的。 - -加上 LF 全中文文档,社区答疑体系在开源中首屈一指。Drools 一堆英文文档,去哪答疑? - -一定要说 LF 比不上的 Drools 的地方。那就是决策表体系。 - -Drools 是不需要指定规则的,由决策表来多项匹配。LF 由于提供了逻辑反转,是需要指定特定的规则的。 - -但是这个 LF 也准备在 2.11.5 中提供,弥补最后核心上的短板。 - -届时 LF 可以平替 Drools。 - -我会为此一直坚守。 - -谨此此篇献给关注过 LF 的同学。 - -## LF 的 VIP 社区 - -LF 开通了 VIP 付费社区,目前正在试运营,加入 VIP 社区可以最大程度保障您的使用体验。 - -VIP 社区可无限制的提问,并且每次问答都保证及时和详细。确保你完全掌握 LF 的使用。 - -并且 VIP 社区中我会坚持分享 LF 以及规则引擎内容的话题。每周一篇精华文章。大部分是独家拥有,不会发到任何其他平台。 - -现在加入会非常划算。享有以下权益: - -1.极致的答疑体验,保证每次回答具体且详细。 - -2.每周一篇的独家精华内容。 - -3.一年享有 2 次的一对一远程答疑。 - -如果有想加入的同学,可以扫以下二维码 - - +--- +title: 白话文解析LiteFlow的理念是什么?什么时候用该怎么用?干货满满 +author: 元人部落 +tag: + - LiteFlow +date: 2024-01-19 +cover: /assets/img/blog/LiteFlow-concept-0.png +head: + - - meta + - name: 博客 +--- + +官网:https://liteflow.cc/ + +Gitee:https://gitee.com/dromara/liteFlow + +Github:https://github.com/dromara/liteflow + +LiteFlow 一个现代化的开源规则引擎框架,以下文中简称 LF。 + +## 前言 + +时常在社区里看到有的小伙伴在那提问: + +LF 在一个流程中如何暂停,等待操作员完成后,进行下一步该怎么做? + +LF 流程失败后,下一次能否继续上次的执行? + +LF 流程适不适合某个我的业务? + +LF 流程如何定时执行我的某个流程? + +还有的同学表示即便全部看完文档,也不知道 LF 该用在何种业务场景。能够带来什么好处。 + +究其原因是错误理解了流程的概念和不知道规则引擎的概念。 + +## 流程 + +我们先说流程。 + +LiteFlow 定位是一个规则引擎,而不是流程引擎。它并不完成流程所要做的事,其实压根 LF 和流程一点关系也没有。 + +那什么是流程呢,标准的定义是,流程由**流程定义**,**节点要做的事**和**角色**组成。每一个角色做一件事,根据定义的流程定义串起来就叫流程。最典型的例子就是审批流:采购员提交了一张采购申请,部门领导审核,审核通过则到了财务这里,财务专员根据预算进行审核,审核通过到了总监这里,总监审核通过,再到 CEO 这里签字批准。整个采购单状态变成待采购。然后进行采购流程。 + +以上就是标准的一个流程。3 大要素,**流程定义**,**事**和**角色**一个都不能少。通常在实际落地过程中,流程引擎负责流程的流转和角色的分派。开发人员只需要定义流程,和开发每个角色需要做的逻辑即可。 + +流程引擎重点强调 2 点: + +**流程的定义,下一步是什么,整体的流向,有多少分支。** + +**角色的分派,即下一步该由谁完成**。 + +大部分流程引擎为了灵活性,也提供了流程定义的热更新以及添加角色,修改流程节点更改绑定角色的功能。 + +**虽然 LF 从 EL 规则上来看,似乎也是一个个小模块的流转,但是 LF 并不涉及角色分派这件事。** + +## 规则 + +我们再来看规则引擎的概念。 + +规则引擎主要强调一件事,把业务中最主要的决策逻辑从程序中抽离出来,用预定义的 DSL 来实现。并且可以实时改变这些最主要的决策逻辑。 + +说的再白话点,就是决定逻辑走向的最关键的决策逻辑,不是在代码中的,可以放在外面的任意地方(文件里,数据库里,其他存储,远程获取)。并且这些决策逻辑并不是用你应用的代码语义来实现的。规则引擎提供一套语言,来书写这些决策逻辑。规则引擎也应该提供热更新这些决策代码的功能。 + +可以看出,规则引擎根本不涉及角色的概念,它更多的适用于一个相对比较复杂的逻辑块。把最核心的部分抽出来用规则引擎定义。 + +但是整个逻辑块基本要做的还是一件事情。只是部分抽出来而已。 + +标准的规则引擎处理的例子: + +如果有人 v 我 50 块,我就去家门口的 KFC 吃一顿 + +如果有人 v 我 200 块,我就坐地铁去港式餐厅吃一顿 + +如果有人 v 我 500 块,我就打个车,去吃顿日式烤肉 + +如果有人 v 我 2000 块,我就去买身衣服,去吃顿惠林顿牛排,再整瓶酒。 + +如果有人 v 我 100w,赶紧抽自己一巴掌,看自己醒了没。 + +有同学看到这,可能会说,那我搞个文件,存 groovy 代码,我应用每次执行到关键决策的时候,去读取这个文件里的 groovy 代码,然后解析执行。这不就是规则引擎吗,我要改变决策的时候,每次改那个文件里的 groovy 代码就行了。 + +还真是这样!这就是规则引擎! + +简单来说,规则引擎就强调 3 个点: + +**决策代码不在你的应用程序里** + +**拥有独特的 DSL 语义书写** + +**实时更新,不用改变应用程序** + +所以,一些 DSL 项目也被称作为规则引擎,如 Aviator,QLExpress。这些框架提供了热更的接口,稍作包装,就可以开发出一套最基本的规则引擎。 + +但是我更愿意把这些项目归类为**表达式引擎**,业界还有著名的 SpEL(spring 的 EL),springframwork expression language,其实从全程就可以看出,官方定义了就是**表达式语言**。 + +**LF 满足决策代码可以不写在应用程序里,也拥有独特的 DSL,也支持实时更新。但是 LF 怎么还拥有流转的功能?LF 看起来怎么有点四不像啊?** + +## 那 LF 是什么呢 + +LF 也是作用于一个大的逻辑块的,和角色没关系,满足规则引擎的需要的 3 个关键点。从这点来说,LF 是规则引擎。 + +LiteFlow 中的脚本节点已经满足了规则引擎的全部的定义了。那么 LF 只做脚本节点就可以了。可以热更,拥有独特的 DSL,可以保存在任意地方。 + +但是 LF 也可以流转,从一个节点流转到另外一个节点,但是这里的节点是一个个小的拆分的逻辑块。其实这是 LF 独特的地方,这部分并不是标准规则引擎定义所必须的。我称之为:编排。 + +编排如何理解呢,说的专业点,这就是**逻辑关系反转**,看到这里,有些同学可能又开始看不懂了。你丫的怎么还自己造词呢 。 + +且听我慢慢道来。 + +spring 的出现解决了依赖反转,我们不用 spring 的时候,对象的装载是自己 new 的,spring 出现后,只需要在 xml 里声明后,各个代码块都可以注入了。就不用自己 new 了。这就是依赖反转。相信这个大家学习八股时已经背了无数遍了。 + +那么逻辑关系的进行,我们大部分代码还是 A 类调用 B 类的方法,B 类调用 C 类的方法。这就是逻辑关系的绑死。如果 A,B,C 三个类在代码层面互不相干。但是我在 xml 里去规定,这三个的进行顺序。这就是**逻辑关系反转**。 + +逻辑关系反转有什么好处呢? + +逻辑关系反转后,好处很多,首先每个类不再强依赖,所有的代码都讲究松耦合理论。那么逻辑关系反转后,就完美实现了松耦合。并且 LF 规定了每个类的声明方式,就显得所有的逻辑类都长的差不多。更加统一。 + +其次,逻辑关系反转后,你只需要看 xml 定义就可以大致理解这个大的逻辑块都干了什么事。非常直观。 + +再次,逻辑关系都可以热更,那么这是一件多么优雅的事情啊。 + +那么做逻辑关系反转的引擎我称之为:**编排引擎**。 + +所以,LiteFlow 是**规则引擎**+**编排引擎**的结合体。 + +**它既可以热更新最关键的决策逻辑,又可以热更新逻辑关系的组成。** + +**所以 LiteFlow 做的是更现代化的规则引擎,它在做好规则引擎的基础上,超越了原本规则引擎所规定的范畴。** + +## 如何用好 LF + +其实如果对 LF 理解够深刻,它几乎是一个应用最核心的驱动器。妥妥神器,无论重构,要灵活,要解耦,它都不在话下。 + +如果对 LF 理解不够,那你可能感觉 LF 没什么用。也不知道该怎么用。 + +所以这就是我一开头提到的。理解流程和规则逻辑流的概念很重要。 + +LiteFlow 提供的脚本多达 7 种,这些脚本可以充分书写你的关键决策逻辑。并且 LF 打通了所有脚本和 Java 的互通,用脚本写不了的。也可以调用 java 方法来完成。 + +LiteFlow 提供的独特 EL 适用于做逻辑反转,让你的巨大复杂的逻辑变成一个个小的积木块,通过 DSL 来进行组搭,形成你业务的逻辑流。积木块可以是你的 java 组件,也可以是脚本写的决策逻辑。 + +**所以一般项目推荐的做法,就是首先解耦你的复杂逻辑,按照业务边界拆成一个个小的组件,然后把最经常变动的决策逻辑用脚本组件来实现。加上 DSL 编写的逻辑关系反转表达式。这样形成的系统,其优雅度是非常高的。** + +你想想,你的关键决策代码,和逻辑关系均可以进行热改变,你的系统中耦合性几乎降低到了最低。改一个小组件不会牵扯到其他的组件的改变。 + +## 再回答开头的问题 + +如果你看到这,全都能理解的话,那么最开始的几个问题也就能轻而易举的能回答了。 + +LF 因为没有角色的概念,全部流转的只是小逻辑块。所以没法暂停的。因为就相当于你普通写的瀑布式代码调用。只是换了一种方式来写而已。 + +LF 不保存状态,是一个无状态的东西。一般来说,规则引擎都是无状态的。状态这回事其实还是要业务自己做的。况且有状态这回事情非常危险,一般框架不会去保证这个。 + +LF 一般来说,所有用普通瀑布流代码一层层去调用都可以用 LF 去改造,LF 并不针对于某个特殊的领域,都可以用的。除非你的定义是属于标准的流程引擎干的事。那么的确不应该选型 LF。 + +关于定时,也不是 LF 该干的事。LF 提供接口层面的执行器,外面套层定时框架即可。很简单。 + +## LF 对标的框架是什么? + +LF 的目标始一直没变过,那就是:超越 Drools。 + +LF 独特的理念是国产自研,全部的代码也是国产自研。非常适合拿来做信创项目。 + +Drools 是国外老牌且一向作为行业标杆的规则引擎。相信了解过这行的都有听说过。 + +其实 Drools 只提供了标准的规则引擎,并没有提供逻辑关系反转的特性,而且它的 DSL 学习成本还是比较大的。而 LF 则用现成的语言来提供作为脚本,比如 js,groovy,aviator,python,lua,甚至 java 本身也可以拿来做写脚本,这些根本不用再次学习。 + +LF 除了规则引擎之外,更加拓展了规则引擎的范畴,使得 LF 能做的事情更多。 + +并且 LF 支持的存储中间件之丰富的,也绝非 Drools 能比拟的。 + +加上 LF 全中文文档,社区答疑体系在开源中首屈一指。Drools 一堆英文文档,去哪答疑? + +一定要说 LF 比不上的 Drools 的地方。那就是决策表体系。 + +Drools 是不需要指定规则的,由决策表来多项匹配。LF 由于提供了逻辑反转,是需要指定特定的规则的。 + +但是这个 LF 也准备在 2.11.5 中提供,弥补最后核心上的短板。 + +届时 LF 可以平替 Drools。 + +我会为此一直坚守。 + +谨此此篇献给关注过 LF 的同学。 + +## LF 的 VIP 社区 + +LF 开通了 VIP 付费社区,目前正在试运营,加入 VIP 社区可以最大程度保障您的使用体验。 + +VIP 社区可无限制的提问,并且每次问答都保证及时和详细。确保你完全掌握 LF 的使用。 + +并且 VIP 社区中我会坚持分享 LF 以及规则引擎内容的话题。每周一篇精华文章。大部分是独家拥有,不会发到任何其他平台。 + +现在加入会非常划算。享有以下权益: + +1.极致的答疑体验,保证每次回答具体且详细。 + +2.每周一篇的独家精华内容。 + +3.一年享有 2 次的一对一远程答疑。 + +如果有想加入的同学,可以扫以下二维码 + + diff --git a/src/zh/blog/README.md b/src/zh/blog/README.md index dba466dfcf..386121d435 100644 --- a/src/zh/blog/README.md +++ b/src/zh/blog/README.md @@ -1,33 +1,9 @@ ---- -title: 博客 -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - - - - - - +--- +title: 博客 +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +sidebar: false +layout: siteLayout +--- diff --git a/src/zh/blog/architect-software-craft.md b/src/zh/blog/architect-software-craft.md new file mode 100644 index 0000000000..6d558c3e59 --- /dev/null +++ b/src/zh/blog/architect-software-craft.md @@ -0,0 +1,115 @@ +--- +title: 架构师:软件大厦中的能工巧匠 +author: 猫大人很烦 +date: 2024-12-26 +cover: /assets/img/blog/architect-software-craft-0.webp +head: + - - meta + - name: 博客 +--- + +在这个由软件和人工智能驱动的时代,我们的日常生活与软件紧密相连。在这座软件世界的摩天大楼中,架构师就像是那些建筑大师,他们不仅精通代码的堆砌,更懂得如何用代码和逻辑的砖瓦,构建出既坚固又美观的摩天大楼,并且确保这些建筑能够经受时间的考验。本文将探讨在软件世界中,那些挥舞着键盘和鼠标的架构师们,是如何在构建软件的过程中扮演多重角色的。让我们以轻松的心情阅读,带着微笑思考,甚至不妨在会心一笑后提出您的宝贵意见。 + +## 需求分析(项目经理) + +当一个新的工程项目启动时,项目经理首先需要明确项目的预算、工期和需求。在这个阶段,客观、理性、科学的分析至关重要。 +项目经理需要根据事实和工期,与老板争取更多的资源,并确定合理的交付成果与工期。 + +* 幽默案列 + + +老板:“我们要造一个电商大厦,最好和淘宝一样就行。” +小王:”老板,您预算多少?“ +老板:”10万,不能再多了。1个月内完成。“ +小王:”好的,收到,OKK,保证完成。“ 心里嘀咕到,只能用塑料和泡沫当建筑材料了(先去网上买一套或者去开源社区找一个来改改) +老板:”你到时候,把设计图纸给我看看?“ +小王:”什么,还需要设计图纸?好的,没问题,转身就从网上找了个材料发过去。“ +老板:”小王啊,质量和安全要保证,后续我们还要维保呢?“ +小王:”OKK,没问题“ +项目上线后:质量是不可能有质量的,安全性?扯淡。还要维保?做梦, 从单元测试,集成测试到性能测试是不可能有的。 +二开?不可能的。没那技术和精力。直接删库跑路。 + +## 选址与技术选型(风水师) + +当一个新的工程项目启动时,项目经理首先需要明确项目的预算、工期和需求。 +在这个阶段,客观、理性、科学的分析至关重要。项目经理需要根据事实和工期,与老板争取更多的资源,并确定合理的交付成果与工期。 +比如: + +* 开发语言选择:是选择Java的稳定性和生态完整性,还是Python的灵活性?或者是被誉为世界上最好的语言的PHP? + +* 数据库选择:是选择号称单表2000万行就不行的MySQL,还是功能更强大、扩展性更高的PostgreSQL? + +* 消息队列选择:是选择号称会丢消息但速度极快的Kafka,还是最近很火的Pulsar? + + +每个选型都需要具体落实,以`追求高效作为第一性原理`为指导。 +例如,使用Java是否能够快速完成开发,后期更好维护? +MySQL是否真的单表2000万行就不行了? +Kafka为什么那么快,它的原理是什么,它真的会丢消息吗,它有哪些功能? +这些问题都需要深入研究。不要盲目听从,一定要深入原理,咨询专家,甚至查看代码实现,毕竟在源码面前毫无秘密。 + +## 设计图与架构图(建筑师) + +建筑师用铅笔和尺子绘制设计图,而架构师则用代码和逻辑构建架构图。 +这可不是随便涂鸦,每一笔都关系到软件的生死存亡。 +如果图错了,那灾难是不可想象的。架构师在设计时应该具有非常高的技术前瞻性,秉着`走``**先人一步**`的原则去设计。 + +在设计过程中,也不是一上来就是万亿级的流量、异地多活的灾备,而应根据实际情况和团队资源,因地制宜地绘制架构图。 +图上的每个组件,它们的作用是什么,在什么场景下使用,它们的容错性如何,它们之间的交互协议和接口是什么,都需要仔细考量。 +然后绘制线条与轨迹,说直白点,它就是一张数据流转的逻辑轨迹图。 + +当然,在这一过程中,架构师还需要预留一部分空地,以考虑系统的扩展性,来新增更多功能。其次,万一哪一天老板不满意,也可以尽可能降低替换的成本。 +最后,架构师需要通俗易懂地与项目团队成员沟通和评审,确保每个人都明白自己在做什么以及如何做。 + +## 施工与管理(包工头) + +如果把开发一个软件大厦比作一次小小的战役,那么架构师无疑是战场上的将军。 +了解底下人的战斗力水平、技术水平和脾气秉性,是排兵布阵、用人之长的关键。 +这样,即使遇到不断变化的需求,也可以灵活调整。遇到技术难题时,架构师的口号应该是:`弟兄们,跟我上”(而不是“弟兄们,给我冲)`,带头冲锋,攻克技术难题。 +不要心浮气躁地飘在“PPT”上,这里画个图,那里连个线,就以为事情解决了。真正落地开发时,会发现完全不是那么回事,有多难,需要花多少时间。 + +* 在开发的过程中: + + +1. 首先架构师应该根据需求,以及架构设计,来合理科学的预估每个模块的工期,一定要预留一定的时间。(有时候,遇到一个问题,开发一解决就是好几天) + +2. 编写核心代码,制定代码规范,完成接口协议的设计,制定单元测试,集成测试,E2E 测试等,同时要和团队成员沟通,为什么这么做,以及这么做有什么好处。 + +3. 其次架构师应该时刻关注项目的进度,发现搬砖慢的时候,可以封装一些轮子或者借助 AI 的能力来提升搬砖的速度。 + +4. 最后需要针对每个功能进行验收,需要review代码,保持对质量的执着, + 当对代码的理解不一致的时候,甚至需要自己亲自下场重构代码,然后再和团队成员进行沟通交流。 + + +* 在管理沟通上: + + +1. 好的技术团队其实是不需要管理的,古人说,文人相轻。其实程序员群体也是,他们不会因为你的 tilte,你的各种管理手段, + 而是,他在跟你一起共事的时候,能学会什么,能有什么提高,才会真正组成一条心,才会真正的热爱这个事情。 + +2. 关于沟通上,对内你们是一个整体,一个 team,一定要把问题当面沟通清楚,让每个成员明白你的想法。对外, + 一定要有自己的技术坚持,有自己的个性,当有不合理的需求,要及时的沟通,甚至是争论,确保项目在正轨上,不要让人觉得你是个 + 老好人,软柿子,当然也会给人一种,你不好沟通的样子。那又怎样呢,负责把项目办好就行。 + + +## 交付与运维(物业管理) + +现在大厦盖好了,怎样把房子安全的交互给业主,让业主住的安心呢?在这过程中,架构师应该扮演 +物业管理的角色,应该明确以下事情: + +* 文档 + 文档是整个交付过程中,最关键最核心的交付物。 + 首先得负责文档和知识的管理,确保所有重要的架构决策、设计文档和运维手册都被妥善记录和管理。 + 确保文档`就像是一本食谱,即使换了厨师,也能做出一样的味道`。 + +* DevOps + 试想一下,假如大厦有需要修缮或者零部件的配换的时候, + 架构师们通过自动化持续集成/持续部署(CI/CD)流程,快速响应需求,持续迭代。 + 假如遇到了像电梯坏了这样的问题,除了考虑容错的策略,还需要考虑如何进行滚动,灰度的更新。 + +* 可观测性 + 需要时刻监控和观测小区的运行状态。一定要做到该监控的地方一定要有监控,并且确保监控数据在需要使用的能够还原整个链路。 + 要提前预判系统风险,提前规避异常情况。 + + +在这个充满变化的时代,架构师需要不断地学习新技术,适应新环境,解决新问题。同事也需要保持好奇心,对未知充满探索欲,对新技术充满热情。勇于创新,敢于尝试,不怕失败。这项工作充满挑战,但也充满乐趣。这次架构师同盟是一次思想的碰撞,也更像是一次英雄的会盟。借用英雄联盟(LOL) 里面易大师的台词:`真正的大师,永远怀着一颗学徒的心`。希望各位架构师和致力于成为架构师的工程师们,为中国软件大厦添砖加瓦。 \ No newline at end of file diff --git a/src/zh/blog/dromara-open-source-community.md b/src/zh/blog/dromara-open-source-community.md new file mode 100644 index 0000000000..7cf841be00 --- /dev/null +++ b/src/zh/blog/dromara-open-source-community.md @@ -0,0 +1,188 @@ +--- +title: 怎么加入到开源社区,开心做开源 +author: tom +date: 2022-11-16 +cover: /logo.svg +head: + - - meta + - name: 博客 +--- + +很多同学都会有参与开源社区或开源项目的想法,一个拥有完备成长路线的开源社区可能会更加的吸引。 + +和社区一起成长,一个人会走的很快,但一群人会走得更远。 + +### 先来介绍下Dromara社区 + +Dromara社区是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,日志,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + +社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +### 社区理念 + +**让参与的每一位开源爱好者,体会到开源的快乐**。 + +我们深知开源贡献是一个自发的行为,没有人会为开源项目开发者们付款,因此开源社区不会强制您做任何不想做,不感兴趣的任务,特性。贡献者们自发领取感兴趣的任务,或者有任务想分给贡献者时,PMC也会咨询贡献者是否感兴趣,充分尊重贡献者的意见。我们不能保证参与开源100%都能体会到快乐,但能保证100%不会感到难受。 + +虽说没有人付款,但开源社区会尽可能把更多福利给到开发者们,比如社区礼物周边,今年的中科院开源之夏活动(12000RMB)。 + +您可能是单纯喜爱开源,或者看好此项目的发展想一同成长,或者为了工作简历上的开源亮点,或为了企业内部开发,或为了实现自我价值等。我们非常尊重所有开发者参与开源项目的动机,并尽可能的给予我们能提供的最大支持。 + +### 开源项目成长路线 + +**Contributor**(代码文档等贡献) -> **Committer**(持续项目贡献或有突出贡献者,被PMC推举) -> **PMC**(成为 Committer 2月+,持续贡献积极维护开源项目,被PMC推举) + +社区项目拥有完备的成长路线,贡献不限制于**代码**,一个**单元测试用例**,**文档完善**或者是修复发现的文档上的一个**标点符号错误**,都可以算是项目贡献。首次贡献成功即可成为**开源项目Contributor**,持续性对项目贡献或者有重大特性突出贡献者,可以被PMC提名,无异议即可成为**开源项目Committer**,并且自动成为**Dromara社区Member**。对持续贡献积极维护开源项目的Committer,可以被PMC提名,投票通过后即可成为**开源项目PMC**,成为**开源项目PMC**后,经**Dromara委员会**投票通过后成为**Dromara委员会成员**。 + +## 加入组织 + +**Dromara** 社区欢迎所有热爱开源的小伙伴加入,我们提供完善的社区治理发展与组织成员成长平台。 + +### Dromara社区成员 + +#### 如何成为社区成员? + +1. 您可以通过对Dromara社区下的开源项目进行贡献(代码,文档,案例等多种贡献形式),被推举为**开源项目Committer**后,自动成为**Dromara社区成员**。 + + +#### 社区成员权益与责任 + +**社区成员权益** + +1. 社区将会在官网,仓库等展示社区成员的信息与荣誉。 + +2. Dromara社区专属邮箱 eg: `lili@dromara.org`。 + +3. 作为嘉宾免费邀请至Dromara付费知识星球。 + +4. 参与社区内部会议,发展计划,活动,线下聚会等。 + +5. 社区每年的礼物周边(2022年是卫衣,手托)。 + +6. 各种开源,工作等方面社区资源支持(社区里面大佬很多哦)。 + + +**社区成员责任** + +1. 不得从事违法或损害社区和开源项目的事情。 + +2. 维护社区形象,积极宣传社区。 + + +### Dromara委员会成员 + +1. 您可以通过对Dromara社区下的开源项目持续贡献,成为**开源项目PMC**,经Dromara委员会投票通过后成为**Dromara委员会成员**。 + +2. 或您可以直接捐赠您的开源项目,捐赠成功后自动成为**Dromara委员会成员**。 + + +**委员会成员权益** + +1. 包含所有上述`社区成员`拥有的权益。 + +2. 社区事务意见投票权。 + +3. 社区将会在官网,仓库等展示Dromara社区委员会成员列表。 + +4. 新晋开源项目提名,委员会成员提名投票等。 + +5. 宣讲机会,项目推广等社区各方面资源支持。 + + +**委员会成员责任** + +1. 不得从事违法或损害社区和开源项目的事情。 + +2. 主动维护社区,积极宣传社区。 + + +## 如何一步一步参与开源 + +> 这里以Dromara社区下的开源项目HertzBeat为例。 + +#### 了解熟悉开源项目 + +* 访问项目仓库 https://github.com/dromara/hertzbeat 或官网 https://hertzbeat.com/ 了解项目 + +* 根据项目文档信息使用或启动项目,熟悉功能。 + + +#### 查找您感兴趣的任务 + +* 访问项目仓库Issue列表,找到您感兴趣的任务或标记为\[TASK\]的任务,如果您想试一试的话,直接在下面评论认领,就可以开始啦!当然您可直接加交流群或微信 tan-cloud,告诉他想参与,会给您推荐您感兴趣的任务。 + +* 我们建议您的第一次贡献先从小任务着手,比如单元测试用例编写。 + + +#### 提交 Pull Request + +1. 首先您需要 Fork 目标仓库 hertzbeat repository https://github.com/dromara/hertzbeat. + +2. 然后 用git命令 将代码下载到本地: + + +``` +git clone git@github.com:${YOUR_USERNAME}/hertzbeat.git  +``` + +3. 下载完成后,请参考目标仓库的入门指南或者 README 文件对项目进行初始化。 + +4. 接着,您可以参考如下命令进行代码的提交, 切换新的分支, 进行开发: + + +``` +git checkout -b a-feature-branch +``` + +5. 提交 commit , commit 描述信息需要符合约定格式: \[module name or type name\]feature or bugfix or doc: custom message. + + +``` +git add   +git commit -m '[docs]feature: necessary instructions' +``` + +6. 推送到远程仓库 + + +``` +git push origin a-feature-branch    +``` + +7. 然后您就可以在 GitHub 上发起新的 PR (Pull Request)。 + + +请注意 PR 的标题与内容写上必要的,来方便 Committer 和其他贡献者进行代码审查。 + +#### 等待PR代码被合并 + +在提交了 PR 后,Committer 或者社区的小伙伴们会对您提交的代码进行审查(Code Review),会提出一些修改建议,或者是进行一些讨论,请及时关注您的PR。 + +注意**若后续需要改动,不需要发起一个新的 PR,在原有的分支上提交 commit 并推送到远程仓库后,PR会自动更新**。 + +另外,项目有规范 CI 检查流程,在提交 PR 之后会触发 CI,请注意是否通过 CI 检查。 + +最后,Committer 可以将 PR 合并入**DEV主分支**。 + +#### 代码被合并后 + +在代码被合并后,您就可以在本地和远程仓库删除这个开发分支了: + +``` +git branch -d a-dev-branch +git push origin --delete a-dev-branch +``` + +在主分支上,您可以执行以下操作来同步上游仓库: + +``` +git remote add upstream https://github.com/dromara/hertzbeat.git #Bind the remote warehouse, if it has been executed, it does not need to be executed again +git checkout master  +git pull upstream master +``` + +通过上述步骤,您就是 HertzBeat 的贡献者了。重复前面的步骤,在社区中保持活跃并坚持下去,您就能成为 Committer -> PMC! + +## 写在最后 + +谈到程序员往往伴随着格子衫,老实有点木讷的刻板印象,我们往往也是站在幕后的那批人。开源社区希望开发者们能因为开源,有机会站到前台来展示自己,用开源代码“包装”自己。想象一下你参与的项目被成千上万团队使用或部署,那真的很酷。面试时也不需要用提前一个月准备八股文套路在面试官那证明自己,你的Github/Gitee账号已经告诉他,你很NB并且靠谱! \ No newline at end of file diff --git a/src/zh/blog/dromara-warmflow-assignee-guide.md b/src/zh/blog/dromara-warmflow-assignee-guide.md new file mode 100644 index 0000000000..bc4b6ebfd9 --- /dev/null +++ b/src/zh/blog/dromara-warmflow-assignee-guide.md @@ -0,0 +1,61 @@ +--- +title: Dromara WarmFlow工作流动态指定办理人 +author: WarmFlow +date: 2024-12-03 +cover: /assets/img/blog/dromara-warmflow-assignee-guide-0.webp +head: + - - meta + - name: 博客 +--- + +### 背景: + +审批任务的办理人,通常是在流程设计器中预先设定好办理人,那如果想要在办理过程中指定办理人呢?那不得不提一下本次的主角,来自Dromara组织的WarmFlow工作流,通过他的办理人变量表达式就能很好解决此问题。 + +##### 解决思路 + +* 1、流程设计时,需要动态指定办理人的节点,配置办理人变量表达式`${handler1}` + +* 2、当上一个任务办理时,在流程变量中传入`${handler1}`的值 + +* 3、办理完成会生成本节点任务,即可替换完成 + + +![](/assets/img/blog/dromara-warmflow-assignee-guide-0.webp) + +后端代码设置变量 + +``` +// 流程变量 +Map variable = new HashMap<>(); +variable.put("handler1", "100"); +flowParams.variable(variable); + +Instance instance = insService.skipByInsId(testLeave.getInstanceId(), flowParams); +``` + +##### 高级玩法 + +* 支持动态指定一群人 + +* 支持spel表达式 + +* 支持表达式扩展 + + +把如上代码`"100"`改成`Arrays.asList(4, "5", 100L)`,就可以动态指定一群人 + +``` +// 流程变量 +Map variable = new HashMap<>(); +variable.put("handler1", Arrays.asList(4, "5", 100L)); +flowParams.variable(variable); + +Instance instance = insService.skipByInsId(testLeave.getInstanceId(), flowParams); +``` + +##### 总结 + +是不是通过WarmFlow工作流可以很简单的管理你的审批业务呢,还不快快加入^v^ + +另外Dromara组织还有大量好用的项目,欢迎评鉴 \ No newline at end of file diff --git a/src/zh/blog/hmily_current.md b/src/zh/blog/hmily_current.md index c9081ecb3c..588497a7b3 100644 --- a/src/zh/blog/hmily_current.md +++ b/src/zh/blog/hmily_current.md @@ -1,155 +1,155 @@ ---- -title: Hmily:轻松搞定高并发分布式事务 -author: xiaoyu -date: 2018-11-14 -tag: - - hmily -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: 博客 ---- - -### Hmily 高并发事务处理 - -开始先打个小小的广告 -Hmily 在参加开源中国年度受欢迎投票 https://www.oschina.net/project/top_cn_2018?origin=zhzd 点击链接,搜索 Hmily 帮忙投下票,在第 11 横排第二个,感谢大家! -也欢迎大家关注,或者提交 pr,让 Hmily 变的更好,更完美。 -gitHub: [https://github.com/yu199195/hmily] -gitee: [https://gitee.com/dromara/hmily] - -接下来回答一下 社区的一些问题,和大家一些疑惑的地方! - -### 1. Hmily 的性能问题? - -答:Hmily 是采用 AOP 切面的方式与你的 RPC 方法绑定,无非就是在你 RPC 调用的时候,保存了日志(通过异步 disruptor),传递了一些参数。现在 confrim,cancel 也都为异步的调用,因此其性能与你的 rpc 性能一样。记住 Hmily 不生产事务,Hmily 只是分布式事务的搬运工。之前 Hmily 在 AOP 切面加了一把锁,导致了性能下降,也就是 Spring cloud 中国社区做的那篇文章。现在已经全部修复,并且全部异步化。其实那么测试时不合理的,因为是压测的 demo,都是默认的配置。下文我会讲解,怎么样才能提高 Hmiy 性能。 - -### 2. 关于 RPC 调用超时 Hmily 是怎么处理的? - -答: 我们支持在分布式环境中调用一个 RPC 方法,如果超时了。比如 dubbo 设置的超时时间是 100ms,可能你的方法用了 140ms,但是你的方法是执行成功了的。但是对调用方来说,你是失败的。这个时候需要回滚。所以 Hmily 的做法是。调用者认为你是失败的,不会将加入的回滚调用链条中。因此超时的 rpc 接口方,进行自身的回滚。会有一个定时任务来进行回滚,因为日志状态是 try 阶段,会调用 cancel 方法进行回滚,从而到达最终一致性! - -### 3.Hmily 支持集群部署的问题?以及集群环境中,定时任务日志恢复的问题? - -答:Hmily 是和你的应用 AOP 切面绑定在一起的,天然支持集群。集群环境中定时恢复问题,其实几乎没有,除非你的集群同时一下挂掉,才会有这个问题。当你集群同时挂掉,在恢复的时候,日志会有一个 version 字段,更新成功的,才会去进行恢复。 - -### 4.Hmily 是异步保存日志的,那么很极端情况下(代码刚好执行到这一行,然后 jvm 退出,断电啦什么的),日志还没保存那怎么处理呢? - -答:这种想法的,肯定是没看源码,或者是看了没怎么看懂。在 AOP 切面中,会先进行日志的异步保存,注意状态是 PRE_TRY。在 try 执行完成后,更新为 try。就算存在可能你说的什么断电,什么你在打断电调试,然后 kill 服务之类的。(Mysql 我都可以让他事务失效,你信不信?)我只能说,不要花大力气去解决那些偶然的事情,最好的解决办法是不解决它。 -Hmily 针对高并发时候的参数配置调优。 -可能这部门内容针对熟悉 Hmily 的人来说,不熟悉的也没关系。直接上 github 上看相关文档就好。 -hmily 支持 Spring bean xml 方式的配置,同时也支持 spring boot start yml 方式的配置。 - -```xml - - - - - - - - - - - - - - - - - - - - - -``` - -- serializer :这里我推荐使用是 kroy。当然 hmily 也支持 hessian,protostuff,jdk。在我们测试中表现为: - kroy>hessian>protostuff>jdk - -- recoverDelayTime :定时任务延迟时间(单位是秒,默认 120。这个参数只是要大于你的 rpc 调用的超时时间设置。 - -- retryMax : 最大重复次数,默认 3 次。当你的服务 down 机,定时任务会执行 retryMax 次数去执行你的 cancel 还是 confrim。 - -- bufferSize: disruptor 的 bufferSize,当高并发的时候,可以调大。注意是 2n 次方 - -- consumerThreads distuptor 消费线程数量,高并发的时候,可以调大。 - -- started: 注意在是发起方的时候,把此属性设置为 true。参与方为 false。 - -- asyncThreads 异步执行 confirm 和 cancel 线程池线程的大小,高并发的时候请调大 - -- 接下来是最重要的事务日志的存储 在我们的压测中,推荐使用 mongo。表现为 mongodb>redis 集群>mysql>zookeeper - -- 如果你采用 mongodb 存储日志,配置如下(url 可以配置成 mongdb 集群的 url) - -```xml - - - - - - - - - -``` - -- 如果你采用 redis 存储日志,配置如下: - - - redis 单节点 - -```xml - - - - - - - - -``` - -- redis 哨兵模式集群: - -```xml - - - - - - - - - -``` - -- redis 集群: - -```xml - - - - - - - - -``` - -- 如果你采用 zookeeper 存储日志,配置如下: - -```xml - - - - - - - - -``` - -- 数据库的配置在上面已经有了,使用 file 方式的存储我就不介绍了. -- 以上就是今天分享的内容,一个注解,几行配置轻轻松松搞定高并发分布式事务! +--- +title: Hmily:轻松搞定高并发分布式事务 +author: xiaoyu +date: 2018-11-14 +tag: + - hmily +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: 博客 +--- + +### Hmily 高并发事务处理 + +开始先打个小小的广告 +Hmily 在参加开源中国年度受欢迎投票 https://www.oschina.net/project/top_cn_2018?origin=zhzd 点击链接,搜索 Hmily 帮忙投下票,在第 11 横排第二个,感谢大家! +也欢迎大家关注,或者提交 pr,让 Hmily 变的更好,更完美。 +gitHub: [https://github.com/yu199195/hmily] +gitee: [https://gitee.com/dromara/hmily] + +接下来回答一下 社区的一些问题,和大家一些疑惑的地方! + +### 1. Hmily 的性能问题? + +答:Hmily 是采用 AOP 切面的方式与你的 RPC 方法绑定,无非就是在你 RPC 调用的时候,保存了日志(通过异步 disruptor),传递了一些参数。现在 confrim,cancel 也都为异步的调用,因此其性能与你的 rpc 性能一样。记住 Hmily 不生产事务,Hmily 只是分布式事务的搬运工。之前 Hmily 在 AOP 切面加了一把锁,导致了性能下降,也就是 Spring cloud 中国社区做的那篇文章。现在已经全部修复,并且全部异步化。其实那么测试时不合理的,因为是压测的 demo,都是默认的配置。下文我会讲解,怎么样才能提高 Hmiy 性能。 + +### 2. 关于 RPC 调用超时 Hmily 是怎么处理的? + +答: 我们支持在分布式环境中调用一个 RPC 方法,如果超时了。比如 dubbo 设置的超时时间是 100ms,可能你的方法用了 140ms,但是你的方法是执行成功了的。但是对调用方来说,你是失败的。这个时候需要回滚。所以 Hmily 的做法是。调用者认为你是失败的,不会将加入的回滚调用链条中。因此超时的 rpc 接口方,进行自身的回滚。会有一个定时任务来进行回滚,因为日志状态是 try 阶段,会调用 cancel 方法进行回滚,从而到达最终一致性! + +### 3.Hmily 支持集群部署的问题?以及集群环境中,定时任务日志恢复的问题? + +答:Hmily 是和你的应用 AOP 切面绑定在一起的,天然支持集群。集群环境中定时恢复问题,其实几乎没有,除非你的集群同时一下挂掉,才会有这个问题。当你集群同时挂掉,在恢复的时候,日志会有一个 version 字段,更新成功的,才会去进行恢复。 + +### 4.Hmily 是异步保存日志的,那么很极端情况下(代码刚好执行到这一行,然后 jvm 退出,断电啦什么的),日志还没保存那怎么处理呢? + +答:这种想法的,肯定是没看源码,或者是看了没怎么看懂。在 AOP 切面中,会先进行日志的异步保存,注意状态是 PRE_TRY。在 try 执行完成后,更新为 try。就算存在可能你说的什么断电,什么你在打断电调试,然后 kill 服务之类的。(Mysql 我都可以让他事务失效,你信不信?)我只能说,不要花大力气去解决那些偶然的事情,最好的解决办法是不解决它。 +Hmily 针对高并发时候的参数配置调优。 +可能这部门内容针对熟悉 Hmily 的人来说,不熟悉的也没关系。直接上 github 上看相关文档就好。 +hmily 支持 Spring bean xml 方式的配置,同时也支持 spring boot start yml 方式的配置。 + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +- serializer :这里我推荐使用是 kroy。当然 hmily 也支持 hessian,protostuff,jdk。在我们测试中表现为: + kroy>hessian>protostuff>jdk + +- recoverDelayTime :定时任务延迟时间(单位是秒,默认 120。这个参数只是要大于你的 rpc 调用的超时时间设置。 + +- retryMax : 最大重复次数,默认 3 次。当你的服务 down 机,定时任务会执行 retryMax 次数去执行你的 cancel 还是 confrim。 + +- bufferSize: disruptor 的 bufferSize,当高并发的时候,可以调大。注意是 2n 次方 + +- consumerThreads distuptor 消费线程数量,高并发的时候,可以调大。 + +- started: 注意在是发起方的时候,把此属性设置为 true。参与方为 false。 + +- asyncThreads 异步执行 confirm 和 cancel 线程池线程的大小,高并发的时候请调大 + +- 接下来是最重要的事务日志的存储 在我们的压测中,推荐使用 mongo。表现为 mongodb>redis 集群>mysql>zookeeper + +- 如果你采用 mongodb 存储日志,配置如下(url 可以配置成 mongdb 集群的 url) + +```xml + + + + + + + + + +``` + +- 如果你采用 redis 存储日志,配置如下: + + - redis 单节点 + +```xml + + + + + + + + +``` + +- redis 哨兵模式集群: + +```xml + + + + + + + + + +``` + +- redis 集群: + +```xml + + + + + + + + +``` + +- 如果你采用 zookeeper 存储日志,配置如下: + +```xml + + + + + + + + +``` + +- 数据库的配置在上面已经有了,使用 file 方式的存储我就不介绍了. +- 以上就是今天分享的内容,一个注解,几行配置轻轻松松搞定高并发分布式事务! diff --git a/src/zh/blog/hmily_introduction.md b/src/zh/blog/hmily_introduction.md index c1a6beb067..8f7684feaf 100644 --- a/src/zh/blog/hmily_introduction.md +++ b/src/zh/blog/hmily_introduction.md @@ -1,629 +1,627 @@ ---- -title: Hmily:高性能异步分布式事务TCC框架 -author: xiaoyu -date: 2018-09-25 -tag: - - hmily - - TCC -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: 博客 ---- - -# Hmily 框架特性[https://github.com/yu199195/hmily] - -- 无缝集成 Spring,Spring boot start。 - -- 无缝集成 Dubbo,SpringCloud,Motan 等 rpc 框架。 - -- 多种事务日志的存储方式(redis,mongdb,mysql 等)。 - -- 多种不同日志序列化方式(Kryo,protostuff,hession)。 - -- 事务自动恢复。 - -- 支持内嵌事务的依赖传递。 - -- 代码零侵入,配置简单灵活。 - -# Hmily 为什么这么高性能? - -### 1.采用 disruptor 进行事务日志的异步读写(disruptor 是一个无锁,无 GC 的并发编程框架) - -```java -package com.hmily.tcc.core.disruptor.publisher; - -import com.hmily.tcc.common.bean.entity.TccTransaction; -import com.hmily.tcc.common.enums.EventTypeEnum; -import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory; -import com.hmily.tcc.core.coordinator.CoordinatorService; -import com.hmily.tcc.core.disruptor.event.HmilyTransactionEvent; -import com.hmily.tcc.core.disruptor.factory.HmilyTransactionEventFactory; -import com.hmily.tcc.core.disruptor.handler.HmilyConsumerDataHandler; -import com.hmily.tcc.core.disruptor.translator.HmilyTransactionEventTranslator; -import com.lmax.disruptor.BlockingWaitStrategy; -import com.lmax.disruptor.IgnoreExceptionHandler; -import com.lmax.disruptor.RingBuffer; -import com.lmax.disruptor.dsl.Disruptor; -import com.lmax.disruptor.dsl.ProducerType; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * event publisher. - * - * @author xiaoyu(Myth) - */ -@Component -public class HmilyTransactionEventPublisher implements DisposableBean { - - private Disruptor disruptor; - - private final CoordinatorService coordinatorService; - - @Autowired - public HmilyTransactionEventPublisher(final CoordinatorService coordinatorService) { - this.coordinatorService = coordinatorService; - } - - /** - * disruptor start. - * - * @param bufferSize this is disruptor buffer size. - * @param threadSize this is disruptor consumer thread size. - */ - public void start(final int bufferSize, final int threadSize) { - disruptor = new Disruptor<>(new HmilyTransactionEventFactory(), bufferSize, r -> { - AtomicInteger index = new AtomicInteger(1); - return new Thread(null, r, "disruptor-thread-" + index.getAndIncrement()); - }, ProducerType.MULTI, new BlockingWaitStrategy()); - - final Executor executor = new ThreadPoolExecutor(threadSize, threadSize, 0, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - HmilyThreadFactory.create("hmily-log-disruptor", false), - new ThreadPoolExecutor.AbortPolicy()); - - HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize]; - for (int i = 0; i < threadSize; i++) { - consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService); - } - disruptor.handleEventsWithWorkerPool(consumers); - disruptor.setDefaultExceptionHandler(new IgnoreExceptionHandler()); - disruptor.start(); - } - - /** - * publish disruptor event. - * - * @param tccTransaction {@linkplain com.hmily.tcc.common.bean.entity.TccTransaction } - * @param type {@linkplain EventTypeEnum} - */ - public void publishEvent(final TccTransaction tccTransaction, final int type) { - final RingBuffer ringBuffer = disruptor.getRingBuffer(); - ringBuffer.publishEvent(new HmilyTransactionEventTranslator(type), tccTransaction); - } - - @Override - public void destroy() { - disruptor.shutdown(); - } -} -``` - -- 在这里 bufferSize 的默认值是 4094 \* 4,用户可以根据自行的情况进行配置。 - -```java - - HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize]; - for (int i = 0; i < threadSize; i++) { - consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService); - } - disruptor.handleEventsWithWorkerPool(consumers); -``` - -- 这里是采用多个消费者去处理队列里面的任务。 - -### 2.异步执行 confrim,cancel 方法。 - -```java -package com.hmily.tcc.core.service.handler; - -import com.hmily.tcc.common.bean.context.TccTransactionContext; -import com.hmily.tcc.common.bean.entity.TccTransaction; -import com.hmily.tcc.common.enums.TccActionEnum; -import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory; -import com.hmily.tcc.core.service.HmilyTransactionHandler; -import com.hmily.tcc.core.service.executor.HmilyTransactionExecutor; -import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * this is transaction starter. - * - * @author xiaoyu - */ -@Component -public class StarterHmilyTransactionHandler implements HmilyTransactionHandler { - - private static final int MAX_THREAD = Runtime.getRuntime().availableProcessors() << 1; - - private final HmilyTransactionExecutor hmilyTransactionExecutor; - - private final Executor executor = new ThreadPoolExecutor(MAX_THREAD, MAX_THREAD, 0, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - HmilyThreadFactory.create("hmily-execute", false), - new ThreadPoolExecutor.AbortPolicy()); - - @Autowired - public StarterHmilyTransactionHandler(final HmilyTransactionExecutor hmilyTransactionExecutor) { - this.hmilyTransactionExecutor = hmilyTransactionExecutor; - } - - @Override - public Object handler(final ProceedingJoinPoint point, final TccTransactionContext context) - throws Throwable { - Object returnValue; - try { - TccTransaction tccTransaction = hmilyTransactionExecutor.begin(point); - try { - //execute try - returnValue = point.proceed(); - tccTransaction.setStatus(TccActionEnum.TRYING.getCode()); - hmilyTransactionExecutor.updateStatus(tccTransaction); - } catch (Throwable throwable) { - //if exception ,execute cancel - final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction(); - executor.execute(() -> hmilyTransactionExecutor - .cancel(currentTransaction)); - throw throwable; - } - //execute confirm - final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction(); - executor.execute(() -> hmilyTransactionExecutor.confirm(currentTransaction)); - } finally { - hmilyTransactionExecutor.remove(); - } - return returnValue; - } -} -``` - -- 当 try 方法的 AOP 切面有异常的时候,采用线程池异步去执行 cancel,无异常的时候去执行 confrim 方法。 - -### 这里有人可能会问:那么 cancel 方法异常,或者 confrim 方法异常怎么办呢? - -答:首先这种情况是非常罕见的,因为你上一面才刚刚执行完 try。其次如果出现这种情况,在 try 阶段会保存好日志,Hmily 有内置的调度线程池来进行恢复,不用担心。 - -### 有人又会问:这里如果日志保存异常了怎么办? - -答:首先这又是一个牛角尖问题,首先日志配置的参数,在框架启动的时候,会要求你配置的。其次,就算在运行过程中日志保存异常,这时候框架会取缓存中的,并不会影响程序正确执行。最后,万一日志保存异常了,系统又在很极端的情况下 down 机了,恭喜你,你可以去买彩票了,最好的解决办法就是不去解决它。 - -### 3.ThreadLocal 缓存的使用。 - -```java - /** - * transaction begin. - * - * @param point cut point. - * @return TccTransaction - */ - public TccTransaction begin(final ProceedingJoinPoint point) { - LogUtil.debug(LOGGER, () -> "......hmily transaction!start...."); - //build tccTransaction - final TccTransaction tccTransaction = buildTccTransaction(point, TccRoleEnum.START.getCode(), null); - //save tccTransaction in threadLocal - CURRENT.set(tccTransaction); - //publishEvent - hmilyTransactionEventPublisher.publishEvent(tccTransaction, EventTypeEnum.SAVE.getCode()); - //set TccTransactionContext this context transfer remote - TccTransactionContext context = new TccTransactionContext(); - //set action is try - context.setAction(TccActionEnum.TRYING.getCode()); - context.setTransId(tccTransaction.getTransId()); - context.setRole(TccRoleEnum.START.getCode()); - TransactionContextLocal.getInstance().set(context); - return tccTransaction; - } -``` - -- 首先要理解,threadLocal 保存的发起者一方法的事务信息。这个很重要,不要会有点懵逼。rpc 的调用,会形成调用链,进行保存。 - -```java - -/** - * add participant. - * - * @param participant {@linkplain Participant} - */ - public void enlistParticipant(final Participant participant) { - if (Objects.isNull(participant)) { - return; - } - Optional.ofNullable(getCurrentTransaction()) - .ifPresent(c -> { - c.registerParticipant(participant); - updateParticipant(c); - }); - } -``` - -### 4.GuavaCache 的使用 - -```java -package com.hmily.tcc.core.cache; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.cache.Weigher; -import com.hmily.tcc.common.bean.entity.TccTransaction; -import com.hmily.tcc.core.coordinator.CoordinatorService; -import com.hmily.tcc.core.helper.SpringBeanUtils; -import org.apache.commons.lang3.StringUtils; - -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -/** - * use google guava cache. - * @author xiaoyu - */ -public final class TccTransactionCacheManager { - - private static final int MAX_COUNT = 10000; - - private static final LoadingCache LOADING_CACHE = - CacheBuilder.newBuilder().maximumWeight(MAX_COUNT) - .weigher((Weigher) (string, tccTransaction) -> getSize()) - .build(new CacheLoader() { - @Override - public TccTransaction load(final String key) { - return cacheTccTransaction(key); - } - }); - - private static CoordinatorService coordinatorService = SpringBeanUtils.getInstance().getBean(CoordinatorService.class); - - private static final TccTransactionCacheManager TCC_TRANSACTION_CACHE_MANAGER = new TccTransactionCacheManager(); - - private TccTransactionCacheManager() { - - } - - /** - * TccTransactionCacheManager. - * - * @return TccTransactionCacheManager - */ - public static TccTransactionCacheManager getInstance() { - return TCC_TRANSACTION_CACHE_MANAGER; - } - - private static int getSize() { - return (int) LOADING_CACHE.size(); - } - - private static TccTransaction cacheTccTransaction(final String key) { - return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction()); - } - - /** - * cache tccTransaction. - * - * @param tccTransaction {@linkplain TccTransaction} - */ - public void cacheTccTransaction(final TccTransaction tccTransaction) { - LOADING_CACHE.put(tccTransaction.getTransId(), tccTransaction); - } - - /** - * acquire TccTransaction. - * - * @param key this guava key. - * @return {@linkplain TccTransaction} - */ - public TccTransaction getTccTransaction(final String key) { - try { - return LOADING_CACHE.get(key); - } catch (ExecutionException e) { - return new TccTransaction(); - } - } - - /** - * remove guava cache by key. - * @param key guava cache key. - */ - public void removeByKey(final String key) { - if (StringUtils.isNotEmpty(key)) { - LOADING_CACHE.invalidate(key); - } - } - -} -``` - -- 在参与者中,我们使用了 ThreadLocal,而在参与者中,我们为什么不使用呢? - 其实原因有二点:首先.因为 try,和 confrim 会不在一个线程里,会造成 ThreadLocal 失效。当考虑到 RPC 集群的时候,可能会负载到不同的机器上。这里有一个细节就是: - -```java - private static TccTransaction cacheTccTransaction(final String key) { - return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction()); - } -``` - -当 GuavaCache 里面没有的时候,会去查询日志返回,这样就保证了对集群环境的支持。 - -### 以上 4 点造就了 Hmily 是一个异步的高性能分布式事务 TCC 框架的原因。 - -### Hmily 如何使用?(https://github.com/yu199195/hmily/tree/master/hmily-tcc-demo) - -首先因为之前的包命名问题,框架包并没有上传到 maven 中心仓库,固需要使用者自己拉取代码,编译 deploy 到自己的私服。 - -### 1.dubbo 用户 - -- 在你的 Api 接口项目引入 - -```xml - - - com.hmily.tcc - hmily-tcc-annotation - {you version} - -``` - -- 在你的服务提供者项目引入 - -```xml - - com.hmily.tcc - hmily-tcc-dubbo - {you version} - -``` - -- 配置启动 bean - -```xml - - - - - - - - - - - - - - - - - - - - - - -``` - -- 当然配置属性很多,这里我只给出了 demo,具体可以参考这个类: - -```java -package com.hmily.tcc.common.config; - -import com.hmily.tcc.common.enums.RepositorySupportEnum; -import lombok.Data; - -/** - * hmily config. - * - * @author xiaoyu - */ -@Data -public class TccConfig { - - - /** - * Resource suffix this parameter please fill in about is the transaction store path. - * If it's a table store this is a table suffix, it's stored the same way. - * If this parameter is not filled in, the applicationName of the application is retrieved by default - */ - private String repositorySuffix; - - /** - * log serializer. - * {@linkplain com.hmily.tcc.common.enums.SerializeEnum} - */ - private String serializer = "kryo"; - - /** - * scheduledPool Thread size. - */ - private int scheduledThreadMax = Runtime.getRuntime().availableProcessors() << 1; - - /** - * scheduledPool scheduledDelay unit SECONDS. - */ - private int scheduledDelay = 60; - - /** - * retry max. - */ - private int retryMax = 3; - - /** - * recoverDelayTime Unit seconds - * (note that this time represents how many seconds after the local transaction was created before execution). - */ - private int recoverDelayTime = 60; - - /** - * Parameters when participants perform their own recovery. - * 1.such as RPC calls time out - * 2.such as the starter down machine - */ - private int loadFactor = 2; - - /** - * repositorySupport. - * {@linkplain RepositorySupportEnum} - */ - private String repositorySupport = "db"; - - /** - * disruptor bufferSize. - */ - private int bufferSize = 4096 * 2 * 2; - - /** - * this is disruptor consumerThreads. - */ - private int consumerThreads = Runtime.getRuntime().availableProcessors() << 1; - - /** - * db config. - */ - private TccDbConfig tccDbConfig; - - /** - * mongo config. - */ - private TccMongoConfig tccMongoConfig; - - /** - * redis config. - */ - private TccRedisConfig tccRedisConfig; - - /** - * zookeeper config. - */ - private TccZookeeperConfig tccZookeeperConfig; - - /** - * file config. - */ - private TccFileConfig tccFileConfig; - -} -``` - -### SpringCloud 用户 - -```xml - - com.hmily.tcc - hmily-tcc-springcloud - {you version} - -``` - -### Motan 用户 - -```xml - - com.hmily.tcc - hmily-tcc-motan - {you version} - -``` - -### hmily-spring-boot-start 那这个就更容易了,只需要根据你的 RPC 框架去引入不同的 jar 包。 - -- 如果你是 dubbo 用户,那么引入 - -```xml - - com.hmily.tcc - hmily-tcc-spring-boot-starter-dubbo - ${your version} - -``` - -- 如果你是 SpringCloud 用户,那么引入 - -```xml - - com.hmily.tcc - hmily-tcc-spring-boot-starter-springcloud - ${your version} - -``` - -- 如果你是 Motan 用户,那么引入: - -```xml - - com.hmily.tcc - hmily-tcc-spring-boot-starter-motan - ${your version} - -``` - -- 然后在你的 yml 里面进行如下配置: - -```yml -hmily: - tcc: - serializer: kryo - recoverDelayTime: 128 - retryMax: 3 - scheduledDelay: 128 - scheduledThreadMax: 10 - repositorySupport: db - tccDbConfig: - driverClassName: com.mysql.jdbc.Driver - url: jdbc:mysql://192.168.1.98:3306/tcc?useUnicode=true&characterEncoding=utf8 - username: root - password: 123456 - - #repositorySupport : redis - #tccRedisConfig: - #masterName: mymaster - #sentinel : true - #sentinelUrl : 192.168.1.91:26379;192.168.1.92:26379;192.168.1.93:26379 - #password : foobaredbbexONE123 - - # repositorySupport : zookeeper - # host : 92.168.1.73:2181 - # sessionTimeOut : 100000 - # rootPath : /tcc - - # repositorySupport : mongodb - # mongoDbUrl : 192.168.1.68:27017 - # mongoDbName : happylife - # mongoUserName : xiaoyu - # mongoUserPwd : 123456 - - # repositorySupport : file - # path : /account - # prefix : account -``` - -- 就这么简单,然后就可以在接口方法上加上@Tcc 注解,进行愉快的使用了。 - -- 当然因为篇幅问题,很多东西只是简单的描述,尤其是逻辑方面的。 - -- 如果你感兴趣,可以在 github 上进行 star 和 fork,也可以加微信和 QQ 群进行交流。 - -- 下面是 github 地址:https://github.com/yu199195/hmily -- 最后再次感谢大家,如果有兴趣的朋友,可以提供你的优秀牛逼轰轰的 PR。。。。 +--- +title: Hmily:高性能异步分布式事务TCC框架 +author: xiaoyu +date: 2018-09-25 +tag: + - hmily + - TCC +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: 博客 +--- + +# Hmily 框架特性[https://github.com/yu199195/hmily] + +- 无缝集成 Spring,Spring boot start。 + +- 无缝集成 Dubbo,SpringCloud,Motan 等 rpc 框架。 + +- 多种事务日志的存储方式(redis,mongdb,mysql 等)。 + +- 多种不同日志序列化方式(Kryo,protostuff,hession)。 + +- 事务自动恢复。 + +- 支持内嵌事务的依赖传递。 + +- 代码零侵入,配置简单灵活。 + +# Hmily 为什么这么高性能? + +### 1.采用 disruptor 进行事务日志的异步读写(disruptor 是一个无锁,无 GC 的并发编程框架) + +```java +package com.hmily.tcc.core.disruptor.publisher; + +import com.hmily.tcc.common.bean.entity.TccTransaction; +import com.hmily.tcc.common.enums.EventTypeEnum; +import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory; +import com.hmily.tcc.core.coordinator.CoordinatorService; +import com.hmily.tcc.core.disruptor.event.HmilyTransactionEvent; +import com.hmily.tcc.core.disruptor.factory.HmilyTransactionEventFactory; +import com.hmily.tcc.core.disruptor.handler.HmilyConsumerDataHandler; +import com.hmily.tcc.core.disruptor.translator.HmilyTransactionEventTranslator; +import com.lmax.disruptor.BlockingWaitStrategy; +import com.lmax.disruptor.IgnoreExceptionHandler; +import com.lmax.disruptor.RingBuffer; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.dsl.ProducerType; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * event publisher. + * + * @author xiaoyu(Myth) + */ +@Component +public class HmilyTransactionEventPublisher implements DisposableBean { + + private Disruptor disruptor; + + private final CoordinatorService coordinatorService; + + @Autowired + public HmilyTransactionEventPublisher(final CoordinatorService coordinatorService) { + this.coordinatorService = coordinatorService; + } + + /** + * disruptor start. + * + * @param bufferSize this is disruptor buffer size. + * @param threadSize this is disruptor consumer thread size. + */ + public void start(final int bufferSize, final int threadSize) { + disruptor = new Disruptor<>(new HmilyTransactionEventFactory(), bufferSize, r -> { + AtomicInteger index = new AtomicInteger(1); + return new Thread(null, r, "disruptor-thread-" + index.getAndIncrement()); + }, ProducerType.MULTI, new BlockingWaitStrategy()); + + final Executor executor = new ThreadPoolExecutor(threadSize, threadSize, 0, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + HmilyThreadFactory.create("hmily-log-disruptor", false), + new ThreadPoolExecutor.AbortPolicy()); + + HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize]; + for (int i = 0; i < threadSize; i++) { + consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService); + } + disruptor.handleEventsWithWorkerPool(consumers); + disruptor.setDefaultExceptionHandler(new IgnoreExceptionHandler()); + disruptor.start(); + } + + /** + * publish disruptor event. + * + * @param tccTransaction {@linkplain com.hmily.tcc.common..entity.TccTransaction } + * @param type {@linkplain EventTypeEnum} + */ + public void publishEvent(final TccTransaction tccTransaction, final int type) { + final RingBuffer ringBuffer = disruptor.getRingBuffer(); + ringBuffer.publishEvent(new HmilyTransactionEventTranslator(type), tccTransaction); + } + + @Override + public void destroy() { + disruptor.shutdown(); + } +} +``` + +- 在这里 bufferSize 的默认值是 4094 \* 4,用户可以根据自行的情况进行配置。 + +```java + + HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize]; + for (int i = 0; i < threadSize; i++) { + consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService); + } + disruptor.handleEventsWithWorkerPool(consumers); +``` + +- 这里是采用多个消费者去处理队列里面的任务。 + +### 2.异步执行 confrim,cancel 方法。 + +```java +package com.hmily.tcc.core.service.handler; + +import com.hmily.tcc.common.bean.context.TccTransactionContext; +import com.hmily.tcc.common.bean.entity.TccTransaction; +import com.hmily.tcc.common.enums.TccActionEnum; +import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory; +import com.hmily.tcc.core.service.HmilyTransactionHandler; +import com.hmily.tcc.core.service.executor.HmilyTransactionExecutor; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * this is transaction starter. + * + * @author xiaoyu + */ +@Component +public class StarterHmilyTransactionHandler implements HmilyTransactionHandler { + + private static final int MAX_THREAD = Runtime.getRuntime().availableProcessors() << 1; + + private final HmilyTransactionExecutor hmilyTransactionExecutor; + + private final Executor executor = new ThreadPoolExecutor(MAX_THREAD, MAX_THREAD, 0, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + HmilyThreadFactory.create("hmily-execute", false), + new ThreadPoolExecutor.AbortPolicy()); + + @Autowired + public StarterHmilyTransactionHandler(final HmilyTransactionExecutor hmilyTransactionExecutor) { + this.hmilyTransactionExecutor = hmilyTransactionExecutor; + } + + @Override + public Object handler(final ProceedingJoinPoint point, final TccTransactionContext context) + throws Throwable { + Object returnValue; + try { + TccTransaction tccTransaction = hmilyTransactionExecutor.begin(point); + try { + //execute try + returnValue = point.proceed(); + tccTransaction.setStatus(TccActionEnum.TRYING.getCode()); + hmilyTransactionExecutor.updateStatus(tccTransaction); + } catch (Throwable throwable) { + //if exception ,execute cancel + final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction(); + executor.execute(() -> hmilyTransactionExecutor + .cancel(currentTransaction)); + throw throwable; + } + //execute confirm + final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction(); + executor.execute(() -> hmilyTransactionExecutor.confirm(currentTransaction)); + } finally { + hmilyTransactionExecutor.remove(); + } + return returnValue; + } +} +``` + +- 当 try 方法的 AOP 切面有异常的时候,采用线程池异步去执行 cancel,无异常的时候去执行 confrim 方法。 + +### 这里有人可能会问:那么 cancel 方法异常,或者 confrim 方法异常怎么办呢? + +答:首先这种情况是非常罕见的,因为你上一面才刚刚执行完 try。其次如果出现这种情况,在 try 阶段会保存好日志,Hmily 有内置的调度线程池来进行恢复,不用担心。 + +### 有人又会问:这里如果日志保存异常了怎么办? + +答:首先这又是一个牛角尖问题,首先日志配置的参数,在框架启动的时候,会要求你配置的。其次,就算在运行过程中日志保存异常,这时候框架会取缓存中的,并不会影响程序正确执行。最后,万一日志保存异常了,系统又在很极端的情况下 down 机了,恭喜你,你可以去买彩票了,最好的解决办法就是不去解决它。 + +### 3.ThreadLocal 缓存的使用。 + +```java + /** + * transaction begin. + * + * @param point cut point. + * @return TccTransaction + */ + public TccTransaction begin(final ProceedingJoinPoint point) { + LogUtil.debug(LOGGER, () -> "......hmily transaction!start...."); + //build tccTransaction + final TccTransaction tccTransaction = buildTccTransaction(point, TccRoleEnum.START.getCode(), null); + //save tccTransaction in threadLocal + CURRENT.set(tccTransaction); + //publishEvent + hmilyTransactionEventPublisher.publishEvent(tccTransaction, EventTypeEnum.SAVE.getCode()); + //set TccTransactionContext this context transfer remote + TccTransactionContext context = new TccTransactionContext(); + //set action is try + context.setAction(TccActionEnum.TRYING.getCode()); + context.setTransId(tccTransaction.getTransId()); + context.setRole(TccRoleEnum.START.getCode()); + TransactionContextLocal.getInstance().set(context); + return tccTransaction; + } +``` + +- 首先要理解,threadLocal 保存的发起者一方法的事务信息。这个很重要,不要会有点懵逼。rpc 的调用,会形成调用链,进行保存。 + +```java + +/** + * add participant. + * + * @param participant {@linkplain Participant} + */ + public void enlistParticipant(final Participant participant) { + if (Objects.isNull(participant)) { + return; + } + Optional.ofNullable(getCurrentTransaction()) + .ifPresent(c -> { + c.registerParticipant(participant); + updateParticipant(c); + }); + } +``` + +### 4.GuavaCache 的使用 + +```java +package com.hmily.tcc.core.cache; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.Weigher; +import com.hmily.tcc.common.bean.entity.TccTransaction; +import com.hmily.tcc.core.coordinator.CoordinatorService; +import com.hmily.tcc.core.helper.SpringBeanUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +/** + * use google guava cache. + * @author xiaoyu + */ +public final class TccTransactionCacheManager { + + private static final int MAX_COUNT = 10000; + + private static final LoadingCache LOADING_CACHE = + CacheBuilder.newBuilder().maximumWeight(MAX_COUNT) + .weigher((Weigher) (string, tccTransaction) -> getSize()) + .build(new CacheLoader() { + @Override + public TccTransaction load(final String key) { + return cacheTccTransaction(key); + } + }); + + private static CoordinatorService coordinatorService = SpringBeanUtils.getInstance().getBean(CoordinatorService.class); + + private static final TccTransactionCacheManager TCC_TRANSACTION_CACHE_MANAGER = new TccTransactionCacheManager(); + + private TccTransactionCacheManager() { + + } + + /** + * TccTransactionCacheManager. + * + * @return TccTransactionCacheManager + */ + public static TccTransactionCacheManager getInstance() { + return TCC_TRANSACTION_CACHE_MANAGER; + } + + private static int getSize() { + return (int) LOADING_CACHE.size(); + } + + private static TccTransaction cacheTccTransaction(final String key) { + return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction()); + } + + /** + * cache tccTransaction. + * + * @param tccTransaction {@linkplain TccTransaction} + */ + public void cacheTccTransaction(final TccTransaction tccTransaction) { + LOADING_CACHE.put(tccTransaction.getTransId(), tccTransaction); + } + + /** + * acquire TccTransaction. + * + * @param key this guava key. + * @return {@linkplain TccTransaction} + */ + public TccTransaction getTccTransaction(final String key) { + try { + return LOADING_CACHE.get(key); + } catch (ExecutionException e) { + return new TccTransaction(); + } + } + + /** + * remove guava cache by key. + * @param key guava cache key. + */ + public void removeByKey(final String key) { + if (StringUtils.isNotEmpty(key)) { + LOADING_CACHE.invalidate(key); + } + } + +} +``` + +- 在参与者中,我们使用了 ThreadLocal,而在参与者中,我们为什么不使用呢? + 其实原因有二点:首先.因为 try,和 confrim 会不在一个线程里,会造成 ThreadLocal 失效。当考虑到 RPC 集群的时候,可能会负载到不同的机器上。这里有一个细节就是: + +```java + private static TccTransaction cacheTccTransaction(final String key) { + return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction()); + } +``` + +当 GuavaCache 里面没有的时候,会去查询日志返回,这样就保证了对集群环境的支持。 + +### 以上 4 点造就了 Hmily 是一个异步的高性能分布式事务 TCC 框架的原因。 + +### Hmily 如何使用?(https://github.com/yu199195/hmily/tree/master/hmily-tcc-demo) + +首先因为之前的包命名问题,框架包并没有上传到 maven 中心仓库,固需要使用者自己拉取代码,编译 deploy 到自己的私服。 + +### 1.dubbo 用户 + +- 在你的 Api 接口项目引入 + +```xml + + com.hmily.tcc + hmily-tcc-annotation + {you version} + +``` + +- 在你的服务提供者项目引入 + +```xml + + com.hmily.tcc + hmily-tcc-dubbo + {you version} + +``` + +- 配置启动 bean + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +- 当然配置属性很多,这里我只给出了 demo,具体可以参考这个类: + +```java +package com.hmily.tcc.common.config; + +import com.hmily.tcc.common.enums.RepositorySupportEnum; +import lombok.Data; + +/** + * hmily config. + * + * @author xiaoyu + */ +@Data +public class TccConfig { + + + /** + * Resource suffix this parameter please fill in about is the transaction store path. + * If it's a table store this is a table suffix, it's stored the same way. + * If this parameter is not filled in, the applicationName of the application is retrieved by default + */ + private String repositorySuffix; + + /** + * log serializer. + * {@linkplain com.hmily.tcc.common.enums.SerializeEnum} + */ + private String serializer = "kryo"; + + /** + * scheduledPool Thread size. + */ + private int scheduledThreadMax = Runtime.getRuntime().availableProcessors() << 1; + + /** + * scheduledPool scheduledDelay unit SECONDS. + */ + private int scheduledDelay = 60; + + /** + * retry max. + */ + private int retryMax = 3; + + /** + * recoverDelayTime Unit seconds + * (note that this time represents how many seconds after the local transaction was created before execution). + */ + private int recoverDelayTime = 60; + + /** + * Parameters when participants perform their own recovery. + * 1.such as RPC calls time out + * 2.such as the starter down machine + */ + private int loadFactor = 2; + + /** + * repositorySupport. + * {@linkplain RepositorySupportEnum} + */ + private String repositorySupport = "db"; + + /** + * disruptor bufferSize. + */ + private int bufferSize = 4096 * 2 * 2; + + /** + * this is disruptor consumerThreads. + */ + private int consumerThreads = Runtime.getRuntime().availableProcessors() << 1; + + /** + * db config. + */ + private TccDbConfig tccDbConfig; + + /** + * mongo config. + */ + private TccMongoConfig tccMongoConfig; + + /** + * redis config. + */ + private TccRedisConfig tccRedisConfig; + + /** + * zookeeper config. + */ + private TccZookeeperConfig tccZookeeperConfig; + + /** + * file config. + */ + private TccFileConfig tccFileConfig; + +} +``` + +### SpringCloud 用户 + +```xml + + com.hmily.tcc + hmily-tcc-springcloud + {you version} + +``` + +### Motan 用户 + +```xml + + com.hmily.tcc + hmily-tcc-motan + {you version} + +``` + +### hmily-spring-boot-start 那这个就更容易了,只需要根据你的 RPC 框架去引入不同的 jar 包。 + +- 如果你是 dubbo 用户,那么引入 + +```xml + + com.hmily.tcc + hmily-tcc-spring-boot-starter-dubbo + ${your version} + +``` + +- 如果你是 SpringCloud 用户,那么引入 + +```xml + + com.hmily.tcc + hmily-tcc-spring-boot-starter-springcloud + ${your version} + +``` + +- 如果你是 Motan 用户,那么引入: + +```xml + + com.hmily.tcc + hmily-tcc-spring-boot-starter-motan + ${your version} + +``` + +- 然后在你的 yml 里面进行如下配置: + +```yml +hmily: + tcc: + serializer: kryo + recoverDelayTime: 128 + retryMax: 3 + scheduledDelay: 128 + scheduledThreadMax: 10 + repositorySupport: db + tccDbConfig: + driverClassName: com.mysql.jdbc.Driver + url: jdbc:mysql://192.168.1.98:3306/tcc?useUnicode=true&characterEncoding=utf8 + username: root + password: 123456 + + #repositorySupport : redis + #tccRedisConfig: + #masterName: mymaster + #sentinel : true + #sentinelUrl : 192.168.1.91:26379;192.168.1.92:26379;192.168.1.93:26379 + #password : foobaredbbexONE123 + + # repositorySupport : zookeeper + # host : 92.168.1.73:2181 + # sessionTimeOut : 100000 + # rootPath : /tcc + + # repositorySupport : mongodb + # mongoDbUrl : 192.168.1.68:27017 + # mongoDbName : happylife + # mongoUserName : xiaoyu + # mongoUserPwd : 123456 + + # repositorySupport : file + # path : /account + # prefix : account +``` + +- 就这么简单,然后就可以在接口方法上加上@Tcc 注解,进行愉快的使用了。 + +- 当然因为篇幅问题,很多东西只是简单的描述,尤其是逻辑方面的。 + +- 如果你感兴趣,可以在 github 上进行 star 和 fork,也可以加微信和 QQ 群进行交流。 + +- 下面是 github 地址:https://github.com/yu199195/hmily +- 最后再次感谢大家,如果有兴趣的朋友,可以提供你的优秀牛逼轰轰的 PR。。。。 diff --git a/src/zh/blog/jdk17-230601.md b/src/zh/blog/jdk17-230601.md index 73792ef4c4..9dd08d7f21 100644 --- a/src/zh/blog/jdk17-230601.md +++ b/src/zh/blog/jdk17-230601.md @@ -1,193 +1,193 @@ ---- -title: 都JDK17了,你还在用8吗 -author: QIANGLU -date: 2023-06-01 -cover: /assets/img/blog/jdk17-230601.png -head: - - - meta - - name: 博客 ---- - -**Spring Boot 3.1.0-M1** 已经发布一段时间了,不知道各位小伙伴是否关注了。随着 Spring 6.0 以及 SpringBoot 3.0 的发布,整个开发界也逐步进入到 jdk17 的时代。大有当年从 jdk6 到 jdk8 升级过程,痛苦并快乐着。 - -为了不被时代抛弃,开发者应追逐新的技术发展,拥抱变化,不要固步自封。 - -## 0x01 纵观发展 - -- Pre-alpha(Dev)指在软件项目进行正式测试之前执行的所有活动 -- LTS(Long-Term Support)版本指的是长期支持版本 -- Alpha   软件发布生命周期的 alpha 阶段是软件测试的第一阶段 -- Beta 阶段是紧随 alpha 阶段之后的软件开发阶段,以希腊字母第二个字母命名 -- Release candidate   发行候选版(RC),也被称为“银色版本”,是具备成为稳定产品的潜力的 beta 版本,除非出现重大错误,否则准备好发布 -- Stable release   稳定版又称为生产版本,是通过所有验证和测试阶段的最后一个发行候选版(RC) -- Release   一旦发布,软件通常被称为“稳定版” - -**下面我们来看下 JDK9~JDK17 的发展:** - -| 版本 | 发布时间 | 版本类型 | 支持时间 | 新特性 | -| ------ | ------------ | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------ | -| JDK 9 | 2017 年 9 月 | 长期支持版(LTS) | 5 年 | -模块化系统(Jigsaw)- JShell - 接口的私有方法- 改进的 try-with-resources - 集合工厂方法 - 改进的 Stream API | -| JDK 10 | 2018 年 3 月 | 短期支持版(non-LTS) | 6 个月 | \- 局部变量类型推断 - G1 垃圾回收器并行全阶段 \- 应用级别的 Java 类数据共享 | -| JDK 11 | 2018 年 9 月 | 长期支持版(LTS) | 8 年 | \- HTTP 客户端 API \- ZGC 垃圾回收器 \- 移除 Java EE 和 CORBA 模块 | -| JDK 12 | 2019 年 3 月 | 短期支持版(non-LTS) | 6 个月 | \- switch 表达式 \- JVM 原生 HTTP 客户端 \- 微基准测试套件 | -| JDK 13 | 2019 年 9 月 | 短期支持版(non-LTS) | 6 个月 | \-  switch 表达式增强 \- 文本块 \- ZGC 垃圾回收器增强 | -| JDK 14 | 2020 年 3 月 | 短期支持版(non-LTS) | 6 个月 | \- switch 表达式增强 \- 记录类型 \- Pattern Matching for instanceof | -| JDK 15 | 2020 年 9 月 | 短期支持版(non-LTS) | 6 个月 | \- 移除 Nashorn JavaScript 引擎 \- ZGC 垃圾回收器增强 \- 隐藏类和动态类文件 | -| JDK 16 | 2021 年 3 月 | 短期支持版(non-LTS) | 6 个月 | \- 位操作符增强\- Records 类型的完整性 \- Vector API | -| JDK 17 | 2021 年 9 月 | 长期支持版(LTS) | 8 年 | \- 垃圾回收器改进 \- Sealed 类和接口 \- kafka 客户端更新 \- 全新的安全存储机制 | - -需要注意的是,LTS 版本的支持时间可能会受到 Oracle 官方政策变化的影响,因此表格中的支持时间仅供参考。 - -## 0x02 详细解读 - -### JDK9 新特性 - -JDK 9 是 Java 平台的一个重大版本,于 2017 年 9 月发布。它引入了多项新特性,其中最重要的是模块化系统。以下是 JDK 9 新增内容的详细解释: - -1. 模块化系统(Jigsaw): - -Jigsaw 是 JDK 9 引入的一个模块化系统,它将 JDK 拆分为约 90 个模块。这些模块相互独立,可以更好地管理依赖关系和可见性,从而提高了代码的可维护性和可重用性。模块化系统还提供了一些新的工具和命令,如 jmod 命令和 jlink 命令,用于构建和组装模块化应用程序。 - -2. JShell: - -JShell 是一个交互式的 Java 命令行工具,可以在命令行中执行 Java 代码片段。它可以非常方便地进行代码测试和调试,并且可以快速地查看和修改代码。JShell 还提供了一些有用的功能,如自动补全、实时反馈和历史记录等。 - -3. 接口的私有方法: - -JDK 9 允许在接口中定义 private 和 private static 方法。这些方法可以被接口中的其他方法调用,但不能被实现该接口的类使用。这样可以避免在接口中重复编写相同的代码,提高了代码的重用性和可读性。 - -4. 改进的 try-with-resources: - -在 JDK 9 中,可以在 try-with-resources 语句块中使用 final 或 effectively final 的变量。这样可以避免在 finally 语句块中手动关闭资源,提高了代码的可读性和可维护性。 - -5. 集合工厂方法: - -JDK 9 提供了一系列工厂方法,用于创建 List、Set 和 Map 等集合对象。这些方法可以使代码更加简洁和易读,而且还可以为集合对象指定初始容量和类型参数。 - -6. 改进的 Stream API: - -JDK 9 引入了一些新的 Stream API 方法,如 takeWhile、dropWhile 和 ofNullable 等。takeWhile 和 dropWhile 方法可以根据指定的条件从流中选择元素,而 ofNullable 方法可以创建一个包含一个非空元素或空元素的 Stream 对象。 - -除了以上几个新特性,JDK 9 还引入了一些其他的改进和优化,如改进的 Stack-Walking API、改进的 CompletableFuture API、Java 应用程序启动时优化(Application Class-Data Sharing)等等。这些新特性和改进都为 Java 应用程序的开发和运行提供了更好的支持。 - -### JDK10 新特性 - -JDK10 是 JDK 的一个短期支持版本,于 2018 年 3 月发布。它的主要特性如下: - -1. 局部变量类型推断:Java 10 中引入了一种新的语法——`var`关键字,可以用于推断局部变量的类型,使代码更加简洁。例如,可以这样定义一个字符串类型的局部变量:`var str = "hello"`。 -2. G1 垃圾回收器并行全阶段:JDK10 中对 G1 垃圾回收器进行了改进,使其可以在并行全阶段进行垃圾回收,从而提高了 GC 效率。 -3. 应用级别的 Java 类数据共享:Java 10 中引入了一项新的特性,即应用级别的 Java 类数据共享(AppCDS),可以在多个 JVM 进程之间共享 Java 类元数据,从而加速 JVM 的启动时间。 -4. 线程局部握手协议:Java 10 中引入了线程局部握手协议(Thread-Local Handshakes),可以在不影响整个 JVM 性能的情况下,暂停所有线程执行特定的操作。 -5. 其他改进:Java 10 还包含一些其他的改进,例如对 Unicode 10.0 的支持,对时间 API 的改进,以及对容器 API 的改进等等。 - -总的来说,JDK10 主要关注于提高 Java 应用程序的性能和可维护性,通过引入一些新的特性和改进对 JDK 进行优化。 - -### JDK11 新特性 - -JDK11 是 JDK 的一个长期支持版本,于 2018 年 9 月发布。它的主要特性如下: - -1. HTTP 客户端 API:Java 11 中引入了一个全新的 HTTP 客户端 API,可以用于发送 HTTP 请求和接收 HTTP 响应,而不需要依赖第三方库。 -2. ZGC 垃圾回收器:Java 11 中引入了 ZGC 垃圾回收器(Z Garbage Collector),它是一种可伸缩且低延迟的垃圾回收器,可以在数百 GB 的堆上运行,且最大停顿时间不超过 10ms。 -3. 移除 Java EE 和 CORBA 模块:Java 11 中移除了 Java EE 和 CORBA 模块,这些模块在 Java 9 中已被标记为“过时”,并在 Java 11 中被完全移除。 -4. Epsilon 垃圾回收器:Java 11 中引入了一种新的垃圾回收器,称为 Epsilon 垃圾回收器,它是一种无操作的垃圾回收器,可以在不进行垃圾回收的情况下运行应用程序,适用于测试和基准测试等场景。 -5. 其他改进:Java 11 还包含一些其他的改进,例如对 Lambda 参数的本地变量类型推断,对字符串 API 的改进,以及对嵌套的访问控制的改进等等。 - -总的来说,JDK11 主要关注于提高 Java 应用程序的性能和安全性,通过引入一些新的特性和改进对 JDK 进行优化。其中,HTTP 客户端 API 和 ZGC 垃圾回收器是最值得关注的特性之一。 - -### JDK12 新特性 - -JDK12 是 JDK 的一个短期支持版本,于 2019 年 3 月发布。它的主要特性如下: - -1. Switch 表达式:Java 12 中引入了一种新的 Switch 表达式,可以使用 Lambda 表达式风格来简化代码。此外,Switch 表达式也支持返回值,从而可以更方便地进行流程控制。 -2. Microbenchmark Suite:Java 12 中引入了一个 Microbenchmark Suite,可以用于进行微基准测试,从而更好地评估 Java 程序的性能。 -3. JVM Constants API:Java 12 中引入了 JVM Constants API,可以用于在运行时获取常量池中的常量,从而更好地支持动态语言和元编程。 -4. Shenandoah 垃圾回收器:Java 12 中引入了 Shenandoah 垃圾回收器,它是一种低暂停时间的垃圾回收器,可以在非常大的堆上运行,且最大暂停时间不超过几毫秒。 -5. 其他改进:Java 12 还包含一些其他的改进,例如对 Unicode 11.0 的支持,对预览功能的改进,以及对集合 API 的改进等等。 - -总的来说,JDK12 主要关注于提高 Java 应用程序的性能和可维护性,通过引入一些新的特性和改进对 JDK 进行优化。其中,Switch 表达式和 Shenandoah 垃圾回收器是最值得关注的特性之一。 - -### JDK13 新特性 - -JDK13 是 JDK 的一个短期支持版本,于 2019 年 9 月发布。它的主要特性如下: - -1. Text Blocks:Java 13 中引入了一种新的语法,称为 Text Blocks,可以用于在代码中编写多行字符串,从而简化代码编写的复杂度。 -2. Switch 表达式增强:Java 13 中对 Switch 表达式进行了增强,支持多个表达式和 Lambda 表达式。 -3. ZGC 并行处理引用操作:Java 13 中对 ZGC 垃圾回收器进行了改进,支持并行处理引用操作,从而提高了 GC 效率。 -4. Reimplement the Legacy Socket API:Java 13 中重新实现了 Legacy Socket API,从而提高了网络编程的性能和可维护性。 -5. 其他改进:Java 13 还包含一些其他的改进,例如对预览功能的改进,对嵌套访问控制的改进,以及对集合 API 的改进等等。 - -总的来说,JDK13 主要关注于提高 Java 应用程序的性能和可维护性,通过引入一些新的特性和改进对 JDK 进行优化。其中,Text Blocks 和 Switch 表达式增强是最值得关注的特性之一。 - -### JDK14 新特性 - -JDK14 是 JDK 的一个短期支持版本,于 2020 年 3 月发布。它的主要特性如下: - -1. Records:Java 14 中引入了一种新的语法,称为 Records,可以用于定义不可变的数据类,从而简化代码编写的复杂度。 -2. Switch 表达式增强:Java 14 中对 Switch 表达式进行了增强,支持使用关键字 yield 返回值,从而可以更方便地进行流程控制。 -3. Text Blocks 增强:Java 14 中对 Text Blocks 进行了增强,支持在 Text Blocks 中嵌入表达式,从而可以更方便地生成动态字符串。 -4. Pattern Matching for instanceof:Java 14 中引入了一种新的语法,称为 Pattern Matching for instanceof,可以用于在判断对象类型时,同时对对象进行转换和赋值。 -5. 其他改进:Java 14 还包含一些其他的改进,例如对垃圾回收器和 JVM 的改进,对预览功能的改进,以及对 JFR 的改进等等。 - -总的来说,JDK14 主要关注于提高 Java 应用程序的可维护性和易用性,通过引入一些新的特性和改进对 JDK 进行优化。其中,Records 和 Pattern Matching for instanceof 是最值得关注的特性之一。 - -### JDK15 新特性 - -JDK15 是 JDK 的一个短期支持版本,于 2020 年 9 月发布。它的主要特性如下: - -1. Sealed Classes:Java 15 中引入了一种新的语法,称为 Sealed Classes,可以用于限制某个类的子类的数量,从而提高代码的可维护性。 -2. Text Blocks 增强:Java 15 中对 Text Blocks 进行了增强,支持在 Text Blocks 中使用反斜杠和$符号来表示特殊字符,从而可以更方便地生成动态字符串。 -3. Hidden Classes:Java 15 中引入了一种新的类,称为 Hidden Classes,可以在运行时动态创建和卸载类,从而提高代码的灵活性和安全性。 -4. ZGC 并发垃圾回收器增强:Java 15 中对 ZGC 垃圾回收器进行了增强,支持在启动时指定内存大小,从而提高了 GC 效率。 -5. 其他改进:Java 15 还包含一些其他的改进,例如对预览功能的改进,对 JVM 和垃圾回收器的改进,以及对集合 API 和 I/O API 的改进等等。 - -总的来说,JDK15 主要关注于提高 Java 应用程序的可维护性和性能,通过引入一些新的特性和改进对 JDK 进行优化。其中,Sealed Classes 和 Hidden Classes 是最值得关注的特性之一。 - -### JDK16 新特性 - -JDK16 是 JDK 的一个短期支持版本,于 2021 年 3 月发布。它的主要特性如下: - -1. Records 增强:Java 16 中对 Records 进行了增强,支持在 Records 中定义静态方法和构造方法,从而可以更方便地进行对象的创建和操作。 -2. Pattern Matching for instanceof 增强:Java 16 中对 Pattern Matching for instanceof 进行了增强,支持在判断对象类型时,同时对对象进行转换和赋值,并支持对 switch 语句进行模式匹配。 -3. Vector API:Java 16 中引入了一种新的 API,称为 Vector API,可以用于进行 SIMD(Single Instruction Multiple Data)向量计算,从而提高计算效率。 -4. JEP 388:Java 16 中引入了一个新的 JEP(JDK Enhancement Proposal),称为 JEP 388,可以用于提高 Java 应用程序的性能和可维护性。 -5. 其他改进:Java 16 还包含一些其他的改进,例如对垃圾回收器、JVM 和 JFR 的改进,对预览功能的改进,以及对集合 API 和 I/O API 的改进等等。 - -总的来说,JDK16 主要关注于提高 Java 应用程序的性能和可维护性,通过引入一些新的特性和改进对 JDK 进行优化。其中,Records 增强和 Pattern Matching for instanceof 增强是最值得关注的特性之一。 - -### JDK17 新特性 - -JDK17 是 JDK 的一个长期支持版本,于 2021 年 9 月发布。它的主要特性如下: - -1. Sealed Classes 增强:Java 17 中对 Sealed Classes 进行了增强,支持在 Sealed Classes 中定义接口和枚举类型,从而提高代码的灵活性。 -2. Pattern Matching for switch 增强:Java 17 中对 Pattern Matching for switch 进行了增强,支持在 switch 语句中使用嵌套模式和 or 运算符,从而提高代码的可读性和灵活性。 -3. Foreign Function and Memory API:Java 17 中引入了一种新的 API,称为 Foreign Function and Memory API,可以用于在 Java 中调用 C 和 C++的函数和库,从而提高代码的可扩展性和互操作性。 -4. JEP 391:Java 17 中引入了一个新的 JEP(JDK Enhancement Proposal),称为 JEP 391,可以用于提高 Java 应用程序的性能和可维护性。 -5. 其他改进:Java 17 还包含一些其他的改进,例如对垃圾回收器、JVM 和 JFR 的改进,对预览功能的改进,以及对集合 API 和 I/O API 的改进等等。 - -总的来说,JDK17 主要关注于提高 Java 应用程序的灵活性、可扩展性和性能,通过引入一些新的特性和改进对 JDK 进行优化。其中,Sealed Classes 增强和 Foreign Function and Memory API 是最值得关注的特性之一。 - -## 总结 - -- JDK9:引入了模块化系统、JShell、私有接口方法、多版本兼容性等新特性 -- JDK10:引入了局部变量类型推断、垃圾回收器接口、并行全垃圾回收器等新特性 -- JDK11:引入了 ZGC 垃圾回收器、HTTP 客户端 API、VarHandles API 等新特性 -- JDK12:引入了 Switch 表达式、新的字符串方法、HTTP/2 客户端 API 等新特性 -- JDK13:引入了 Text Blocks、Switch 表达式增强、改进的 ZGC 性能等新特性 -- JDK14:引入了 Records、Switch 表达式增强、Pattern Matching for instanceof 等新特性 -- JDK15:引入了 Sealed Classes、Text Blocks 增强、Hidden Classes 等新特性 -- JDK16:引入了 Records 增强、Pattern Matching for instanceof 增强、Vector API 等新特性 -- JDK17:引入了 Sealed Classes 增强、Pattern Matching for switch 增强、Foreign Function and Memory API 等新特性 - -总的来说,JDK9 到 JDK17 的更新涵盖了 Java 应用程序开发的各个方面,包括模块化、垃圾回收、性能优化、API 增强等等,为 Java 开发者提供了更多的选择和工具,以提高代码的质量和效率 - -## 小记 - -Java 作为一门长盛不衰的编程语言,未来的发展仍然有许多潜力和机会。 - -- 云计算和大数据:随着云计算和大数据的发展,Java 在这些领域的应用也越来越广泛。Java 已经成为了许多云计算平台和大数据处理框架的首选语言之一。 -- 移动端和 IoT:Java 也逐渐开始在移动端和物联网领域崭露头角。Java 的跨平台特性和安全性,使得它成为了许多移动应用和物联网设备的首选开发语言。 -- 前沿技术的应用:Java 社区一直在积极探索和应用前沿技术,例如人工智能、机器学习、区块链等。Java 在这些领域的应用和发展也将会是未来的趋势。 -- 开源社区的发展:Java 开源社区的发展也将会对 Java 的未来产生重要影响。Java 社区的开源项目和社区贡献者数量不断增加,将会为 Java 的发展提供更多的动力和资源。 -- 新的 Java 版本:Oracle 已经宣布将在未来两年内发布两个新的 Java 版本,其中一个是短期支持版本,另一个是长期支持版本。这将会为 Java 开发者提供更多的新特性和改进,以满足不断变化的需求。 - -总的来说,Java 作为一门优秀的编程语言,具有广泛的应用和发展前景。随着技术的不断创新和社区的不断发展,Java 的未来将会更加光明。 +--- +title: 都JDK17了,你还在用8吗 +author: QIANGLU +date: 2023-06-01 +cover: /assets/img/blog/jdk17-230601.png +head: + - - meta + - name: 博客 +--- + +**Spring Boot 3.1.0-M1** 已经发布一段时间了,不知道各位小伙伴是否关注了。随着 Spring 6.0 以及 SpringBoot 3.0 的发布,整个开发界也逐步进入到 jdk17 的时代。大有当年从 jdk6 到 jdk8 升级过程,痛苦并快乐着。 + +为了不被时代抛弃,开发者应追逐新的技术发展,拥抱变化,不要固步自封。 + +## 0x01 纵观发展 + +- Pre-alpha(Dev)指在软件项目进行正式测试之前执行的所有活动 +- LTS(Long-Term Support)版本指的是长期支持版本 +- Alpha   软件发布生命周期的 alpha 阶段是软件测试的第一阶段 +- Beta 阶段是紧随 alpha 阶段之后的软件开发阶段,以希腊字母第二个字母命名 +- Release candidate   发行候选版(RC),也被称为“银色版本”,是具备成为稳定产品的潜力的 beta 版本,除非出现重大错误,否则准备好发布 +- Stable release   稳定版又称为生产版本,是通过所有验证和测试阶段的最后一个发行候选版(RC) +- Release   一旦发布,软件通常被称为“稳定版” + +**下面我们来看下 JDK9~JDK17 的发展:** + +| 版本 | 发布时间 | 版本类型 | 支持时间 | 新特性 | +| ------ | ------------ | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------ | +| JDK 9 | 2017 年 9 月 | 长期支持版(LTS) | 5 年 | -模块化系统(Jigsaw)- JShell - 接口的私有方法- 改进的 try-with-resources - 集合工厂方法 - 改进的 Stream API | +| JDK 10 | 2018 年 3 月 | 短期支持版(non-LTS) | 6 个月 | \- 局部变量类型推断 - G1 垃圾回收器并行全阶段 \- 应用级别的 Java 类数据共享 | +| JDK 11 | 2018 年 9 月 | 长期支持版(LTS) | 8 年 | \- HTTP 客户端 API \- ZGC 垃圾回收器 \- 移除 Java EE 和 CORBA 模块 | +| JDK 12 | 2019 年 3 月 | 短期支持版(non-LTS) | 6 个月 | \- switch 表达式 \- JVM 原生 HTTP 客户端 \- 微基准测试套件 | +| JDK 13 | 2019 年 9 月 | 短期支持版(non-LTS) | 6 个月 | \-  switch 表达式增强 \- 文本块 \- ZGC 垃圾回收器增强 | +| JDK 14 | 2020 年 3 月 | 短期支持版(non-LTS) | 6 个月 | \- switch 表达式增强 \- 记录类型 \- Pattern Matching for instanceof | +| JDK 15 | 2020 年 9 月 | 短期支持版(non-LTS) | 6 个月 | \- 移除 Nashorn JavaScript 引擎 \- ZGC 垃圾回收器增强 \- 隐藏类和动态类文件 | +| JDK 16 | 2021 年 3 月 | 短期支持版(non-LTS) | 6 个月 | \- 位操作符增强\- Records 类型的完整性 \- Vector API | +| JDK 17 | 2021 年 9 月 | 长期支持版(LTS) | 8 年 | \- 垃圾回收器改进 \- Sealed 类和接口 \- kafka 客户端更新 \- 全新的安全存储机制 | + +需要注意的是,LTS 版本的支持时间可能会受到 Oracle 官方政策变化的影响,因此表格中的支持时间仅供参考。 + +## 0x02 详细解读 + +### JDK9 新特性 + +JDK 9 是 Java 平台的一个重大版本,于 2017 年 9 月发布。它引入了多项新特性,其中最重要的是模块化系统。以下是 JDK 9 新增内容的详细解释: + +1. 模块化系统(Jigsaw): + +Jigsaw 是 JDK 9 引入的一个模块化系统,它将 JDK 拆分为约 90 个模块。这些模块相互独立,可以更好地管理依赖关系和可见性,从而提高了代码的可维护性和可重用性。模块化系统还提供了一些新的工具和命令,如 jmod 命令和 jlink 命令,用于构建和组装模块化应用程序。 + +2. JShell: + +JShell 是一个交互式的 Java 命令行工具,可以在命令行中执行 Java 代码片段。它可以非常方便地进行代码测试和调试,并且可以快速地查看和修改代码。JShell 还提供了一些有用的功能,如自动补全、实时反馈和历史记录等。 + +3. 接口的私有方法: + +JDK 9 允许在接口中定义 private 和 private static 方法。这些方法可以被接口中的其他方法调用,但不能被实现该接口的类使用。这样可以避免在接口中重复编写相同的代码,提高了代码的重用性和可读性。 + +4. 改进的 try-with-resources: + +在 JDK 9 中,可以在 try-with-resources 语句块中使用 final 或 effectively final 的变量。这样可以避免在 finally 语句块中手动关闭资源,提高了代码的可读性和可维护性。 + +5. 集合工厂方法: + +JDK 9 提供了一系列工厂方法,用于创建 List、Set 和 Map 等集合对象。这些方法可以使代码更加简洁和易读,而且还可以为集合对象指定初始容量和类型参数。 + +6. 改进的 Stream API: + +JDK 9 引入了一些新的 Stream API 方法,如 takeWhile、dropWhile 和 ofNullable 等。takeWhile 和 dropWhile 方法可以根据指定的条件从流中选择元素,而 ofNullable 方法可以创建一个包含一个非空元素或空元素的 Stream 对象。 + +除了以上几个新特性,JDK 9 还引入了一些其他的改进和优化,如改进的 Stack-Walking API、改进的 CompletableFuture API、Java 应用程序启动时优化(Application Class-Data Sharing)等等。这些新特性和改进都为 Java 应用程序的开发和运行提供了更好的支持。 + +### JDK10 新特性 + +JDK10 是 JDK 的一个短期支持版本,于 2018 年 3 月发布。它的主要特性如下: + +1. 局部变量类型推断:Java 10 中引入了一种新的语法——`var`关键字,可以用于推断局部变量的类型,使代码更加简洁。例如,可以这样定义一个字符串类型的局部变量:`var str = "hello"`。 +2. G1 垃圾回收器并行全阶段:JDK10 中对 G1 垃圾回收器进行了改进,使其可以在并行全阶段进行垃圾回收,从而提高了 GC 效率。 +3. 应用级别的 Java 类数据共享:Java 10 中引入了一项新的特性,即应用级别的 Java 类数据共享(AppCDS),可以在多个 JVM 进程之间共享 Java 类元数据,从而加速 JVM 的启动时间。 +4. 线程局部握手协议:Java 10 中引入了线程局部握手协议(Thread-Local Handshakes),可以在不影响整个 JVM 性能的情况下,暂停所有线程执行特定的操作。 +5. 其他改进:Java 10 还包含一些其他的改进,例如对 Unicode 10.0 的支持,对时间 API 的改进,以及对容器 API 的改进等等。 + +总的来说,JDK10 主要关注于提高 Java 应用程序的性能和可维护性,通过引入一些新的特性和改进对 JDK 进行优化。 + +### JDK11 新特性 + +JDK11 是 JDK 的一个长期支持版本,于 2018 年 9 月发布。它的主要特性如下: + +1. HTTP 客户端 API:Java 11 中引入了一个全新的 HTTP 客户端 API,可以用于发送 HTTP 请求和接收 HTTP 响应,而不需要依赖第三方库。 +2. ZGC 垃圾回收器:Java 11 中引入了 ZGC 垃圾回收器(Z Garbage Collector),它是一种可伸缩且低延迟的垃圾回收器,可以在数百 GB 的堆上运行,且最大停顿时间不超过 10ms。 +3. 移除 Java EE 和 CORBA 模块:Java 11 中移除了 Java EE 和 CORBA 模块,这些模块在 Java 9 中已被标记为“过时”,并在 Java 11 中被完全移除。 +4. Epsilon 垃圾回收器:Java 11 中引入了一种新的垃圾回收器,称为 Epsilon 垃圾回收器,它是一种无操作的垃圾回收器,可以在不进行垃圾回收的情况下运行应用程序,适用于测试和基准测试等场景。 +5. 其他改进:Java 11 还包含一些其他的改进,例如对 Lambda 参数的本地变量类型推断,对字符串 API 的改进,以及对嵌套的访问控制的改进等等。 + +总的来说,JDK11 主要关注于提高 Java 应用程序的性能和安全性,通过引入一些新的特性和改进对 JDK 进行优化。其中,HTTP 客户端 API 和 ZGC 垃圾回收器是最值得关注的特性之一。 + +### JDK12 新特性 + +JDK12 是 JDK 的一个短期支持版本,于 2019 年 3 月发布。它的主要特性如下: + +1. Switch 表达式:Java 12 中引入了一种新的 Switch 表达式,可以使用 Lambda 表达式风格来简化代码。此外,Switch 表达式也支持返回值,从而可以更方便地进行流程控制。 +2. Microbenchmark Suite:Java 12 中引入了一个 Microbenchmark Suite,可以用于进行微基准测试,从而更好地评估 Java 程序的性能。 +3. JVM Constants API:Java 12 中引入了 JVM Constants API,可以用于在运行时获取常量池中的常量,从而更好地支持动态语言和元编程。 +4. Shenandoah 垃圾回收器:Java 12 中引入了 Shenandoah 垃圾回收器,它是一种低暂停时间的垃圾回收器,可以在非常大的堆上运行,且最大暂停时间不超过几毫秒。 +5. 其他改进:Java 12 还包含一些其他的改进,例如对 Unicode 11.0 的支持,对预览功能的改进,以及对集合 API 的改进等等。 + +总的来说,JDK12 主要关注于提高 Java 应用程序的性能和可维护性,通过引入一些新的特性和改进对 JDK 进行优化。其中,Switch 表达式和 Shenandoah 垃圾回收器是最值得关注的特性之一。 + +### JDK13 新特性 + +JDK13 是 JDK 的一个短期支持版本,于 2019 年 9 月发布。它的主要特性如下: + +1. Text Blocks:Java 13 中引入了一种新的语法,称为 Text Blocks,可以用于在代码中编写多行字符串,从而简化代码编写的复杂度。 +2. Switch 表达式增强:Java 13 中对 Switch 表达式进行了增强,支持多个表达式和 Lambda 表达式。 +3. ZGC 并行处理引用操作:Java 13 中对 ZGC 垃圾回收器进行了改进,支持并行处理引用操作,从而提高了 GC 效率。 +4. Reimplement the Legacy Socket API:Java 13 中重新实现了 Legacy Socket API,从而提高了网络编程的性能和可维护性。 +5. 其他改进:Java 13 还包含一些其他的改进,例如对预览功能的改进,对嵌套访问控制的改进,以及对集合 API 的改进等等。 + +总的来说,JDK13 主要关注于提高 Java 应用程序的性能和可维护性,通过引入一些新的特性和改进对 JDK 进行优化。其中,Text Blocks 和 Switch 表达式增强是最值得关注的特性之一。 + +### JDK14 新特性 + +JDK14 是 JDK 的一个短期支持版本,于 2020 年 3 月发布。它的主要特性如下: + +1. Records:Java 14 中引入了一种新的语法,称为 Records,可以用于定义不可变的数据类,从而简化代码编写的复杂度。 +2. Switch 表达式增强:Java 14 中对 Switch 表达式进行了增强,支持使用关键字 yield 返回值,从而可以更方便地进行流程控制。 +3. Text Blocks 增强:Java 14 中对 Text Blocks 进行了增强,支持在 Text Blocks 中嵌入表达式,从而可以更方便地生成动态字符串。 +4. Pattern Matching for instanceof:Java 14 中引入了一种新的语法,称为 Pattern Matching for instanceof,可以用于在判断对象类型时,同时对对象进行转换和赋值。 +5. 其他改进:Java 14 还包含一些其他的改进,例如对垃圾回收器和 JVM 的改进,对预览功能的改进,以及对 JFR 的改进等等。 + +总的来说,JDK14 主要关注于提高 Java 应用程序的可维护性和易用性,通过引入一些新的特性和改进对 JDK 进行优化。其中,Records 和 Pattern Matching for instanceof 是最值得关注的特性之一。 + +### JDK15 新特性 + +JDK15 是 JDK 的一个短期支持版本,于 2020 年 9 月发布。它的主要特性如下: + +1. Sealed Classes:Java 15 中引入了一种新的语法,称为 Sealed Classes,可以用于限制某个类的子类的数量,从而提高代码的可维护性。 +2. Text Blocks 增强:Java 15 中对 Text Blocks 进行了增强,支持在 Text Blocks 中使用反斜杠和$符号来表示特殊字符,从而可以更方便地生成动态字符串。 +3. Hidden Classes:Java 15 中引入了一种新的类,称为 Hidden Classes,可以在运行时动态创建和卸载类,从而提高代码的灵活性和安全性。 +4. ZGC 并发垃圾回收器增强:Java 15 中对 ZGC 垃圾回收器进行了增强,支持在启动时指定内存大小,从而提高了 GC 效率。 +5. 其他改进:Java 15 还包含一些其他的改进,例如对预览功能的改进,对 JVM 和垃圾回收器的改进,以及对集合 API 和 I/O API 的改进等等。 + +总的来说,JDK15 主要关注于提高 Java 应用程序的可维护性和性能,通过引入一些新的特性和改进对 JDK 进行优化。其中,Sealed Classes 和 Hidden Classes 是最值得关注的特性之一。 + +### JDK16 新特性 + +JDK16 是 JDK 的一个短期支持版本,于 2021 年 3 月发布。它的主要特性如下: + +1. Records 增强:Java 16 中对 Records 进行了增强,支持在 Records 中定义静态方法和构造方法,从而可以更方便地进行对象的创建和操作。 +2. Pattern Matching for instanceof 增强:Java 16 中对 Pattern Matching for instanceof 进行了增强,支持在判断对象类型时,同时对对象进行转换和赋值,并支持对 switch 语句进行模式匹配。 +3. Vector API:Java 16 中引入了一种新的 API,称为 Vector API,可以用于进行 SIMD(Single Instruction Multiple Data)向量计算,从而提高计算效率。 +4. JEP 388:Java 16 中引入了一个新的 JEP(JDK Enhancement Proposal),称为 JEP 388,可以用于提高 Java 应用程序的性能和可维护性。 +5. 其他改进:Java 16 还包含一些其他的改进,例如对垃圾回收器、JVM 和 JFR 的改进,对预览功能的改进,以及对集合 API 和 I/O API 的改进等等。 + +总的来说,JDK16 主要关注于提高 Java 应用程序的性能和可维护性,通过引入一些新的特性和改进对 JDK 进行优化。其中,Records 增强和 Pattern Matching for instanceof 增强是最值得关注的特性之一。 + +### JDK17 新特性 + +JDK17 是 JDK 的一个长期支持版本,于 2021 年 9 月发布。它的主要特性如下: + +1. Sealed Classes 增强:Java 17 中对 Sealed Classes 进行了增强,支持在 Sealed Classes 中定义接口和枚举类型,从而提高代码的灵活性。 +2. Pattern Matching for switch 增强:Java 17 中对 Pattern Matching for switch 进行了增强,支持在 switch 语句中使用嵌套模式和 or 运算符,从而提高代码的可读性和灵活性。 +3. Foreign Function and Memory API:Java 17 中引入了一种新的 API,称为 Foreign Function and Memory API,可以用于在 Java 中调用 C 和 C++的函数和库,从而提高代码的可扩展性和互操作性。 +4. JEP 391:Java 17 中引入了一个新的 JEP(JDK Enhancement Proposal),称为 JEP 391,可以用于提高 Java 应用程序的性能和可维护性。 +5. 其他改进:Java 17 还包含一些其他的改进,例如对垃圾回收器、JVM 和 JFR 的改进,对预览功能的改进,以及对集合 API 和 I/O API 的改进等等。 + +总的来说,JDK17 主要关注于提高 Java 应用程序的灵活性、可扩展性和性能,通过引入一些新的特性和改进对 JDK 进行优化。其中,Sealed Classes 增强和 Foreign Function and Memory API 是最值得关注的特性之一。 + +## 总结 + +- JDK9:引入了模块化系统、JShell、私有接口方法、多版本兼容性等新特性 +- JDK10:引入了局部变量类型推断、垃圾回收器接口、并行全垃圾回收器等新特性 +- JDK11:引入了 ZGC 垃圾回收器、HTTP 客户端 API、VarHandles API 等新特性 +- JDK12:引入了 Switch 表达式、新的字符串方法、HTTP/2 客户端 API 等新特性 +- JDK13:引入了 Text Blocks、Switch 表达式增强、改进的 ZGC 性能等新特性 +- JDK14:引入了 Records、Switch 表达式增强、Pattern Matching for instanceof 等新特性 +- JDK15:引入了 Sealed Classes、Text Blocks 增强、Hidden Classes 等新特性 +- JDK16:引入了 Records 增强、Pattern Matching for instanceof 增强、Vector API 等新特性 +- JDK17:引入了 Sealed Classes 增强、Pattern Matching for switch 增强、Foreign Function and Memory API 等新特性 + +总的来说,JDK9 到 JDK17 的更新涵盖了 Java 应用程序开发的各个方面,包括模块化、垃圾回收、性能优化、API 增强等等,为 Java 开发者提供了更多的选择和工具,以提高代码的质量和效率 + +## 小记 + +Java 作为一门长盛不衰的编程语言,未来的发展仍然有许多潜力和机会。 + +- 云计算和大数据:随着云计算和大数据的发展,Java 在这些领域的应用也越来越广泛。Java 已经成为了许多云计算平台和大数据处理框架的首选语言之一。 +- 移动端和 IoT:Java 也逐渐开始在移动端和物联网领域崭露头角。Java 的跨平台特性和安全性,使得它成为了许多移动应用和物联网设备的首选开发语言。 +- 前沿技术的应用:Java 社区一直在积极探索和应用前沿技术,例如人工智能、机器学习、区块链等。Java 在这些领域的应用和发展也将会是未来的趋势。 +- 开源社区的发展:Java 开源社区的发展也将会对 Java 的未来产生重要影响。Java 社区的开源项目和社区贡献者数量不断增加,将会为 Java 的发展提供更多的动力和资源。 +- 新的 Java 版本:Oracle 已经宣布将在未来两年内发布两个新的 Java 版本,其中一个是短期支持版本,另一个是长期支持版本。这将会为 Java 开发者提供更多的新特性和改进,以满足不断变化的需求。 + +总的来说,Java 作为一门优秀的编程语言,具有广泛的应用和发展前景。随着技术的不断创新和社区的不断发展,Java 的未来将会更加光明。 diff --git a/src/zh/blog/milvus-easyai-face-java.md b/src/zh/blog/milvus-easyai-face-java.md new file mode 100644 index 0000000000..b7efa29614 --- /dev/null +++ b/src/zh/blog/milvus-easyai-face-java.md @@ -0,0 +1,145 @@ +--- +title: Milvus×EasyAi:如何用java从零搭建人脸识别应用 +author: 2024年12月26日 08:36 +date: 2024-12-26 +cover: /assets/img/blog/milvus-easyai-face-java-0.webp +head: + - - meta + - name: 博客 +--- + +![](/assets/img/blog/milvus-easyai-face-java-0.webp) + +![](/assets/img/blog/milvus-easyai-face-java-1.png) + +**如何从零搭建一个人脸识别应用?不妨试试原生Java人工智能算法:EasyAi + Milvus 的组合拳**。 + +**本文将使用到的软件和工具包括**: + +* EasyAi:人脸特征向量提取 + +* Milvus:向量数据库用于高效存储和检索数据。 + + +**01**. + +**EasyAi:国内人气最高的Java人工智能算法框架** + +作为纯java开发 Ai 应用的框架,EasyAi无任何依赖,它是一个原生Java人工智能算法框架。首先,它可以Maven一键丝滑引入我们的Java项目,无需任何额外的环境配置与依赖,做到开箱即用。再者,它既有一些我们已经封装好的图像目标检测及人工智能客服的模块,也提供各种深度学习,机器学习,强化学习,启发式学习,矩阵运算,求导函数,求偏导函数等底层算法工具。开发者可以通过简单的学习,就能完成根据自身业务,深度开发符合自己业务的小微模型。 + +**02**. + +**EasyAi-Face:基于Easy-Ai的人脸识别应用** + +**一,生成人类平均脸**,将所有人脸样本样本缩放到统一尺寸后,多余的上下截断,不足的进行补0,所有像素通道求和后获取平均数,将样本输出生成平均脸。 + +**二,通过事先训练好的人脸定位fastYolo模型**,对目标照片进行一次定位,并设置一个阈值,只有当可信度超过该阈值时才被认为是人脸。 + +**三,获取目标照片中可信度最高的人脸定位框**,基于该定位进行人脸位置的二次修正。 + +_二次修正方案:_ + +* 通过粒子群,设置四个特征维度寻求最优解,它们分别是人脸位置左上角的x与y坐标与宽高。自适应函数返回值设置为最小值最优。xy与宽高四维粒子调整活动范围,上下限制为一次定位坐标与宽高的+-50像素的范围(自行可调)。 + +* 适应函数计算流程为通过四个维度粒子锁定的坐标将人脸截取下来,将它与先前获取的平均脸按照之前的缩放方案,再次缩放到指定的一个更小的尺寸,并将它们的灰度通道通过softMax将整个矩阵的所有数值概率化。 + +* 对比平均脸与粒子此时锁定人脸灰度概率图像的欧式距离,并返回。让粒子探索(在指定迭代次数中)最小值最优解。 + + +**四,获取人脸特征**,获取最后粒子寻找的最优坐标,根据该坐标将图像截取下来,并截取其从上到下高度的0.7倍的图像位置(将嘴部扔掉,嘴部的稳定性比较差),获取此时图像的LBP局部二值化纹理特征。 + +**03**. + +**EasyAi-Face + Milvus搭建人脸识别应用** + +**3.1 提取人脸特征** + +**引入依赖** +``` +         +            org.dromara.easyai +            seeFace +            1.0.5 +         +``` +**初始化Face** +``` +    @Bean +    public Face face(FaceConfig faceConfig ){ + +        if (StringUtils.isNotBlank(faceConfig.getAvgFace()) && StringUtils.isNotBlank(faceConfig.getFaceModel())){ +            return FaceFactory.getFace(faceConfig.getAvgFace(), faceConfig.getFaceModel()); + +        } +        return FaceFactory.getFace(); + + +    } +``` +**提取人脸特征** +``` +    private List getFloats(InputStream inputStream) { +        ThreeChannelMatrix m = Picture.getThreeMatrix(inputStream, false); +        ErrorMessage errorMessage = face.look(m, idWorker.nextId(), 30); +        final Matrix feature = errorMessage.getFaceMessage().getFeature(); +        return MatrixUtil.matrixToFloatList(feature); +    }    +``` +**3.2 存到向量库** +``` +    public void initUserVector(UserDTO userDTO, List features) { +        List names = Collections.singletonList(userDTO.getUserName()); +        List userIds = Collections.singletonList(userDTO.getUserId()); +        List getFaceUrl = Collections.singletonList(userDTO.getFaceUrl()); +        List getFaceFeatureUrl = Collections.singletonList(userDTO.getFaceFeatureUrl()); +        List> vectors = Collections.singletonList(features); +        List fields = new ArrayList(); +        fields.add(new Field("vector", vectors)); +        fields.add(new Field("face_url", getFaceUrl)); +        fields.add(new Field("face_feature_url", getFaceFeatureUrl)); +        fields.add(new Field("user_id", userIds)); +        fields.add(new Field("user_name", names)); +        InsertParam insertParam = InsertParam.newBuilder().withCollectionName(milvusConfig.getCollectionName()).withFields(fields).build(); +        this.milvusClient.insert(insertParam); +    } +``` +**3.3 【识别人脸】人脸特征L2相似性查找** +``` +    public List search(List floatList, Integer topK) { +        final List idScoreList = vectorService.search(floatList, topK); +        List list = new ArrayList<>(); +        idScoreList.forEach(idScore -> { +            UserDTO imageDTO = new UserDTO(); +            final float score = idScore.getScore(); +            final Map fieldValues = idScore.getFieldValues(); +            imageDTO.setAutoId(Long.valueOf(String.valueOf( fieldValues.getOrDefault("Auto_id", "-1")))); +            imageDTO.setUserId(Long.valueOf(String.valueOf( fieldValues.getOrDefault("user_id", "-1")))); +            imageDTO.setUserName(String.valueOf((fieldValues.getOrDefault("user_name", "")))); +            imageDTO.setFaceUrl(String.valueOf((fieldValues.getOrDefault("face_url", "")))); +            imageDTO.setFaceFeatureUrl(String.valueOf((fieldValues.getOrDefault("face_feature_url", "")))); +            imageDTO.setScore(Math.sqrt(score)); +            list.add(imageDTO); + +        }); +        return list; +    } +``` +**04**. + +**总结** + +本文展示了如何使用 **EasyAi 和 Milvus** 搭建一个人脸识别应用。通过结合Java生态EasyAi和Milvus向量搜索的优势,我们可以快速的使用java搭建自己的人脸识别的项目。我们希望这篇文章对您有所帮助。同时,我们鼓励您在自己的项目中使用EasyAi和向量搜索,探索更多可能性。本文涉及的代码可以通过 **Gitee** 获取:Easy-Ai-Face_(https://gitee.com/fushoujiang/easy-ai-face)_。 + + + +**推荐阅读** + +[![](/assets/img/blog/milvus-easyai-face-java-2.png)](https://mp.weixin.qq.com/s?__biz=MzUzMDI5OTA5NQ==&mid=2247507230&idx=1&sn=3dc46b15f39e6ac1666614bb7aa338d6&scene=21#wechat_redirect) + +[![](/assets/img/blog/milvus-easyai-face-java-3.png)](https://mp.weixin.qq.com/s?__biz=MzUzMDI5OTA5NQ==&mid=2247507157&idx=1&sn=0ac4343d81b7ef655e358e49c496e37b&scene=21#wechat_redirect) + + + +![](/assets/img/blog/milvus-easyai-face-java-4.webp) + +![](/assets/img/blog/milvus-easyai-face-java-5.webp) \ No newline at end of file diff --git a/src/zh/blog/os-data-community-team.md b/src/zh/blog/os-data-community-team.md new file mode 100644 index 0000000000..b8a0d6fdc9 --- /dev/null +++ b/src/zh/blog/os-data-community-team.md @@ -0,0 +1,261 @@ +--- +title: 国内开源数据社区运营天团 +author: 2024年12月20日 13:59 +date: 2024-12-20 +cover: /assets/img/blog/os-data-community-team-0.gif +head: + - - meta + - name: 博客 +--- + +![](/assets/img/blog/os-data-community-team-0.gif) + + +**PowerData** + +数据之力 非同凡想 + +■ ■ ■ + +思考  交流  贡献  共赢 + +○ + +○ + +   **全文共  4632 字,建议阅读 10 分钟** + +  **文章导读 /** Company Nature + + +       最近看到一篇尹海文老师的文章[《数据库运营天团plus》](https://mp.weixin.qq.com/s?__biz=Mzg3MTk4MzYyMQ==&mid=2247486110&idx=1&sn=dd60d01a97e6f8aaa1fbbc9315009959&scene=21#wechat_redirect),突发奇想,开源数据社区也有很多有能力有颜值有思想的运营同学,于是写了这篇关于国内开源数据运营天团的介绍。 + +     作者:PowerData-李奇峰 | 编辑:PowerData-李钊 + + + +**Chapter** + +**01** + +**DolphinScheduler** + +曾辉 + +Apache DolphinScheduler Committer 主要负责 Apache DolphinScheduler and SeaTunnel 社区的运营工作,主导项目的生态建设,维护开发者关系,提升“开源项目”在全球的影响力及社区内部的建设,致力于传播开源文化,如果你对开源大数据技术活动感兴趣,🙏欢迎👏👏联系我一起玩,平时喜欢音乐、户外和打羽毛球! + +**运营心得**:社区运营的核心在于激活开发者、创造价值和建立连接。首先,要理解开发者需求,通过高质量内容和活动吸引用户参与,比如技术分享、案例解读和贡献激励。其次,降低参与门槛,提供清晰的贡献指南和相关教程,帮助新人快速上手并融入社区。注重核心贡献者的培养与激励也是特别重要的,赋予用户身份感和荣誉感,形成长期的贡献动力。定期收集用户反馈,快速响应问题,增强社区的归属感与信任感。最后,保持与技术和生态的联动,让社区为产品和业务带来实际价值,形成良性循环。  + +![](/assets/img/blog/os-data-community-team-6.jpg) + +**辉哥,业内公认开源好声音(记得有次开源晚宴,一首海阔天空直接征服全场),我心中的开源运营好大哥,专业、热情、谦逊,在PowerData成立之初,就跟辉哥一起搞活动,直到现在,虽然未曾谋面,但是心向往之。辉哥目前儿女双全,家庭幸福美满,开源事业蒸蒸日上,真的是令人羡慕不已啊**。 + +**Chapter** + +**02** + +               **SelectDB** + +椰子 + + +活动运营一枚,24g 网上冲浪选手,酷爱研究关注各个社区又出了什么有趣好玩的活动,最喜欢的是线下活动周边集邮! + +**运营心得**:运营得有活人感,在明确社区目标和用户需求的基础上举办活动,打造社区特色标签,注重与用户之间的互动,鼓励大家积极参与社区活动并给予一定的激励。 + +**在中国开源年会和昕冉同学线下交流过,还请昕冉同学举着PD的标牌拍照,为PD代言。言归正传,和昕冉同学合作了很多次线上与线下活动,是一位很有趣、很专业、很热情的运营同学。(她说自己是社恐,其实不是嗷,介于i人与e人之间的状态)**。 + + + +杨素丽 + + +Apache Doris 是我真正意义上参与运营的数据库开源项目,接触该项目近 3 年的时间,负责 Doris 社区 & 飞轮科技的内容运营工作,包括技术解析、用户案例、解决方案等文章类型,以及电子书、案例集的策划、编撰与宣传,目前已策划输出 100+ 篇原创文章,全渠道收获 500w+阅读量,策划了公众号单篇阅读 4w+文章。在这 3 年,我与社区共同成长,Doris 社区的强大成就了我,我也尽我所能为社区繁荣贡献力量。 + +**运营心得**:谈心得还资历尚浅。运营工作既需要创新和洞察力,也要求具备极强的执行力和沟通能力,而了解业务,以用户需求及体验为基础也是运营所具备的基本能力。开源用户这一细分群体,拥有较强的技术素养和独特的见解,因此运营工作也要更加开放、包容、务实、尊重。在内容运营中,从用户需求出发、解决实际问题为第一要务,创造有价值、高质量的内容,加以有效运营策略,持之以恒,方能实现目标。 + +![](/assets/img/blog/os-data-community-team-9.jpg) + +![](/assets/img/blog/os-data-community-team-10.jpg) + +**素丽同学还是太谦虚了,作为SelectDB的内容运营,我在排版公众号的时候,没有灵感就看SelectDB的公众号(借鉴不是抄袭嗷,手动狗头)。23年的时候在Doris Summit现场面基过,特随和,特热情。希望以后有机会再线下交流**。 + + + +**Chapter** + +**01** + +**NebulaGraph** + +张慧 + +NebulaGraph 开发者关系负责人,CNCF Ambassador,技术出身,擅长从开发者和用户角度在社区与开发者打交道。 + +**运营心得**:以人为本, Trust is everything. + +![](/assets/img/blog/os-data-community-team-11.jpg) + +![](/assets/img/blog/os-data-community-team-12.jpg) + +![](/assets/img/blog/os-data-community-team-13.jpg) + +**跟慧姐最近才认识,在交流的过程中感受到慧姐对于开源的热爱,以及对于社区运营的责任心,以后可以一起搞更多有趣的开源活动。攒这篇文章的过程中,慧姐还主动帮我邀请TiDB社区运营表妹,非常感谢~** + +**Chapter** + +**04** + +**IoTDB** + +秦楚晴 + +大家好,我是楚晴,进行着IoTDB社区的运营,同时也是天谋科技的运营负责人。IoTDB是一款时序数据库,由清华大学发起研制,在2020年的时候从Apache顺利毕业成为顶级项目。运营IoTDB社区三年多,我们组织或参与了近五十场活动,和数千位开源用户进行联系和沟通,致力于将IoTDB打造成有温度、有收获的开源社区。 + +**运营心得**:社区是一个神奇的群体。散落在全国甚至全球各地的伙伴们进行协同从而形成了社区,这是一个自发形成的甚至是弱连接的行为。运营社区,个人认为是有定位人群和识别需求这两个重点工作。第一步,定位人群,这是指深入了解社区群体并分析相关特性。将人群定位清晰、对社区有深度的了解,是进行社区运营的基石。第二步是,识别需求。意思是将社区中需要运营者提供帮助的地方识别出来。比如说社区用户有深入了解产品架构的需要,那么可以组织相关分享或者博客的编写。 + +![](/assets/img/blog/os-data-community-team-14.jpg) + +**楚晴姐,清华学霸,气质美女,运营专家,今年邀请楚晴姐在开源年会进行分享并进行交流,其对于IoTDB相关的技术细节了如指掌,市场、运营一肩挑,十分全面。PD今年和IoTDB多次合作,感觉IoTDB的同学都很谦逊、专业,交流如沐春风**。 + +**Chapter** + +**05** + +**Pulsar** + +傅腾 + +Hi,各位大佬好,我是傅腾,目前在谙流科技从事 Pulsar 商业化和社区的运营工作。入行一晃 12 年,已是白了少年头。我是一开始就坚定了 Programmer 的路线,后来做了产品和项目,现在开始做运营和支撑。喜欢开源,热衷跑步和健身,拒绝内耗,希望和平。快奔四的年纪,最重视的就是每况愈下的身体和希望能充满活力的家庭。当然还是很喜欢工作的,真实有效。希望能认识大家。 + +**运营心得**:作为一个快速发展的开源消息和流处理平台(嗯,其实也有 12 年的历史了),Apache Pulsar 已经深入国内外众多头部大厂。开源和社区在这里,起到了至关重要的作用。从 2000 年开源的浪潮掀起,就像旋风一般,席卷了全球软件基础设施。不知道,大家怎么看开源?从我们从事开源商业化的角度看,基础设施因为其复杂性,开源实际达成了全球领域专家的合作,以无与伦比的高效性迅速淘汰了很多闭源的“黑盒子”。这大大降低了销售成本。因此,对开源社区的运营,其实就是一个集品牌、市场和产品于一身的活。这实在是一个苦活,毕竟什么都要做。也实在是一个好活,因为开源,你可以触及到各行各业的大佬。大家可以相互增益,度过寒冬。非要说有什么心得,那就是要先付出,持续的、坚挺的付出! + +![](/assets/img/blog/os-data-community-team-15.jpg) + +**腾哥,整天就是push我干活,奇峰推文别忘了转发、奇峰北京活动来支持一下、奇峰代言一下Pulsar,还老是很客气,说了都是哥们,哎,到底腾哥年纪大了,不过最近也有在努力融入PD的氛围。今年跟腾哥一起搞了很多场活动,合作特别愉快,而且对PD的支持力度也很大,以后继续**。 + +**Chapter** + +**06** + +**开源社** + +董吉甫 + +KCC@南京(开源社南京城市社区)发起人,秉持开源社宗旨,以“开源知行,笃实刚健”为思想,在南京推广开源文化、与各社区联合举办线下活动、共建开源生态、共创开源传承。 + +**运营心得**:一切从开源中来,一切从开源中去。通过开源结识了很多朋友,同时通过参与开源活动也学习了很多新知识。付出行动,收获欢喜。 + +![](/assets/img/blog/os-data-community-team-16.jpg) + +![](/assets/img/blog/os-data-community-team-17.jpg) + +   + +![](/assets/img/blog/os-data-community-team-18.jpg) + +**吉甫哥,我在南京的亲密开源伙伴,还有上图中的马证哥、大庆哥,我们四人是南京的开源整活小分队,从23年,每年都要在南京搞场活动,吉甫哥作为牵头组织者、记录者,他写的会后总结文章是我心中开源活动总结文章no.1,记录详细、资料共享,结合自己的思考,就像再听了一遍分享。附上一篇吉甫哥的活动总结文章:[AI开源南京分享会回顾录](https://mp.weixin.qq.com/s?__biz=MzA4NTM4NDc4NQ==&mid=2247542008&idx=1&sn=6209251a4a343932e5451cc04d35461e&scene=21#wechat_redirect)** + +**Chapter** + +**07** + +**Dromara** + +李楠 + +大家好,我是开源社&Dromara开源社区的李楠(楠楠),喜欢漫画、刷网剧,好奇心很重,喜欢瞎倒腾的“闲”女子。 + +**运营心得**:多互动、多倾听,鼓励大家交流分享,及时回应问题。多宣传,吸引更多人加入。还要维护好社区规则,保持氛围友好,这样每个人都能找到归属感。 + +![](/assets/img/blog/os-data-community-team-19.jpg) + +**和李楠同学在PD杭州开源行的时候线下交流过,是一个非常可爱、且非常热诚的女孩子,李楠同学不是技术出身,但是一直志愿于开源运营,今年的PD开源行,也给了社区很多的支持,希望李楠同学以后可以一直可爱下去~** + +**按理说这篇是开源数据社区的运营人员介绍,但是还是将吉甫哥还有李楠同学放在了这篇文章里,想让大家看到开源领域有这么两位热诚、可爱的人**。 + +**Chapter** + +**08** + +**ByConity** + +刘小米 + +开源运营妹子一枚,有时候会有点社恐的ENFP。从毕业前实习开始就一直在开源圈内工作,这几年也接触了各种大佬,每天能学习到很多知识,希望未来可以继续在社区建设上贡献自己的一份力量🐮 + +**运营心得**:在我看来,社区运营最重要的一点是维护开发者关系,通过与开发人员建立强有力的关系,社区可以利用广泛的开发人员专业知识,增加产品的迭代,并迅速获得社区的反馈。通过从成熟度、基础设施等方面分析项目的状态后使用正确的运营手段维护开发者关系,可以为社区的开发者们创造更好的环境。 + +![](/assets/img/blog/os-data-community-team-20.jpg) + +**小米同学,没有见过面,但是一起合作了很多场活动,今年PD的城市行,小米同学在多场活动均提供了场地、讲师、周边的支持。为人特别真诚,难怪能够将开发者关系维护工作开展的很好。没有和小米同学线下交流过,是我的遗憾之一,希望明年有机会能和小米同学当面请教**。 + +**Chapter** + +**09** + +               **TiDB** + +黄漫绅(表妹) + +在这个数字化飞速发展的时代,开源社区已经成为了技术交流、知识共享的重要平台。而在我心中,理想的国内开源社区应该是一个充满活力、开放包容、协作共赢的生态系统。它不仅仅是技术的聚集地,更是梦想和热情的交汇点。 + +**运营心得**:在 TiDB 社区中,所有的决策过程、技术路线图、发展规划都将对社区成员公开,让每个人都能够了解社区的运作方式和未来方向。这种透明不仅能够增强社区成员的信任感,还能够吸引更多的优秀人才加入。鼓励每个人都能够提出自己的想法和建议,无论是对现有功能的改进,还是对未来发展的设想,都能够成为社区发展的宝贵财富。 + +![](/assets/img/blog/os-data-community-team-21.jpg) + +**在开源数据社区运营领域,一定少不了TiDB表妹(我应该得叫表姐)的身影,TiDB社区的活跃离不开表妹的专业和付出。我这个外行也通过[《理想中的开源社区是怎么样的?来自 TiDB 社区运营表妹的浅认识》](https://mp.weixin.qq.com/s?__biz=MzA4MTgzNDQzMQ==&mid=2651502145&idx=1&sn=5225ec33313a4284d72143296441a8aa&scene=21#wechat_redirect)这篇文章,学到了特别多实用可落地的运营知识。希望以后有机会,能够当面向表妹请教~** + +**Chapter** + +**10** + +**爱可生** + +管长龙 + +爱可生开源社区运营经理,从服务器机房到培训讲台,热爱开源技术布道。 + +**运营心得**:作为一名社区运营,需要热情与耐心。建立高效的沟通渠道,方便开发者交流。积极回应成员问题,营造友好氛围。如果有机会线下交流,那效果绝对是最棒的。 + +    跟龙哥相识于上海的一次开源活动,并在中国开源年会有过深入交流,当时PD和爱可生的展位挨着。作为技术出身的运营大佬,在技术布道层面有着天生的优势,加上龙哥侃侃而谈的表达,以及耐心平稳的语气,你会不自觉的被他吸引。而且还很乐于助人,活动中给我拍了很多帅照。龙哥也是介于i人和e人之间,无事i人,有活儿特e。 + +<<< 总结 >>> + +    开源社区强者如云,不仅是开发人员,运营人员也是各个身怀绝技,天赋异禀。希望今天这篇文章,能够让你看到开源运营人员的专业、热情、帅气、美丽 。 + + + +**往期精彩文章合集** + +![](/assets/img/blog/os-data-community-team-22.gif) + + + + + +【开源人物专栏】 + + [开源人物-禅道社区王春生:让项目管理更便捷](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488278&idx=1&sn=f3e915780246a3e0084fb398b5f392e5&scene=21#wechat_redirect) + + [开源人物—IoTDB乔嘉林:征服工业互联网数据](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488845&idx=1&sn=101879acffe0007ab7a5db28a26b3f2f&scene=21#wechat_redirect) + +【技术文章专栏】 + + [Kafka 源码学习(一)生产者源码](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247487990&idx=1&sn=5b98967588a71af796130e85801f86b4&scene=21#wechat_redirect) + + [kafka源码学习(二)服务端源码](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488095&idx=1&sn=b3ae348379c7ab827e2b1828b48cc344&scene=21#wechat_redirect) + + [kafka源码学习(三)消费者源码](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488219&idx=1&sn=01636401250533e1bd5b44a7975e8cb1&scene=21#wechat_redirect) + +[【技术实践】Doris数据查询性能解析:Explain 与 Profile 功能深度应用](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247488755&idx=1&sn=42bdf28a3ba55ddb9a6d416b315a4b44&scene=21#wechat_redirect) + +【社区活动专栏】 + + [活动回顾 |【数字经济·城市脉动】PowerData西安开源行](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247487699&idx=1&sn=f1ccdcf2e0681d9dcca47e91838a64a4&scene=21#wechat_redirect) + + [活动回顾 |【数字经济·城市脉动】PowerData 杭州开源行](https://mp.weixin.qq.com/s?__biz=MzUyMTA1NTcyOA==&mid=2247487817&idx=1&sn=8e1a9e621032a95d74b9881bdb4f2498&scene=21#wechat_redirect) + + + +<<<  END >>> \ No newline at end of file diff --git a/src/zh/blog/soul_resource_learning_07_admin.md b/src/zh/blog/soul_resource_learning_07_admin.md index eea93d010e..2d186ccd2a 100644 --- a/src/zh/blog/soul_resource_learning_07_admin.md +++ b/src/zh/blog/soul_resource_learning_07_admin.md @@ -1,139 +1,139 @@ ---- -title: Soul网关学习Admin源码分析 -author: 曾林辉 -date: 2021-01-20 -tag: - - Soul -cover: /img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -# 源码分析 - -1. **页面操作源码分析** - -在分析源码之前,先看下图,页面显示加载的插件列表会对应后端的请求,根据后端请求,找到对应的 controller 类 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210117034006267.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -然后找到对应的方法,在上图可以看到这里是访问 plugin 中默认为空的 mapping,传入到分页相关的参数,然后去查询数据库中对应的插件记录 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210117034215738.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -数据库中对应的表为下图所示,divide 状态是启用,在上一篇中,就是用这个插件来测试网关 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210117035235400.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -同时请求的还有选择器,请求的 controller 见下图。在上篇的演示中,我们直接在页面把选择器中的条件 CRUD,可以实时反应到网关中去,而不需要重启网关,所以这里除了 query 方法中,增加、删除、和修改方法中,在保存到数据库之后都有一个 publishEvent 方法。就是这个事件方法,可以让用户直接在 soul 后台配置规则,从而达到时时生效的目地 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210117040000892.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -``` -public int createOrUpdate(final SelectorDTO selectorDTO) { - int selectorCount; - SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO); - List selectorConditionDTOs = selectorDTO.getSelectorConditions(); - if (StringUtils.isEmpty(selectorDTO.getId())) { - selectorCount = selectorMapper.insertSelective(selectorDO); - selectorConditionDTOs.forEach(selectorConditionDTO -> { - selectorConditionDTO.setSelectorId(selectorDO.getId()); - selectorConditionMapper.insertSelective(SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO)); - }); - } else { - selectorCount = selectorMapper.updateSelective(selectorDO); - //delete rule condition then add - selectorConditionMapper.deleteByQuery(new SelectorConditionQuery(selectorDO.getId())); - selectorConditionDTOs.forEach(selectorConditionDTO -> { - selectorConditionDTO.setSelectorId(selectorDO.getId()); - SelectorConditionDO selectorConditionDO = SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO); - selectorConditionMapper.insertSelective(selectorConditionDO); - }); - } - publishEvent(selectorDO, selectorConditionDTOs); - return selectorCount; - } -``` - -2. **与 soul-bootstrap 数据同步(websocket)源码分析** - -之前介绍了 admin 页面操作之后把数据保存数据库,然后用了 spring 自带的响应式编程把数据同步到 bootstrap 项目,以达到动态刷新网关规则及插件,而不用添加配置后去重启网关。 -当 soul-bootstrap 启动时,看日志会打出来这么一段 - -``` -2021-01-21 00:33:39.620 INFO 14276 --- [0.0-9095-exec-5] o.d.s.a.l.websocket.WebsocketCollector : websocket on open successful.... -``` - -那么问题来了,它用 websocket 和谁连接了,又是怎么连接的?下面通过找到打日志的代码,再通过打断点的方式来调试一下,这里是打日志出来的地方 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121004835890.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -先来分析一下这个代码: - -- 从 websocketConfig 这个配置里面拿到配的请求地址,这个配置文件当然是在下图的这个地方配的 -- 拿到这个配置地址后,创建了一个定时的线程池,线程池大小为 urls.length,线程名称前缀为"websocket-connect"的守护线程。这里为什么要创建守护线程,因为这只是为了保证 bootstrap 和 admin 的 websocket 连接不断,类似于心跳的作用,所以用守护线程是最好的 -- 根据创建的 client 端,一个一个的去请求配置文件配的地址,然后打印之前所找到的日志 -- 后面就启动线程去判断 client 是否关闭,如果关闭就会去重新连接(初始间隔 10 秒,然后每 30 秒去执行一次检查,所以如果你看到控制台有时会打印多个连接成功的日志,说明重连了) - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121005138940.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -- 我们再来看看在 admin 后台操作的数据是怎么同步到 bootstrap 中的呢,之前有说过,在后台保存或者更新数据之后,会调用 publishEvent 方法,这个是 spring 自带响应式编程的方法,既然是响应式,那就是基于事件的,那就得有 listener - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121011826544.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -一找果不其然,上图画红框的地方是不是很眼熟,没错,监听器,和 websocket 相关的监听器,如果还是有点看不明白监听和之前的 publishEvent 有什么关系,那就把监听器里的代码打上断点,调试一把。我这里为了方便,就点了这个同步所有数据 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121012123937.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -这里进的是 DataChangedEventDispatcher 这个类,调用了 event 相关的方法,在左下角这个地方,是不是看到了熟悉了方法了,没错就是上面说的 publishEvent -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121012240486.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -- 然后会跳转到 WebsocketDataChangedListener 这个类中,这里重点看一下在调试方法中 send 方法 - ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021012101272614.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -- 这里通过 send 方法把更新的数据发到 bootstrap 中,到此 admin 怎么同步数据到 bootstrap 中就真相大白了 - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121013002688.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -3. **与 soul-bootstrap 数据同步(zookeeper)源码分析** - 话不多说,先上图,把 websocket 的配置先注释掉,打开 zookeeper 的配置,前提是把本地或者远程的 zookeeper 服务打开,然后启动 soul-admin - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121152407500.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - 首先进入了 ZookeeperDataInit 类的 run 方法,这个方法执行完之后,奇怪的一点是跳到了 WebsocketDataChangedListener 类中去了 - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121233759520.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - 这一点没弄明白,等这个类里面的 onPluginChanged 方法执行完了之后,回到了 ZookeeperDataChangedListener 类里来了 - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234036652.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - 如果不是删除的话,就会更新 zkNode 节点数据 - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234326495.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - 更新 zk 节点的方法 - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234628900.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - 而且之后的 onSelectorChanged、onMetaDataChanged 、onRuleChanged 方法都会先走 WebsocketDataChangedListener 类里面相对应的方法,然后才会进 ZookeeperDataChangedListener 类的方法。如果插件数据有更改,也是通过上面的步骤重新来一遍。 - 这里面同步数据会进两个 Listener 类的问题到这还没解决,突然想到在 pom 文件里面有对 websocket 的依赖,因为 application.yml 文件中已经把 websocket 这个配置注释掉了(不是把 enable=false),先把这个依赖注释掉再看,然后看着代码编译都不通过了。还有一个办法是把 websocket 改成不启用,改完重启发现不会再跳到 websocket 相关的类中了 - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122000547192.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -4. **与 soul-bootstrap 数据同步(http)源码分析** - -老规矩,改 yml 文件中的配置,然后找到对应的 listener 类打断点调试,这里如果是用 http 的话 websocket 相关的类还是会被访问到,所以这里一样的不能直接注释 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122005803207.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -下面来看看里面的代码: -这里一个构造方法,里面实例化了一个 clients 数组阻塞队列,大小为 1024。一个定时任务线程池,线程数为 1,名字前缀为 "long-polling" 的后台守护线程,看名字可以知道,这是用来长轮询的。一个相关的属性配置 -初始化前方法里面启动了定时线程,间隔 5 分钟之后,每 5 分钟执行一次,去执行 refreshLocalCache 这个刷新本地缓存的方法 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122004153175.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) - -``` - private void refreshLocalCache() { - this.updateAppAuthCache(); - this.updatePluginCache(); - this.updateRuleCache(); - this.updateSelectorCache(); - this.updateMetaDataCache(); - } -``` - -如果是手动点同步数据时,会去执行下面这些相关的方法,也是通过定时线程池来执行,只不过是立即执行 -![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122010007881.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) -五分钟后,执行相关的刷新方法,打印的日志 - -``` -2021-01-22 01:00:19.007 INFO 20800 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config start. -2021-01-22 01:00:19.010 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[APP_AUTH], old: {group='APP_AUTH', md5='d751713988987e9331980363e24189ce', lastModifyTime=1611248118794}, updated: {group='APP_AUTH', md5='d751713988987e9331980363e24189ce', lastModifyTime=1611248419010} -2021-01-22 01:00:19.012 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[PLUGIN], old: {group='PLUGIN', md5='70b269257d47f0f6404ae7b7e976d8f1', lastModifyTime=1611248295740}, updated: {group='PLUGIN', md5='70b269257d47f0f6404ae7b7e976d8f1', lastModifyTime=1611248419012} -2021-01-22 01:00:19.069 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[RULE], old: {group='RULE', md5='5811b56257e31109621976d39fc226aa', lastModifyTime=1611248301607}, updated: {group='RULE', md5='5811b56257e31109621976d39fc226aa', lastModifyTime=1611248419069} -2021-01-22 01:00:19.075 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[SELECTOR], old: {group='SELECTOR', md5='70bad5ebb1cf6e3fc55278eef2df42f3', lastModifyTime=1611248299419}, updated: {group='SELECTOR', md5='70bad5ebb1cf6e3fc55278eef2df42f3', lastModifyTime=1611248419075} -2021-01-22 01:00:19.077 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[META_DATA], old: {group='META_DATA', md5='5f79d821e3b601330631a2d53294fb34', lastModifyTime=1611248302571}, updated: {group='META_DATA', md5='5f79d821e3b601330631a2d53294fb34', lastModifyTime=1611248419077} -2021-01-22 01:00:19.077 INFO 20800 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config success. -``` - -5. soul 中还有其它方法同步数据,这些后面有精力再分析,soul-admin 源码先分析到这,如果后续再分析的话,会另外再写一遍文章,这里就先到此为止 - -# 总结 - -soul-admin 中还有功能现在还没有使用到,还有很多好玩的东西,这篇会持续更新,到用到的时候再去具体分析里面的源码。 - -1. 2021-01-20 分析了 soul-admin 用 websocket 同步数据到 soul-bootstrap 中 -2. 2021-01-21 分析了 soul-admin 用 zookeeper 同步数据到 soul-bootstrap 中 -3. 2021-01-21 分析了 soul-admin 用 http 同步数据到 soul-bootstrap 中 +--- +title: Soul网关学习Admin源码分析 +author: 曾林辉 +date: 2021-01-20 +tag: + - Soul +cover: /img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +# 源码分析 + +1. **页面操作源码分析** + +在分析源码之前,先看下图,页面显示加载的插件列表会对应后端的请求,根据后端请求,找到对应的 controller 类 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210117034006267.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +然后找到对应的方法,在上图可以看到这里是访问 plugin 中默认为空的 mapping,传入到分页相关的参数,然后去查询数据库中对应的插件记录 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210117034215738.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +数据库中对应的表为下图所示,divide 状态是启用,在上一篇中,就是用这个插件来测试网关 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210117035235400.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +同时请求的还有选择器,请求的 controller 见下图。在上篇的演示中,我们直接在页面把选择器中的条件 CRUD,可以实时反应到网关中去,而不需要重启网关,所以这里除了 query 方法中,增加、删除、和修改方法中,在保存到数据库之后都有一个 publishEvent 方法。就是这个事件方法,可以让用户直接在 soul 后台配置规则,从而达到时时生效的目地 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210117040000892.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +``` +public int createOrUpdate(final SelectorDTO selectorDTO) { + int selectorCount; + SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO); + List selectorConditionDTOs = selectorDTO.getSelectorConditions(); + if (StringUtils.isEmpty(selectorDTO.getId())) { + selectorCount = selectorMapper.insertSelective(selectorDO); + selectorConditionDTOs.forEach(selectorConditionDTO -> { + selectorConditionDTO.setSelectorId(selectorDO.getId()); + selectorConditionMapper.insertSelective(SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO)); + }); + } else { + selectorCount = selectorMapper.updateSelective(selectorDO); + //delete rule condition then add + selectorConditionMapper.deleteByQuery(new SelectorConditionQuery(selectorDO.getId())); + selectorConditionDTOs.forEach(selectorConditionDTO -> { + selectorConditionDTO.setSelectorId(selectorDO.getId()); + SelectorConditionDO selectorConditionDO = SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO); + selectorConditionMapper.insertSelective(selectorConditionDO); + }); + } + publishEvent(selectorDO, selectorConditionDTOs); + return selectorCount; + } +``` + +2. **与 soul-bootstrap 数据同步(websocket)源码分析** + +之前介绍了 admin 页面操作之后把数据保存数据库,然后用了 spring 自带的响应式编程把数据同步到 bootstrap 项目,以达到动态刷新网关规则及插件,而不用添加配置后去重启网关。 +当 soul-bootstrap 启动时,看日志会打出来这么一段 + +``` +2021-01-21 00:33:39.620 INFO 14276 --- [0.0-9095-exec-5] o.d.s.a.l.websocket.WebsocketCollector : websocket on open successful.... +``` + +那么问题来了,它用 websocket 和谁连接了,又是怎么连接的?下面通过找到打日志的代码,再通过打断点的方式来调试一下,这里是打日志出来的地方 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121004835890.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +先来分析一下这个代码: + +- 从 websocketConfig 这个配置里面拿到配的请求地址,这个配置文件当然是在下图的这个地方配的 +- 拿到这个配置地址后,创建了一个定时的线程池,线程池大小为 urls.length,线程名称前缀为"websocket-connect"的守护线程。这里为什么要创建守护线程,因为这只是为了保证 bootstrap 和 admin 的 websocket 连接不断,类似于心跳的作用,所以用守护线程是最好的 +- 根据创建的 client 端,一个一个的去请求配置文件配的地址,然后打印之前所找到的日志 +- 后面就启动线程去判断 client 是否关闭,如果关闭就会去重新连接(初始间隔 10 秒,然后每 30 秒去执行一次检查,所以如果你看到控制台有时会打印多个连接成功的日志,说明重连了) + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121005138940.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +- 我们再来看看在 admin 后台操作的数据是怎么同步到 bootstrap 中的呢,之前有说过,在后台保存或者更新数据之后,会调用 publishEvent 方法,这个是 spring 自带响应式编程的方法,既然是响应式,那就是基于事件的,那就得有 listener + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121011826544.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +一找果不其然,上图画红框的地方是不是很眼熟,没错,监听器,和 websocket 相关的监听器,如果还是有点看不明白监听和之前的 publishEvent 有什么关系,那就把监听器里的代码打上断点,调试一把。我这里为了方便,就点了这个同步所有数据 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121012123937.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +这里进的是 DataChangedEventDispatcher 这个类,调用了 event 相关的方法,在左下角这个地方,是不是看到了熟悉了方法了,没错就是上面说的 publishEvent +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121012240486.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +- 然后会跳转到 WebsocketDataChangedListener 这个类中,这里重点看一下在调试方法中 send 方法 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021012101272614.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +- 这里通过 send 方法把更新的数据发到 bootstrap 中,到此 admin 怎么同步数据到 bootstrap 中就真相大白了 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121013002688.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +3. **与 soul-bootstrap 数据同步(zookeeper)源码分析** + 话不多说,先上图,把 websocket 的配置先注释掉,打开 zookeeper 的配置,前提是把本地或者远程的 zookeeper 服务打开,然后启动 soul-admin + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121152407500.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + 首先进入了 ZookeeperDataInit 类的 run 方法,这个方法执行完之后,奇怪的一点是跳到了 WebsocketDataChangedListener 类中去了 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121233759520.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + 这一点没弄明白,等这个类里面的 onPluginChanged 方法执行完了之后,回到了 ZookeeperDataChangedListener 类里来了 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234036652.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + 如果不是删除的话,就会更新 zkNode 节点数据 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234326495.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + 更新 zk 节点的方法 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121234628900.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + 而且之后的 onSelectorChanged、onMetaDataChanged 、onRuleChanged 方法都会先走 WebsocketDataChangedListener 类里面相对应的方法,然后才会进 ZookeeperDataChangedListener 类的方法。如果插件数据有更改,也是通过上面的步骤重新来一遍。 + 这里面同步数据会进两个 Listener 类的问题到这还没解决,突然想到在 pom 文件里面有对 websocket 的依赖,因为 application.yml 文件中已经把 websocket 这个配置注释掉了(不是把 enable=false),先把这个依赖注释掉再看,然后看着代码编译都不通过了。还有一个办法是把 websocket 改成不启用,改完重启发现不会再跳到 websocket 相关的类中了 + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122000547192.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +4. **与 soul-bootstrap 数据同步(http)源码分析** + +老规矩,改 yml 文件中的配置,然后找到对应的 listener 类打断点调试,这里如果是用 http 的话 websocket 相关的类还是会被访问到,所以这里一样的不能直接注释 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122005803207.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +下面来看看里面的代码: +这里一个构造方法,里面实例化了一个 clients 数组阻塞队列,大小为 1024。一个定时任务线程池,线程数为 1,名字前缀为 "long-polling" 的后台守护线程,看名字可以知道,这是用来长轮询的。一个相关的属性配置 +初始化前方法里面启动了定时线程,间隔 5 分钟之后,每 5 分钟执行一次,去执行 refreshLocalCache 这个刷新本地缓存的方法 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122004153175.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) + +``` + private void refreshLocalCache() { + this.updateAppAuthCache(); + this.updatePluginCache(); + this.updateRuleCache(); + this.updateSelectorCache(); + this.updateMetaDataCache(); + } +``` + +如果是手动点同步数据时,会去执行下面这些相关的方法,也是通过定时线程池来执行,只不过是立即执行 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20210122010007881.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGFuYm8xMjM=,size_16,color_FFFFFF,t_70) +五分钟后,执行相关的刷新方法,打印的日志 + +``` +2021-01-22 01:00:19.007 INFO 20800 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config start. +2021-01-22 01:00:19.010 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[APP_AUTH], old: {group='APP_AUTH', md5='d751713988987e9331980363e24189ce', lastModifyTime=1611248118794}, updated: {group='APP_AUTH', md5='d751713988987e9331980363e24189ce', lastModifyTime=1611248419010} +2021-01-22 01:00:19.012 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[PLUGIN], old: {group='PLUGIN', md5='70b269257d47f0f6404ae7b7e976d8f1', lastModifyTime=1611248295740}, updated: {group='PLUGIN', md5='70b269257d47f0f6404ae7b7e976d8f1', lastModifyTime=1611248419012} +2021-01-22 01:00:19.069 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[RULE], old: {group='RULE', md5='5811b56257e31109621976d39fc226aa', lastModifyTime=1611248301607}, updated: {group='RULE', md5='5811b56257e31109621976d39fc226aa', lastModifyTime=1611248419069} +2021-01-22 01:00:19.075 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[SELECTOR], old: {group='SELECTOR', md5='70bad5ebb1cf6e3fc55278eef2df42f3', lastModifyTime=1611248299419}, updated: {group='SELECTOR', md5='70bad5ebb1cf6e3fc55278eef2df42f3', lastModifyTime=1611248419075} +2021-01-22 01:00:19.077 INFO 20800 --- [-long-polling-2] o.d.s.a.l.AbstractDataChangedListener : update config cache[META_DATA], old: {group='META_DATA', md5='5f79d821e3b601330631a2d53294fb34', lastModifyTime=1611248302571}, updated: {group='META_DATA', md5='5f79d821e3b601330631a2d53294fb34', lastModifyTime=1611248419077} +2021-01-22 01:00:19.077 INFO 20800 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config success. +``` + +5. soul 中还有其它方法同步数据,这些后面有精力再分析,soul-admin 源码先分析到这,如果后续再分析的话,会另外再写一遍文章,这里就先到此为止 + +# 总结 + +soul-admin 中还有功能现在还没有使用到,还有很多好玩的东西,这篇会持续更新,到用到的时候再去具体分析里面的源码。 + +1. 2021-01-20 分析了 soul-admin 用 websocket 同步数据到 soul-bootstrap 中 +2. 2021-01-21 分析了 soul-admin 用 zookeeper 同步数据到 soul-bootstrap 中 +3. 2021-01-21 分析了 soul-admin 用 http 同步数据到 soul-bootstrap 中 diff --git a/src/zh/blog/soul_source_learning_01.md b/src/zh/blog/soul_source_learning_01.md index e42fc05e3d..2c4e74854b 100644 --- a/src/zh/blog/soul_source_learning_01.md +++ b/src/zh/blog/soul_source_learning_01.md @@ -1,222 +1,222 @@ ---- -title: Soul网关学习(1)环境配置 -author: 陈曦 -date: 2021-01-15 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -# Soul 源码分析(1) 环境配置 - -> soul is a High-Performance Java API Gateway -> -> GitHub:https://github.com/dromara/soul -> -> 官方文档:https://dromara.org/zh-cn/docs/soul/soul.html - -## 1. 源代码准备 - -### 1.1. fork [dromara/soul](https://github.com/dromara/soul.git)源代码至自己的仓库[cchenxi/soul](https://github.com/cchenxi/soul.git) - -### 1.2. clone 自己仓库中的 soul 源代码至本地 - -```shell -git clone https://github.com/cchenxi/soul.git -``` - -### 1.3.使用 idea 打开 soul 源代码 - -### 1.4.编译 soul 源代码 - -执行以下 maven 命令,等待编译完成 - -![-w1723](/assets/img/01/16106054898861.jpg) - -```shell -mvn clean package install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Drat.skip=true -Dcheckstyle.skip=true -``` - -## 2. 启动 `soul` - -### 2.1. 启动`soul-admin`模块 - -> `soul-admin`是 soul 网关的后台管理系统 - -选择使用 MySQL 数据库存储网关数据,修改数据源配置为自己的数据库配置。 - -![-w1186](/assets/img/01/16106065488032.jpg) - -运行启动类 `org.dromara.soul.admin.SoulAdminBootstrap`。 - -启动成功后,访问地址 http://localhost:9095/ ,跳转到登录页 ↓ - -![-w593](/assets/img/01/16106069731233.jpg) - -使用用户名`admin`,密码 `123456` 登录。 - -![-w1262](/assets/img/01/16106073045599.jpg) - -### 2.2. 启动`soul-bootstrap`模块 - -> `soul-bootstrap`是网关系统的核心 - -检查`soul-bootstrap`的配置 - -![-w917](/assets/img/01/16106076385761.jpg) - -这里需要配置成 `soul-admin`的 ip 和端口 - -控制台输出如下内容表示 `soul-bootstrap`启动成功 - -```plain text -2021-01-14 15:01:15.832 INFO 17943 --- [ main] b.s.s.d.w.WebsocketSyncDataConfiguration : you use websocket sync soul data....... -2021-01-14 15:01:15.924 INFO 17943 --- [ main] o.d.s.p.s.d.w.WebsocketSyncDataService : websocket connection is successful..... -2021-01-14 15:01:16.113 INFO 17943 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' -log4j:WARN No appenders could be found for logger (com.alibaba.dubbo.common.logger.LoggerFactory). -log4j:WARN Please initialize the log4j system properly. -log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. -2021-01-14 15:01:17.150 INFO 17943 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195 -2021-01-14 15:01:17.154 INFO 17943 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 5.508 seconds (JVM running for 6.762) -``` - -## 3. 测试 http 请求转发 - -> 为了方便测试,把`soul-examples`模块添加到 soul 的 pom 里 - -### 3.1. 启动一个服务 - -启动`soul-examples-http`项目 - -`soul-examples-http`的 pom 中引入了依赖 - -```xml - - org.dromara - soul-spring-boot-starter-client-springmvc - ${soul.version} - -``` - -在 `application.yml`中配置 - -```yaml -soul: - http: - adminUrl: http://localhost:9095 - port: 8188 - contextPath: /http - appName: http - full: false -``` - -如果 soul.http.full=false,则需要在具体的 http 接口上配置 `@SoulSpringMvcClient` 注解 - -#### 3.1.1. 测试 http 服务 - -执行 http 请求 `http://localhost:8188/test/findByUserId?userId=1` 结果如下图 - -![-w684](/assets/img/01/16106235724795.jpg) - -#### 3.1.2. 测试网关转发 - -执行 http 请求 `http://localhost:9195/http/test/findByUserId?userId=1` 结果如下图 - -![-w665](/assets/img/01/16106237733891.jpg) -在`soul-bootstrap`的控制台中输出如下信息 - -```shell -2021-01-14 20:42:57.123 INFO 29812 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:42:57.125 INFO 29812 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:42:57.126 INFO 29812 --- [work-threads-11] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -``` - -可以观察到网关可以将请求正常转发。 - -### 3.2. 启动两个服务模拟负载均衡 - -勾选 `Allow parallel run`,修改端口为`8189`,再次启动`soul-examples-http`项目 - -![-w1104](/assets/img/01/16106249542903.jpg) - -#### 3.2.1. 测试 http 服务 - -执行 http 请求 `http://localhost:8189/test/findByUserId?userId=1` 结果如下图 - -![-w693](/assets/img/01/16106250513285.jpg) - -#### 3.2.2. 测试负载均衡 - -![-w1096](/assets/img/01/16106266610601.jpg) - -将 8188 和 8189 两个端口对应的服务配置到选择器中 - -多次执行 http 请求 `http://localhost:9195/http/test/findByUserId?userId=1` 结果如下图 - -![-w595](/assets/img/01/16106267572581.jpg) -在`soul-bootstrap`的控制台中输出如下信息 - -```shell -2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8189/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:38.755 INFO 29812 --- [work-threads-23] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:38.756 INFO 29812 --- [work-threads-23] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:38.756 INFO 29812 --- [work-threads-23] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8189/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -2021-01-14 20:48:40.976 INFO 29812 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http -2021-01-14 20:48:40.976 INFO 29812 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** -2021-01-14 20:48:40.977 INFO 29812 --- [-work-threads-1] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 -``` - -可以观察到请求既有转发到 8188 端口的,也有转发到 8189 的,可以实现负载均衡 - -#### 3.2.3. 压测 - -简单对直连和使用网关两种方式的请求进行压测 - -```shell -➜ soul git:(master) ✗ wrk -t8 -c40 -d30s http://localhost:8189/test/findByUserId\?userId\=1 -Running 30s test @ http://localhost:8189/test/findByUserId?userId=1 - 8 threads and 40 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 6.06ms 28.81ms 442.25ms 98.22% - Req/Sec 2.05k 493.86 2.84k 74.82% - 486269 requests in 30.05s, 51.01MB read -Requests/sec: 16179.68 -Transfer/sec: 1.70MB -➜ soul git:(master) ✗ wrk -t8 -c40 -d30s http://localhost:9195/http/test/findByUserId\?userId\=1 -Running 30s test @ http://localhost:9195/http/test/findByUserId?userId=1 - 8 threads and 40 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 14.37ms 18.11ms 255.66ms 93.06% - Req/Sec 459.41 139.11 1.01k 74.23% - 109533 requests in 30.09s, 11.49MB read -Requests/sec: 3639.60 -Transfer/sec: 390.98KB -``` - -可以发现,使用网关后性能有些下降,主要是因为多了一层转发。 - -#### 3.2.4. 问题 - -在启动 8189 端口时,注册的客户端端口还是 8188 - -![-w1675](/assets/img/01/16106270140398.jpg) - -先手动配置选择器的配置,后来在群友的帮助下定位到是 `soul.http.port`没有改 - -修改后的配置如下 - -![-w520](/assets/img/01/16106405075031.jpg) +--- +title: Soul网关学习(1)环境配置 +author: 陈曦 +date: 2021-01-15 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +# Soul 源码分析(1) 环境配置 + +> soul is a High-Performance Java API Gateway +> +> GitHub:https://github.com/dromara/soul +> +> 官方文档:https://dromara.org/zh-cn/docs/soul/soul.html + +## 1. 源代码准备 + +### 1.1. fork [dromara/soul](https://github.com/dromara/soul.git)源代码至自己的仓库[cchenxi/soul](https://github.com/cchenxi/soul.git) + +### 1.2. clone 自己仓库中的 soul 源代码至本地 + +```shell +git clone https://github.com/cchenxi/soul.git +``` + +### 1.3.使用 idea 打开 soul 源代码 + +### 1.4.编译 soul 源代码 + +执行以下 maven 命令,等待编译完成 + +![-w1723](/assets/img/01/16106054898861.jpg) + +```shell +mvn clean package install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Drat.skip=true -Dcheckstyle.skip=true +``` + +## 2. 启动 `soul` + +### 2.1. 启动`soul-admin`模块 + +> `soul-admin`是 soul 网关的后台管理系统 + +选择使用 MySQL 数据库存储网关数据,修改数据源配置为自己的数据库配置。 + +![-w1186](/assets/img/01/16106065488032.jpg) + +运行启动类 `org.dromara.soul.admin.SoulAdminBootstrap`。 + +启动成功后,访问地址 http://localhost:9095/ ,跳转到登录页 ↓ + +![-w593](/assets/img/01/16106069731233.jpg) + +使用用户名`admin`,密码 `123456` 登录。 + +![-w1262](/assets/img/01/16106073045599.jpg) + +### 2.2. 启动`soul-bootstrap`模块 + +> `soul-bootstrap`是网关系统的核心 + +检查`soul-bootstrap`的配置 + +![-w917](/assets/img/01/16106076385761.jpg) + +这里需要配置成 `soul-admin`的 ip 和端口 + +控制台输出如下内容表示 `soul-bootstrap`启动成功 + +```plain text +2021-01-14 15:01:15.832 INFO 17943 --- [ main] b.s.s.d.w.WebsocketSyncDataConfiguration : you use websocket sync soul data....... +2021-01-14 15:01:15.924 INFO 17943 --- [ main] o.d.s.p.s.d.w.WebsocketSyncDataService : websocket connection is successful..... +2021-01-14 15:01:16.113 INFO 17943 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' +log4j:WARN No appenders could be found for logger (com.alibaba.dubbo.common.logger.LoggerFactory). +log4j:WARN Please initialize the log4j system properly. +log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. +2021-01-14 15:01:17.150 INFO 17943 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195 +2021-01-14 15:01:17.154 INFO 17943 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 5.508 seconds (JVM running for 6.762) +``` + +## 3. 测试 http 请求转发 + +> 为了方便测试,把`soul-examples`模块添加到 soul 的 pom 里 + +### 3.1. 启动一个服务 + +启动`soul-examples-http`项目 + +`soul-examples-http`的 pom 中引入了依赖 + +```xml + + org.dromara + soul-spring-boot-starter-client-springmvc + ${soul.version} + +``` + +在 `application.yml`中配置 + +```yaml +soul: + http: + adminUrl: http://localhost:9095 + port: 8188 + contextPath: /http + appName: http + full: false +``` + +如果 soul.http.full=false,则需要在具体的 http 接口上配置 `@SoulSpringMvcClient` 注解 + +#### 3.1.1. 测试 http 服务 + +执行 http 请求 `http://localhost:8188/test/findByUserId?userId=1` 结果如下图 + +![-w684](/assets/img/01/16106235724795.jpg) + +#### 3.1.2. 测试网关转发 + +执行 http 请求 `http://localhost:9195/http/test/findByUserId?userId=1` 结果如下图 + +![-w665](/assets/img/01/16106237733891.jpg) +在`soul-bootstrap`的控制台中输出如下信息 + +```shell +2021-01-14 20:42:57.123 INFO 29812 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:42:57.125 INFO 29812 --- [work-threads-11] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:42:57.126 INFO 29812 --- [work-threads-11] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +``` + +可以观察到网关可以将请求正常转发。 + +### 3.2. 启动两个服务模拟负载均衡 + +勾选 `Allow parallel run`,修改端口为`8189`,再次启动`soul-examples-http`项目 + +![-w1104](/assets/img/01/16106249542903.jpg) + +#### 3.2.1. 测试 http 服务 + +执行 http 请求 `http://localhost:8189/test/findByUserId?userId=1` 结果如下图 + +![-w693](/assets/img/01/16106250513285.jpg) + +#### 3.2.2. 测试负载均衡 + +![-w1096](/assets/img/01/16106266610601.jpg) + +将 8188 和 8189 两个端口对应的服务配置到选择器中 + +多次执行 http 请求 `http://localhost:9195/http/test/findByUserId?userId=1` 结果如下图 + +![-w595](/assets/img/01/16106267572581.jpg) +在`soul-bootstrap`的控制台中输出如下信息 + +```shell +2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:34.460 INFO 29812 --- [work-threads-21] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8189/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:35.147 INFO 29812 --- [work-threads-22] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:38.755 INFO 29812 --- [work-threads-23] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:38.756 INFO 29812 --- [work-threads-23] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:38.756 INFO 29812 --- [work-threads-23] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:39.609 INFO 29812 --- [work-threads-24] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8189/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:40.317 INFO 29812 --- [work-threads-25] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +2021-01-14 20:48:40.976 INFO 29812 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http +2021-01-14 20:48:40.976 INFO 29812 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http/test/** +2021-01-14 20:48:40.977 INFO 29812 --- [-work-threads-1] o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://172.27.121.155:8188/test/findByUserId?userId=1, retryTimes is 0 +``` + +可以观察到请求既有转发到 8188 端口的,也有转发到 8189 的,可以实现负载均衡 + +#### 3.2.3. 压测 + +简单对直连和使用网关两种方式的请求进行压测 + +```shell +➜ soul git:(master) ✗ wrk -t8 -c40 -d30s http://localhost:8189/test/findByUserId\?userId\=1 +Running 30s test @ http://localhost:8189/test/findByUserId?userId=1 + 8 threads and 40 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 6.06ms 28.81ms 442.25ms 98.22% + Req/Sec 2.05k 493.86 2.84k 74.82% + 486269 requests in 30.05s, 51.01MB read +Requests/sec: 16179.68 +Transfer/sec: 1.70MB +➜ soul git:(master) ✗ wrk -t8 -c40 -d30s http://localhost:9195/http/test/findByUserId\?userId\=1 +Running 30s test @ http://localhost:9195/http/test/findByUserId?userId=1 + 8 threads and 40 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 14.37ms 18.11ms 255.66ms 93.06% + Req/Sec 459.41 139.11 1.01k 74.23% + 109533 requests in 30.09s, 11.49MB read +Requests/sec: 3639.60 +Transfer/sec: 390.98KB +``` + +可以发现,使用网关后性能有些下降,主要是因为多了一层转发。 + +#### 3.2.4. 问题 + +在启动 8189 端口时,注册的客户端端口还是 8188 + +![-w1675](/assets/img/01/16106270140398.jpg) + +先手动配置选择器的配置,后来在群友的帮助下定位到是 `soul.http.port`没有改 + +修改后的配置如下 + +![-w520](/assets/img/01/16106405075031.jpg) diff --git a/src/zh/blog/soul_source_learning_02_divide_plugin.md b/src/zh/blog/soul_source_learning_02_divide_plugin.md index 01795dc24d..8efc34c0b1 100644 --- a/src/zh/blog/soul_source_learning_02_divide_plugin.md +++ b/src/zh/blog/soul_source_learning_02_divide_plugin.md @@ -1,134 +1,134 @@ ---- -title: Soul网关学习(2-1)Http代理之divide插件使用 -author: 袁杰 -date: 2021-01-16 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -# Divide 插件使用 - -## 一、启动项目 - -先启动 soul-bootstrap(9195)、soul-admin(9095)两个模块,我们通过 bootstrap 配置文件可以看到,两者是通过 WebSocket 协议进行数据同步: - -![图片](https://uploader.shimo.im/f/nGr4Gtt1RDaxFZhp.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -通过 bootstrap 日志也可以看到: - -![图片](https://uploader.shimo.im/f/cvJNUI1WLaJEk0Pe.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -所谓的数据同步是指将 soul-admin 中配置的数据,同步到 soul 集群中的 JVM 内存里面,是网关高性能的关键。 - -我们启动两个项目之后就可以通过后台管理系统测试 divide 插件了。 - -## 二、divide 插件介绍 - -divide 插件是网关处理 http 协议请求的核心处理插件,也是 soul 唯一默认开启的插件: - -![图片](https://uploader.shimo.im/f/0CIBpm0YatPSWUUu.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -我们可以想象一下网关到底是做什么的,去猜测一下处理 http 请求的 divide 插件可能具备哪些功能呢? - -首先,作为微服务网关,它的背后一定存在多条业务线的分布式微服务集群,而网关作为所有服务的统一入口,必须具备的能力就是流量分发/路由/负载均衡等,而 divide 这个单词顾名思义就是分配、分发的意思,所以我们可以猜测 divide 插件就是对 http 请求进行各种规则的路由转发,这也是网关最基础的能力。 - -我们打开管理界面上的插件列表,可以看到所有插件都是由两部分组成:**选择器**(selector)和**选择器规则**。 - -插件化设计思想是 soul 网关最核心的设计思想,而选择器和规则这两个概念也是 soul 网关的灵魂所在,理论上来说,我们掌握好它,就能对任何接入网关的流量进行管理。 - -一个插件有多个选择器,一个选择器对应多种规则。选择器相当于是对流量的第一次筛选,规则就是最终的筛选。 - -### 选择器 - -![图片](https://uploader.shimo.im/f/KlNWtqo6shqyJYWZ.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - - * **名称**:为你的选择器起一个容易分辨的名字 - * **类型**:custom flow 是自定义流量。full flow 是全流量。自定义流量就是请求会走你下面的匹配方式与条件。全流量则不走。 - * **匹配方式**:and 或者or 是指下面多个条件是按照and 还是or的方式来组合。 - * **条件**: - * uri:是指你根据uri的方式来筛选流量,match的方式支持模糊匹配(/**) - * header:是指根据请求头里面的字段来筛选流量。 - * query:是指根据uri的查询条件来进行筛选流量。 - * ip:是指根据你请求的真实ip,来筛选流量。 - * host:是指根据你请求的真实host,来筛选流量。 - * post:建议不要使用。 - * 条件匹配: - * match : 模糊匹配,建议和uri条件搭配,支持 restful风格的匹配。(/test/**) - * = : 前后值相等,才能匹配。 - * regEx : 正则匹配,表示前面一个值去匹配后面的正则表达式。 - * like :字符串模糊匹配。 - * **是否开启**:打开才会生效 - * **打印日志**:打开的时候,当匹配上的时候,会打印匹配日志。 - * **执行顺序**:当多个选择器的时候,执行顺序小的优先执行。 - -### 选择器规则 - -![图片](https://uploader.shimo.im/f/If4ekdjZ1T0j11fy.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -![图片](https://uploader.shimo.im/f/CTJJ5j55VhfIxVsS.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -可以看到,规则的配置和选择器类似,可以理解为更细粒度的自定义配置。 - -## 三、divide 插件使用 - -废话少说,我们直接运行 soul 提供的 examples 模块来演示 divide 插件。 - -![图片](https://uploader.shimo.im/f/8i3YFAMvzXsKJg7o.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -注意,我们最终运行的是 soul-examples-http 模块。配置文件可以使用默认的,也可以自定义 contextPath 和 appName,如上图。 - -我们需要注意,contextPath 这个属性非常重要,相当于是我们所有 http 请求的 namespace,和选择器一一对齐。一般来说,我们可以配置一个业务对应一个 contextPath,一个业务下面配置相同 contextPath 的多个服务实例会自动映射到同一个选择器进行负载均衡。 - -我们启动端口为 8188 的这个进程后,可以发现管理控制台 divide 插件列表中自动配置了这个实例对应的选择器、规则: - -![图片](https://uploader.shimo.im/f/ozvPWCqVaXEGwG2E.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -可以看到我启动的这个 8188 项目地址自动注册上去了: - -![图片](https://uploader.shimo.im/f/MzTmhBkyZSRIiPAp.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -### 测试网关路由 - -通过 postman 先测试不经过网关转发: - -```plain -http://localhost:8188/order/findById?id=1 -``` - -![图片](https://uploader.shimo.im/f/OJi1lpFiwlHN53EE.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -然后再测试通过网关转发到这个接口: - -```plain -http://localhost:9195/my-http/order/findById?id=1 -``` - -![图片](https://uploader.shimo.im/f/8p4u4OKuWp3inEVh.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -看日志发现确实经过了网关转发到了 8188 接口地址: - -![图片](https://uploader.shimo.im/f/iE6V4aNqbaaUQz2K.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -### 测试负载均衡 - -我们修改端口为 8189,启动第二个进程。 - -![图片](https://uploader.shimo.im/f/arghWSgrccJ5262m.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -注意 IDEA 需要取消 Single instance only 的限制: - -![图片](https://uploader.shimo.im/f/cMdvwK0RI7AxmLf6.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -我们再进入管理控制台,发现 my-http 选择器下出现两个配置地址: - -![图片](https://uploader.shimo.im/f/nC35SJOlCZnNIrAz.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -此时我们继续测试,发现负载均衡策略确实生效了: - -![图片](https://uploader.shimo.im/f/int2660TqS1nAkYB.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) - -今天只是演示了 divide 插件最基础的配置,还有其他各种规则配置后面都可以试一试~ +--- +title: Soul网关学习(2-1)Http代理之divide插件使用 +author: 袁杰 +date: 2021-01-16 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +# Divide 插件使用 + +## 一、启动项目 + +先启动 soul-bootstrap(9195)、soul-admin(9095)两个模块,我们通过 bootstrap 配置文件可以看到,两者是通过 WebSocket 协议进行数据同步: + +![图片](https://uploader.shimo.im/f/nGr4Gtt1RDaxFZhp.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +通过 bootstrap 日志也可以看到: + +![图片](https://uploader.shimo.im/f/cvJNUI1WLaJEk0Pe.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +所谓的数据同步是指将 soul-admin 中配置的数据,同步到 soul 集群中的 JVM 内存里面,是网关高性能的关键。 + +我们启动两个项目之后就可以通过后台管理系统测试 divide 插件了。 + +## 二、divide 插件介绍 + +divide 插件是网关处理 http 协议请求的核心处理插件,也是 soul 唯一默认开启的插件: + +![图片](https://uploader.shimo.im/f/0CIBpm0YatPSWUUu.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +我们可以想象一下网关到底是做什么的,去猜测一下处理 http 请求的 divide 插件可能具备哪些功能呢? + +首先,作为微服务网关,它的背后一定存在多条业务线的分布式微服务集群,而网关作为所有服务的统一入口,必须具备的能力就是流量分发/路由/负载均衡等,而 divide 这个单词顾名思义就是分配、分发的意思,所以我们可以猜测 divide 插件就是对 http 请求进行各种规则的路由转发,这也是网关最基础的能力。 + +我们打开管理界面上的插件列表,可以看到所有插件都是由两部分组成:**选择器**(selector)和**选择器规则**。 + +插件化设计思想是 soul 网关最核心的设计思想,而选择器和规则这两个概念也是 soul 网关的灵魂所在,理论上来说,我们掌握好它,就能对任何接入网关的流量进行管理。 + +一个插件有多个选择器,一个选择器对应多种规则。选择器相当于是对流量的第一次筛选,规则就是最终的筛选。 + +### 选择器 + +![图片](https://uploader.shimo.im/f/KlNWtqo6shqyJYWZ.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + + * **名称**:为你的选择器起一个容易分辨的名字 + * **类型**:custom flow 是自定义流量。full flow 是全流量。自定义流量就是请求会走你下面的匹配方式与条件。全流量则不走。 + * **匹配方式**:and 或者or 是指下面多个条件是按照and 还是or的方式来组合。 + * **条件**: + * uri:是指你根据uri的方式来筛选流量,match的方式支持模糊匹配(/**) + * header:是指根据请求头里面的字段来筛选流量。 + * query:是指根据uri的查询条件来进行筛选流量。 + * ip:是指根据你请求的真实ip,来筛选流量。 + * host:是指根据你请求的真实host,来筛选流量。 + * post:建议不要使用。 + * 条件匹配: + * match : 模糊匹配,建议和uri条件搭配,支持 restful风格的匹配。(/test/**) + * = : 前后值相等,才能匹配。 + * regEx : 正则匹配,表示前面一个值去匹配后面的正则表达式。 + * like :字符串模糊匹配。 + * **是否开启**:打开才会生效 + * **打印日志**:打开的时候,当匹配上的时候,会打印匹配日志。 + * **执行顺序**:当多个选择器的时候,执行顺序小的优先执行。 + +### 选择器规则 + +![图片](https://uploader.shimo.im/f/If4ekdjZ1T0j11fy.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +![图片](https://uploader.shimo.im/f/CTJJ5j55VhfIxVsS.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +可以看到,规则的配置和选择器类似,可以理解为更细粒度的自定义配置。 + +## 三、divide 插件使用 + +废话少说,我们直接运行 soul 提供的 examples 模块来演示 divide 插件。 + +![图片](https://uploader.shimo.im/f/8i3YFAMvzXsKJg7o.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +注意,我们最终运行的是 soul-examples-http 模块。配置文件可以使用默认的,也可以自定义 contextPath 和 appName,如上图。 + +我们需要注意,contextPath 这个属性非常重要,相当于是我们所有 http 请求的 namespace,和选择器一一对齐。一般来说,我们可以配置一个业务对应一个 contextPath,一个业务下面配置相同 contextPath 的多个服务实例会自动映射到同一个选择器进行负载均衡。 + +我们启动端口为 8188 的这个进程后,可以发现管理控制台 divide 插件列表中自动配置了这个实例对应的选择器、规则: + +![图片](https://uploader.shimo.im/f/ozvPWCqVaXEGwG2E.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +可以看到我启动的这个 8188 项目地址自动注册上去了: + +![图片](https://uploader.shimo.im/f/MzTmhBkyZSRIiPAp.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +### 测试网关路由 + +通过 postman 先测试不经过网关转发: + +```plain +http://localhost:8188/order/findById?id=1 +``` + +![图片](https://uploader.shimo.im/f/OJi1lpFiwlHN53EE.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +然后再测试通过网关转发到这个接口: + +```plain +http://localhost:9195/my-http/order/findById?id=1 +``` + +![图片](https://uploader.shimo.im/f/8p4u4OKuWp3inEVh.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +看日志发现确实经过了网关转发到了 8188 接口地址: + +![图片](https://uploader.shimo.im/f/iE6V4aNqbaaUQz2K.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +### 测试负载均衡 + +我们修改端口为 8189,启动第二个进程。 + +![图片](https://uploader.shimo.im/f/arghWSgrccJ5262m.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +注意 IDEA 需要取消 Single instance only 的限制: + +![图片](https://uploader.shimo.im/f/cMdvwK0RI7AxmLf6.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +我们再进入管理控制台,发现 my-http 选择器下出现两个配置地址: + +![图片](https://uploader.shimo.im/f/nC35SJOlCZnNIrAz.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +此时我们继续测试,发现负载均衡策略确实生效了: + +![图片](https://uploader.shimo.im/f/int2660TqS1nAkYB.png!thumbnail?fileGuid=fGQAODvCNjs7kNIH) + +今天只是演示了 divide 插件最基础的配置,还有其他各种规则配置后面都可以试一试~ diff --git a/src/zh/blog/soul_source_learning_02_divide_plugin_source.md b/src/zh/blog/soul_source_learning_02_divide_plugin_source.md index 9ffc338e04..e066d7965f 100644 --- a/src/zh/blog/soul_source_learning_02_divide_plugin_source.md +++ b/src/zh/blog/soul_source_learning_02_divide_plugin_source.md @@ -1,15 +1,15 @@ ---- -title: Soul网关学习(2-2)Http代理之divide插件源码解析 -author: 季鹏 -date: 2021-01-17 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -# Divide 插件如何转发 http 请求 - -待补,文章内部有报错 +--- +title: Soul网关学习(2-2)Http代理之divide插件源码解析 +author: 季鹏 +date: 2021-01-17 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +# Divide 插件如何转发 http 请求 + +待补,文章内部有报错 diff --git a/src/zh/blog/soul_source_learning_02_http_client_register.md b/src/zh/blog/soul_source_learning_02_http_client_register.md index 9d8d08d03c..dc1e053f9f 100644 --- a/src/zh/blog/soul_source_learning_02_http_client_register.md +++ b/src/zh/blog/soul_source_learning_02_http_client_register.md @@ -1,427 +1,427 @@ ---- -title: Soul网关学习(2-3)Http客户端接入源码解析 -author: 范金鹏 -date: 2021-01-18 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -# HTTP 用户接入 Soul 网关注册逻辑分析 - -## 1. 注册入口 - -HTTP 用户接入 Soul 网关时,会调用 soul-admin 一个接口,把需要 Soul 网关管理的接口注册,今天就具体看看到底干了点儿啥。 - -先看下调用的接口信息如下: - -```java -// SpringMvcClientBeanPostProcessor.java -/** - * Instantiates a new Soul client bean post processor. - * - * @param soulSpringMvcConfig the soul spring mvc config - */ -public SpringMvcClientBeanPostProcessor(final SoulSpringMvcConfig soulSpringMvcConfig) { - ValidateUtils.validate(soulSpringMvcConfig); - this.soulSpringMvcConfig = soulSpringMvcConfig; - url = soulSpringMvcConfig.getAdminUrl() + "/soul-client/springmvc-register"; - executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); -} -``` - -## 2. springmvc-register 接口逻辑 - -全局搜索 "springmvc-register",找到 soul-admin 模块下的 SoulClientController,看到这里,对于经常写 CRUD 的我们是不是很熟悉?哈哈~ - -```java -// SoulClientController.java -/** - * Register spring mvc string. - * - * @param springMvcRegisterDTO the spring mvc register dto - * @return the string - */ -@PostMapping("/springmvc-register") -public String registerSpringMvc(@RequestBody final SpringMvcRegisterDTO springMvcRegisterDTO) { - return soulClientRegisterService.registerSpringMvc(springMvcRegisterDTO); -} -``` - -Service 层实现类: - -```java -// SoulClientRegisterServiceImpl.java -@Override -@Transactional -public String registerSpringMvc(final SpringMvcRegisterDTO dto) { - if (dto.isRegisterMetaData()) { - MetaDataDO exist = metaDataMapper.findByPath(dto.getPath()); - if (Objects.isNull(exist)) { - saveSpringMvcMetaData(dto); - } - } - String selectorId = handlerSpringMvcSelector(dto); - handlerSpringMvcRule(selectorId, dto); - return SoulResultMessage.SUCCESS; -} -``` - -dto.isRegisterMetaData() 这个是否注册元数据信息的判断,不知道什么时候用,存疑 //TODO,先往下走。 - -### 2.1 先看看这个方法 handlerSpringMvcSelector,处理 Selector。 - -```java -// SoulClientRegisterServiceImpl.java -private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) { - String contextPath = dto.getContext(); - // 根据 contextPath 到数据库里查询,是否已经注册过。 - SelectorDO selectorDO = selectorService.findByName(contextPath); - String selectorId; - String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort())); - if (Objects.isNull(selectorDO)) { - // 还没有注册过 - selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri); - } else { - // 已经注册过,业务系统重启了会到这里 - selectorId = selectorDO.getId(); - //update upstream - String handle = selectorDO.getHandle(); - String handleAdd; - DivideUpstream addDivideUpstream = buildDivideUpstream(uri); - SelectorData selectorData = selectorService.buildByName(contextPath); - if (StringUtils.isBlank(handle)) { - handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream)); - } else { - List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class); - for (DivideUpstream upstream : exist) { - if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) { - return selectorId; - } - } - exist.add(addDivideUpstream); - handleAdd = GsonUtils.getInstance().toJson(exist); - } - selectorDO.setHandle(handleAdd); - selectorData.setHandle(handleAdd); - // update db - selectorMapper.updateSelective(selectorDO); - // submit upstreamCheck - upstreamCheckService.submit(contextPath, addDivideUpstream); - // publish change event. - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, - Collections.singletonList(selectorData))); - } - return selectorId; -} -``` - -#### 2.1.1 第一次接入 Soul 网关 - -新接入的,到数据库里肯定查不到 selectorDO,进入 registerSelector 方法,仔细看看到底往哪些数据库表中插数据了。 - -```java -// SoulClientRegisterServiceImpl.java -private String registerSelector(final String contextPath, final String rpcType, final String appName, final String uri) { - SelectorDTO selectorDTO = SelectorDTO.builder() - .name(contextPath) - .type(SelectorTypeEnum.CUSTOM_FLOW.getCode()) - .matchMode(MatchModeEnum.AND.getCode()) - .enabled(Boolean.TRUE) - .loged(Boolean.TRUE) - .continued(Boolean.TRUE) - .sort(1) - .build(); - if (RpcTypeEnum.DUBBO.getName().equals(rpcType)) { - selectorDTO.setPluginId(getPluginId(PluginEnum.DUBBO.getName())); - } else if (RpcTypeEnum.SPRING_CLOUD.getName().equals(rpcType)) { - selectorDTO.setPluginId(getPluginId(PluginEnum.SPRING_CLOUD.getName())); - selectorDTO.setHandle(GsonUtils.getInstance().toJson(buildSpringCloudSelectorHandle(appName))); - } else if (RpcTypeEnum.SOFA.getName().equals(rpcType)) { - selectorDTO.setPluginId(getPluginId(PluginEnum.SOFA.getName())); - selectorDTO.setHandle(appName); - } else if (RpcTypeEnum.TARS.getName().equals(rpcType)) { - selectorDTO.setPluginId(getPluginId(PluginEnum.TARS.getName())); - selectorDTO.setHandle(appName); - } else { - //is divide - DivideUpstream divideUpstream = buildDivideUpstream(uri); - String handler = GsonUtils.getInstance().toJson(Collections.singletonList(divideUpstream)); - selectorDTO.setHandle(handler); - selectorDTO.setPluginId(getPluginId(PluginEnum.DIVIDE.getName())); - upstreamCheckService.submit(selectorDTO.getName(), divideUpstream); - } - SelectorConditionDTO selectorConditionDTO = new SelectorConditionDTO(); - selectorConditionDTO.setParamType(ParamTypeEnum.URI.getName()); - selectorConditionDTO.setParamName("/"); - selectorConditionDTO.setOperator(OperatorEnum.MATCH.getAlias()); - selectorConditionDTO.setParamValue(contextPath + "/**"); - selectorDTO.setSelectorConditions(Collections.singletonList(selectorConditionDTO)); - return selectorService.register(selectorDTO); -} -``` - -看到这么多 if else,是不是很兴奋,小伙伴们可以想想怎么优化掉这么多 if else,PR 搞起来 ^ - ^。 - -写了这么多,无非是封装 SelectorDTO 对象,最后调用 selectorService.register(selectorDTO) 入库,继续跟进去。 - -```java -// SelectorServiceImpl.java -@Override -public String register(final SelectorDTO selectorDTO) { - SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO); - List selectorConditionDTOs = selectorDTO.getSelectorConditions(); - if (StringUtils.isEmpty(selectorDTO.getId())) { - selectorMapper.insertSelective(selectorDO); - selectorConditionDTOs.forEach(selectorConditionDTO -> { - selectorConditionDTO.setSelectorId(selectorDO.getId()); - // 这里在 for 循环里调用 dao 层插入数据,是不是可以考虑挪出去一次性批量插入? - selectorConditionMapper.insertSelective(SelectorConditionDO - .buildSelectorConditionDO(selectorConditionDTO)); - }); - } - publishEvent(selectorDO, selectorConditionDTOs); - return selectorDO.getId(); -} -``` - -看到这里有 2 条入库方法,分别向 selector 和 selector_condition 表中插入了数据。这里我们先不具体追究表结构及业务意义,后面补上。 - -publishEvent 方法,涉及到 ApplicationEventPublisher 接口,是观察者模式的一个实现,发布事件后通过监听器完成后续操作,这里先按下不表,后续单写一篇文章分析。 - -#### 2.1.2 已经接入 Soul 网关 - -就跟盗梦空间似的,我们回退 2 层梦境,回到插入数据的另一个分支,可以想见,就是,已经接入过 Soul 网关的系统重启,或新节点启动走的逻辑。 - -把前面的代码再贴过来: - -```java -// SoulClientRegisterServiceImpl.java -private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) { - String contextPath = dto.getContext(); - // 根据 contextPath 到数据库里查询,是否已经注册过。 - SelectorDO selectorDO = selectorService.findByName(contextPath); - String selectorId; - String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort())); - if (Objects.isNull(selectorDO)) { - // 还没有注册过 - selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri); - } else { - // 已接入的业务系统重启,或新节点启动,会到这里 - selectorId = selectorDO.getId(); - //update upstream - // handle 字段存储这个接口真实节点信息,可能存在多台机器需要负载均衡的场景 - String handle = selectorDO.getHandle(); - String handleAdd; - DivideUpstream addDivideUpstream = buildDivideUpstream(uri); - SelectorData selectorData = selectorService.buildByName(contextPath); - if (StringUtils.isBlank(handle)) { - // 这个接口虽然之前注册过,但第1个服务器节点接入 Soul 时会进来 - handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream)); - } else { - // 如果已经至少有1个服务器节点已接入,会进到这里,判断是否是同一个节点(使用 upstreamUrl 区分),如果相同直接返回 - List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class); - for (DivideUpstream upstream : exist) { - if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) { - return selectorId; - } - } - // 如果不是同一个节点,把新节点加入到 handle 字段中 - exist.add(addDivideUpstream); - handleAdd = GsonUtils.getInstance().toJson(exist); - } - selectorDO.setHandle(handleAdd); - selectorData.setHandle(handleAdd); - // update db 更新数据库 - selectorMapper.updateSelective(selectorDO); - // submit upstreamCheck - upstreamCheckService.submit(contextPath, addDivideUpstream); - // publish change event. - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, - Collections.singletonList(selectorData))); - } - return selectorId; -} -``` - -因为还没有研究数据库表结构设计,根据已知部分猜测,1 个 selector 对应一个 divide 插件,这个插件以 contextPath 为标识(在这里就是 "/http"),一个 contextPath 可以部署多个服务器节点,这些节点信息已 json 形式保存在 handle 字段中。 - -```json -// handle/handleAdd 数据格式 -[ - { - "upstreamHost": "localhost", - "protocol": "http://", - "upstreamUrl": "10.0.0.12:8188", - "weight": 50, - "status": true, - "timestamp": 0, - "warmup": 0 - } -] -``` - -下面紧接着就是更新数据库 updateSelective。 - -upstreamCheckService.submit(contextPath, addDivideUpstream); 把真实服务器节点信息缓存在一个 Map(UPSTREAM_MAP) 里,有定时任务定期探活,如果发现服务节点宕机了,就把他剔除出去,防止把请求发送到已经宕机的节点上。 - -然后就是 eventPublisher.publishEvent(),跟前面的 publishEvent 方法一样,发布事件后通过监听器完成后续操作(简单介绍下,这里是通过与 Soul 网关建立的 websocket 长连接发送数据 SelectorData 修改的消息,Soul 网关根据消息修改数据,这个具体改的什么数据,怎么修改的,后面分析)。 - -到这里终于把 handlerSpringMvcSelector 这个方法分析完了。 - -### 2.2 再来看看这个方法 handlerSpringMvcRule,处理 Rule。 - -```java -// SoulClientRegisterServiceImpl.java -private void handlerSpringMvcRule(final String selectorId, final SpringMvcRegisterDTO dto) { - RuleDO ruleDO = ruleMapper.findByName(dto.getRuleName()); - if (Objects.isNull(ruleDO)) { - registerRule(selectorId, dto.getPath(), dto.getRpcType(), dto.getRuleName()); - } -} -``` - -首先拿着规则名字,到 rule 表里捞数据,如果捞到了表名已经注册过了,无操作。 - -看下数据库数据,就是业务系统下的接口地址。 - -```bash -mysql> use soul; -Database changed - -mysql> select * from rule where name = '/http/order/findById' \G -*************************** 1. row *************************** - id: 1349650371868782592 - selector_id: 1349650371302551552 - match_mode: 0 - name: /http/order/findById - enabled: 1 - loged: 1 - sort: 1 - handle: {"loadBalance":"random","retry":0,"timeout":3000} -date_created: 2021-01-14 17:31:39 -date_updated: 2021-01-14 17:31:39 -1 row in set (0.00 sec) -``` - -如果没捞到数据,注册这个规则。 - -```java -// SoulClientRegisterServiceImpl.java -private void registerRule(final String selectorId, final String path, final String rpcType, final String ruleName) { - RuleHandle ruleHandle = RuleHandleFactory.ruleHandle(RpcTypeEnum.acquireByName(rpcType), path); - RuleDTO ruleDTO = RuleDTO.builder() - .selectorId(selectorId) - .name(ruleName) - .matchMode(MatchModeEnum.AND.getCode()) - .enabled(Boolean.TRUE) - .loged(Boolean.TRUE) - .sort(1) - .handle(ruleHandle.toJson()) - .build(); - RuleConditionDTO ruleConditionDTO = RuleConditionDTO.builder() - .paramType(ParamTypeEnum.URI.getName()) - .paramName("/") - .paramValue(path) - .build(); - if (path.indexOf("*") > 1) { - ruleConditionDTO.setOperator(OperatorEnum.MATCH.getAlias()); - } else { - ruleConditionDTO.setOperator(OperatorEnum.EQ.getAlias()); - } - ruleDTO.setRuleConditions(Collections.singletonList(ruleConditionDTO)); - ruleService.register(ruleDTO); -} -``` - -第 1 行,根据 rpcType("http") 获取其对应的 RuleHandle,这里,默认内置 3 种类型,我们这里的是 HTTP,对应 DivideRuleHandle。 - -```java -// RuleHandleFactory.java -public final class RuleHandleFactory { - - /** - * The RpcType to RuleHandle class map. - */ - private static final Map> RPC_TYPE_TO_RULE_HANDLE_CLASS = new ConcurrentHashMap<>(); - - /** - * The default RuleHandle. - */ - private static final Class DEFAULT_RULE_HANDLE = SpringCloudRuleHandle.class; - - static { - RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.HTTP, DivideRuleHandle.class); - RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.DUBBO, DubboRuleHandle.class); - RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.SOFA, SofaRuleHandle.class); - } - - /** - * Get a RuleHandle object with given rpc type and path. - * @param rpcType rpc type. - * @param path path. - * @return RuleHandle object. - */ - public static RuleHandle ruleHandle(final RpcTypeEnum rpcType, final String path) { - if (Objects.isNull(rpcType)) { - return null; - } - Class clazz = RPC_TYPE_TO_RULE_HANDLE_CLASS.getOrDefault(rpcType, DEFAULT_RULE_HANDLE); - try { - return clazz.newInstance().createDefault(path); - } catch (InstantiationException | IllegalAccessException e) { - throw new SoulException( - String.format("Init RuleHandle failed with rpc type: %s, rule class: %s, exception: %s", - rpcType, - clazz.getSimpleName(), - e.getMessage())); - } - } -} -``` - -下面构造 RuleDTO 对象,注册规则。 - -```java -// RuleServiceImpl.java -@Override -public String register(final RuleDTO ruleDTO) { - RuleDO ruleDO = RuleDO.buildRuleDO(ruleDTO); - List ruleConditions = ruleDTO.getRuleConditions(); - if (StringUtils.isEmpty(ruleDTO.getId())) { - ruleMapper.insertSelective(ruleDO); - ruleConditions.forEach(ruleConditionDTO -> { - ruleConditionDTO.setRuleId(ruleDO.getId()); - // 这里在 for 循环里调用 dao 层插入数据,是不是可以考虑挪出去一次性批量插入? - ruleConditionMapper.insertSelective(RuleConditionDO - .buildRuleConditionDO(ruleConditionDTO)); - }); - } - publishEvent(ruleDO, ruleConditions); - return ruleDO.getId(); -} -``` - -分别向 rule 和 rule_condition 表中插入数据。 - -publishEvent() 方法,通过 websocket 长连接,向 Soul 网关发送 RuleData 数据。 - -## 3.总结 - -到这里,调用 "/soul-client/springmvc-register" 接口逻辑分析完了,我们总结下: - -- 处理 selector - - 新增或修改 selector、selector_condition 表数据,持久化到 MySQL。 - - 通过 websocket 向 Soul 网关发送数据改动信息。 -- 处理 rule - - 新增或修改 rule、rule_condition 表数据,持久化到 MySQL。 - - 通过 websocket 向 Soul 网关发送数据改动信息。 - -其中表结构及字段含义还需进一步学习和研究,websocket 发送给 Soul 网关后,网关做了什么处理也需要后续分析。 - -到这里,HTTP 用户接入 Soul 网关注册逻辑就分析完了。 - -如果在工作中你有使用网关的需求,或是个人有学习网关的追求,欢迎来跟我一起分析和学习,Soul 网关,你值得拥有。 +--- +title: Soul网关学习(2-3)Http客户端接入源码解析 +author: 范金鹏 +date: 2021-01-18 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +# HTTP 用户接入 Soul 网关注册逻辑分析 + +## 1. 注册入口 + +HTTP 用户接入 Soul 网关时,会调用 soul-admin 一个接口,把需要 Soul 网关管理的接口注册,今天就具体看看到底干了点儿啥。 + +先看下调用的接口信息如下: + +```java +// SpringMvcClientBeanPostProcessor.java +/** + * Instantiates a new Soul client bean post processor. + * + * @param soulSpringMvcConfig the soul spring mvc config + */ +public SpringMvcClientBeanPostProcessor(final SoulSpringMvcConfig soulSpringMvcConfig) { + ValidateUtils.validate(soulSpringMvcConfig); + this.soulSpringMvcConfig = soulSpringMvcConfig; + url = soulSpringMvcConfig.getAdminUrl() + "/soul-client/springmvc-register"; + executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); +} +``` + +## 2. springmvc-register 接口逻辑 + +全局搜索 "springmvc-register",找到 soul-admin 模块下的 SoulClientController,看到这里,对于经常写 CRUD 的我们是不是很熟悉?哈哈~ + +```java +// SoulClientController.java +/** + * Register spring mvc string. + * + * @param springMvcRegisterDTO the spring mvc register dto + * @return the string + */ +@PostMapping("/springmvc-register") +public String registerSpringMvc(@RequestBody final SpringMvcRegisterDTO springMvcRegisterDTO) { + return soulClientRegisterService.registerSpringMvc(springMvcRegisterDTO); +} +``` + +Service 层实现类: + +```java +// SoulClientRegisterServiceImpl.java +@Override +@Transactional +public String registerSpringMvc(final SpringMvcRegisterDTO dto) { + if (dto.isRegisterMetaData()) { + MetaDataDO exist = metaDataMapper.findByPath(dto.getPath()); + if (Objects.isNull(exist)) { + saveSpringMvcMetaData(dto); + } + } + String selectorId = handlerSpringMvcSelector(dto); + handlerSpringMvcRule(selectorId, dto); + return SoulResultMessage.SUCCESS; +} +``` + +dto.isRegisterMetaData() 这个是否注册元数据信息的判断,不知道什么时候用,存疑 //TODO,先往下走。 + +### 2.1 先看看这个方法 handlerSpringMvcSelector,处理 Selector。 + +```java +// SoulClientRegisterServiceImpl.java +private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) { + String contextPath = dto.getContext(); + // 根据 contextPath 到数据库里查询,是否已经注册过。 + SelectorDO selectorDO = selectorService.findByName(contextPath); + String selectorId; + String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort())); + if (Objects.isNull(selectorDO)) { + // 还没有注册过 + selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri); + } else { + // 已经注册过,业务系统重启了会到这里 + selectorId = selectorDO.getId(); + //update upstream + String handle = selectorDO.getHandle(); + String handleAdd; + DivideUpstream addDivideUpstream = buildDivideUpstream(uri); + SelectorData selectorData = selectorService.buildByName(contextPath); + if (StringUtils.isBlank(handle)) { + handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream)); + } else { + List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class); + for (DivideUpstream upstream : exist) { + if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) { + return selectorId; + } + } + exist.add(addDivideUpstream); + handleAdd = GsonUtils.getInstance().toJson(exist); + } + selectorDO.setHandle(handleAdd); + selectorData.setHandle(handleAdd); + // update db + selectorMapper.updateSelective(selectorDO); + // submit upstreamCheck + upstreamCheckService.submit(contextPath, addDivideUpstream); + // publish change event. + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, + Collections.singletonList(selectorData))); + } + return selectorId; +} +``` + +#### 2.1.1 第一次接入 Soul 网关 + +新接入的,到数据库里肯定查不到 selectorDO,进入 registerSelector 方法,仔细看看到底往哪些数据库表中插数据了。 + +```java +// SoulClientRegisterServiceImpl.java +private String registerSelector(final String contextPath, final String rpcType, final String appName, final String uri) { + SelectorDTO selectorDTO = SelectorDTO.builder() + .name(contextPath) + .type(SelectorTypeEnum.CUSTOM_FLOW.getCode()) + .matchMode(MatchModeEnum.AND.getCode()) + .enabled(Boolean.TRUE) + .loged(Boolean.TRUE) + .continued(Boolean.TRUE) + .sort(1) + .build(); + if (RpcTypeEnum.DUBBO.getName().equals(rpcType)) { + selectorDTO.setPluginId(getPluginId(PluginEnum.DUBBO.getName())); + } else if (RpcTypeEnum.SPRING_CLOUD.getName().equals(rpcType)) { + selectorDTO.setPluginId(getPluginId(PluginEnum.SPRING_CLOUD.getName())); + selectorDTO.setHandle(GsonUtils.getInstance().toJson(buildSpringCloudSelectorHandle(appName))); + } else if (RpcTypeEnum.SOFA.getName().equals(rpcType)) { + selectorDTO.setPluginId(getPluginId(PluginEnum.SOFA.getName())); + selectorDTO.setHandle(appName); + } else if (RpcTypeEnum.TARS.getName().equals(rpcType)) { + selectorDTO.setPluginId(getPluginId(PluginEnum.TARS.getName())); + selectorDTO.setHandle(appName); + } else { + //is divide + DivideUpstream divideUpstream = buildDivideUpstream(uri); + String handler = GsonUtils.getInstance().toJson(Collections.singletonList(divideUpstream)); + selectorDTO.setHandle(handler); + selectorDTO.setPluginId(getPluginId(PluginEnum.DIVIDE.getName())); + upstreamCheckService.submit(selectorDTO.getName(), divideUpstream); + } + SelectorConditionDTO selectorConditionDTO = new SelectorConditionDTO(); + selectorConditionDTO.setParamType(ParamTypeEnum.URI.getName()); + selectorConditionDTO.setParamName("/"); + selectorConditionDTO.setOperator(OperatorEnum.MATCH.getAlias()); + selectorConditionDTO.setParamValue(contextPath + "/**"); + selectorDTO.setSelectorConditions(Collections.singletonList(selectorConditionDTO)); + return selectorService.register(selectorDTO); +} +``` + +看到这么多 if else,是不是很兴奋,小伙伴们可以想想怎么优化掉这么多 if else,PR 搞起来 ^ - ^。 + +写了这么多,无非是封装 SelectorDTO 对象,最后调用 selectorService.register(selectorDTO) 入库,继续跟进去。 + +```java +// SelectorServiceImpl.java +@Override +public String register(final SelectorDTO selectorDTO) { + SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO); + List selectorConditionDTOs = selectorDTO.getSelectorConditions(); + if (StringUtils.isEmpty(selectorDTO.getId())) { + selectorMapper.insertSelective(selectorDO); + selectorConditionDTOs.forEach(selectorConditionDTO -> { + selectorConditionDTO.setSelectorId(selectorDO.getId()); + // 这里在 for 循环里调用 dao 层插入数据,是不是可以考虑挪出去一次性批量插入? + selectorConditionMapper.insertSelective(SelectorConditionDO + .buildSelectorConditionDO(selectorConditionDTO)); + }); + } + publishEvent(selectorDO, selectorConditionDTOs); + return selectorDO.getId(); +} +``` + +看到这里有 2 条入库方法,分别向 selector 和 selector_condition 表中插入了数据。这里我们先不具体追究表结构及业务意义,后面补上。 + +publishEvent 方法,涉及到 ApplicationEventPublisher 接口,是观察者模式的一个实现,发布事件后通过监听器完成后续操作,这里先按下不表,后续单写一篇文章分析。 + +#### 2.1.2 已经接入 Soul 网关 + +就跟盗梦空间似的,我们回退 2 层梦境,回到插入数据的另一个分支,可以想见,就是,已经接入过 Soul 网关的系统重启,或新节点启动走的逻辑。 + +把前面的代码再贴过来: + +```java +// SoulClientRegisterServiceImpl.java +private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) { + String contextPath = dto.getContext(); + // 根据 contextPath 到数据库里查询,是否已经注册过。 + SelectorDO selectorDO = selectorService.findByName(contextPath); + String selectorId; + String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort())); + if (Objects.isNull(selectorDO)) { + // 还没有注册过 + selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri); + } else { + // 已接入的业务系统重启,或新节点启动,会到这里 + selectorId = selectorDO.getId(); + //update upstream + // handle 字段存储这个接口真实节点信息,可能存在多台机器需要负载均衡的场景 + String handle = selectorDO.getHandle(); + String handleAdd; + DivideUpstream addDivideUpstream = buildDivideUpstream(uri); + SelectorData selectorData = selectorService.buildByName(contextPath); + if (StringUtils.isBlank(handle)) { + // 这个接口虽然之前注册过,但第1个服务器节点接入 Soul 时会进来 + handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream)); + } else { + // 如果已经至少有1个服务器节点已接入,会进到这里,判断是否是同一个节点(使用 upstreamUrl 区分),如果相同直接返回 + List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class); + for (DivideUpstream upstream : exist) { + if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) { + return selectorId; + } + } + // 如果不是同一个节点,把新节点加入到 handle 字段中 + exist.add(addDivideUpstream); + handleAdd = GsonUtils.getInstance().toJson(exist); + } + selectorDO.setHandle(handleAdd); + selectorData.setHandle(handleAdd); + // update db 更新数据库 + selectorMapper.updateSelective(selectorDO); + // submit upstreamCheck + upstreamCheckService.submit(contextPath, addDivideUpstream); + // publish change event. + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, + Collections.singletonList(selectorData))); + } + return selectorId; +} +``` + +因为还没有研究数据库表结构设计,根据已知部分猜测,1 个 selector 对应一个 divide 插件,这个插件以 contextPath 为标识(在这里就是 "/http"),一个 contextPath 可以部署多个服务器节点,这些节点信息已 json 形式保存在 handle 字段中。 + +```json +// handle/handleAdd 数据格式 +[ + { + "upstreamHost": "localhost", + "protocol": "http://", + "upstreamUrl": "10.0.0.12:8188", + "weight": 50, + "status": true, + "timestamp": 0, + "warmup": 0 + } +] +``` + +下面紧接着就是更新数据库 updateSelective。 + +upstreamCheckService.submit(contextPath, addDivideUpstream); 把真实服务器节点信息缓存在一个 Map(UPSTREAM_MAP) 里,有定时任务定期探活,如果发现服务节点宕机了,就把他剔除出去,防止把请求发送到已经宕机的节点上。 + +然后就是 eventPublisher.publishEvent(),跟前面的 publishEvent 方法一样,发布事件后通过监听器完成后续操作(简单介绍下,这里是通过与 Soul 网关建立的 websocket 长连接发送数据 SelectorData 修改的消息,Soul 网关根据消息修改数据,这个具体改的什么数据,怎么修改的,后面分析)。 + +到这里终于把 handlerSpringMvcSelector 这个方法分析完了。 + +### 2.2 再来看看这个方法 handlerSpringMvcRule,处理 Rule。 + +```java +// SoulClientRegisterServiceImpl.java +private void handlerSpringMvcRule(final String selectorId, final SpringMvcRegisterDTO dto) { + RuleDO ruleDO = ruleMapper.findByName(dto.getRuleName()); + if (Objects.isNull(ruleDO)) { + registerRule(selectorId, dto.getPath(), dto.getRpcType(), dto.getRuleName()); + } +} +``` + +首先拿着规则名字,到 rule 表里捞数据,如果捞到了表名已经注册过了,无操作。 + +看下数据库数据,就是业务系统下的接口地址。 + +```bash +mysql> use soul; +Database changed + +mysql> select * from rule where name = '/http/order/findById' \G +*************************** 1. row *************************** + id: 1349650371868782592 + selector_id: 1349650371302551552 + match_mode: 0 + name: /http/order/findById + enabled: 1 + loged: 1 + sort: 1 + handle: {"loadBalance":"random","retry":0,"timeout":3000} +date_created: 2021-01-14 17:31:39 +date_updated: 2021-01-14 17:31:39 +1 row in set (0.00 sec) +``` + +如果没捞到数据,注册这个规则。 + +```java +// SoulClientRegisterServiceImpl.java +private void registerRule(final String selectorId, final String path, final String rpcType, final String ruleName) { + RuleHandle ruleHandle = RuleHandleFactory.ruleHandle(RpcTypeEnum.acquireByName(rpcType), path); + RuleDTO ruleDTO = RuleDTO.builder() + .selectorId(selectorId) + .name(ruleName) + .matchMode(MatchModeEnum.AND.getCode()) + .enabled(Boolean.TRUE) + .loged(Boolean.TRUE) + .sort(1) + .handle(ruleHandle.toJson()) + .build(); + RuleConditionDTO ruleConditionDTO = RuleConditionDTO.builder() + .paramType(ParamTypeEnum.URI.getName()) + .paramName("/") + .paramValue(path) + .build(); + if (path.indexOf("*") > 1) { + ruleConditionDTO.setOperator(OperatorEnum.MATCH.getAlias()); + } else { + ruleConditionDTO.setOperator(OperatorEnum.EQ.getAlias()); + } + ruleDTO.setRuleConditions(Collections.singletonList(ruleConditionDTO)); + ruleService.register(ruleDTO); +} +``` + +第 1 行,根据 rpcType("http") 获取其对应的 RuleHandle,这里,默认内置 3 种类型,我们这里的是 HTTP,对应 DivideRuleHandle。 + +```java +// RuleHandleFactory.java +public final class RuleHandleFactory { + + /** + * The RpcType to RuleHandle class map. + */ + private static final Map> RPC_TYPE_TO_RULE_HANDLE_CLASS = new ConcurrentHashMap<>(); + + /** + * The default RuleHandle. + */ + private static final Class DEFAULT_RULE_HANDLE = SpringCloudRuleHandle.class; + + static { + RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.HTTP, DivideRuleHandle.class); + RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.DUBBO, DubboRuleHandle.class); + RPC_TYPE_TO_RULE_HANDLE_CLASS.put(RpcTypeEnum.SOFA, SofaRuleHandle.class); + } + + /** + * Get a RuleHandle object with given rpc type and path. + * @param rpcType rpc type. + * @param path path. + * @return RuleHandle object. + */ + public static RuleHandle ruleHandle(final RpcTypeEnum rpcType, final String path) { + if (Objects.isNull(rpcType)) { + return null; + } + Class clazz = RPC_TYPE_TO_RULE_HANDLE_CLASS.getOrDefault(rpcType, DEFAULT_RULE_HANDLE); + try { + return clazz.newInstance().createDefault(path); + } catch (InstantiationException | IllegalAccessException e) { + throw new SoulException( + String.format("Init RuleHandle failed with rpc type: %s, rule class: %s, exception: %s", + rpcType, + clazz.getSimpleName(), + e.getMessage())); + } + } +} +``` + +下面构造 RuleDTO 对象,注册规则。 + +```java +// RuleServiceImpl.java +@Override +public String register(final RuleDTO ruleDTO) { + RuleDO ruleDO = RuleDO.buildRuleDO(ruleDTO); + List ruleConditions = ruleDTO.getRuleConditions(); + if (StringUtils.isEmpty(ruleDTO.getId())) { + ruleMapper.insertSelective(ruleDO); + ruleConditions.forEach(ruleConditionDTO -> { + ruleConditionDTO.setRuleId(ruleDO.getId()); + // 这里在 for 循环里调用 dao 层插入数据,是不是可以考虑挪出去一次性批量插入? + ruleConditionMapper.insertSelective(RuleConditionDO + .buildRuleConditionDO(ruleConditionDTO)); + }); + } + publishEvent(ruleDO, ruleConditions); + return ruleDO.getId(); +} +``` + +分别向 rule 和 rule_condition 表中插入数据。 + +publishEvent() 方法,通过 websocket 长连接,向 Soul 网关发送 RuleData 数据。 + +## 3.总结 + +到这里,调用 "/soul-client/springmvc-register" 接口逻辑分析完了,我们总结下: + +- 处理 selector + - 新增或修改 selector、selector_condition 表数据,持久化到 MySQL。 + - 通过 websocket 向 Soul 网关发送数据改动信息。 +- 处理 rule + - 新增或修改 rule、rule_condition 表数据,持久化到 MySQL。 + - 通过 websocket 向 Soul 网关发送数据改动信息。 + +其中表结构及字段含义还需进一步学习和研究,websocket 发送给 Soul 网关后,网关做了什么处理也需要后续分析。 + +到这里,HTTP 用户接入 Soul 网关注册逻辑就分析完了。 + +如果在工作中你有使用网关的需求,或是个人有学习网关的追求,欢迎来跟我一起分析和学习,Soul 网关,你值得拥有。 diff --git a/src/zh/blog/soul_source_learning_05_plugin.md b/src/zh/blog/soul_source_learning_05_plugin.md index 4036738ff9..2fc370f3a0 100644 --- a/src/zh/blog/soul_source_learning_05_plugin.md +++ b/src/zh/blog/soul_source_learning_05_plugin.md @@ -1,492 +1,492 @@ ---- -title: Soul网关学习插件链与负载均衡解析 -author: 朱明 -date: 2021-01-15 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -# 插件链总结 - -从一个类关系图说起: - -![plugin关系图](/assets/img/01.png) - -其中两个最基本的插件类: - -- SoulPlugin: 定义插件职责的接口, 重点方法`execute()` 被上层调用, `skip()` 方法可以使某些插件在某些请求中被跳过. - -- AbstractPlugin: 抽象类, 实现接口的 `execute()`, 定义一套通用的执行流程, 并使用模板方法的设计模式, 提供`doExecute()`抽象方法供实现类写自己的逻辑. - -## AbstractSoulPlugin - -具体分析下 `AbstractSoulPlugin` 类的 `execute()`: - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - String pluginName = named(); - final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); - // 如果 pluginData.getEnabled() 为 false, 会直接跳到下个插件, 仅有为数不多的插件会进入这个条件判断 (DividePlugin、AlibabaDubboPlugin等) - if (pluginData != null && pluginData.getEnabled()) { - // 获得插件上的所有选择器 - final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); - if (CollectionUtils.isEmpty(selectors)) { - return CheckUtils.checkSelector(pluginName, exchange, chain); - } - // 检查上下文中的请求路径, 是否与选择器匹配, 并得到唯一一个匹配的选择器数据 - final SelectorData selectorData = matchSelector(exchange, selectors); - if (Objects.isNull(selectorData)) { - if (PluginEnum.WAF.getName().equals(pluginName)) { - return doExecute(exchange, chain, null, null); - } - return CheckUtils.checkSelector(pluginName, exchange, chain); - } - if (selectorData.getLoged()) { - log.info("{} selector success match , selector name :{}", pluginName, selectorData.getName()); - } - // 获得选择器中的各个资源规则 - final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); - if (CollectionUtils.isEmpty(rules)) { - if (PluginEnum.WAF.getName().equals(pluginName)) { - return doExecute(exchange, chain, null, null); - } - return CheckUtils.checkRule(pluginName, exchange, chain); - } - RuleData rule; - if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { - rule = rules.get(rules.size() - 1); - } else { - // 匹配路径, 获得唯一一个规则 - rule = matchRule(exchange, rules); - } - if (Objects.isNull(rule)) { - return CheckUtils.checkRule(pluginName, exchange, chain); - } - if (rule.getLoged()) { - log.info("{} rule success match ,rule name :{}", pluginName, rule.getName()); - } - // 执行子类的方法 - return doExecute(exchange, chain, selectorData, rule); - } - // 执行插件链上的下个插件 - return chain.execute(exchange); -} -``` - -通过代码分析, 可以得到一些结论: - -- execute() 有两个逻辑: 一是请求路径与选择器和规则的匹配, 最终确认一个唯一规则, 并调用子类 doExecute(); 二是执行插件链上的下个插件. -- execute() 实际抽象了一套规则匹配逻辑, 供所有"转发类型"的插件使用的, 转发类型的插件目前我了解的有 `DividePlugin` (http 请求) 和 `AlibabaDubboPlugin` (dubbo 请求), 其他类型的插件如果不重写 execute() 的方法, 会直接走入下个插件. - -## SoulPluginChain - -这里还有个点, 就是插件链的形成与链式调用, 我们来分析下 `SoulPluginChain` 这块: - -![plugin02](/assets/img/02.png) - -SoulPluginChain 接口同样定义了 `execute()` 方法供调用者使用, 它的唯一子类 DefaultSoulPluginChain 实现了链式调用: - -```java -public Mono execute(final ServerWebExchange exchange) { - return Mono.defer(() -> { - // plugins 中包含所有网关加载的插件 - if (this.index < plugins.size()) { - // 每次调用execute()方法, index索引自增, 会调用到下一个插件 - SoulPlugin plugin = plugins.get(this.index++); - // 结合上下文判断当前插件是否需要跳过 - Boolean skip = plugin.skip(exchange); - if (skip) { - return this.execute(exchange); - } else { - return plugin.execute(exchange, this); - } - } else { - return Mono.empty(); - } - }); -} -``` - -看到这会很好奇 `plugins` 这个插件列表是哪里来的, 这里来解释下, DefaultSoulPluginChain 是 SoulWebHandler 的静态内部类, `plugins`就是 SoulWebHandle 中的一个属性: - -```java -public final class SoulWebHandler implements WebHandler { - - private List plugins; - - public SoulWebHandler(final List plugins) { - this.plugins = plugins; - // ... - } - - @Override - public Mono handle(@NonNull final ServerWebExchange exchange) { - // ... - return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler) - .doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time))); - } - - private static class DefaultSoulPluginChain implements SoulPluginChain { - } -} -``` - -那么 SoulWebHandler 中的 `plugins` 又是怎么来的呢? 可以继续追溯下它构造器被调用的地方: - -```java -@Configuration -public class SoulConfiguration { - - @Bean("webHandler") - public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { - List pluginList = plugins.getIfAvailable(Collections::emptyList); - final List soulPlugins = pluginList.stream() - .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); - soulPlugins.forEach(soulPlugin -> log.info("loader plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); - return new SoulWebHandler(soulPlugins); - } -} -``` - -可以看到是通过 Spring Bean 的方式开始写入 `plugins`, 即在容器启动时, 加载了所有插件. 这里入口参数使用 `ObjectProvider` 就是延迟加载所有 SoulPlugin 类型的 Bean(若一个都没有也不会报错), 并注入到 SoulWebHandler 中. - -**有个小坑需要注意 !** - -包括 DividePlugin、AlibabaDubboPlugin 等等的所有插件, 都是由各自的 `soul-spring-boot-starter-plugin-xx` 项目中的 XXPluginConfiguration 配置类, 注册自己的插件成为 Bean, 类似下面示例: - -```java -@Configuration -public class DividePluginConfiguration { - - @Bean - public SoulPlugin dividePlugin() { - return new DividePlugin(); - } -} -``` - -所以在网关项目 `soul-bootstrap` 中, 如果需要用到某个插件, 不仅仅是在管理后台开启这个插件, 还需要确认下 `soul-bootstrap` 的 `pom.xml` 中是否存在相关插件所在的 `soul-spring-boot-starter-plugin-xx` 依赖, 比如: - -```xml - - org.dromara - soul-spring-boot-starter-plugin-divide - ${project.version} - -``` - -如果这里你给注释了或者根本不存在, 不要幻想在插件链上看到它... - -## 插件项目结构 - -最后简单说下各个插件项目的功能: - -1. 首先是刚刚提到的 spring bean 启动类项目 , 列出个大概: - - ``` - soul-spring-boot-starter-plugin-alibaba-dubbo - soul-spring-boot-starter-plugin-apache-dubbo - soul-spring-boot-starter-plugin-context-path - soul-spring-boot-starter-plugin-divide - soul-spring-boot-starter-plugin-global - soul-spring-boot-starter-plugin-httpclient - soul-spring-boot-starter-plugin-hystrix - soul-spring-boot-starter-plugin-monitor - soul-spring-boot-starter-plugin-ratelimiter - soul-spring-boot-starter-plugin-resilience4j - soul-spring-boot-starter-plugin-rewrite - soul-spring-boot-starter-plugin-sentinel - soul-spring-boot-starter-plugin-sign - soul-spring-boot-starter-plugin-sofa - soul-spring-boot-starter-plugin-springcloud - soul-spring-boot-starter-plugin-tars - soul-spring-boot-starter-plugin-waf - ``` - - 它们的主要作用刚刚也提到了, 将自身的 SoulPlugin 子类注册为 spring bean, 以及注册 spring bean 给 AbstractSoulPlugin 中调用到的 PluginDataHandler 接口, 提供自身的实现子类, 比如 DividePluginDataHandler. - -2. 具体的插件类所在项目: - - ``` - soul-plugin-alibaba-dubbo - soul-plugin-apache-dubbo - soul-plugin-api - soul-plugin-base - soul-plugin-context-path - soul-plugin-divide - soul-plugin-global - soul-plugin-httpclient - soul-plugin-hystrix - soul-plugin-monitor - soul-plugin-ratelimiter - soul-plugin-resilience4j - soul-plugin-rewrite - soul-plugin-sentinel - soul-plugin-sign - soul-plugin-sofa - soul-plugin-springcloud - soul-plugin-tars - soul-plugin-waf - ``` - - 拿 `soul-plugin-divide` 项目举例, 刚刚提到的 DividePlugin 与 DividePluginDataHandler 就在其中. 并且项目里还有节点信息缓存管理器 UpstreamCacheManager, 负载均衡策略类 LoadBalance 等等. - -# DividePlugin - -DividePlugin 的作用是匹配 Http 请求, 既然有 Http 请求自然也有转发下游和返回响应, 所以这里我们会分析到三个插件: DividePlugin、WebClientPlugin、WebClientResponsePlugin. - -首先来看 DividePlugin 中 `doExecute()` 具体实现, 这里我只保留了核心点: - -```java -@Override -protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); - // 通过选择器ID, 在缓存中得到服务节点集群 - final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); - // 调用负载均衡方法并传入策略类型, 得到唯一节点 - DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); - // 获得节点的真实url, 并放入 exchange 上下文中 - String domain = buildDomain(divideUpstream); - String realURL = buildRealURL(domain, soulContext, exchange); - exchange.getAttributes().put(Constants.HTTP_URL, realURL); - // 继续调用下个插件 - return chain.execute(exchange); -} -``` - -可以看到, 在执行完 DividePlugin 的 `doExecute()` 方法后, 我们的 ServerWebExchange 上下文中已经有了下游服务节点的真实路径, 接下来只要请求它就搞定. 不过先别着急, 这里的负载均衡策略也是关键点, 接着分析下. - -## 负载均衡 - -Soul 网关的负载均衡如何执行, 不止涉及到各种策略 (hasn、随机、轮询), 也涉及到 "权重分数" 这个概念, 管理后台的具体配置如下: - -待补,文章内部有报错 - -待补,文章内部有报错 - -待补,文章内部有报错 - -展示完后台配置, 我们具体看看各个策略的代码实现. - -## Hash - -```java -public DivideUpstream doSelect(final List upstreamList, final String ip) { - final ConcurrentSkipListMap treeMap = new ConcurrentSkipListMap<>(); - for (DivideUpstream address : upstreamList) { - // 每个节点*VIRTUAL_NODE_NUM(默认5), 使hash更加均匀 - for (int i = 0; i < VIRTUAL_NODE_NUM; i++) { - long addressHash = hash("SOUL-" + address.getUpstreamUrl() + "-HASH-" + i); - treeMap.put(addressHash, address); - } - } - // 从当前ip得到一个hash值, 并比对treemap(有序), 找到大于此hash值的位置 - long hash = hash(String.valueOf(ip)); - SortedMap lastRing = treeMap.tailMap(hash); - // 只要服务节点不增减, 同一个ip得到的节点就可以保持不变 - if (!lastRing.isEmpty()) { - return lastRing.get(lastRing.firstKey()); - } - return treeMap.firstEntry().getValue(); -} -``` - -hash 算法的负载均衡, 并没有使用到 "权重分数" 这个概念, 也就是说针对每个未知的 IP 各个节点被访问的可能性是一致的. (当然, 同一个 IP 多次调用只会访问同个节点) - -## RandomLoadBalance - -```java -public DivideUpstream doSelect(final List upstreamList, final String ip) { - // 总个数 - int length = upstreamList.size(); - // 总权重 - int totalWeight = 0; - // 权重是否都一样 - boolean sameWeight = true; - for (int i = 0; i < length; i++) { - int weight = upstreamList.get(i).getWeight(); - // 累计总权重 - totalWeight += weight; - if (sameWeight && i > 0 - && weight != upstreamList.get(i - 1).getWeight()) { - // 计算所有权重是否一样 - sameWeight = false; - } - } - if (totalWeight > 0 && !sameWeight) { - // 如果权重不相同且权重大于0则按总权重数随机 - int offset = RANDOM.nextInt(totalWeight); - // 并确定随机值落在哪个片断上 - for (DivideUpstream divideUpstream : upstreamList) { - offset -= divideUpstream.getWeight(); - if (offset < 0) { - return divideUpstream; - } - } - } - // 如果权重相同或权重为0则均等随机 - return upstreamList.get(RANDOM.nextInt(length)); -} -``` - -当使用 `random` 规则时, 所有节点权重分累加并随机得到数字, 看具体是落在那个节点的权重片段上; 如果分数 0 或者相同则很直接的随机集群长度即可. - -## RoundRobinLoadBalance - -```java -public DivideUpstream doSelect(final List upstreamList, final String ip) { - String key = upstreamList.get(0).getUpstreamUrl(); - ConcurrentMap map = methodWeightMap.get(key); - if (map == null) { - methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<>(16)); - map = methodWeightMap.get(key); - } - int totalWeight = 0; - long maxCurrent = Long.MIN_VALUE; - long now = System.currentTimeMillis(); - DivideUpstream selectedInvoker = null; - WeightedRoundRobin selectedWRR = null; - for (DivideUpstream upstream : upstreamList) { - String rKey = upstream.getUpstreamUrl(); - // 取出节点在缓存中的信息 - WeightedRoundRobin weightedRoundRobin = map.get(rKey); - int weight = upstream.getWeight(); - if (weightedRoundRobin == null) { - weightedRoundRobin = new WeightedRoundRobin(); - weightedRoundRobin.setWeight(weight); - map.putIfAbsent(rKey, weightedRoundRobin); - } - if (weight != weightedRoundRobin.getWeight()) { - weightedRoundRobin.setWeight(weight); - } - // 这里是第一个关键: 缓存中的分数增加当前节点权重分 - long cur = weightedRoundRobin.increaseCurrent(); - weightedRoundRobin.setLastUpdate(now); - // 选择缓存分值高的节点 - if (cur > maxCurrent) { - maxCurrent = cur; - selectedInvoker = upstream; - selectedWRR = weightedRoundRobin; - } - totalWeight += weight; - } - if (!updateLock.get() && upstreamList.size() != map.size() && updateLock.compareAndSet(false, true)) { - try { - ConcurrentMap newMap = new ConcurrentHashMap<>(map); - newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > recyclePeriod); - methodWeightMap.put(key, newMap); - } finally { - updateLock.set(false); - } - } - if (selectedInvoker != null) { - // 这里是第二个关键: 缓存中的分数, 减少总节点权重分 - selectedWRR.sel(totalWeight); - return selectedInvoker; - } - return upstreamList.get(0); -} -``` - -这个算法有点复杂, 我解释下核心计算权重的方面: - -- 两个分值分别为 2、100 的节点进入, 缓存中保留它们各自, 分值从 0 开始 -- 经过 for 循环后, 两个节点在缓存中的分值会以自身为基数增加, 假设后面步骤不进行, 则缓存第一次为 2、100, 第二次为 4、200, 依次类推. -- 关键的第三步, 选出节点缓存中分值最高的, 进行"处罚"措施, 减少所有节点的累计分值, 即 102. - -根据这个算法的步骤, 一直没有被选中的节点, 作为"成长奖励", 会持续以自身为基数自增; 而被选中的节点, 作为"惩罚", 会减少其他节点的权重分之和. - -可以预见, 权重分小的节点, 要自增到很久之后, 才会等来自身被选中的一刻, 然而那一刻它被惩罚的力度会非常大, 导致它一朝回到解放前, 又要开始漫长的积蓄力量. 而权重分大的节点, 每次被选上的惩罚力度很小, 即使多次后分数太低没被选上, 他的奖励分数(自身)也特别高, 一次增加就远远超越其他节点. - -## WebClientPlugin - -经过 DividePlugin 插件的调用后, 下游服务节点路径被确定, 接着就是 WebClientPlugin 插件发挥作用了. 它直接实现了 SoulPlugin 接口并实现了 `execute()` 方法 (仅保留核心代码): - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - String urlPath = exchange.getAttribute(Constants.HTTP_URL); - // 请求类型: Get请求orPost请求等 - HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue()); - // 构建一个请求对象空壳, 注入请求类型和URL - WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath); - return handleRequestBody(requestBodySpec, exchange, timeout, chain); -} - -private Mono handleRequestBody(final WebClient.RequestBodySpec requestBodySpec, - final ServerWebExchange exchange, - final long timeout, - final SoulPluginChain chain) { - return requestBodySpec.headers(httpHeaders -> { - // 补充上下文中请求头... 后面也是补充些属性, 不赘述 - httpHeaders.addAll(exchange.getRequest().getHeaders()); - httpHeaders.remove(HttpHeaders.HOST); - }) - .contentType(buildMediaType(exchange)) - .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody())) - // 开始异步http调用下游服务 - .exchange() - .doOnError(e -> log.error(e.getMessage())) - .timeout(Duration.ofMillis(timeout)) - // 回调接收返回值 - .flatMap(e -> doNext(e, exchange, chain)); -} - -// 这里是异步的回调方法, 在另一个线程中工作 -private Mono doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) { - // ... - // 继续完成剩下的插件链调用 - return chain.execute(exchange); -} -``` - -简单看下 `handleRequestBody()` 中 `exchange()` 这个方法的实现, 这里有关键的 Http 调用: - -```java -class DefaultWebClient implements WebClient { - @Override - public Mono exchange() { - ClientRequest request = (this.inserter != null ? - initRequestBuilder().body(this.inserter).build() : - initRequestBuilder().build()); - // 这里是关键调用, 会走到 spring-web-reactive - return Mono.defer(() -> exchangeFunction.exchange(request) - .checkpoint("Request to " + this.httpMethod.name() + " " + this.uri + " [DefaultWebClient]") - .switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR)); - } -} -``` - -总结下, 经过 WebClientPlugin 的处理会异步调用下游服务, 等待响应后再在另一个线程中, 执行后续的插件链调用. - -## WebClientResponseClient - -最后插件链走到 WebClientResponseClient 这一环, 封装响应信息: - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - return chain.execute(exchange).then(Mono.defer(() -> { - // 获取上下文中存放的响应信息 - ServerHttpResponse response = exchange.getResponse(); - ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR); - if (Objects.isNull(clientResponse) - || response.getStatusCode() == HttpStatus.BAD_GATEWAY - || response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) { - Object error = SoulResultWarp.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } else if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { - Object error = SoulResultWarp.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // 各种拼装 - response.setStatusCode(clientResponse.statusCode()); - response.getCookies().putAll(clientResponse.cookies()); - response.getHeaders().putAll(clientResponse.headers().asHttpHeaders()); - return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())); - })); -} -``` +--- +title: Soul网关学习插件链与负载均衡解析 +author: 朱明 +date: 2021-01-15 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +# 插件链总结 + +从一个类关系图说起: + +![plugin关系图](/assets/img/01.png) + +其中两个最基本的插件类: + +- SoulPlugin: 定义插件职责的接口, 重点方法`execute()` 被上层调用, `skip()` 方法可以使某些插件在某些请求中被跳过. + +- AbstractPlugin: 抽象类, 实现接口的 `execute()`, 定义一套通用的执行流程, 并使用模板方法的设计模式, 提供`doExecute()`抽象方法供实现类写自己的逻辑. + +## AbstractSoulPlugin + +具体分析下 `AbstractSoulPlugin` 类的 `execute()`: + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + String pluginName = named(); + final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); + // 如果 pluginData.getEnabled() 为 false, 会直接跳到下个插件, 仅有为数不多的插件会进入这个条件判断 (DividePlugin、AlibabaDubboPlugin等) + if (pluginData != null && pluginData.getEnabled()) { + // 获得插件上的所有选择器 + final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); + if (CollectionUtils.isEmpty(selectors)) { + return CheckUtils.checkSelector(pluginName, exchange, chain); + } + // 检查上下文中的请求路径, 是否与选择器匹配, 并得到唯一一个匹配的选择器数据 + final SelectorData selectorData = matchSelector(exchange, selectors); + if (Objects.isNull(selectorData)) { + if (PluginEnum.WAF.getName().equals(pluginName)) { + return doExecute(exchange, chain, null, null); + } + return CheckUtils.checkSelector(pluginName, exchange, chain); + } + if (selectorData.getLoged()) { + log.info("{} selector success match , selector name :{}", pluginName, selectorData.getName()); + } + // 获得选择器中的各个资源规则 + final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); + if (CollectionUtils.isEmpty(rules)) { + if (PluginEnum.WAF.getName().equals(pluginName)) { + return doExecute(exchange, chain, null, null); + } + return CheckUtils.checkRule(pluginName, exchange, chain); + } + RuleData rule; + if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { + rule = rules.get(rules.size() - 1); + } else { + // 匹配路径, 获得唯一一个规则 + rule = matchRule(exchange, rules); + } + if (Objects.isNull(rule)) { + return CheckUtils.checkRule(pluginName, exchange, chain); + } + if (rule.getLoged()) { + log.info("{} rule success match ,rule name :{}", pluginName, rule.getName()); + } + // 执行子类的方法 + return doExecute(exchange, chain, selectorData, rule); + } + // 执行插件链上的下个插件 + return chain.execute(exchange); +} +``` + +通过代码分析, 可以得到一些结论: + +- execute() 有两个逻辑: 一是请求路径与选择器和规则的匹配, 最终确认一个唯一规则, 并调用子类 doExecute(); 二是执行插件链上的下个插件. +- execute() 实际抽象了一套规则匹配逻辑, 供所有"转发类型"的插件使用的, 转发类型的插件目前我了解的有 `DividePlugin` (http 请求) 和 `AlibabaDubboPlugin` (dubbo 请求), 其他类型的插件如果不重写 execute() 的方法, 会直接走入下个插件. + +## SoulPluginChain + +这里还有个点, 就是插件链的形成与链式调用, 我们来分析下 `SoulPluginChain` 这块: + +![plugin02](/assets/img/02.png) + +SoulPluginChain 接口同样定义了 `execute()` 方法供调用者使用, 它的唯一子类 DefaultSoulPluginChain 实现了链式调用: + +```java +public Mono execute(final ServerWebExchange exchange) { + return Mono.defer(() -> { + // plugins 中包含所有网关加载的插件 + if (this.index < plugins.size()) { + // 每次调用execute()方法, index索引自增, 会调用到下一个插件 + SoulPlugin plugin = plugins.get(this.index++); + // 结合上下文判断当前插件是否需要跳过 + Boolean skip = plugin.skip(exchange); + if (skip) { + return this.execute(exchange); + } else { + return plugin.execute(exchange, this); + } + } else { + return Mono.empty(); + } + }); +} +``` + +看到这会很好奇 `plugins` 这个插件列表是哪里来的, 这里来解释下, DefaultSoulPluginChain 是 SoulWebHandler 的静态内部类, `plugins`就是 SoulWebHandle 中的一个属性: + +```java +public final class SoulWebHandler implements WebHandler { + + private List plugins; + + public SoulWebHandler(final List plugins) { + this.plugins = plugins; + // ... + } + + @Override + public Mono handle(@NonNull final ServerWebExchange exchange) { + // ... + return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler) + .doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time))); + } + + private static class DefaultSoulPluginChain implements SoulPluginChain { + } +} +``` + +那么 SoulWebHandler 中的 `plugins` 又是怎么来的呢? 可以继续追溯下它构造器被调用的地方: + +```java +@Configuration +public class SoulConfiguration { + + @Bean("webHandler") + public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { + List pluginList = plugins.getIfAvailable(Collections::emptyList); + final List soulPlugins = pluginList.stream() + .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); + soulPlugins.forEach(soulPlugin -> log.info("loader plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); + return new SoulWebHandler(soulPlugins); + } +} +``` + +可以看到是通过 Spring Bean 的方式开始写入 `plugins`, 即在容器启动时, 加载了所有插件. 这里入口参数使用 `ObjectProvider` 就是延迟加载所有 SoulPlugin 类型的 Bean(若一个都没有也不会报错), 并注入到 SoulWebHandler 中. + +**有个小坑需要注意 !** + +包括 DividePlugin、AlibabaDubboPlugin 等等的所有插件, 都是由各自的 `soul-spring-boot-starter-plugin-xx` 项目中的 XXPluginConfiguration 配置类, 注册自己的插件成为 Bean, 类似下面示例: + +```java +@Configuration +public class DividePluginConfiguration { + + @Bean + public SoulPlugin dividePlugin() { + return new DividePlugin(); + } +} +``` + +所以在网关项目 `soul-bootstrap` 中, 如果需要用到某个插件, 不仅仅是在管理后台开启这个插件, 还需要确认下 `soul-bootstrap` 的 `pom.xml` 中是否存在相关插件所在的 `soul-spring-boot-starter-plugin-xx` 依赖, 比如: + +```xml + + org.dromara + soul-spring-boot-starter-plugin-divide + ${project.version} + +``` + +如果这里你给注释了或者根本不存在, 不要幻想在插件链上看到它... + +## 插件项目结构 + +最后简单说下各个插件项目的功能: + +1. 首先是刚刚提到的 spring bean 启动类项目 , 列出个大概: + + ``` + soul-spring-boot-starter-plugin-alibaba-dubbo + soul-spring-boot-starter-plugin-apache-dubbo + soul-spring-boot-starter-plugin-context-path + soul-spring-boot-starter-plugin-divide + soul-spring-boot-starter-plugin-global + soul-spring-boot-starter-plugin-httpclient + soul-spring-boot-starter-plugin-hystrix + soul-spring-boot-starter-plugin-monitor + soul-spring-boot-starter-plugin-ratelimiter + soul-spring-boot-starter-plugin-resilience4j + soul-spring-boot-starter-plugin-rewrite + soul-spring-boot-starter-plugin-sentinel + soul-spring-boot-starter-plugin-sign + soul-spring-boot-starter-plugin-sofa + soul-spring-boot-starter-plugin-springcloud + soul-spring-boot-starter-plugin-tars + soul-spring-boot-starter-plugin-waf + ``` + + 它们的主要作用刚刚也提到了, 将自身的 SoulPlugin 子类注册为 spring bean, 以及注册 spring bean 给 AbstractSoulPlugin 中调用到的 PluginDataHandler 接口, 提供自身的实现子类, 比如 DividePluginDataHandler. + +2. 具体的插件类所在项目: + + ``` + soul-plugin-alibaba-dubbo + soul-plugin-apache-dubbo + soul-plugin-api + soul-plugin-base + soul-plugin-context-path + soul-plugin-divide + soul-plugin-global + soul-plugin-httpclient + soul-plugin-hystrix + soul-plugin-monitor + soul-plugin-ratelimiter + soul-plugin-resilience4j + soul-plugin-rewrite + soul-plugin-sentinel + soul-plugin-sign + soul-plugin-sofa + soul-plugin-springcloud + soul-plugin-tars + soul-plugin-waf + ``` + + 拿 `soul-plugin-divide` 项目举例, 刚刚提到的 DividePlugin 与 DividePluginDataHandler 就在其中. 并且项目里还有节点信息缓存管理器 UpstreamCacheManager, 负载均衡策略类 LoadBalance 等等. + +# DividePlugin + +DividePlugin 的作用是匹配 Http 请求, 既然有 Http 请求自然也有转发下游和返回响应, 所以这里我们会分析到三个插件: DividePlugin、WebClientPlugin、WebClientResponsePlugin. + +首先来看 DividePlugin 中 `doExecute()` 具体实现, 这里我只保留了核心点: + +```java +@Override +protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); + // 通过选择器ID, 在缓存中得到服务节点集群 + final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); + // 调用负载均衡方法并传入策略类型, 得到唯一节点 + DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); + // 获得节点的真实url, 并放入 exchange 上下文中 + String domain = buildDomain(divideUpstream); + String realURL = buildRealURL(domain, soulContext, exchange); + exchange.getAttributes().put(Constants.HTTP_URL, realURL); + // 继续调用下个插件 + return chain.execute(exchange); +} +``` + +可以看到, 在执行完 DividePlugin 的 `doExecute()` 方法后, 我们的 ServerWebExchange 上下文中已经有了下游服务节点的真实路径, 接下来只要请求它就搞定. 不过先别着急, 这里的负载均衡策略也是关键点, 接着分析下. + +## 负载均衡 + +Soul 网关的负载均衡如何执行, 不止涉及到各种策略 (hasn、随机、轮询), 也涉及到 "权重分数" 这个概念, 管理后台的具体配置如下: + +待补,文章内部有报错 + +待补,文章内部有报错 + +待补,文章内部有报错 + +展示完后台配置, 我们具体看看各个策略的代码实现. + +## Hash + +```java +public DivideUpstream doSelect(final List upstreamList, final String ip) { + final ConcurrentSkipListMap treeMap = new ConcurrentSkipListMap<>(); + for (DivideUpstream address : upstreamList) { + // 每个节点*VIRTUAL_NODE_NUM(默认5), 使hash更加均匀 + for (int i = 0; i < VIRTUAL_NODE_NUM; i++) { + long addressHash = hash("SOUL-" + address.getUpstreamUrl() + "-HASH-" + i); + treeMap.put(addressHash, address); + } + } + // 从当前ip得到一个hash值, 并比对treemap(有序), 找到大于此hash值的位置 + long hash = hash(String.valueOf(ip)); + SortedMap lastRing = treeMap.tailMap(hash); + // 只要服务节点不增减, 同一个ip得到的节点就可以保持不变 + if (!lastRing.isEmpty()) { + return lastRing.get(lastRing.firstKey()); + } + return treeMap.firstEntry().getValue(); +} +``` + +hash 算法的负载均衡, 并没有使用到 "权重分数" 这个概念, 也就是说针对每个未知的 IP 各个节点被访问的可能性是一致的. (当然, 同一个 IP 多次调用只会访问同个节点) + +## RandomLoadBalance + +```java +public DivideUpstream doSelect(final List upstreamList, final String ip) { + // 总个数 + int length = upstreamList.size(); + // 总权重 + int totalWeight = 0; + // 权重是否都一样 + boolean sameWeight = true; + for (int i = 0; i < length; i++) { + int weight = upstreamList.get(i).getWeight(); + // 累计总权重 + totalWeight += weight; + if (sameWeight && i > 0 + && weight != upstreamList.get(i - 1).getWeight()) { + // 计算所有权重是否一样 + sameWeight = false; + } + } + if (totalWeight > 0 && !sameWeight) { + // 如果权重不相同且权重大于0则按总权重数随机 + int offset = RANDOM.nextInt(totalWeight); + // 并确定随机值落在哪个片断上 + for (DivideUpstream divideUpstream : upstreamList) { + offset -= divideUpstream.getWeight(); + if (offset < 0) { + return divideUpstream; + } + } + } + // 如果权重相同或权重为0则均等随机 + return upstreamList.get(RANDOM.nextInt(length)); +} +``` + +当使用 `random` 规则时, 所有节点权重分累加并随机得到数字, 看具体是落在那个节点的权重片段上; 如果分数 0 或者相同则很直接的随机集群长度即可. + +## RoundRobinLoadBalance + +```java +public DivideUpstream doSelect(final List upstreamList, final String ip) { + String key = upstreamList.get(0).getUpstreamUrl(); + ConcurrentMap map = methodWeightMap.get(key); + if (map == null) { + methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<>(16)); + map = methodWeightMap.get(key); + } + int totalWeight = 0; + long maxCurrent = Long.MIN_VALUE; + long now = System.currentTimeMillis(); + DivideUpstream selectedInvoker = null; + WeightedRoundRobin selectedWRR = null; + for (DivideUpstream upstream : upstreamList) { + String rKey = upstream.getUpstreamUrl(); + // 取出节点在缓存中的信息 + WeightedRoundRobin weightedRoundRobin = map.get(rKey); + int weight = upstream.getWeight(); + if (weightedRoundRobin == null) { + weightedRoundRobin = new WeightedRoundRobin(); + weightedRoundRobin.setWeight(weight); + map.putIfAbsent(rKey, weightedRoundRobin); + } + if (weight != weightedRoundRobin.getWeight()) { + weightedRoundRobin.setWeight(weight); + } + // 这里是第一个关键: 缓存中的分数增加当前节点权重分 + long cur = weightedRoundRobin.increaseCurrent(); + weightedRoundRobin.setLastUpdate(now); + // 选择缓存分值高的节点 + if (cur > maxCurrent) { + maxCurrent = cur; + selectedInvoker = upstream; + selectedWRR = weightedRoundRobin; + } + totalWeight += weight; + } + if (!updateLock.get() && upstreamList.size() != map.size() && updateLock.compareAndSet(false, true)) { + try { + ConcurrentMap newMap = new ConcurrentHashMap<>(map); + newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > recyclePeriod); + methodWeightMap.put(key, newMap); + } finally { + updateLock.set(false); + } + } + if (selectedInvoker != null) { + // 这里是第二个关键: 缓存中的分数, 减少总节点权重分 + selectedWRR.sel(totalWeight); + return selectedInvoker; + } + return upstreamList.get(0); +} +``` + +这个算法有点复杂, 我解释下核心计算权重的方面: + +- 两个分值分别为 2、100 的节点进入, 缓存中保留它们各自, 分值从 0 开始 +- 经过 for 循环后, 两个节点在缓存中的分值会以自身为基数增加, 假设后面步骤不进行, 则缓存第一次为 2、100, 第二次为 4、200, 依次类推. +- 关键的第三步, 选出节点缓存中分值最高的, 进行"处罚"措施, 减少所有节点的累计分值, 即 102. + +根据这个算法的步骤, 一直没有被选中的节点, 作为"成长奖励", 会持续以自身为基数自增; 而被选中的节点, 作为"惩罚", 会减少其他节点的权重分之和. + +可以预见, 权重分小的节点, 要自增到很久之后, 才会等来自身被选中的一刻, 然而那一刻它被惩罚的力度会非常大, 导致它一朝回到解放前, 又要开始漫长的积蓄力量. 而权重分大的节点, 每次被选上的惩罚力度很小, 即使多次后分数太低没被选上, 他的奖励分数(自身)也特别高, 一次增加就远远超越其他节点. + +## WebClientPlugin + +经过 DividePlugin 插件的调用后, 下游服务节点路径被确定, 接着就是 WebClientPlugin 插件发挥作用了. 它直接实现了 SoulPlugin 接口并实现了 `execute()` 方法 (仅保留核心代码): + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + String urlPath = exchange.getAttribute(Constants.HTTP_URL); + // 请求类型: Get请求orPost请求等 + HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue()); + // 构建一个请求对象空壳, 注入请求类型和URL + WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath); + return handleRequestBody(requestBodySpec, exchange, timeout, chain); +} + +private Mono handleRequestBody(final WebClient.RequestBodySpec requestBodySpec, + final ServerWebExchange exchange, + final long timeout, + final SoulPluginChain chain) { + return requestBodySpec.headers(httpHeaders -> { + // 补充上下文中请求头... 后面也是补充些属性, 不赘述 + httpHeaders.addAll(exchange.getRequest().getHeaders()); + httpHeaders.remove(HttpHeaders.HOST); + }) + .contentType(buildMediaType(exchange)) + .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody())) + // 开始异步http调用下游服务 + .exchange() + .doOnError(e -> log.error(e.getMessage())) + .timeout(Duration.ofMillis(timeout)) + // 回调接收返回值 + .flatMap(e -> doNext(e, exchange, chain)); +} + +// 这里是异步的回调方法, 在另一个线程中工作 +private Mono doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) { + // ... + // 继续完成剩下的插件链调用 + return chain.execute(exchange); +} +``` + +简单看下 `handleRequestBody()` 中 `exchange()` 这个方法的实现, 这里有关键的 Http 调用: + +```java +class DefaultWebClient implements WebClient { + @Override + public Mono exchange() { + ClientRequest request = (this.inserter != null ? + initRequestBuilder().body(this.inserter).build() : + initRequestBuilder().build()); + // 这里是关键调用, 会走到 spring-web-reactive + return Mono.defer(() -> exchangeFunction.exchange(request) + .checkpoint("Request to " + this.httpMethod.name() + " " + this.uri + " [DefaultWebClient]") + .switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR)); + } +} +``` + +总结下, 经过 WebClientPlugin 的处理会异步调用下游服务, 等待响应后再在另一个线程中, 执行后续的插件链调用. + +## WebClientResponseClient + +最后插件链走到 WebClientResponseClient 这一环, 封装响应信息: + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + return chain.execute(exchange).then(Mono.defer(() -> { + // 获取上下文中存放的响应信息 + ServerHttpResponse response = exchange.getResponse(); + ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR); + if (Objects.isNull(clientResponse) + || response.getStatusCode() == HttpStatus.BAD_GATEWAY + || response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) { + Object error = SoulResultWarp.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } else if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { + Object error = SoulResultWarp.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // 各种拼装 + response.setStatusCode(clientResponse.statusCode()); + response.getCookies().putAll(clientResponse.cookies()); + response.getHeaders().putAll(clientResponse.headers().asHttpHeaders()); + return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())); + })); +} +``` diff --git a/src/zh/blog/soul_source_learning_08_httplongpolling_01.md b/src/zh/blog/soul_source_learning_08_httplongpolling_01.md index a7927dea46..6a65241e2e 100644 --- a/src/zh/blog/soul_source_learning_08_httplongpolling_01.md +++ b/src/zh/blog/soul_source_learning_08_httplongpolling_01.md @@ -1,384 +1,384 @@ ---- -title: Soul网关学习Http长轮询解析01 -author: 朱明 -date: 2021-01-25 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -## 后台与网关数据同步 (Http 长轮询篇) - -### 配置 - -**后台信息模式切换** - -在上篇分析 Zookeeper 同步的文章 ([Soul 网关源码分析-11 期](https://blog.csdn.net/zm469568595/article/details/113065463)) 中, 我们通过 DataSyncConfiguration 这个配置类做的切换, 这次有了经验, 直接贴配置 - -```yml -soul: - sync: - websocket: - enabled: false - http: - enabled: true -``` - -**网关信息模式切换** - -后台模式切换完成, 接下来就是网关, 继续照葫芦画瓢找到关键配置类上的参数设置. 这里也直接贴网关配置 - -```yml -soul: - sync: -# websocket: -# urls: ws://localhost:9095/websocket - http: - url: http://localhost:9095 -``` - -### DataChangedListener 体系 - -后台数据初始化 DataSyncConfiguration 配置关键 Bean , 看看这里关于 Http 长轮询的 Bean - -```java -@Configuration -public class DataSyncConfiguration { - - @Configuration - @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true") - @EnableConfigurationProperties(HttpSyncProperties.class) - static class HttpLongPollingListener { - - @Bean - @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class) - public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) { - return new HttpLongPollingDataChangedListener(httpSyncProperties); - } - } -} -``` - -HttpLongPollingDataChangedListener 继承自 AbstractDataChangedListener, 他们都实现自接口 DataChangedListener. - -DataChangedListener 这个接口我们应该非常熟悉了, 它提供了众多不同数据类型变动的方法, 供 DataChangedEventDispatcher 调用, 这个类更是一个 "老朋友" 了, 作为一个中转站, 辛勤的**处理数据同步的事件分类及分发** - -```java -public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { - // 持有 DataChangedListener 集合 - private List listeners; - - // 事件变动时, 通知 DataChangedListener 的不同事件类型的方法 - public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - case APP_AUTH: - listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); - break; - case PLUGIN: - listener.onPluginChanged((List) event.getSource(), event.getEventType()); - break; - case RULE: - listener.onRuleChanged((List) event.getSource(), event.getEventType()); - break; - case SELECTOR: - listener.onSelectorChanged((List) event.getSource(), event.getEventType()); - break; - case META_DATA: - listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); - break; - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } - } -} -``` - -```java -public interface DataChangedListener { - - default void onAppAuthChanged(List changed, DataEventTypeEnum eventType) {} - - default void onPluginChanged(List changed, DataEventTypeEnum eventType) {} - - default void onSelectorChanged(List changed, DataEventTypeEnum eventType) {} - - default void onMetaDataChanged(List changed, DataEventTypeEnum eventType) {} - - default void onRuleChanged(List changed, DataEventTypeEnum eventType) {} -} -``` - -这两个的作用了解了, 那 AbstractDataChangedListener 又做了什么事情? 举个 onPluginChanged() 的例子: - -```java -public abstract class AbstractDataChangedListener implements DataChangedListener, InitializingBean { - - protected static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); - - @Override - public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) { - if (CollectionUtils.isEmpty(changed)) { - return; - } - this.updatePluginCache(); - this.afterPluginChanged(changed, eventType); - } - - // 修改缓存 (可重写) - protected void updatePluginCache() { - this.updateCache(ConfigGroupEnum.PLUGIN, pluginService.listAll()); - } - - protected void updateCache(final ConfigGroupEnum group, final List data) { - String json = GsonUtils.getInstance().toJson(data); - ConfigDataCache newVal = new ConfigDataCache(group.name(), json, Md5Utils.md5(json), System.currentTimeMillis()); - ConfigDataCache oldVal = CACHE.put(newVal.getGroup(), newVal); - log.info("update config cache[{}], old: {}, updated: {}", group, oldVal, newVal); - } - - // 钩子, 自定义结束数据变动后要干什么 (可重写) - protected void afterPluginChanged(final List changed, final DataEventTypeEnum eventType) { - } -} -``` - -对于一个插件数据变动方法 (onPluginChanged), 其实 AbstractDataChangedListener 就是定义了一个模板, 让子类可以按照指定步骤进行工作, 具体每个步骤的工作细节可以由子类自己实现. - -其次, 如果不重写它的缓存更新, 就由这个类在 CACHE 中维护. - -### 其他同步策略此时在干什么? - -在 DataChangedEventDispatcher 调取 onPluginChanged() 之后, 长轮询模块会怎么实现呢? **不妨先想想其他同步方式在此时做了什么** - -举例 websocket 模式, 它自己重写了 onPluginChanged(), 发送 websocket 信息给持有会话, 其中就有网关. - -```java -public class WebsocketDataChangedListener implements DataChangedListener { - - @Override - public void onPluginChanged(final List pluginDataList, final DataEventTypeEnum eventType) { - WebsocketData websocketData = - new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); - } -} -``` - -再看 zookeeper 模式, 它也重写了 onPluginChanged(), 去修改 zookeeper 上的节点信息, 这样网关端会监听到他们的节点变动. - -```java -public class ZookeeperDataChangedListener implements DataChangedListener { - - @Override - public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) { - for (PluginData data : changed) { - String pluginPath = ZkPathConstants.buildPluginPath(data.getName()); - // delete - if (eventType == DataEventTypeEnum.DELETE) { - deleteZkPathRecursive(pluginPath); - String selectorParentPath = ZkPathConstants.buildSelectorParentPath(data.getName()); - deleteZkPathRecursive(selectorParentPath); - String ruleParentPath = ZkPathConstants.buildRuleParentPath(data.getName()); - deleteZkPathRecursive(ruleParentPath); - continue; - } - //create or update - insertZkNode(pluginPath, data); - } - } -} -``` - -可以知道, 到这个节骨眼, 其他同步策略已经在忙着通知网关了, 那 Http 长轮询也肯定要做这事. - -这两个策略的通知方式也不同, websocket 是好人做到底, 直接找到 session 会话把信息亲自送过去. zookeeper 将节点信息改变后撒手不管, 网关自己监听到变更再做的同步. - -那么我们的 Http 长轮询现在要以何种方式去通知网关呢? 接着看. - -### 长轮询实现方式思考 - -先思考下我自己设计长轮询, 会怎么实现 ? - -正常的长轮询实现应该由网关主动请求, 后台接住这个请求并 hold 住, 如果有更新就直接返回, 没有就阻塞一定时间. 而后台则是做好数据的更新, hold 住时检查数据是否有变化. - -那这里涉及到三个点: - -1. 数据怎样知道是有变化的, 是不是设置个最后更新时间, 与网关的请求时间比较, 得出是否有数据修改? -2. hold 住之后, 后台怎么获知是否数据更新, 反复遍历还是阻塞等待? -3. 那些用于更新的数据放哪里, 用缓存的话, 考虑后台缓存与数据库的交互是怎样的. - -### HttpLongPollingDataChangedListener 长轮询实现 - -围绕我们的思考, 看看 HttpLongPollingDataChangedListener 是如何实现的. 先看看关于父类 onPluginChanged() 这块的实现 - -```java -public class HttpLongPollingDataChangedListener extends AbstractDataChangedListener { - - private final ScheduledExecutorService scheduler; - - @Override - protected void afterPluginChanged(final List changed, final DataEventTypeEnum eventType) { - scheduler.execute(new DataChangeTask(ConfigGroupEnum.PLUGIN)); - } -} -``` - -Http 长轮询不会直接覆盖 onPluginChanged() 而是直接使用其父类的, 意味着使用了它的 CACHE, 那最终我们的信息获取肯定也少不了分析这个, 先暂放一边. - -接下来的逻辑会调用到我们这块实现的 afterPluginChanged() 方法, 这里用了一个定时类型的线程池, 去跑一个 Runnable 类型的任务 DataChangeTask. - -```java -class DataChangeTask implements Runnable { - - @Override - public void run() { - // 遍历 clients - for (Iterator iter = clients.iterator(); iter.hasNext();) { - LongPollingClient client = iter.next(); - iter.remove(); - // 说明完成 response 响应了 - client.sendResponse(Collections.singletonList(groupKey)); - log.info("send response with the changed group,ip={}, group={}, changeTime={}", client.ip, groupKey, changeTime); - } - } -} -``` - -数据变动后使用线程池调到了这个方法, 拿取所有 `clients` , 一边遍历一边剔除元素, 且调用方法 sendResponse(), 像是标记已完成了响应. - -我来猜测下它干了什么, 这里的 `clients` 很有可能就是网关被 hold 住的请求, 而 sendResponse() 则很有可能就是真的给请求上下文加了响应信息. 还有一个关键动作就是结束 hold, 让网关接收到响应信息, 并在集合中剔除这个请求. - -我们现在追踪下 `client` 的产生, 它是 HttpLongPollingDataChangedListener 里的一个 BlockingQueue 阻塞队列, 在 LongPollingClient 中被定时检测 - -```java -class LongPollingClient implements Runnable { - - @Override - public void run() { - this.asyncTimeoutFuture = scheduler.schedule(() -> { - clients.remove(LongPollingClient.this); - List changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); - sendResponse(changedGroups); - }, timeoutTime, TimeUnit.MILLISECONDS); - // 这里是关键, 表明来源 - clients.add(this); - } -} -``` - -先不去分析这个 remove() 的检测代码块, 直接看到最后一句的 add(), 这里就是 `clients` 数据来源. - -找到 LongPollingClient 被调用处, HttpLongPollingDataChangedListener#doLongPolling - -```java -public void doLongPolling(final HttpServletRequest request, final HttpServletResponse response) { - - // ... - - // listen for configuration changed. - // 开启同步阻塞请求 - final AsyncContext asyncContext = request.startAsync(); - - // AsyncContext.settimeout() does not timeout properly, so you have to control it yourself - asyncContext.setTimeout(0L); - - // block client's thread. - // 线程池调用 LongPollingClient#run - scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); -} -``` - -这里的最后一句会调用并添加 `client`, 这里有行关键代码阻塞住了请求: - -```java -final AsyncContext asyncContext = request.startAsync(); -``` - -而在 LongPollingClient#sendResponse 中, 刚刚也分析了, 除了包装注入响应信息, 还会将 hold 住的请求释放 - -```java -class LongPollingClient implements Runnable { - - void sendResponse(final List changedGroups) { - // cancel scheduler - if (null != asyncTimeoutFuture) { - asyncTimeoutFuture.cancel(false); - } - generateResponse((HttpServletResponse) asyncContext.getResponse(), changedGroups); - // 同步完成结束阻塞 - asyncContext.complete(); - } -} -``` - -这块分析完了再回到 doLongPolling(), 其中线程池调用这还有个关键点 - -```java -scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); -``` - -这里给 LongPollingClient 传入了 60S 的 timeout 时间, 做什么用的呢? 还记得我们在 LongPollingClient#run 时略过的一块代码吗 - -```java -class LongPollingClient implements Runnable { - - @Override - public void run() { - // 定时启动, 延迟时间根据 timeoutTime - this.asyncTimeoutFuture = scheduler.schedule(() -> { - // 移除管理的连接 - clients.remove(LongPollingClient.this); - List changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); - // 这个方法会将阻塞的请求释放 - sendResponse(changedGroups); - }, timeoutTime, TimeUnit.MILLISECONDS); - - clients.add(this); - } -} -``` - -这里我们已经搞懂了后台这块对长轮询流程的实现, 最后再看看 doLongPolling() 是怎么被调用到的, 找到调用类 ConfigController - -```java -@ConditionalOnBean(HttpLongPollingDataChangedListener.class) -@RestController -@RequestMapping("/configs") -@Slf4j -public class ConfigController { - - @PostMapping(value = "/listener") - public void listener(final HttpServletRequest request, final HttpServletResponse response) { - longPollingListener.doLongPolling(request, response); - } -} -``` - -看到这也基本明了, 后台通过这个 Controller 暴露 http 路径供网关调用并监听数据变化. - -### 总结 - -- 后台通过 Controller 层暴露 API 给网关, 网关请求后台时后台并不是立即返回响应 (数据有无变化), 而是 hold 住请求最大 60 秒的时间. 这些被 hold 住的请求会加入到阻塞队列中作为内存缓存. -- 这 60 秒钟如果有数据变化, 通过 DataChangedEventDispatcher 分发到我们的 HttpLongPollingDataChangedListener , 则 **立即调用线程池** 在阻塞队列中遍历所有被 hold 住的请求, 塞入响应信息并释放掉. -- 如果 60 秒过后依然没有数据变化, hold 住的请求会被释放, 且阻塞队列的对应请求对象被剔除. - -到这里, 我们已经理清它最最基本的长轮询逻辑, 那么对应下一开始的思考, 看有什么结论 or 疑惑. - -> 1. 数据怎样知道是有变化的, 是不是设置个最后更新时间, 与网关的请求时间比较, 得出是否有数据修改? -> 2. hold 住之后, 后台怎么获知是否数据更新, 反复遍历还是阻塞等待? -> 3. 那些用于更新的数据放哪里, 用缓存的话, 考虑后台缓存与数据库的交互是怎样的. - -针对第 1 点, 我们是如何得知数据有变化的呢? - -- 目前我们分析的数据变动来源是 DataChangedEventDispatcher, 它可不仅仅只会在数据变动时告知我们信息, 每次手动点下后台同步这里立马就来调用了. - - 那么这里肯定有新旧数据比对之类的东西了, 不然每次调用就直接把网关的阻塞请求放跑了, 这可不成, 白白的 IO 消耗肯定不是个好设计. - -针对第 2 点, 我们现在知道了模式是阻塞等待, 利用的是 `AsyncContext` 这种方式, 这块我也没有了解过, 会出个番外讨论一二. - -针对第 3 点, 我们知道后台配置肯定修改完是要落盘到数据库的, 所以这块缓存与数据库的交互也是个值得分析的点. 这些疑问我会在下一章继续分析~ +--- +title: Soul网关学习Http长轮询解析01 +author: 朱明 +date: 2021-01-25 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +## 后台与网关数据同步 (Http 长轮询篇) + +### 配置 + +**后台信息模式切换** + +在上篇分析 Zookeeper 同步的文章 ([Soul 网关源码分析-11 期](https://blog.csdn.net/zm469568595/article/details/113065463)) 中, 我们通过 DataSyncConfiguration 这个配置类做的切换, 这次有了经验, 直接贴配置 + +```yml +soul: + sync: + websocket: + enabled: false + http: + enabled: true +``` + +**网关信息模式切换** + +后台模式切换完成, 接下来就是网关, 继续照葫芦画瓢找到关键配置类上的参数设置. 这里也直接贴网关配置 + +```yml +soul: + sync: +# websocket: +# urls: ws://localhost:9095/websocket + http: + url: http://localhost:9095 +``` + +### DataChangedListener 体系 + +后台数据初始化 DataSyncConfiguration 配置关键 Bean , 看看这里关于 Http 长轮询的 Bean + +```java +@Configuration +public class DataSyncConfiguration { + + @Configuration + @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true") + @EnableConfigurationProperties(HttpSyncProperties.class) + static class HttpLongPollingListener { + + @Bean + @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class) + public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) { + return new HttpLongPollingDataChangedListener(httpSyncProperties); + } + } +} +``` + +HttpLongPollingDataChangedListener 继承自 AbstractDataChangedListener, 他们都实现自接口 DataChangedListener. + +DataChangedListener 这个接口我们应该非常熟悉了, 它提供了众多不同数据类型变动的方法, 供 DataChangedEventDispatcher 调用, 这个类更是一个 "老朋友" 了, 作为一个中转站, 辛勤的**处理数据同步的事件分类及分发** + +```java +public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { + // 持有 DataChangedListener 集合 + private List listeners; + + // 事件变动时, 通知 DataChangedListener 的不同事件类型的方法 + public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + case APP_AUTH: + listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); + break; + case PLUGIN: + listener.onPluginChanged((List) event.getSource(), event.getEventType()); + break; + case RULE: + listener.onRuleChanged((List) event.getSource(), event.getEventType()); + break; + case SELECTOR: + listener.onSelectorChanged((List) event.getSource(), event.getEventType()); + break; + case META_DATA: + listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); + break; + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } + } +} +``` + +```java +public interface DataChangedListener { + + default void onAppAuthChanged(List changed, DataEventTypeEnum eventType) {} + + default void onPluginChanged(List changed, DataEventTypeEnum eventType) {} + + default void onSelectorChanged(List changed, DataEventTypeEnum eventType) {} + + default void onMetaDataChanged(List changed, DataEventTypeEnum eventType) {} + + default void onRuleChanged(List changed, DataEventTypeEnum eventType) {} +} +``` + +这两个的作用了解了, 那 AbstractDataChangedListener 又做了什么事情? 举个 onPluginChanged() 的例子: + +```java +public abstract class AbstractDataChangedListener implements DataChangedListener, InitializingBean { + + protected static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); + + @Override + public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) { + if (CollectionUtils.isEmpty(changed)) { + return; + } + this.updatePluginCache(); + this.afterPluginChanged(changed, eventType); + } + + // 修改缓存 (可重写) + protected void updatePluginCache() { + this.updateCache(ConfigGroupEnum.PLUGIN, pluginService.listAll()); + } + + protected void updateCache(final ConfigGroupEnum group, final List data) { + String json = GsonUtils.getInstance().toJson(data); + ConfigDataCache newVal = new ConfigDataCache(group.name(), json, Md5Utils.md5(json), System.currentTimeMillis()); + ConfigDataCache oldVal = CACHE.put(newVal.getGroup(), newVal); + log.info("update config cache[{}], old: {}, updated: {}", group, oldVal, newVal); + } + + // 钩子, 自定义结束数据变动后要干什么 (可重写) + protected void afterPluginChanged(final List changed, final DataEventTypeEnum eventType) { + } +} +``` + +对于一个插件数据变动方法 (onPluginChanged), 其实 AbstractDataChangedListener 就是定义了一个模板, 让子类可以按照指定步骤进行工作, 具体每个步骤的工作细节可以由子类自己实现. + +其次, 如果不重写它的缓存更新, 就由这个类在 CACHE 中维护. + +### 其他同步策略此时在干什么? + +在 DataChangedEventDispatcher 调取 onPluginChanged() 之后, 长轮询模块会怎么实现呢? **不妨先想想其他同步方式在此时做了什么** + +举例 websocket 模式, 它自己重写了 onPluginChanged(), 发送 websocket 信息给持有会话, 其中就有网关. + +```java +public class WebsocketDataChangedListener implements DataChangedListener { + + @Override + public void onPluginChanged(final List pluginDataList, final DataEventTypeEnum eventType) { + WebsocketData websocketData = + new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); + } +} +``` + +再看 zookeeper 模式, 它也重写了 onPluginChanged(), 去修改 zookeeper 上的节点信息, 这样网关端会监听到他们的节点变动. + +```java +public class ZookeeperDataChangedListener implements DataChangedListener { + + @Override + public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) { + for (PluginData data : changed) { + String pluginPath = ZkPathConstants.buildPluginPath(data.getName()); + // delete + if (eventType == DataEventTypeEnum.DELETE) { + deleteZkPathRecursive(pluginPath); + String selectorParentPath = ZkPathConstants.buildSelectorParentPath(data.getName()); + deleteZkPathRecursive(selectorParentPath); + String ruleParentPath = ZkPathConstants.buildRuleParentPath(data.getName()); + deleteZkPathRecursive(ruleParentPath); + continue; + } + //create or update + insertZkNode(pluginPath, data); + } + } +} +``` + +可以知道, 到这个节骨眼, 其他同步策略已经在忙着通知网关了, 那 Http 长轮询也肯定要做这事. + +这两个策略的通知方式也不同, websocket 是好人做到底, 直接找到 session 会话把信息亲自送过去. zookeeper 将节点信息改变后撒手不管, 网关自己监听到变更再做的同步. + +那么我们的 Http 长轮询现在要以何种方式去通知网关呢? 接着看. + +### 长轮询实现方式思考 + +先思考下我自己设计长轮询, 会怎么实现 ? + +正常的长轮询实现应该由网关主动请求, 后台接住这个请求并 hold 住, 如果有更新就直接返回, 没有就阻塞一定时间. 而后台则是做好数据的更新, hold 住时检查数据是否有变化. + +那这里涉及到三个点: + +1. 数据怎样知道是有变化的, 是不是设置个最后更新时间, 与网关的请求时间比较, 得出是否有数据修改? +2. hold 住之后, 后台怎么获知是否数据更新, 反复遍历还是阻塞等待? +3. 那些用于更新的数据放哪里, 用缓存的话, 考虑后台缓存与数据库的交互是怎样的. + +### HttpLongPollingDataChangedListener 长轮询实现 + +围绕我们的思考, 看看 HttpLongPollingDataChangedListener 是如何实现的. 先看看关于父类 onPluginChanged() 这块的实现 + +```java +public class HttpLongPollingDataChangedListener extends AbstractDataChangedListener { + + private final ScheduledExecutorService scheduler; + + @Override + protected void afterPluginChanged(final List changed, final DataEventTypeEnum eventType) { + scheduler.execute(new DataChangeTask(ConfigGroupEnum.PLUGIN)); + } +} +``` + +Http 长轮询不会直接覆盖 onPluginChanged() 而是直接使用其父类的, 意味着使用了它的 CACHE, 那最终我们的信息获取肯定也少不了分析这个, 先暂放一边. + +接下来的逻辑会调用到我们这块实现的 afterPluginChanged() 方法, 这里用了一个定时类型的线程池, 去跑一个 Runnable 类型的任务 DataChangeTask. + +```java +class DataChangeTask implements Runnable { + + @Override + public void run() { + // 遍历 clients + for (Iterator iter = clients.iterator(); iter.hasNext();) { + LongPollingClient client = iter.next(); + iter.remove(); + // 说明完成 response 响应了 + client.sendResponse(Collections.singletonList(groupKey)); + log.info("send response with the changed group,ip={}, group={}, changeTime={}", client.ip, groupKey, changeTime); + } + } +} +``` + +数据变动后使用线程池调到了这个方法, 拿取所有 `clients` , 一边遍历一边剔除元素, 且调用方法 sendResponse(), 像是标记已完成了响应. + +我来猜测下它干了什么, 这里的 `clients` 很有可能就是网关被 hold 住的请求, 而 sendResponse() 则很有可能就是真的给请求上下文加了响应信息. 还有一个关键动作就是结束 hold, 让网关接收到响应信息, 并在集合中剔除这个请求. + +我们现在追踪下 `client` 的产生, 它是 HttpLongPollingDataChangedListener 里的一个 BlockingQueue 阻塞队列, 在 LongPollingClient 中被定时检测 + +```java +class LongPollingClient implements Runnable { + + @Override + public void run() { + this.asyncTimeoutFuture = scheduler.schedule(() -> { + clients.remove(LongPollingClient.this); + List changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); + sendResponse(changedGroups); + }, timeoutTime, TimeUnit.MILLISECONDS); + // 这里是关键, 表明来源 + clients.add(this); + } +} +``` + +先不去分析这个 remove() 的检测代码块, 直接看到最后一句的 add(), 这里就是 `clients` 数据来源. + +找到 LongPollingClient 被调用处, HttpLongPollingDataChangedListener#doLongPolling + +```java +public void doLongPolling(final HttpServletRequest request, final HttpServletResponse response) { + + // ... + + // listen for configuration changed. + // 开启同步阻塞请求 + final AsyncContext asyncContext = request.startAsync(); + + // AsyncContext.settimeout() does not timeout properly, so you have to control it yourself + asyncContext.setTimeout(0L); + + // block client's thread. + // 线程池调用 LongPollingClient#run + scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); +} +``` + +这里的最后一句会调用并添加 `client`, 这里有行关键代码阻塞住了请求: + +```java +final AsyncContext asyncContext = request.startAsync(); +``` + +而在 LongPollingClient#sendResponse 中, 刚刚也分析了, 除了包装注入响应信息, 还会将 hold 住的请求释放 + +```java +class LongPollingClient implements Runnable { + + void sendResponse(final List changedGroups) { + // cancel scheduler + if (null != asyncTimeoutFuture) { + asyncTimeoutFuture.cancel(false); + } + generateResponse((HttpServletResponse) asyncContext.getResponse(), changedGroups); + // 同步完成结束阻塞 + asyncContext.complete(); + } +} +``` + +这块分析完了再回到 doLongPolling(), 其中线程池调用这还有个关键点 + +```java +scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); +``` + +这里给 LongPollingClient 传入了 60S 的 timeout 时间, 做什么用的呢? 还记得我们在 LongPollingClient#run 时略过的一块代码吗 + +```java +class LongPollingClient implements Runnable { + + @Override + public void run() { + // 定时启动, 延迟时间根据 timeoutTime + this.asyncTimeoutFuture = scheduler.schedule(() -> { + // 移除管理的连接 + clients.remove(LongPollingClient.this); + List changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); + // 这个方法会将阻塞的请求释放 + sendResponse(changedGroups); + }, timeoutTime, TimeUnit.MILLISECONDS); + + clients.add(this); + } +} +``` + +这里我们已经搞懂了后台这块对长轮询流程的实现, 最后再看看 doLongPolling() 是怎么被调用到的, 找到调用类 ConfigController + +```java +@ConditionalOnBean(HttpLongPollingDataChangedListener.class) +@RestController +@RequestMapping("/configs") +@Slf4j +public class ConfigController { + + @PostMapping(value = "/listener") + public void listener(final HttpServletRequest request, final HttpServletResponse response) { + longPollingListener.doLongPolling(request, response); + } +} +``` + +看到这也基本明了, 后台通过这个 Controller 暴露 http 路径供网关调用并监听数据变化. + +### 总结 + +- 后台通过 Controller 层暴露 API 给网关, 网关请求后台时后台并不是立即返回响应 (数据有无变化), 而是 hold 住请求最大 60 秒的时间. 这些被 hold 住的请求会加入到阻塞队列中作为内存缓存. +- 这 60 秒钟如果有数据变化, 通过 DataChangedEventDispatcher 分发到我们的 HttpLongPollingDataChangedListener , 则 **立即调用线程池** 在阻塞队列中遍历所有被 hold 住的请求, 塞入响应信息并释放掉. +- 如果 60 秒过后依然没有数据变化, hold 住的请求会被释放, 且阻塞队列的对应请求对象被剔除. + +到这里, 我们已经理清它最最基本的长轮询逻辑, 那么对应下一开始的思考, 看有什么结论 or 疑惑. + +> 1. 数据怎样知道是有变化的, 是不是设置个最后更新时间, 与网关的请求时间比较, 得出是否有数据修改? +> 2. hold 住之后, 后台怎么获知是否数据更新, 反复遍历还是阻塞等待? +> 3. 那些用于更新的数据放哪里, 用缓存的话, 考虑后台缓存与数据库的交互是怎样的. + +针对第 1 点, 我们是如何得知数据有变化的呢? + +- 目前我们分析的数据变动来源是 DataChangedEventDispatcher, 它可不仅仅只会在数据变动时告知我们信息, 每次手动点下后台同步这里立马就来调用了. + + 那么这里肯定有新旧数据比对之类的东西了, 不然每次调用就直接把网关的阻塞请求放跑了, 这可不成, 白白的 IO 消耗肯定不是个好设计. + +针对第 2 点, 我们现在知道了模式是阻塞等待, 利用的是 `AsyncContext` 这种方式, 这块我也没有了解过, 会出个番外讨论一二. + +针对第 3 点, 我们知道后台配置肯定修改完是要落盘到数据库的, 所以这块缓存与数据库的交互也是个值得分析的点. 这些疑问我会在下一章继续分析~ diff --git a/src/zh/blog/soul_source_learning_09_httplongpolling_02.md b/src/zh/blog/soul_source_learning_09_httplongpolling_02.md index d6355dc406..f6b005005e 100644 --- a/src/zh/blog/soul_source_learning_09_httplongpolling_02.md +++ b/src/zh/blog/soul_source_learning_09_httplongpolling_02.md @@ -1,265 +1,265 @@ ---- -title: Soul网关学习Http长轮询解析02 -author: 朱明 -date: 2021-01-27 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -## 后台与网关数据同步 (Http 长轮询篇) - -长轮询分析的最后一篇, 总结网关端的长轮询的实现, 以及数据流动方式. - -网关端长轮询的流程总体也分两个模块: 一是启动时拉取, 二是轮询监听变化 - -## 网关启动时拉取数据 - -网关启动后, 会调用后台提供的接口拉取数据, 并将数据发送到各个插件的数据处理类中 - -下面展示下网关启动拉取数据的处理流程: -![01](/assets/img/blog1/01.png) - -这几个处理步骤被分散到下面这些类的方法协作中: - -![02](/assets/img/blog1/02.png) - -HttpSyncDataService#start: 网关启动时, HttpSyncDataService 初始化会调用 `start()` 方法, 该方法会调用后台拉取数据, 并开启多个线程进行轮询监听 (这块在下个模块分析) - -```java -public class HttpSyncDataService implements SyncDataService, AutoCloseable { - - private void start() { - // 防止二次调用的CAS操作 - if (RUNNING.compareAndSet(false, true)) { - // 这里是本次流程的重点, 调用拉取数据的方法 - this.fetchGroupConfig(ConfigGroupEnum.values()); - int threadSize = serverList.size(); - // 这里将在下个模块分析, 会根据后台集群开启线程轮询监听 - this.executor = new ThreadPoolExecutor(threadSize, threadSize, 60L, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), - SoulThreadFactory.create("http-long-polling", true)); - this.serverList.forEach(server -> this.executor.execute(new HttpLongPollingTask(server))); - } else { - log.info("soul http long polling was started, executor=[{}]", executor); - } - } -} -``` - -HttpSyncDataService#fetchGroupConfig: 作用仅是根据数据类型, 循环多次调用拉取数据方法(针对同一个后台会请求多次, 每次拉取某一种数据类型的信息), 这里的数据类型指的是 plugin、rule、selector 等 - -```java -private void fetchGroupConfig(final ConfigGroupEnum... groups) throws SoulException { - for (int index = 0; index < this.serverList.size(); index++) { - String server = serverList.get(index); - try { - // 根据传入的数据类型枚举, 多次调用拉取数据方法 - this.doFetchGroupConfig(server, groups); - break; - } catch (SoulException e) { - if (index >= serverList.size() - 1) { - throw e; - } - log.warn("fetch config fail, try another one: {}", serverList.get(index + 1)); - } - } -} -``` - -HttpSyncDataService#doFetchGroupConfig: 请求后台的 `/configs/fetch` 接口, 拿到某个类型的数据, 并更新缓存. 更新缓存前会检测是否变动, 如果变动则结束, **数据未发生变动则睡眠 30s** (由于是第一次启动, 数据为空的情况下肯定会更新缓存, 所以会直接结束) - -```java -private void doFetchGroupConfig(final String server, final ConfigGroupEnum... groups) { - StringBuilder params = new StringBuilder(); - for (ConfigGroupEnum groupKey : groups) { - params.append("groupKeys").append("=").append(groupKey.name()).append("&"); - } - // 具体请求路径, 拉取后台数据 - String url = server + "/configs/fetch?" + StringUtils.removeEnd(params.toString(), "&"); - log.info("request configs: [{}]", url); - String json = null; - try { - json = this.httpClient.getForObject(url, String.class); - } catch (RestClientException e) { - String message = String.format("fetch config fail from server[%s], %s", url, e.getMessage()); - log.warn(message); - throw new SoulException(message, e); - } - // 修改缓存信息 - boolean updated = this.updateCacheWithJson(json); - // 判断是否修改, 修改则直接结束 - if (updated) { - log.info("get latest configs: [{}]", json); - return; - } - log.info("The config of the server[{}] has not been updated or is out of date. Wait for 30s to listen for changes again.", server); - ThreadUtils.sleep(TimeUnit.SECONDS, 30); -} -``` - -HttpSyncDataService#updateCacheWithJson: 取出响应信息中的 `data` , 即变化的数据信息, 传给数据刷新工厂 DataRefreshFactory - -```java -private DataRefreshFactory factory; - -public HttpSyncDataService(...){ - this.factory = new DataRefreshFactory(pluginDataSubscriber, metaDataSubscribers, authDataSubscribers); -} - -private boolean updateCacheWithJson(final String json) { - JsonObject jsonObject = GSON.fromJson(json, JsonObject.class); - JsonObject data = jsonObject.getAsJsonObject("data"); - return factory.executor(data); -} -``` - -DataRefreshFactory#executor: 将数据发送给各类数据刷新类 (这里没有去区别信息类型, 而是通知所有数据刷新类, 可考虑优化) - -```java -public final class DataRefreshFactory { - - private static final EnumMap ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class); - - public DataRefreshFactory(final PluginDataSubscriber pluginDataSubscriber, - final List metaDataSubscribers, - final List authDataSubscribers) { - // 注入各类型订阅器到 MAP 中 - ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataRefresh(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataRefresh(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataRefresh(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AppAuthDataRefresh(authDataSubscribers)); - ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataRefresh(metaDataSubscribers)); - } - - public boolean executor(final JsonObject data) { - final boolean[] success = {false}; - // Tureen: 所有数据类型的 DataRefresh 全调用 - ENUM_MAP.values().parallelStream().forEach(dataRefresh -> success[0] = dataRefresh.refresh(data)); - return success[0]; - } -} -``` - -AbstractDataRefresh#refresh: 判断是否要更新缓存, 若更新则调用各类型的 `refresh()` 方法 - -```java -@Override -public Boolean refresh(final JsonObject data) { - boolean updated = false; - JsonObject jsonObject = convert(data); - if (null != jsonObject) { - ConfigData result = fromJson(jsonObject); - if (this.updateCacheIfNeed(result)) { - updated = true; - // Turren: 调用 refresh - refresh(result.getData()); - } - } - return updated; -} -``` - -PluginDataRefresh#refresh: 调用 plugin 的订阅器, 接下来会通知所有扩展插件的相关事件变动 - -```java -@Override -protected void refresh(final List data) { - if (CollectionUtils.isEmpty(data)) { - log.info("clear all plugin data cache"); - pluginDataSubscriber.refreshPluginDataAll(); - } else { - pluginDataSubscriber.refreshPluginDataAll(); - // Turren: http同步, 调用插件数据订阅器 - data.forEach(pluginDataSubscriber::onSubscribe); - } -} -``` - -## 网关轮询监听变化 - -网关启动时, 同时也开启了线程做后台监听请求, 监听请求做了 while 死循环来轮询, 在后台端会劫持住请求, 这块在后台总结中有具体分析 ([后台与网关数据同步 (Http 长轮询篇 <二>)](https://blog.csdn.net/zm469568595/article/details/113207367)) - -下面展示下网关监听数据变动的整体流程: - -![03](/assets/img/blog1/03.png) - -对应的实际代码实现如下: - -![04](/assets/img/blog1/04.png) - -**网关端监听的流程实现都在 HttpSyncDataService 类中, 在最后会经由 `doFetchGroupConfig()` 传到到各类订阅器, 后面的流程与启动时的一致** - -HttpSyncDataService#start: 启动线程执行 HttpLongPollingTask 这个 Runnable - -HttpLongPollingTask#run: 开启循环调用轮询方法. - -```java -@Override -public void run() { - while (RUNNING.get()) { - for (int time = 1; time <= retryTimes; time++) { - try { - doLongPolling(server); - } catch (Exception e) { - if (time < retryTimes) { - log.warn("Long polling failed, tried {} times, {} times left, will be suspended for a while! {}", - time, retryTimes - time, e.getMessage()); - ThreadUtils.sleep(TimeUnit.SECONDS, 5); - continue; - } - log.error("Long polling failed, try again after 5 minutes!", e); - ThreadUtils.sleep(TimeUnit.MINUTES, 5); - } - } - } -} -``` - -HttpLongPollingTask#doLongPolling: 得到监听请求的响应结果, 如果返回值中有变化的类型, 则调用数据拉取方法. - -```java -private void doLongPolling(final String server) { - // 从缓存中获取数据 - MultiValueMap params = new LinkedMultiValueMap<>(8); - for (ConfigGroupEnum group : ConfigGroupEnum.values()) { - ConfigData cacheConfig = factory.cacheConfigData(group); - String value = String.join(",", cacheConfig.getMd5(), String.valueOf(cacheConfig.getLastModifyTime())); - params.put(group.name(), Lists.newArrayList(value)); - } - // 构建 http 请求信息 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - HttpEntity httpEntity = new HttpEntity(params, headers); - String listenerUrl = server + "/configs/listener"; - log.debug("request listener configs: [{}]", listenerUrl); - JsonArray groupJson = null; - try { - String json = this.httpClient.postForEntity(listenerUrl, httpEntity, String.class).getBody(); - groupJson = GSON.fromJson(json, JsonObject.class).getAsJsonArray("data"); - } catch (RestClientException e) { - String message = String.format("listener configs fail, server:[%s], %s", server, e.getMessage()); - throw new SoulException(message, e); - } - // 得到变化的类型 - if (groupJson != null) { - ConfigGroupEnum[] changedGroups = GSON.fromJson(groupJson, ConfigGroupEnum[].class); - if (ArrayUtils.isNotEmpty(changedGroups)) { - log.info("Group config changed: {}", Arrays.toString(changedGroups)); - // 拉取后台对应类型的数据 - this.doFetchGroupConfig(server, changedGroups); - } - } -} -``` - -LongPollingClient#doFetchGroupConfig: - -之前的启动里分析了这块的代码, 它与启动里最不同的点是, **如果拉取的数据与缓存比对后, 发现没有变化则睡眠 30s, 会导致下次的监听延后 30s**. - -什么意思呢? 如果网关去 `fetch` 后台的数据, 拿回来比对后发现被骗了! 啥变化也没有, 就等 30s 再启动下次监听, 这个期间如果后台发生数据变化肯定是没法通知到网关的. - -网关为什么这么做? 自然是为了防止大量的循环的无用拉取, 如果后台出现问题不断的通知数据变动, 但实际没有任何变动, 那么网关不延迟就会与后台产生大量无用的 网络 IO 与 数据交换 +--- +title: Soul网关学习Http长轮询解析02 +author: 朱明 +date: 2021-01-27 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +## 后台与网关数据同步 (Http 长轮询篇) + +长轮询分析的最后一篇, 总结网关端的长轮询的实现, 以及数据流动方式. + +网关端长轮询的流程总体也分两个模块: 一是启动时拉取, 二是轮询监听变化 + +## 网关启动时拉取数据 + +网关启动后, 会调用后台提供的接口拉取数据, 并将数据发送到各个插件的数据处理类中 + +下面展示下网关启动拉取数据的处理流程: +![01](/assets/img/blog1/01.png) + +这几个处理步骤被分散到下面这些类的方法协作中: + +![02](/assets/img/blog1/02.png) + +HttpSyncDataService#start: 网关启动时, HttpSyncDataService 初始化会调用 `start()` 方法, 该方法会调用后台拉取数据, 并开启多个线程进行轮询监听 (这块在下个模块分析) + +```java +public class HttpSyncDataService implements SyncDataService, AutoCloseable { + + private void start() { + // 防止二次调用的CAS操作 + if (RUNNING.compareAndSet(false, true)) { + // 这里是本次流程的重点, 调用拉取数据的方法 + this.fetchGroupConfig(ConfigGroupEnum.values()); + int threadSize = serverList.size(); + // 这里将在下个模块分析, 会根据后台集群开启线程轮询监听 + this.executor = new ThreadPoolExecutor(threadSize, threadSize, 60L, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + SoulThreadFactory.create("http-long-polling", true)); + this.serverList.forEach(server -> this.executor.execute(new HttpLongPollingTask(server))); + } else { + log.info("soul http long polling was started, executor=[{}]", executor); + } + } +} +``` + +HttpSyncDataService#fetchGroupConfig: 作用仅是根据数据类型, 循环多次调用拉取数据方法(针对同一个后台会请求多次, 每次拉取某一种数据类型的信息), 这里的数据类型指的是 plugin、rule、selector 等 + +```java +private void fetchGroupConfig(final ConfigGroupEnum... groups) throws SoulException { + for (int index = 0; index < this.serverList.size(); index++) { + String server = serverList.get(index); + try { + // 根据传入的数据类型枚举, 多次调用拉取数据方法 + this.doFetchGroupConfig(server, groups); + break; + } catch (SoulException e) { + if (index >= serverList.size() - 1) { + throw e; + } + log.warn("fetch config fail, try another one: {}", serverList.get(index + 1)); + } + } +} +``` + +HttpSyncDataService#doFetchGroupConfig: 请求后台的 `/configs/fetch` 接口, 拿到某个类型的数据, 并更新缓存. 更新缓存前会检测是否变动, 如果变动则结束, **数据未发生变动则睡眠 30s** (由于是第一次启动, 数据为空的情况下肯定会更新缓存, 所以会直接结束) + +```java +private void doFetchGroupConfig(final String server, final ConfigGroupEnum... groups) { + StringBuilder params = new StringBuilder(); + for (ConfigGroupEnum groupKey : groups) { + params.append("groupKeys").append("=").append(groupKey.name()).append("&"); + } + // 具体请求路径, 拉取后台数据 + String url = server + "/configs/fetch?" + StringUtils.removeEnd(params.toString(), "&"); + log.info("request configs: [{}]", url); + String json = null; + try { + json = this.httpClient.getForObject(url, String.class); + } catch (RestClientException e) { + String message = String.format("fetch config fail from server[%s], %s", url, e.getMessage()); + log.warn(message); + throw new SoulException(message, e); + } + // 修改缓存信息 + boolean updated = this.updateCacheWithJson(json); + // 判断是否修改, 修改则直接结束 + if (updated) { + log.info("get latest configs: [{}]", json); + return; + } + log.info("The config of the server[{}] has not been updated or is out of date. Wait for 30s to listen for changes again.", server); + ThreadUtils.sleep(TimeUnit.SECONDS, 30); +} +``` + +HttpSyncDataService#updateCacheWithJson: 取出响应信息中的 `data` , 即变化的数据信息, 传给数据刷新工厂 DataRefreshFactory + +```java +private DataRefreshFactory factory; + +public HttpSyncDataService(...){ + this.factory = new DataRefreshFactory(pluginDataSubscriber, metaDataSubscribers, authDataSubscribers); +} + +private boolean updateCacheWithJson(final String json) { + JsonObject jsonObject = GSON.fromJson(json, JsonObject.class); + JsonObject data = jsonObject.getAsJsonObject("data"); + return factory.executor(data); +} +``` + +DataRefreshFactory#executor: 将数据发送给各类数据刷新类 (这里没有去区别信息类型, 而是通知所有数据刷新类, 可考虑优化) + +```java +public final class DataRefreshFactory { + + private static final EnumMap ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class); + + public DataRefreshFactory(final PluginDataSubscriber pluginDataSubscriber, + final List metaDataSubscribers, + final List authDataSubscribers) { + // 注入各类型订阅器到 MAP 中 + ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataRefresh(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataRefresh(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataRefresh(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AppAuthDataRefresh(authDataSubscribers)); + ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataRefresh(metaDataSubscribers)); + } + + public boolean executor(final JsonObject data) { + final boolean[] success = {false}; + // Tureen: 所有数据类型的 DataRefresh 全调用 + ENUM_MAP.values().parallelStream().forEach(dataRefresh -> success[0] = dataRefresh.refresh(data)); + return success[0]; + } +} +``` + +AbstractDataRefresh#refresh: 判断是否要更新缓存, 若更新则调用各类型的 `refresh()` 方法 + +```java +@Override +public Boolean refresh(final JsonObject data) { + boolean updated = false; + JsonObject jsonObject = convert(data); + if (null != jsonObject) { + ConfigData result = fromJson(jsonObject); + if (this.updateCacheIfNeed(result)) { + updated = true; + // Turren: 调用 refresh + refresh(result.getData()); + } + } + return updated; +} +``` + +PluginDataRefresh#refresh: 调用 plugin 的订阅器, 接下来会通知所有扩展插件的相关事件变动 + +```java +@Override +protected void refresh(final List data) { + if (CollectionUtils.isEmpty(data)) { + log.info("clear all plugin data cache"); + pluginDataSubscriber.refreshPluginDataAll(); + } else { + pluginDataSubscriber.refreshPluginDataAll(); + // Turren: http同步, 调用插件数据订阅器 + data.forEach(pluginDataSubscriber::onSubscribe); + } +} +``` + +## 网关轮询监听变化 + +网关启动时, 同时也开启了线程做后台监听请求, 监听请求做了 while 死循环来轮询, 在后台端会劫持住请求, 这块在后台总结中有具体分析 ([后台与网关数据同步 (Http 长轮询篇 <二>)](https://blog.csdn.net/zm469568595/article/details/113207367)) + +下面展示下网关监听数据变动的整体流程: + +![03](/assets/img/blog1/03.png) + +对应的实际代码实现如下: + +![04](/assets/img/blog1/04.png) + +**网关端监听的流程实现都在 HttpSyncDataService 类中, 在最后会经由 `doFetchGroupConfig()` 传到到各类订阅器, 后面的流程与启动时的一致** + +HttpSyncDataService#start: 启动线程执行 HttpLongPollingTask 这个 Runnable + +HttpLongPollingTask#run: 开启循环调用轮询方法. + +```java +@Override +public void run() { + while (RUNNING.get()) { + for (int time = 1; time <= retryTimes; time++) { + try { + doLongPolling(server); + } catch (Exception e) { + if (time < retryTimes) { + log.warn("Long polling failed, tried {} times, {} times left, will be suspended for a while! {}", + time, retryTimes - time, e.getMessage()); + ThreadUtils.sleep(TimeUnit.SECONDS, 5); + continue; + } + log.error("Long polling failed, try again after 5 minutes!", e); + ThreadUtils.sleep(TimeUnit.MINUTES, 5); + } + } + } +} +``` + +HttpLongPollingTask#doLongPolling: 得到监听请求的响应结果, 如果返回值中有变化的类型, 则调用数据拉取方法. + +```java +private void doLongPolling(final String server) { + // 从缓存中获取数据 + MultiValueMap params = new LinkedMultiValueMap<>(8); + for (ConfigGroupEnum group : ConfigGroupEnum.values()) { + ConfigData cacheConfig = factory.cacheConfigData(group); + String value = String.join(",", cacheConfig.getMd5(), String.valueOf(cacheConfig.getLastModifyTime())); + params.put(group.name(), Lists.newArrayList(value)); + } + // 构建 http 请求信息 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + HttpEntity httpEntity = new HttpEntity(params, headers); + String listenerUrl = server + "/configs/listener"; + log.debug("request listener configs: [{}]", listenerUrl); + JsonArray groupJson = null; + try { + String json = this.httpClient.postForEntity(listenerUrl, httpEntity, String.class).getBody(); + groupJson = GSON.fromJson(json, JsonObject.class).getAsJsonArray("data"); + } catch (RestClientException e) { + String message = String.format("listener configs fail, server:[%s], %s", server, e.getMessage()); + throw new SoulException(message, e); + } + // 得到变化的类型 + if (groupJson != null) { + ConfigGroupEnum[] changedGroups = GSON.fromJson(groupJson, ConfigGroupEnum[].class); + if (ArrayUtils.isNotEmpty(changedGroups)) { + log.info("Group config changed: {}", Arrays.toString(changedGroups)); + // 拉取后台对应类型的数据 + this.doFetchGroupConfig(server, changedGroups); + } + } +} +``` + +LongPollingClient#doFetchGroupConfig: + +之前的启动里分析了这块的代码, 它与启动里最不同的点是, **如果拉取的数据与缓存比对后, 发现没有变化则睡眠 30s, 会导致下次的监听延后 30s**. + +什么意思呢? 如果网关去 `fetch` 后台的数据, 拿回来比对后发现被骗了! 啥变化也没有, 就等 30s 再启动下次监听, 这个期间如果后台发生数据变化肯定是没法通知到网关的. + +网关为什么这么做? 自然是为了防止大量的循环的无用拉取, 如果后台出现问题不断的通知数据变动, 但实际没有任何变动, 那么网关不延迟就会与后台产生大量无用的 网络 IO 与 数据交换 diff --git a/src/zh/blog/soul_source_learning_10_websocket.md b/src/zh/blog/soul_source_learning_10_websocket.md index 6179fe5a8c..4a81d45ce4 100644 --- a/src/zh/blog/soul_source_learning_10_websocket.md +++ b/src/zh/blog/soul_source_learning_10_websocket.md @@ -1,785 +1,785 @@ ---- -title: Soul网关学习WebSocket数据同步解析 -author: 范金鹏, 朱明 -date: 2021-01-22 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -> 范金鹏 - -# 1.前情回顾 - -在第 4 篇中,我们分析了 HTTP 用户业务系统接入 Soul 网关后,会调用 soul-admin 的注册接口,把需要网关代理的接口信息全部注册到 soul-admin 上,在最后,会通过 websocket 长连接,将 soul-admin 接收到的接口信息同步给 Soul 网关(即 soul-bootstrap),今天就来接着继续分析,数据是怎么同步到 soul-bootstrap 的。 - -不清楚流程的可以出门左转看下第 4 篇文章 [【Soul 源码阅读】4.HTTP 用户接入 Soul 调用 /soul-client/springmvc-register 接口逻辑分析](https://blog.csdn.net/hellboy0621/article/details/112727101) - -# 2.soul-admin 与 soul-bootstrap 数据同步 - -这里为了验证数据同步流程,其实也没必要非得启动业务系统,完全可以只启动 soul-admin 和 soul-bootstrap 两个系统即可,可以在页面打开或关闭插件,看看这个流程是怎么实现的。 - -数据同步策略官网链接 https://dromara.org/zh-cn/docs/soul/user-dataSync.html - -## 2.1 启动 2 个系统 - -都是按照项目默认启动的,无需修改任何配置文件。 - -## 2.2 页面操作查找接口 - -这里把 divide 插件启动,F12,看下前台会调用 soul-admin 哪个接口。 - -![open_divide_plugin](/assets/img/blog3/open_divide_plugin.png) - -可以看到前台向后台发送了一个 PUT 请求:http://localhost:9095/plugin/5 - -## 2.3 后台接口 - -在项目中搜索这个接口 - -```java -// PluginController.java -@RestController -@RequestMapping("/plugin") -public class PluginController { - -... - - /** - * update plugin. - * - * @param id primary key. - * @param pluginDTO plugin. - * @return {@linkplain SoulAdminResult} - */ - @PutMapping("/{id}") - public SoulAdminResult updatePlugin(@PathVariable("id") final String id, @RequestBody final PluginDTO pluginDTO) { - Objects.requireNonNull(pluginDTO); - pluginDTO.setId(id); - final String result = pluginService.createOrUpdate(pluginDTO); - if (StringUtils.isNoneBlank(result)) { - return SoulAdminResult.error(result); - } - return SoulAdminResult.success(SoulResultMessage.UPDATE_SUCCESS); - } - -... - -} -``` - -进到实现类里 - -```java -// PluginServiceImpl.java -/** - * create or update plugin. - * - * @param pluginDTO {@linkplain PluginDTO} - * @return rows - */ - @Override - @Transactional(rollbackFor = Exception.class) - public String createOrUpdate(final PluginDTO pluginDTO) { - final String msg = checkData(pluginDTO); - if (StringUtils.isNoneBlank(msg)) { - return msg; - } - PluginDO pluginDO = PluginDO.buildPluginDO(pluginDTO); - DataEventTypeEnum eventType = DataEventTypeEnum.CREATE; - if (StringUtils.isBlank(pluginDTO.getId())) { - pluginMapper.insertSelective(pluginDO); - } else { - eventType = DataEventTypeEnum.UPDATE; - pluginMapper.updateSelective(pluginDO); - } - - // publish change event. - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType, - Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO)))); - return StringUtils.EMPTY; - } -``` - -这里可以看出来,前半部分都是在操作数据库,把相关信息持久化;后半部分是发布了一个事件。 - -## 2.4 发布事件 - -这里发布的事件用 DataChangedEvent 封装了一层,再看里面有 1 个枚举,这里有很多种类型: - -```java -/** - * configuration group. - * - * @author huangxiaofeng - */ -public enum ConfigGroupEnum { - - APP_AUTH, - - PLUGIN, - - RULE, - - SELECTOR, - - META_DATA; - -... - -} -``` - -看到这几种类型,如果对第 4 篇还有印象的话,可以看出当时发送事件的类型就是 SELECTOR 和 RULE,现在是 PLUGIN,虽然类型不同,但不影响我们继续分析后面的逻辑,我们继续。 - -另外一个 eventType 也是枚举,这里有 DELETE、CREATE、UPDATE、REFRESH、MYSELF 5 种类型,此时是 UPDATE。 - -```java -/** - * The enum Data event type. - * - * @author xiaoyu - */ -public enum DataEventTypeEnum { - /** - * delete event. - */ - DELETE, - /** - * insert event. - */ - CREATE, - /** - * update event. - */ - UPDATE, - /** - * REFRESH data event type enum. - */ - REFRESH, - /** - * Myself data event type enum. - */ - MYSELF; - -... - -} -``` - -## 2.5 监听事件 - -找到监听事件的代码: - -```java -// DataChangedEventDispatcher.java -@Component -public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { - - private ApplicationContext applicationContext; - - private List listeners; - - public DataChangedEventDispatcher(final ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - @Override - @SuppressWarnings("unchecked") - public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - case APP_AUTH: - listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); - break; - case PLUGIN: - listener.onPluginChanged((List) event.getSource(), event.getEventType()); - break; - case RULE: - listener.onRuleChanged((List) event.getSource(), event.getEventType()); - break; - case SELECTOR: - listener.onSelectorChanged((List) event.getSource(), event.getEventType()); - break; - case META_DATA: - listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); - break; - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } - } - - @Override - public void afterPropertiesSet() { - Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); - this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); - } -} -``` - -### 2.5.1 监听器注入 - -可以看到 DataChangedEventDispatcher 实现了 InitializingBean 接口,覆写了 afterPropertiesSet 方法,并且使用了 @Component,当 Spring 启动时,会在容器加载完成后调用这个覆写方法。 -afterPropertiesSet 方法中,把 DataChangedListener 类型的 Bean 全部获取后,放到类属性 listeners 里。 - -那么问题来了,这些监听器是什么时候注入到容器中的呢? - -先看下 DataChangedListener 接口定义: - -```java -/** - * Event listener, used to send notification of event changes, - * used to support HTTP, websocket, zookeeper and other event notifications. - * - * @author huangxiaofeng - * @author xiaoyu - */ -public interface DataChangedListener { - - /** - * invoke this method when AppAuth was received. - * - * @param changed the changed - * @param eventType the event type - */ - default void onAppAuthChanged(List changed, DataEventTypeEnum eventType) { - } - - /** - * invoke this method when Plugin was received. - * - * @param changed the changed - * @param eventType the event type - */ - default void onPluginChanged(List changed, DataEventTypeEnum eventType) { - } - - /** - * invoke this method when Selector was received. - * - * @param changed the changed - * @param eventType the event type - */ - default void onSelectorChanged(List changed, DataEventTypeEnum eventType) { - } - - /** - * On meta data changed. - * - * @param changed the changed - * @param eventType the event type - */ - default void onMetaDataChanged(List changed, DataEventTypeEnum eventType) { - - } - - /** - * invoke this method when Rule was received. - * - * @param changed the changed - * @param eventType the event type - */ - default void onRuleChanged(List changed, DataEventTypeEnum eventType) { - } - -} -``` - -可以看到接口中定义了 5 个方法,分别针对当监听到 appAuth、plugin、selector、metaData、rule 数据变动时对应的处理方法。 - -其继承关系: - -![DataChangedListener](/assets/img/blog3/DataChangedListener.png) - -因为默认是采用的 websocket,这里的监听器对应的就是 WebsocketDataChangedListener,Alt + F7,搜索到这个类实例化的地方,就是如下的配置类: - -```java -// DataSyncConfiguration.java -@Configuration -public class DataSyncConfiguration { - - /** - * http long polling. - */ - @Configuration - @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true") - @EnableConfigurationProperties(HttpSyncProperties.class) - static class HttpLongPollingListener { - @Bean - @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class) - public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) { - return new HttpLongPollingDataChangedListener(httpSyncProperties); - } - } - - /** - * The type Zookeeper listener. - */ - @Configuration - @ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url") - @Import(ZookeeperConfiguration.class) - static class ZookeeperListener { - @Bean - @ConditionalOnMissingBean(ZookeeperDataChangedListener.class) - public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) { - return new ZookeeperDataChangedListener(zkClient); - } - @Bean - @ConditionalOnMissingBean(ZookeeperDataInit.class) - public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { - return new ZookeeperDataInit(zkClient, syncDataService); - } - } - - /** - * The type Nacos listener. - */ - @Configuration - @ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url") - @Import(NacosConfiguration.class) - static class NacosListener { - @Bean - @ConditionalOnMissingBean(NacosDataChangedListener.class) - public DataChangedListener nacosDataChangedListener(final ConfigService configService) { - return new NacosDataChangedListener(configService); - } - } - - /** - * The WebsocketListener(default strategy). - */ - @Configuration - @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true) - @EnableConfigurationProperties(WebsocketSyncProperties.class) - static class WebsocketListener { - @Bean - @ConditionalOnMissingBean(WebsocketDataChangedListener.class) - public DataChangedListener websocketDataChangedListener() { - return new WebsocketDataChangedListener(); - } - @Bean - @ConditionalOnMissingBean(WebsocketCollector.class) - public WebsocketCollector websocketCollector() { - return new WebsocketCollector(); - } - @Bean - @ConditionalOnMissingBean(ServerEndpointExporter.class) - public ServerEndpointExporter serverEndpointExporter() { - return new ServerEndpointExporter(); - } - } -} -``` - -一共有 4 种同步数据策略,http 长轮询、zookeeper、nacos、websocket(默认策略)。 - -看到 websocket 的注解 @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true),到配置文件中找到如下配置: - -```yaml -soul: - sync: - websocket: - enabled: true -``` - -到这里就真相大白了。 - -如果不想使用 websocket 的默认同步策略,在配置文件中写上对应的配置即可。 - -### 2.5.2 监听事件处理逻辑 - -为了防止大家再翻回去看,不方便,我这里再把处理逻辑代码贴出来: - -```java -// DataChangedEventDispatcher.java -@Override - @SuppressWarnings("unchecked") - public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - case APP_AUTH: - listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); - break; - case PLUGIN: - listener.onPluginChanged((List) event.getSource(), event.getEventType()); - break; - case RULE: - listener.onRuleChanged((List) event.getSource(), event.getEventType()); - break; - case SELECTOR: - listener.onSelectorChanged((List) event.getSource(), event.getEventType()); - break; - case META_DATA: - listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); - break; - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } - } -``` - -这里遍历所有的监听器,对于目前的 websocket 只会有一个监听器,其他多个的情况暂时不知道什么时候会出现,这里存疑,后续碰到相关的 case 时再回来补充(// TODO)。 - -根据发布事件的类型走不同的逻辑,这里分的类型跟 DataChangedListener 接口中定义的方法一一对应。 - -这里的 listener 是 WebsocketDataChangedListener 的实例,会进到类中对应的方法: - -```java -// WebsocketDataChangedListener.java -public class WebsocketDataChangedListener implements DataChangedListener { - - @Override - public void onPluginChanged(final List pluginDataList, final DataEventTypeEnum eventType) { - WebsocketData websocketData = - new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); - } - - @Override - public void onSelectorChanged(final List selectorDataList, final DataEventTypeEnum eventType) { - WebsocketData websocketData = - new WebsocketData<>(ConfigGroupEnum.SELECTOR.name(), eventType.name(), selectorDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); - } - - @Override - public void onRuleChanged(final List ruleDataList, final DataEventTypeEnum eventType) { - WebsocketData configData = - new WebsocketData<>(ConfigGroupEnum.RULE.name(), eventType.name(), ruleDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); - } - - @Override - public void onAppAuthChanged(final List appAuthDataList, final DataEventTypeEnum eventType) { - WebsocketData configData = - new WebsocketData<>(ConfigGroupEnum.APP_AUTH.name(), eventType.name(), appAuthDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); - } - - @Override - public void onMetaDataChanged(final List metaDataList, final DataEventTypeEnum eventType) { - WebsocketData configData = - new WebsocketData<>(ConfigGroupEnum.META_DATA.name(), eventType.name(), metaDataList); - WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); - } - -} -``` - -看到代码中,将数据封装为 WebsocketData 后,使用 WebsocketController.send 方法发送出去了。 - -## 2.6 同步数据给 soul-bootstrap - -```java -// WebsocketCollector.java -@Slf4j -@ServerEndpoint("/websocket") -public class WebsocketCollector { - - private static final Set SESSION_SET = new CopyOnWriteArraySet<>(); - - private static final String SESSION_KEY = "sessionKey"; - - /** - * On open. - * - * @param session the session - */ - @OnOpen - public void onOpen(final Session session) { - log.info("websocket on open successful...."); - SESSION_SET.add(session); - } - - /** - * On message. - * - * @param message the message - * @param session the session - */ - @OnMessage - public void onMessage(final String message, final Session session) { - if (message.equals(DataEventTypeEnum.MYSELF.name())) { - try { - ThreadLocalUtil.put(SESSION_KEY, session); - SpringBeanUtils.getInstance().getBean(SyncDataService.class).syncAll(DataEventTypeEnum.MYSELF); - } finally { - ThreadLocalUtil.clear(); - } - } - } - - /** - * On close. - * - * @param session the session - */ - @OnClose - public void onClose(final Session session) { - SESSION_SET.remove(session); - ThreadLocalUtil.clear(); - } - - /** - * On error. - * - * @param session the session - * @param error the error - */ - @OnError - public void onError(final Session session, final Throwable error) { - SESSION_SET.remove(session); - ThreadLocalUtil.clear(); - log.error("websocket collection error: ", error); - } - - /** - * Send. - * - * @param message the message - * @param type the type - */ - public static void send(final String message, final DataEventTypeEnum type) { - if (StringUtils.isNotBlank(message)) { - if (DataEventTypeEnum.MYSELF == type) { - try { - Session session = (Session) ThreadLocalUtil.get(SESSION_KEY); - if (session != null) { - session.getBasicRemote().sendText(message); - } - } catch (IOException e) { - log.error("websocket send result is exception: ", e); - } - return; - } - for (Session session : SESSION_SET) { - try { - session.getBasicRemote().sendText(message); - } catch (IOException e) { - log.error("websocket send result is exception: ", e); - } - } - } - } -} -``` - -WebsocketController 使用了 @ServerEndpoint("/websocket") 注解,开启了一个 websocket 服务接口,等待连接。 - -当 soul-bootstrap 启动后,会连接这个 websocket,这时触发 onOpen 方法,将此次连接信息的 Session 存放在 SESSION_SET 这个 Set 集合里。 - -在 send 方法中,会先判断 DataEventTypeEnum type 是不是 MYSELF,这个 type 可以追溯到 2.3-2.4,此次是 UPDATE,关于什么时候是 MYSELF,还需要后续补充,此处存疑(// TODO)。 - -下面的 for 循环遍历所有的 websocket 连接 Session,发送变动数据。 - -到这里,默认的 websocket 同步数据策略就分析清楚了。 - -> 朱明 - -## 后台与网关数据同步 (Websocket 篇) - -### 后台如何建立 Websocket? - -![05](/assets/img/blog1/05.png) -DataSyncConfiguration: 作为 Spring Bean 的配置工厂, 可以根据配置信息, 构建各类监听器, 包括 HTTP 长轮询方式、Zookeeper 方式、Nacos 方式、Websocket 方法. - -```java -@Configuration -public class DataSyncConfiguration { - - // soul-admin 项目的配置信息中, 使用 soul.sync.websocket.enabled 开启或关闭 websocket - @Configuration - @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true) - @EnableConfigurationProperties(WebsocketSyncProperties.class) - static class WebsocketListener { - - @Bean - @ConditionalOnMissingBean(WebsocketCollector.class) - public WebsocketCollector websocketCollector() { - return new WebsocketCollector(); - } - } -} -``` - -WebsocketListener: 作为 `DataSyncConfiguration` 的内部类, 负责 websocket 监听器初始化. -WebsocketCollector: 监听 websocket 连接及接收信息, 维护所有连接后台的 session 会话, 提供 `send()` 方法通知 session 信息. - -### 网关如何建立 Websocket? - -![06](/assets/img/blog1/06.png) - -WebsocketSyncDataConfiguration: 作为 Spring Bean 的配置工厂, 是网关端构建 Websocket 通信的入口. (独立出一个启动项目 `soul-spring-boot-starter-sync-data-websocket` , 供网关自由选用) - -```java -@Configuration -@ConditionalOnClass(WebsocketSyncDataService.class) -@ConditionalOnProperty(prefix = "soul.sync.websocket", name = "urls") -@Slf4j -public class WebsocketSyncDataConfiguration { - - // 收集所有注册为 Bean 的订阅器, 如 PluginDataSubscriber、MetaDataSubscriber、AuthDataSubscriber - @Bean - public SyncDataService websocketSyncDataService(final ObjectProvider websocketConfig, final ObjectProvider pluginSubscriber, final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { - log.info("you use websocket sync soul data......."); - return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new), pluginSubscriber.getIfAvailable(), metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); - } - - // soul-bootstrap 项目的配置信息中, 使用 soul.sync.websocket 配置要建立连接的后台路径 - @Bean - @ConfigurationProperties(prefix = "soul.sync.websocket") - public WebsocketConfig websocketConfig() { - return new WebsocketConfig(); - } -} -``` - -WebsocketSyncDataService: 获取所有注册为 Bean 的 `WebsocketConfig` 以及各种 `DataSubscriber` 订阅器, 构建实现了 `WebsocketClient` 的 `SoulWebsocketClient` 列表 - -SoulWebsocketClient: `Websocket` 通信类, 监听 websocket 连接及接收信息, 在接收到后台传来的信息后会通知各个订阅器. - -```java -public final class SoulWebsocketClient extends WebSocketClient { - - private final WebsocketDataHandler websocketDataHandler; - - private void handleResult(final String result) { - WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class); - ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType()); - // 根据传入信息得到数据变更的事件类型, 如 refresh、update、delete 等 - String eventType = websocketData.getEventType(); - String json = GsonUtils.getInstance().toJson(websocketData.getData()); - websocketDataHandler.executor(groupEnum, json, eventType); - } -} -``` - -WebsocketDataHandler: 初始化时构建各类实现 `AbstractDataHandler` 的数据处理类并缓存. - -```java -public class WebsocketDataHandler { - - // 缓存所有 DataHandler 数据变动处理类 - private static final EnumMap ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class); - - public WebsocketDataHandler(final PluginDataSubscriber pluginDataSubscriber, - final List metaDataSubscribers, - final List authDataSubscribers) { - ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataHandler(pluginDataSubscriber)); - ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AuthDataHandler(authDataSubscribers)); - ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataHandler(metaDataSubscribers)); - } - - public void executor(final ConfigGroupEnum type, final String json, final String eventType) { - // 根据数据变动事件类型, 调用相应的 DataHandler 数据处理类 - ENUM_MAP.get(type).handle(json, eventType); - } -} -``` - -### 网关数据变动调用链 - -实现 Websocket 通信的入口类 `SoulWebsocketClient` 在接到后台通信后, 调用 `WebsocketDataHandler` 的 `executor()` 方法匹配信息类型, 并调用对应的 `DataHandler` 的 `handler()` 去处理信息. - -![07](/assets/img/blog1/07.png) - -AbstractDataHandler: 实现 `handler()` 方法, 根据事件的类型 (如刷新、更新、创建、删除等), 调用对应事件抽象方法. - -```java -public abstract class AbstractDataHandler implements DataHandler { - - // 根据数据的事件类型 (eventType) 分发到各自方法, 这些被调用的方法由子类实现, 因为不同类型的元数据处理类的处理方式不同 - @Override - public void handle(final String json, final String eventType) { - List dataList = convert(json); - if (CollectionUtils.isNotEmpty(dataList)) { - DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType); - switch (eventTypeEnum) { - case REFRESH: - case MYSELF: - doRefresh(dataList); - break; - case UPDATE: - case CREATE: - doUpdate(dataList); - break; - case DELETE: - doDelete(dataList); - break; - default: - break; - } - } - } -} -``` - -XXXDataHandler: 这里指的是 `AbstractDataHandler` 的各个实现类 (如 `PluginDataHandler` 等), 主要作用是调用其订阅器. - -不同的 `DataHandler` 调用的订阅方法不同: - -- `PluginDataHandler` 会调用 `onSubscribe()` 通知插件元数据变更 -- `SelectorDataHandler` 会调用 `onSelectorSubscribe()` 通知选择器元数据变更 -- `RuleDataHandler` 会调用 `onRuleSubscribe()` 通知规则元数据变更 - -```java -@RequiredArgsConstructor -public class PluginDataHandler extends AbstractDataHandler { - - private final PluginDataSubscriber pluginDataSubscriber; - - @Override - protected void doUpdate(final List dataList) { - // 调用订阅器的 onSubscribe(), 发送数据对象 PluginData - dataList.forEach(pluginDataSubscriber::onSubscribe); - } - - // ... -} -``` - -CommonPluginDataSubscriber: 订阅器的 `onSubscribe()` 方法会通知到所有注入为 Bean 的 `PluginDataHandler` 类 (不要和前面的同名类混淆, 它是 `soul-plugin-base` 下的接口, 它的实现类在各个可插拔插件包) - -![image-20210122172333111](/assets/img/blog1/image-20210122172333111.png) - -```java -public class CommonPluginDataSubscriber implements PluginDataSubscriber { - - // 收集所有注册为 Bean 的数据处理器并缓存, 比如 HTTP 插件 divide 下的 DividePluginDataHandler - private final Map handlerMap; - - // 插件元数据变动调用 - @Override - public void onSubscribe(final PluginData pluginData) { - BaseDataCache.getInstance().cachePluginData(pluginData); - Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData)); - } - - // 选择器元数据变动调用 - @Override - public void onSelectorSubscribe(final SelectorData selectorData) { - BaseDataCache.getInstance().cacheSelectData(selectorData); - Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData)); - } - - // 规则元数据变动调用 - @Override - public void onRuleSubscribe(final RuleData ruleData) { - BaseDataCache.getInstance().cacheRuleData(ruleData); - Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData)); - } -} -``` - -### TIPS - -整个大项目下存在两个同名的类 PluginDataHandler, 其中一个在项目 `soul-sync-data-websocket` 下, 用于通知插件元数据变更, 另一个在 `soul-plugin-base` 下, 用于定义各个插件的各个类型元数据更新. - -总结下这两个类命名的意义, **`soul-sync-data-websocket` 下类名的 "plugin" 指元数据的类型为插件类, `soul-plugin-base` 下类名的 "plugin" 指继承它的子类来自与各个可插播插件, 比如 divide、dubbo 插件等** +--- +title: Soul网关学习WebSocket数据同步解析 +author: 范金鹏, 朱明 +date: 2021-01-22 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +> 范金鹏 + +# 1.前情回顾 + +在第 4 篇中,我们分析了 HTTP 用户业务系统接入 Soul 网关后,会调用 soul-admin 的注册接口,把需要网关代理的接口信息全部注册到 soul-admin 上,在最后,会通过 websocket 长连接,将 soul-admin 接收到的接口信息同步给 Soul 网关(即 soul-bootstrap),今天就来接着继续分析,数据是怎么同步到 soul-bootstrap 的。 + +不清楚流程的可以出门左转看下第 4 篇文章 [【Soul 源码阅读】4.HTTP 用户接入 Soul 调用 /soul-client/springmvc-register 接口逻辑分析](https://blog.csdn.net/hellboy0621/article/details/112727101) + +# 2.soul-admin 与 soul-bootstrap 数据同步 + +这里为了验证数据同步流程,其实也没必要非得启动业务系统,完全可以只启动 soul-admin 和 soul-bootstrap 两个系统即可,可以在页面打开或关闭插件,看看这个流程是怎么实现的。 + +数据同步策略官网链接 https://dromara.org/zh-cn/docs/soul/user-dataSync.html + +## 2.1 启动 2 个系统 + +都是按照项目默认启动的,无需修改任何配置文件。 + +## 2.2 页面操作查找接口 + +这里把 divide 插件启动,F12,看下前台会调用 soul-admin 哪个接口。 + +![open_divide_plugin](/assets/img/blog3/open_divide_plugin.png) + +可以看到前台向后台发送了一个 PUT 请求:http://localhost:9095/plugin/5 + +## 2.3 后台接口 + +在项目中搜索这个接口 + +```java +// PluginController.java +@RestController +@RequestMapping("/plugin") +public class PluginController { + +... + + /** + * update plugin. + * + * @param id primary key. + * @param pluginDTO plugin. + * @return {@linkplain SoulAdminResult} + */ + @PutMapping("/{id}") + public SoulAdminResult updatePlugin(@PathVariable("id") final String id, @RequestBody final PluginDTO pluginDTO) { + Objects.requireNonNull(pluginDTO); + pluginDTO.setId(id); + final String result = pluginService.createOrUpdate(pluginDTO); + if (StringUtils.isNoneBlank(result)) { + return SoulAdminResult.error(result); + } + return SoulAdminResult.success(SoulResultMessage.UPDATE_SUCCESS); + } + +... + +} +``` + +进到实现类里 + +```java +// PluginServiceImpl.java +/** + * create or update plugin. + * + * @param pluginDTO {@linkplain PluginDTO} + * @return rows + */ + @Override + @Transactional(rollbackFor = Exception.class) + public String createOrUpdate(final PluginDTO pluginDTO) { + final String msg = checkData(pluginDTO); + if (StringUtils.isNoneBlank(msg)) { + return msg; + } + PluginDO pluginDO = PluginDO.buildPluginDO(pluginDTO); + DataEventTypeEnum eventType = DataEventTypeEnum.CREATE; + if (StringUtils.isBlank(pluginDTO.getId())) { + pluginMapper.insertSelective(pluginDO); + } else { + eventType = DataEventTypeEnum.UPDATE; + pluginMapper.updateSelective(pluginDO); + } + + // publish change event. + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType, + Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO)))); + return StringUtils.EMPTY; + } +``` + +这里可以看出来,前半部分都是在操作数据库,把相关信息持久化;后半部分是发布了一个事件。 + +## 2.4 发布事件 + +这里发布的事件用 DataChangedEvent 封装了一层,再看里面有 1 个枚举,这里有很多种类型: + +```java +/** + * configuration group. + * + * @author huangxiaofeng + */ +public enum ConfigGroupEnum { + + APP_AUTH, + + PLUGIN, + + RULE, + + SELECTOR, + + META_DATA; + +... + +} +``` + +看到这几种类型,如果对第 4 篇还有印象的话,可以看出当时发送事件的类型就是 SELECTOR 和 RULE,现在是 PLUGIN,虽然类型不同,但不影响我们继续分析后面的逻辑,我们继续。 + +另外一个 eventType 也是枚举,这里有 DELETE、CREATE、UPDATE、REFRESH、MYSELF 5 种类型,此时是 UPDATE。 + +```java +/** + * The enum Data event type. + * + * @author xiaoyu + */ +public enum DataEventTypeEnum { + /** + * delete event. + */ + DELETE, + /** + * insert event. + */ + CREATE, + /** + * update event. + */ + UPDATE, + /** + * REFRESH data event type enum. + */ + REFRESH, + /** + * Myself data event type enum. + */ + MYSELF; + +... + +} +``` + +## 2.5 监听事件 + +找到监听事件的代码: + +```java +// DataChangedEventDispatcher.java +@Component +public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { + + private ApplicationContext applicationContext; + + private List listeners; + + public DataChangedEventDispatcher(final ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + @SuppressWarnings("unchecked") + public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + case APP_AUTH: + listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); + break; + case PLUGIN: + listener.onPluginChanged((List) event.getSource(), event.getEventType()); + break; + case RULE: + listener.onRuleChanged((List) event.getSource(), event.getEventType()); + break; + case SELECTOR: + listener.onSelectorChanged((List) event.getSource(), event.getEventType()); + break; + case META_DATA: + listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); + break; + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } + } + + @Override + public void afterPropertiesSet() { + Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); + this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); + } +} +``` + +### 2.5.1 监听器注入 + +可以看到 DataChangedEventDispatcher 实现了 InitializingBean 接口,覆写了 afterPropertiesSet 方法,并且使用了 @Component,当 Spring 启动时,会在容器加载完成后调用这个覆写方法。 +afterPropertiesSet 方法中,把 DataChangedListener 类型的 Bean 全部获取后,放到类属性 listeners 里。 + +那么问题来了,这些监听器是什么时候注入到容器中的呢? + +先看下 DataChangedListener 接口定义: + +```java +/** + * Event listener, used to send notification of event changes, + * used to support HTTP, websocket, zookeeper and other event notifications. + * + * @author huangxiaofeng + * @author xiaoyu + */ +public interface DataChangedListener { + + /** + * invoke this method when AppAuth was received. + * + * @param changed the changed + * @param eventType the event type + */ + default void onAppAuthChanged(List changed, DataEventTypeEnum eventType) { + } + + /** + * invoke this method when Plugin was received. + * + * @param changed the changed + * @param eventType the event type + */ + default void onPluginChanged(List changed, DataEventTypeEnum eventType) { + } + + /** + * invoke this method when Selector was received. + * + * @param changed the changed + * @param eventType the event type + */ + default void onSelectorChanged(List changed, DataEventTypeEnum eventType) { + } + + /** + * On meta data changed. + * + * @param changed the changed + * @param eventType the event type + */ + default void onMetaDataChanged(List changed, DataEventTypeEnum eventType) { + + } + + /** + * invoke this method when Rule was received. + * + * @param changed the changed + * @param eventType the event type + */ + default void onRuleChanged(List changed, DataEventTypeEnum eventType) { + } + +} +``` + +可以看到接口中定义了 5 个方法,分别针对当监听到 appAuth、plugin、selector、metaData、rule 数据变动时对应的处理方法。 + +其继承关系: + +![DataChangedListener](/assets/img/blog3/DataChangedListener.png) + +因为默认是采用的 websocket,这里的监听器对应的就是 WebsocketDataChangedListener,Alt + F7,搜索到这个类实例化的地方,就是如下的配置类: + +```java +// DataSyncConfiguration.java +@Configuration +public class DataSyncConfiguration { + + /** + * http long polling. + */ + @Configuration + @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true") + @EnableConfigurationProperties(HttpSyncProperties.class) + static class HttpLongPollingListener { + @Bean + @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class) + public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) { + return new HttpLongPollingDataChangedListener(httpSyncProperties); + } + } + + /** + * The type Zookeeper listener. + */ + @Configuration + @ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url") + @Import(ZookeeperConfiguration.class) + static class ZookeeperListener { + @Bean + @ConditionalOnMissingBean(ZookeeperDataChangedListener.class) + public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) { + return new ZookeeperDataChangedListener(zkClient); + } + @Bean + @ConditionalOnMissingBean(ZookeeperDataInit.class) + public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { + return new ZookeeperDataInit(zkClient, syncDataService); + } + } + + /** + * The type Nacos listener. + */ + @Configuration + @ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url") + @Import(NacosConfiguration.class) + static class NacosListener { + @Bean + @ConditionalOnMissingBean(NacosDataChangedListener.class) + public DataChangedListener nacosDataChangedListener(final ConfigService configService) { + return new NacosDataChangedListener(configService); + } + } + + /** + * The WebsocketListener(default strategy). + */ + @Configuration + @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true) + @EnableConfigurationProperties(WebsocketSyncProperties.class) + static class WebsocketListener { + @Bean + @ConditionalOnMissingBean(WebsocketDataChangedListener.class) + public DataChangedListener websocketDataChangedListener() { + return new WebsocketDataChangedListener(); + } + @Bean + @ConditionalOnMissingBean(WebsocketCollector.class) + public WebsocketCollector websocketCollector() { + return new WebsocketCollector(); + } + @Bean + @ConditionalOnMissingBean(ServerEndpointExporter.class) + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + } +} +``` + +一共有 4 种同步数据策略,http 长轮询、zookeeper、nacos、websocket(默认策略)。 + +看到 websocket 的注解 @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true),到配置文件中找到如下配置: + +```yaml +soul: + sync: + websocket: + enabled: true +``` + +到这里就真相大白了。 + +如果不想使用 websocket 的默认同步策略,在配置文件中写上对应的配置即可。 + +### 2.5.2 监听事件处理逻辑 + +为了防止大家再翻回去看,不方便,我这里再把处理逻辑代码贴出来: + +```java +// DataChangedEventDispatcher.java +@Override + @SuppressWarnings("unchecked") + public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + case APP_AUTH: + listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); + break; + case PLUGIN: + listener.onPluginChanged((List) event.getSource(), event.getEventType()); + break; + case RULE: + listener.onRuleChanged((List) event.getSource(), event.getEventType()); + break; + case SELECTOR: + listener.onSelectorChanged((List) event.getSource(), event.getEventType()); + break; + case META_DATA: + listener.onMetaDataChanged((List) event.getSource(), event.getEventType()); + break; + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } + } +``` + +这里遍历所有的监听器,对于目前的 websocket 只会有一个监听器,其他多个的情况暂时不知道什么时候会出现,这里存疑,后续碰到相关的 case 时再回来补充(// TODO)。 + +根据发布事件的类型走不同的逻辑,这里分的类型跟 DataChangedListener 接口中定义的方法一一对应。 + +这里的 listener 是 WebsocketDataChangedListener 的实例,会进到类中对应的方法: + +```java +// WebsocketDataChangedListener.java +public class WebsocketDataChangedListener implements DataChangedListener { + + @Override + public void onPluginChanged(final List pluginDataList, final DataEventTypeEnum eventType) { + WebsocketData websocketData = + new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); + } + + @Override + public void onSelectorChanged(final List selectorDataList, final DataEventTypeEnum eventType) { + WebsocketData websocketData = + new WebsocketData<>(ConfigGroupEnum.SELECTOR.name(), eventType.name(), selectorDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); + } + + @Override + public void onRuleChanged(final List ruleDataList, final DataEventTypeEnum eventType) { + WebsocketData configData = + new WebsocketData<>(ConfigGroupEnum.RULE.name(), eventType.name(), ruleDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); + } + + @Override + public void onAppAuthChanged(final List appAuthDataList, final DataEventTypeEnum eventType) { + WebsocketData configData = + new WebsocketData<>(ConfigGroupEnum.APP_AUTH.name(), eventType.name(), appAuthDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); + } + + @Override + public void onMetaDataChanged(final List metaDataList, final DataEventTypeEnum eventType) { + WebsocketData configData = + new WebsocketData<>(ConfigGroupEnum.META_DATA.name(), eventType.name(), metaDataList); + WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType); + } + +} +``` + +看到代码中,将数据封装为 WebsocketData 后,使用 WebsocketController.send 方法发送出去了。 + +## 2.6 同步数据给 soul-bootstrap + +```java +// WebsocketCollector.java +@Slf4j +@ServerEndpoint("/websocket") +public class WebsocketCollector { + + private static final Set SESSION_SET = new CopyOnWriteArraySet<>(); + + private static final String SESSION_KEY = "sessionKey"; + + /** + * On open. + * + * @param session the session + */ + @OnOpen + public void onOpen(final Session session) { + log.info("websocket on open successful...."); + SESSION_SET.add(session); + } + + /** + * On message. + * + * @param message the message + * @param session the session + */ + @OnMessage + public void onMessage(final String message, final Session session) { + if (message.equals(DataEventTypeEnum.MYSELF.name())) { + try { + ThreadLocalUtil.put(SESSION_KEY, session); + SpringBeanUtils.getInstance().getBean(SyncDataService.class).syncAll(DataEventTypeEnum.MYSELF); + } finally { + ThreadLocalUtil.clear(); + } + } + } + + /** + * On close. + * + * @param session the session + */ + @OnClose + public void onClose(final Session session) { + SESSION_SET.remove(session); + ThreadLocalUtil.clear(); + } + + /** + * On error. + * + * @param session the session + * @param error the error + */ + @OnError + public void onError(final Session session, final Throwable error) { + SESSION_SET.remove(session); + ThreadLocalUtil.clear(); + log.error("websocket collection error: ", error); + } + + /** + * Send. + * + * @param message the message + * @param type the type + */ + public static void send(final String message, final DataEventTypeEnum type) { + if (StringUtils.isNotBlank(message)) { + if (DataEventTypeEnum.MYSELF == type) { + try { + Session session = (Session) ThreadLocalUtil.get(SESSION_KEY); + if (session != null) { + session.getBasicRemote().sendText(message); + } + } catch (IOException e) { + log.error("websocket send result is exception: ", e); + } + return; + } + for (Session session : SESSION_SET) { + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + log.error("websocket send result is exception: ", e); + } + } + } + } +} +``` + +WebsocketController 使用了 @ServerEndpoint("/websocket") 注解,开启了一个 websocket 服务接口,等待连接。 + +当 soul-bootstrap 启动后,会连接这个 websocket,这时触发 onOpen 方法,将此次连接信息的 Session 存放在 SESSION_SET 这个 Set 集合里。 + +在 send 方法中,会先判断 DataEventTypeEnum type 是不是 MYSELF,这个 type 可以追溯到 2.3-2.4,此次是 UPDATE,关于什么时候是 MYSELF,还需要后续补充,此处存疑(// TODO)。 + +下面的 for 循环遍历所有的 websocket 连接 Session,发送变动数据。 + +到这里,默认的 websocket 同步数据策略就分析清楚了。 + +> 朱明 + +## 后台与网关数据同步 (Websocket 篇) + +### 后台如何建立 Websocket? + +![05](/assets/img/blog1/05.png) +DataSyncConfiguration: 作为 Spring Bean 的配置工厂, 可以根据配置信息, 构建各类监听器, 包括 HTTP 长轮询方式、Zookeeper 方式、Nacos 方式、Websocket 方法. + +```java +@Configuration +public class DataSyncConfiguration { + + // soul-admin 项目的配置信息中, 使用 soul.sync.websocket.enabled 开启或关闭 websocket + @Configuration + @ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true) + @EnableConfigurationProperties(WebsocketSyncProperties.class) + static class WebsocketListener { + + @Bean + @ConditionalOnMissingBean(WebsocketCollector.class) + public WebsocketCollector websocketCollector() { + return new WebsocketCollector(); + } + } +} +``` + +WebsocketListener: 作为 `DataSyncConfiguration` 的内部类, 负责 websocket 监听器初始化. +WebsocketCollector: 监听 websocket 连接及接收信息, 维护所有连接后台的 session 会话, 提供 `send()` 方法通知 session 信息. + +### 网关如何建立 Websocket? + +![06](/assets/img/blog1/06.png) + +WebsocketSyncDataConfiguration: 作为 Spring Bean 的配置工厂, 是网关端构建 Websocket 通信的入口. (独立出一个启动项目 `soul-spring-boot-starter-sync-data-websocket` , 供网关自由选用) + +```java +@Configuration +@ConditionalOnClass(WebsocketSyncDataService.class) +@ConditionalOnProperty(prefix = "soul.sync.websocket", name = "urls") +@Slf4j +public class WebsocketSyncDataConfiguration { + + // 收集所有注册为 Bean 的订阅器, 如 PluginDataSubscriber、MetaDataSubscriber、AuthDataSubscriber + @Bean + public SyncDataService websocketSyncDataService(final ObjectProvider websocketConfig, final ObjectProvider pluginSubscriber, final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { + log.info("you use websocket sync soul data......."); + return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new), pluginSubscriber.getIfAvailable(), metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); + } + + // soul-bootstrap 项目的配置信息中, 使用 soul.sync.websocket 配置要建立连接的后台路径 + @Bean + @ConfigurationProperties(prefix = "soul.sync.websocket") + public WebsocketConfig websocketConfig() { + return new WebsocketConfig(); + } +} +``` + +WebsocketSyncDataService: 获取所有注册为 Bean 的 `WebsocketConfig` 以及各种 `DataSubscriber` 订阅器, 构建实现了 `WebsocketClient` 的 `SoulWebsocketClient` 列表 + +SoulWebsocketClient: `Websocket` 通信类, 监听 websocket 连接及接收信息, 在接收到后台传来的信息后会通知各个订阅器. + +```java +public final class SoulWebsocketClient extends WebSocketClient { + + private final WebsocketDataHandler websocketDataHandler; + + private void handleResult(final String result) { + WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class); + ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType()); + // 根据传入信息得到数据变更的事件类型, 如 refresh、update、delete 等 + String eventType = websocketData.getEventType(); + String json = GsonUtils.getInstance().toJson(websocketData.getData()); + websocketDataHandler.executor(groupEnum, json, eventType); + } +} +``` + +WebsocketDataHandler: 初始化时构建各类实现 `AbstractDataHandler` 的数据处理类并缓存. + +```java +public class WebsocketDataHandler { + + // 缓存所有 DataHandler 数据变动处理类 + private static final EnumMap ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class); + + public WebsocketDataHandler(final PluginDataSubscriber pluginDataSubscriber, + final List metaDataSubscribers, + final List authDataSubscribers) { + ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataHandler(pluginDataSubscriber)); + ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AuthDataHandler(authDataSubscribers)); + ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataHandler(metaDataSubscribers)); + } + + public void executor(final ConfigGroupEnum type, final String json, final String eventType) { + // 根据数据变动事件类型, 调用相应的 DataHandler 数据处理类 + ENUM_MAP.get(type).handle(json, eventType); + } +} +``` + +### 网关数据变动调用链 + +实现 Websocket 通信的入口类 `SoulWebsocketClient` 在接到后台通信后, 调用 `WebsocketDataHandler` 的 `executor()` 方法匹配信息类型, 并调用对应的 `DataHandler` 的 `handler()` 去处理信息. + +![07](/assets/img/blog1/07.png) + +AbstractDataHandler: 实现 `handler()` 方法, 根据事件的类型 (如刷新、更新、创建、删除等), 调用对应事件抽象方法. + +```java +public abstract class AbstractDataHandler implements DataHandler { + + // 根据数据的事件类型 (eventType) 分发到各自方法, 这些被调用的方法由子类实现, 因为不同类型的元数据处理类的处理方式不同 + @Override + public void handle(final String json, final String eventType) { + List dataList = convert(json); + if (CollectionUtils.isNotEmpty(dataList)) { + DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType); + switch (eventTypeEnum) { + case REFRESH: + case MYSELF: + doRefresh(dataList); + break; + case UPDATE: + case CREATE: + doUpdate(dataList); + break; + case DELETE: + doDelete(dataList); + break; + default: + break; + } + } + } +} +``` + +XXXDataHandler: 这里指的是 `AbstractDataHandler` 的各个实现类 (如 `PluginDataHandler` 等), 主要作用是调用其订阅器. + +不同的 `DataHandler` 调用的订阅方法不同: + +- `PluginDataHandler` 会调用 `onSubscribe()` 通知插件元数据变更 +- `SelectorDataHandler` 会调用 `onSelectorSubscribe()` 通知选择器元数据变更 +- `RuleDataHandler` 会调用 `onRuleSubscribe()` 通知规则元数据变更 + +```java +@RequiredArgsConstructor +public class PluginDataHandler extends AbstractDataHandler { + + private final PluginDataSubscriber pluginDataSubscriber; + + @Override + protected void doUpdate(final List dataList) { + // 调用订阅器的 onSubscribe(), 发送数据对象 PluginData + dataList.forEach(pluginDataSubscriber::onSubscribe); + } + + // ... +} +``` + +CommonPluginDataSubscriber: 订阅器的 `onSubscribe()` 方法会通知到所有注入为 Bean 的 `PluginDataHandler` 类 (不要和前面的同名类混淆, 它是 `soul-plugin-base` 下的接口, 它的实现类在各个可插拔插件包) + +![image-20210122172333111](/assets/img/blog1/image-20210122172333111.png) + +```java +public class CommonPluginDataSubscriber implements PluginDataSubscriber { + + // 收集所有注册为 Bean 的数据处理器并缓存, 比如 HTTP 插件 divide 下的 DividePluginDataHandler + private final Map handlerMap; + + // 插件元数据变动调用 + @Override + public void onSubscribe(final PluginData pluginData) { + BaseDataCache.getInstance().cachePluginData(pluginData); + Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData)); + } + + // 选择器元数据变动调用 + @Override + public void onSelectorSubscribe(final SelectorData selectorData) { + BaseDataCache.getInstance().cacheSelectData(selectorData); + Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData)); + } + + // 规则元数据变动调用 + @Override + public void onRuleSubscribe(final RuleData ruleData) { + BaseDataCache.getInstance().cacheRuleData(ruleData); + Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData)); + } +} +``` + +### TIPS + +整个大项目下存在两个同名的类 PluginDataHandler, 其中一个在项目 `soul-sync-data-websocket` 下, 用于通知插件元数据变更, 另一个在 `soul-plugin-base` 下, 用于定义各个插件的各个类型元数据更新. + +总结下这两个类命名的意义, **`soul-sync-data-websocket` 下类名的 "plugin" 指元数据的类型为插件类, `soul-plugin-base` 下类名的 "plugin" 指继承它的子类来自与各个可插播插件, 比如 divide、dubbo 插件等** diff --git a/src/zh/blog/soul_source_learning_11_SPI.md b/src/zh/blog/soul_source_learning_11_SPI.md index 2964f23af1..6dd5d7f0f0 100644 --- a/src/zh/blog/soul_source_learning_11_SPI.md +++ b/src/zh/blog/soul_source_learning_11_SPI.md @@ -1,828 +1,828 @@ ---- -title: Soul网关学习SPI学习使用 -author: 朱明 -date: 2021-01-30 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -# SOUL 中 SPI 的使用 - -在之前分析 divide 插件的负载均衡策略时, 有看到过一行代码: - -```java -DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); -``` - -当时很简单的略过了它的实现, 它的作用很容易分析, 调用一个看似工具类的方法, 传入多个节点组成的集群, 返回一个节点. 这是一个负载均衡器. - -但是细节却非常多, 最重要的一点是使用 SPI 来选择具体的实现类. 看看这个方法的代码: - -```java -public class LoadBalanceUtils { - - public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) { - // 调用自定义的 SPI 得到一个子类 - LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm); - return loadBalance.select(upstreamList, ip); - } -} -``` - -后面的是调用具体子类的 `select()` 方法, 根据子类的不同实现, 最终会表现出各种形式. 目前的子类实现有: - -- HashLoadBalance -- RandomLoadBalance -- RoundRobinLoadBalance - -关键就在于 `ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);` 这行. - -在研究它之前, 我们先不妨研究下 Java 提供的 SPI 机制. - -## Java SPI - -_<<高可用可伸缩微服务架构>> 第 3 章 Apache Dubbo 框架的原理与实现_ 中有这样的一句定义. - -> SPI 全称为 Service Provider Interface, 是 JDK 内置的一种服务提供发现功能, 一种动态替换发现的机制. 举个例子, 要想在运行时动态地给一个接口添加实现, 只需要添加一个实现即可. - -书中也有个非常形象的脑图, 展示了 SPI 的使用: - -![08](/assets/img/blog1/08.png) - -也就是说在我们代码中的实现里, 无需去写入一个 Factory 工厂, 用 MAP 去包装一些子类, 最终返回的类型是父接口. 只需要定义好资源文件, 让父接口与它的子类在文件中写明, 即可通过设置好的方式拿到所有定义的子类对象: - -```java -ServiceLoader loaders = ServiceLoader.load(Interface.class) -for(Interface interface : loaders){ - System.out.println(interface.toString()); -} -``` - -这种方式相比与普通的工厂模式, 肯定是更符合开闭原则, 新加入一个子类不用去修改工厂方法, 而是编辑资源文件. - -### 从一个 Demo 开始 - -按照 SPI 的规范, 我建了一个 demo, 看看具体的实现效果 - -![image-20210129095623013](/assets/img/blog1/image-20210129095623013.png) - -![image-20210129095703911](/assets/img/blog1/image-20210129095703911.png) - -Animal 中定义一个 `run()` 方法, 而子类实现它. - -```java -public interface Animal { - void run(); -} - -public class Dog implements Animal { - @Override - public void run() { - System.out.println("狗在跑"); - } -} - -public class Horse implements Animal { - @Override - public void run() { - System.out.println("马在跑"); - } -} -``` - -使用 SPI 的加载类, 得到子类的执行结果: - -```java -private static void test() { - final ServiceLoader load = ServiceLoader.load(Animal.class); - - for (Animal animal : load) { - System.out.println(animal); - animal.run(); - } -} -``` - -![image-20210129103047851](/assets/img/blog1/image-20210129103047851.png) - -在调用后我们得到之前在资源文件中写入的实现类, 并成功调取它们各自的 `run()` 方法. - -到这里我产生一个疑问, **是否每次调用 `ServiceLoader.load(Animal.class)` 返回的都是同一个对象?** 如果是我猜测它是在启动时加载到缓存了, 如果不是, 可能就是在底层用了反射, 每次调用都有一定消耗. 我们看看下面的实验: - -```java -public static void main(String[] args) { - for (int i = 0; i < 2; i++) { - test(); - System.out.println("----------"); - } -} - -private static void test() { - final ServiceLoader load = ServiceLoader.load(Animal.class); - for (Animal animal : load) { - System.out.println(animal); - animal.run(); - } -} -``` - -![image-20210129103451844](/assets/img/blog1/image-20210129103451844.png) - -两次调用出现的对象却不一样, 不由让我替其性能揪心一下, 所以我们先分析下它的代码, 看看到底怎么实现. - -### SPI 的实现 - -找到 `java.util,ServiceLoaders` 这个类, 入眼最醒目的就是之前我们按照规范放置资源文件的目录 - -```java -public final class ServiceLoader implements Iterable { - - private static final String PREFIX = "META-INF/services/"; -} -``` - -在 debug `PREFIX` 属性的被调用处时, 发现 `ServiceLoader.load` 实际是使用懒加载的方式, 并没有在调用它的时候, 找寻到实际返回类, 而是在遍历时查找. - -它的懒加载具体实现在如下代码: - -```java -public final class ServiceLoader implements Iterable { - - public static ServiceLoader load(Class service) { - // 获取当前的类加载器 (我们自己的通常是弟中弟 AppClassLoader ) - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return ServiceLoader.load(service, cl); - } - - public static ServiceLoader load(Class service, ClassLoader loader) { - // 调用构造器初始化对象 (说明每次调用都使用新的 ServiceLoader 对象) - return new ServiceLoader<>(service, loader); - } - - private ServiceLoader(Class svc, ClassLoader cl) { - service = Objects.requireNonNull(svc, "Service interface cannot be null"); - loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; - acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; - // 上面都是将信息放入对象实例属性中, 这行才是关键调用 - reload(); - } - - public void reload() { - providers.clear(); - // 创建懒加载迭代器, 传入关键的接口 Class 以及加载器 - lookupIterator = new LazyIterator(service, loader); - } -} - -``` - -调用 `ServiceLoader.load` 后关键事情都没干, 仅仅是将接口 class 和加载器传给 LazyIterator 这个迭代器的实现类. - -看到这可以猜测, 真正迭代调用返回的对象时, 肯定需要迭代器完成实现类的搜索和初始化, 而传参是 Class 信息和加载器, 实现类的初始化也明显会是反射了. - -看下 LazyIterator 的实现方式, 先从其最开始会被调用到的 `hasNext()` 开始: - -```java -private class LazyIterator implements Iterator { - - public boolean hasNext() { - if (acc == null) { - return hasNextService(); - } else { - // ... - } - } - - private boolean hasNextService() { - if (nextName != null) { - return true; - } - if (configs == null) { - try { - String fullName = PREFIX + service.getName(); - if (loader == null) - configs = ClassLoader.getSystemResources(fullName); - else - // 加载资源文件 - configs = loader.getResources(fullName); - } catch (IOException x) { - fail(service, "Error locating configuration files", x); - } - } - while ((pending == null) || !pending.hasNext()) { - if (!configs.hasMoreElements()) { - return false; - } - // 解析出资源文件中写入的实现类类名 - pending = parse(service, configs.nextElement()); - } - // 获取一个类名 - nextName = pending.next(); - return true; - } -} -``` - -![image-20210129111231212](/assets/img/blog1/image-20210129111231212.png) - -`hasNext()` 的调用可以获取到我们资源中的类名, 写入到实例属性 `nextName` 中, 并返回 `true`, 让迭代器可以进行 `next()` 的调用 - -```java -public S next() { - if (acc == null) { - return nextService(); - } else { - // ... - } -} - -private S nextService() { - if (!hasNextService()) throw new NoSuchElementException(); - String cn = nextName; - nextName = null; - Class c = null; - try { - // 反射得到 Class 对象 - c = Class.forName(cn, false, loader); - } catch (ClassNotFoundException x) { - fail(service, "Provider " + cn + " not found"); - } - if (!service.isAssignableFrom(c)) { - fail(service, "Provider " + cn + " not a subtype"); - } - try { - // 初始化对象, 并判断是否与接口符合 - S p = service.cast(c.newInstance()); - // 将初始化的对象放入hash缓存 (关键步骤) - providers.put(cn, p); - return p; - } catch (Throwable x) { - fail(service, "Provider " + cn + " could not be instantiated", x); - } - throw new Error(); // This cannot happen -} -``` - -看到这里我们明白了, 在初始化后会将对象放入缓存中, key 就是接口 class 二次调用不会再有反射消耗. - -那么之前我们在测试时的方式为什么会产生不同对象实例呢? 原因就是每次调用 `ServiceLoader.load()` 都会产生新的 `ServiceLoader` 对象. 我们将测试方法改进下: - -```java -public static void main(String[] args) { - // 复用 ServiceLoaders - final ServiceLoader load = ServiceLoader.load(Animal.class); - for (int i = 0; i < 2; i++) { - test(load); - System.out.println("----------"); - } -} - -private static void test(ServiceLoader load) { - for (Animal animal : load) { - System.out.println(animal); - animal.run(); - } -} -``` - -![image-20210129113307494](/assets/img/blog1/image-20210129113307494.png) - -### Java SPI 思考 - -Java SPI 中我们还有很多的细节没有描述到, 但主流程就是这些. 我们之前的两个疑问点, 如何实现以及性能情况也可以得到解答: - -1. 如何实现: 通过 IO 流读取到资源文件, 反射加载对应路径并生成 Class 对象, 初始化后放入缓存中 -2. 性能情况: 首次迭代调用即会有反射调用, 但多次使用时, 只要保证是用同一个 ServiceLoader 对象, 即可避免多次反射, 因为会直接复用缓存中的对象. - -写到这我有个非常疑惑的地方, 之前我觉得它和工厂方法很类似但比它有优势, 因为添加子类后仅需用改动资源文件不用变动工厂类. - -但我尝试用 Java SPI 去真正实现时, 发现并不能达到这个效果, 一个重要的原因是, **资源文件中的各个实现类没有区分度**, 我无法去筛选出某一个我需要的缓存在 `ServiceLoaders` 中的实现类. - -那么它的使用场景在哪呢? - -## JDBC SPI 使用方式 - -经过查阅资料得知, 在 JDBC 中最关键的可插拔式驱动设计就是由 SPI 实现. - -### Mysql 驱动包 SPI - -各个数据库连接包中关于 JDBC 方式实现, 都需要实现其 Driver 接口, 这块其实用的就是 SPI 的方式, 我们看看 `mysql-connector-java.jar` - -![image-20210130202512831](/assets/img/blog1/image-20210130202512831.png) - -那么 JDK 中的 JDBC 相关类, 是如何实现这块的? 关键类就是 DriverManager - -```java -public class DriverManager { - - static { - loadInitialDrivers(); - } - - private static void loadInitialDrivers() { - // ... - - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - - // 这里就是 SPI 的实现, 迭代时实际会 Class.forName() 初始化实现类 - ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class); - Iterator driversIterator = loadedDrivers.iterator(); - try{ - while(driversIterator.hasNext()) { - driversIterator.next(); - } - } catch(Throwable t) { - // Do nothing - } - return null; - } - }); - - // ... - } -} -``` - -如果代码中调用到 DriverManager 的静态方法, 即会触发上面这些代码, 而这些代码的**作用便是将 SPI 资源文件中 Driver 实现类全部初始化**, 那么初始化实现类后又有什么作用呢? 接着看看 `com.mysql.jdbc.Driver` - -```java -public class Driver extends NonRegisteringDriver implements java.sql.Driver { - static { - try { - // 调用 DriverManager 的注册方法, 将此 Driver 实现类注册到 JDBC 的 Driver 管理器中 - java.sql.DriverManager.registerDriver(new Driver()); - } catch (SQLException E) { - throw new RuntimeException("Can't register driver!"); - } - } -} -``` - -DriverManager 的注册方法实现很简单, 即将入参放入静态变量作为全局缓存 - -```java -public class DriverManager { - // 缓存 Driver 实现类 - private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>(); - - public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { - registerDriver(driver, null); - } - - public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { - if(driver != null) { - // 注册到变量中 - registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); - } else { - throw new NullPointerException(); - } - } -} -``` - -### 筛选 Driver: 约定大于配置 - -正常使用时, 我们会直接用 `DriverManager.getConnection(url, user, passwd)` 获取到连接, 但这里就有疑问了, 我们在 DriverManager 中注册了多个 Driver, 为什么这里能确定一个唯一 Driver 呢? - -先找到 DriverManager 的 `getConnection()` 方法: - -```java -public static Connection getConnection(String url, String user, String password) throws SQLException { - // ... - return (getConnection(url, info, Reflection.getCallerClass())); -} - -private static Connection getConnection( - String url, java.util.Properties info, Class caller) throws SQLException { - - // ... - - for(DriverInfo aDriver : registeredDrivers) { - // isDriverAllowed() 仅是通过 Class.forName() 初始化, 没有甄别作用 - if(isDriverAllowed(aDriver.driver, callerCL)) { - try { - // 最关键的点在这行, 筛选工作其实在实现类自身的 connect() 方法中, 会根据传入的 url 筛选 - Connection con = aDriver.driver.connect(url, info); - if (con != null) { - return (con); - } - } catch (SQLException ex) { - } - } else { - } - - } - - // ... -} -``` - -看看最重要的 Mysql 的 Driver 中如何实现筛选 (Driver 继承自 NonRegisteringDriver) - -```java -public class NonRegisteringDriver implements java.sql.Driver { - private static final String URL_PREFIX = "jdbc:mysql://"; - private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://"; - private static final String MXJ_URL_PREFIX = "jdbc:mysql:mxj://"; - public static final String LOADBALANCE_URL_PREFIX = "jdbc:mysql:loadbalance://"; - - public java.sql.Connection connect(String url, Properties info) throws SQLException { - // ... - // parseURL() 会匹配 url 是否符合其所在 Driver 的连接方式 - // 这里就是采用"约定大于配置"的思想, 通过匹配路径头做筛选 - if ((props = parseURL(url, info)) == null) { - return null; - } - - // ... - } - - public Properties parseURL(String url, Properties defaults) throws java.sql.SQLException { - // ... - // 如果 url 不匹配此 Driver 的路径则返回null, 最外层会继续尝试下个 Driver - if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX) - && !StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { - return null; - } - - // ... - } -} -``` - -### 总结 MySQL & JDBC - -看到这里我想你已经了解 MySQL & JDBC 中关于 SPI 的实现方式了, 归纳几点 - -- JDBC 中的 DriverManager 会加载 SPI 资源文件, 将 `java.sql.Driver` 的实现类全部初始化 -- 其实现类初始化时, 会自主创建自身对象并注入到 DriverManager 中进行统一管理 -- DriverManager 对于管理的 Driver 筛选方式是交由 Driver 实现类自身进行的, 它仅负责遍历并取出可用的 Driver -- Driver 实现类通过传入的数据库 url 头, 判断是否该返回自身. 如果判断为否则返回 `null`. JDBC 的 DriverManager 接收到 `null` 会继续下个 Driver 实现类的调用. -- MySql 驱动实选方案是路径头匹配, 是一种 **约定大于配置的思想** - -### JDBC Demo - -写完这些分析我们再来看如果实现个简单的 demo. - -先分享个我以前写的方式 - -```java -static { - try { - // 反射, 该类加载时会在静态块中, 向 DriverManager 注册 Driver - Class.forName("com.mysql.jdbc.Driver"); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } -} - -public static void main(String[] args) { - try ( - final Connection conn = DriverManager.getConnection(url, user, passwd); - final Statement stmt = conn.createStatement(); - final ResultSet rs = stmt.executeQuery("select count(1) from test") - ) { - while (rs.next()) { - int count = rs.getInt("count(1)"); - System.out.println(count); - } - } catch (Exception e) { - e.printStackTrace(); - } -} -``` - -虽然这样可以使用, 但不觉得有多余的代码吗? 看看我新写的方式 - -```java -public static void main(String[] args) throws ClassNotFoundException { - try ( - final Connection conn = DriverManager.getConnection(url, user, passwd); - final Statement stmt = conn.createStatement(); - final ResultSet rs = stmt.executeQuery("select count(1) from test") - ) { - while (rs.next()) { - int count = rs.getInt("count(1)"); - System.out.println(count); - } - } catch (Exception e) { - e.printStackTrace(); - } -} -``` - -仅仅需要这些简单的代码即可, `DriverManager.getConnection()` 被调用时 DriverManager 会自动加载 SPI 中的实现类, 不需要我们再去 `Class.forName()` 手动调用 `java.mysql.Driver` 的初始化. - -**看到这里我想你依然明白 SPI 最最重要的作用了. 无需显式的写出接口对应的实现类** - -那么我们还有个在 "Java SPI 思考" 中的问题也解开了. **如何区分出 SPI 中要使用的实现类呢? 让实现类自己判定即可, 外层调用仅需迭代所有. ** - -## SOUL SPI 实现 - -Java 中 SPI 的使用方式我们已经掰开来了解透彻了, 而 Soul 中的 SPI 是自己设计的, 采用 Dubbo 中 SPI 的设计思想. 在 `org.dromara.soul.spi.SPI` 注释类上可以看到相关注释. - -```java -/** - * SPI Extend the processing. - * All spi system reference the apache implementation of - * https://github.com/apache/dubbo/blob/master/dubbo-common/src/main/java/org/apache/dubbo/common/extension. - */ -``` - -### Java SPI 缺陷 - -在上两个模块中分析 Java SPI 使用时, 发现了些缺点: - -1. 如果使用 ServiceLoader 不当, **没有正确利用到它的缓存机制**, 会导致每次获取具体实现类都要反射出类对象以及初始化实例对象, 性能完蛋不说, 每次得到的对象都不一样可能会引发程序问题. -2. 即每次找寻具体实现类都要迭代一遍才行, 虽然子类少的使用没什么影响, 但这种方式还是很傻. 另外参考 MySQL 驱动中 JDBC 的实现, 还需要自行设计一套比较复杂的筛选机制. - -那么 Soul SPI 的实现, 是如何解决这两个问题的? 关键就在接下来的两个子模块中 - -- 优化的 ExtensionLoader -- 增强型 getJoin() - -### 优化的 ExtensionLoader - -先来看 SPI 实现项目的全貌, 项目为 `soul-spi`: - -![image-20210130214402997](/assets/img/blog1/image-20210130214402997.png) - -其中最核心的类就是 ExtensionLoader, 可以说是 Soul 版的 ServiceLoader, 它也定义了 SPI 资源文件的路径位置 - -```java -public final class ExtensionLoader { - private static final String SOUL_DIRECTORY = "META-INF/soul/"; -} -``` - -通过检查它各个方法的调用处, 我们找到入口方法 `getExtensionLoader()` - -```java -public final class ExtensionLoader { - - private static final Map, ExtensionLoader> LOADERS = new ConcurrentHashMap<>(); - - public static ExtensionLoader getExtensionLoader(final Class clazz) { - // ... - - // 根据加载类对象取出缓存中数据, 如果没有则新建 ExtensionLoader 对象并放入缓存 - ExtensionLoader extensionLoader = (ExtensionLoader) LOADERS.get(clazz); - if (extensionLoader != null) { - return extensionLoader; - } - LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz)); - return (ExtensionLoader) LOADERS.get(clazz); - } -} -``` - -这个方法的作用其实就像是 ServiceLoader 的 `load()` 方法, 会返回一个 ServiceLoader 对象. - -只是 Soul 中的实现改了种方式, 将 ExtensionLoader 对象缓存起来, 这样 **二次调用时传入相同 Class 对象也会返回同样的 ExtensionLoader, 避免了 ServiceLoader 使用时不理解其机制导致没有用到它的缓存, 每次迭代都去反射初始化所有实现类** - -### 增强型搜索 getJoin() - -再来看看 ExtensionLoader 的 `getJoin()` 方法, 我将它理解为 **更优的 ServiceLoader 迭代器版实现**. 它同样是做了两件 ServiceLoader 迭代时做过的事情: - -- 初始化 SPI 中的实现类 - -- 将实现类缓存 -> 缓存为 Key-Value 形式的 Map 集合 - -基于 K-V 缓存模式, 它还做了一件我最期待的改造: - -- 时间复杂度 `O(1)` 的直接匹配实现类方式 - -### 多层缓存 - -ExtensionLoader 之所以能做到这种增强型搜索, 无需每次都迭代所有, 是依靠三种不同类型的缓存. - -这三种缓存我将它分为二层, 它们各有不同用途, 总览如下: - -```java -// 一层缓存 -private final Map> cachedInstances = new ConcurrentHashMap<>(); - -// 二层缓存之一 -private final Holder>> cachedClasses = new Holder<>(); - -// 二层缓存之一 -private final Map, Object> joinInstances = new ConcurrentHashMap<>(); -``` - -#### 第一层缓存: cachedInstances - -首先是第一层缓存, 它是我们搜索接口的具体实现类时最先接触到的, 如果命中它则直接可以得到实现类的对象 - -```java -private final Map> cachedInstances = new ConcurrentHashMap<>(); -``` - -它的 `key` 其实就是 Soul SPI 资源文件中我们配置的信息, 比如 Divide 插件的负载均衡实现类的资源文件 - -![image-20210130230250748](/assets/img/blog1/image-20210130230250748.png) - -而它的 `value` 则是 Holder 对象, 其中存有实现类的对象. 调用 `getJoin()` 时传入标识 (比如 random) 获得实现类对象. - -```java -public T getJoin(final String name) { - // ... - Holder objectHolder = cachedInstances.get(name); - Object value = objectHolder.getValue(); - // ... - return (T) value; -} -``` - -#### 第二层缓存之: cachedClasses - -`cachedClasses` 存放的是 标识(random) 与 类对象 的映射 - -```java -private final Holder>> cachedClasses = new Holder<>(); -``` - -`cachedClasses` 缓存的信息如何填充的呢? 是直接触发到检索 SPI 资源文件, 然后解析成 `cachedClasses` 缓存. 具体方法在 `loadResources()` 中 - -```java -private void loadResources(final Map> classes, final URL url) throws IOException { - Properties properties = new Properties(); - // 解析资源文件 - properties.load(inputStream); - properties.forEach((name, classPath) -> { - // 读出 K-V 结构并组装成 classes, 外层调用会包装到 cachedClasses - loadClass(classes, name, classPath); - }); -} -``` - -#### 第二层缓存之: joinInstances - -`joinInstances` 缓存存放的是 类对象与对象实例 的映射 - -```java -private final Map, Object> joinInstances = new ConcurrentHashMap<>(); -``` - -这一层缓存会借助第二层缓存, 得到对应标识(random) 的类对象, 并通过类对象初始化实例, 缓存到自身中. 对应实现方法为 `createExtension()` - -```java -private T createExtension(final String name) { - Class aClass = getExtensionClasses().get(name); - Object o = joinInstances.get(aClass); - if (o == null) { - joinInstances.putIfAbsent(aClass, aClass.newInstance()); - } - return (T) o; -} -``` - -#### 缓存小结 - -通过 ExtensionLoader 加载某个接口的实现类时, 缓存调用流程图如下: - -![09](/assets/img/blog1/09.png) - -### 详细源码分析 (可跳过) - -```java -// name 理解为标识, 用于甄别 SPI 文件中, 想要获取的某个实现类 -public T getJoin(final String name) { - // ... - // cachedInstances 缓存所有 Holder 对象. Holder 对象的 value 属性存放具体实现类 - // 我将 cachedInstances 理解为第一层缓存, 命中则直接返回要找的类 - Holder objectHolder = cachedInstances.get(name); - if (objectHolder == null) { - cachedInstances.putIfAbsent(name, new Holder<>()); - objectHolder = cachedInstances.get(name); - } - Object value = objectHolder.getValue(); - // 双重锁, 如果没有命中则调用 createExtension() - if (value == null) { - synchronized (cachedInstances) { - value = objectHolder.getValue(); - if (value == null) { - value = createExtension(name); - objectHolder.setValue(value); - } - } - } - return (T) value; -} -``` - -```java -private T createExtension(final String name) { - // 关键代码, 搜索标识对应的类对象 - Class aClass = getExtensionClasses().get(name); - if (aClass == null) { - throw new IllegalArgumentException("name is error"); - } - // joinInstances 理解为第二层缓存, K-V 存放类对象与其初始化对象 - Object o = joinInstances.get(aClass); - if (o == null) { - try { - joinInstances.putIfAbsent(aClass, aClass.newInstance()); - o = joinInstances.get(aClass); - } catch (InstantiationException | IllegalAccessException e) { - // ... - } - } - return (T) o; -} -``` - -```java -public Map> getExtensionClasses() { - // cachedClasses 为第三层缓存, 存放标识与类对象映射 - Map> classes = cachedClasses.getValue(); - if (classes == null) { - synchronized (cachedClasses) { - classes = cachedClasses.getValue(); - if (classes == null) { - // 构造 classes 缓存, classes 的 K-V 结构为 标识-类对象 - classes = loadExtensionClass(); - cachedClasses.setValue(classes); - } - } - } - return classes; -} -``` - -```java -private Map> loadExtensionClass() { - // 拿到接口的 SPI 注解 - SPI annotation = clazz.getAnnotation(SPI.class); - if (annotation != null) { - String value = annotation.value(); - if (StringUtils.isNotBlank(value)) { - cachedDefaultName = value; - } - } - // 构造 classes 缓存, classes 的 K-V 结构为 标识-类对象 - Map> classes = new HashMap<>(16); - loadDirectory(classes); - return classes; -} -``` - -```java -private void loadDirectory(final Map> classes) { - String fileName = SOUL_DIRECTORY + clazz.getName(); - try { - ClassLoader classLoader = ExtensionLoader.class.getClassLoader(); - // 读取 SPI 资源文件 - Enumeration urls = classLoader != null ? classLoader.getResources(fileName) - : ClassLoader.getSystemResources(fileName); - if (urls != null) { - while (urls.hasMoreElements()) { - URL url = urls.nextElement(); - // 构造 classes 缓存, classes 的 K-V 结构为 标识-类对象 - loadResources(classes, url); - } - } - } -} -``` - -```java -private void loadResources(final Map> classes, final URL url) throws IOException { - try (InputStream inputStream = url.openStream()) { - Properties properties = new Properties(); - properties.load(inputStream); - // 解析资源文件为 KV 结构 - properties.forEach((k, v) -> { - String name = (String) k; - String classPath = (String) v; - if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(classPath)) { - try { - // 加载路径, 传入 classes 缓存、标识、类路径 - loadClass(classes, name, classPath); - } catch (ClassNotFoundException e) { - throw new IllegalStateException("load extension resources error", e); - } - } - }); - } -} -``` - -```java -private void loadClass(final Map> classes, - final String name, final String classPath) throws ClassNotFoundException { - // 将资源文件中的类路径反射成类对象 - Class subClass = Class.forName(classPath); - // 拿到实现类的 Join 注解 - Join annotation = subClass.getAnnotation(Join.class); - Class oldClass = classes.get(name); - if (oldClass == null) { - // 放入入参 classes 缓存中, K-V 形式为 标识-类对象 - classes.put(name, subClass); - } -} -``` +--- +title: Soul网关学习SPI学习使用 +author: 朱明 +date: 2021-01-30 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +# SOUL 中 SPI 的使用 + +在之前分析 divide 插件的负载均衡策略时, 有看到过一行代码: + +```java +DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); +``` + +当时很简单的略过了它的实现, 它的作用很容易分析, 调用一个看似工具类的方法, 传入多个节点组成的集群, 返回一个节点. 这是一个负载均衡器. + +但是细节却非常多, 最重要的一点是使用 SPI 来选择具体的实现类. 看看这个方法的代码: + +```java +public class LoadBalanceUtils { + + public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) { + // 调用自定义的 SPI 得到一个子类 + LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm); + return loadBalance.select(upstreamList, ip); + } +} +``` + +后面的是调用具体子类的 `select()` 方法, 根据子类的不同实现, 最终会表现出各种形式. 目前的子类实现有: + +- HashLoadBalance +- RandomLoadBalance +- RoundRobinLoadBalance + +关键就在于 `ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);` 这行. + +在研究它之前, 我们先不妨研究下 Java 提供的 SPI 机制. + +## Java SPI + +_<<高可用可伸缩微服务架构>> 第 3 章 Apache Dubbo 框架的原理与实现_ 中有这样的一句定义. + +> SPI 全称为 Service Provider Interface, 是 JDK 内置的一种服务提供发现功能, 一种动态替换发现的机制. 举个例子, 要想在运行时动态地给一个接口添加实现, 只需要添加一个实现即可. + +书中也有个非常形象的脑图, 展示了 SPI 的使用: + +![08](/assets/img/blog1/08.png) + +也就是说在我们代码中的实现里, 无需去写入一个 Factory 工厂, 用 MAP 去包装一些子类, 最终返回的类型是父接口. 只需要定义好资源文件, 让父接口与它的子类在文件中写明, 即可通过设置好的方式拿到所有定义的子类对象: + +```java +ServiceLoader loaders = ServiceLoader.load(Interface.class) +for(Interface interface : loaders){ + System.out.println(interface.toString()); +} +``` + +这种方式相比与普通的工厂模式, 肯定是更符合开闭原则, 新加入一个子类不用去修改工厂方法, 而是编辑资源文件. + +### 从一个 Demo 开始 + +按照 SPI 的规范, 我建了一个 demo, 看看具体的实现效果 + +![image-20210129095623013](/assets/img/blog1/image-20210129095623013.png) + +![image-20210129095703911](/assets/img/blog1/image-20210129095703911.png) + +Animal 中定义一个 `run()` 方法, 而子类实现它. + +```java +public interface Animal { + void run(); +} + +public class Dog implements Animal { + @Override + public void run() { + System.out.println("狗在跑"); + } +} + +public class Horse implements Animal { + @Override + public void run() { + System.out.println("马在跑"); + } +} +``` + +使用 SPI 的加载类, 得到子类的执行结果: + +```java +private static void test() { + final ServiceLoader load = ServiceLoader.load(Animal.class); + + for (Animal animal : load) { + System.out.println(animal); + animal.run(); + } +} +``` + +![image-20210129103047851](/assets/img/blog1/image-20210129103047851.png) + +在调用后我们得到之前在资源文件中写入的实现类, 并成功调取它们各自的 `run()` 方法. + +到这里我产生一个疑问, **是否每次调用 `ServiceLoader.load(Animal.class)` 返回的都是同一个对象?** 如果是我猜测它是在启动时加载到缓存了, 如果不是, 可能就是在底层用了反射, 每次调用都有一定消耗. 我们看看下面的实验: + +```java +public static void main(String[] args) { + for (int i = 0; i < 2; i++) { + test(); + System.out.println("----------"); + } +} + +private static void test() { + final ServiceLoader load = ServiceLoader.load(Animal.class); + for (Animal animal : load) { + System.out.println(animal); + animal.run(); + } +} +``` + +![image-20210129103451844](/assets/img/blog1/image-20210129103451844.png) + +两次调用出现的对象却不一样, 不由让我替其性能揪心一下, 所以我们先分析下它的代码, 看看到底怎么实现. + +### SPI 的实现 + +找到 `java.util,ServiceLoaders` 这个类, 入眼最醒目的就是之前我们按照规范放置资源文件的目录 + +```java +public final class ServiceLoader implements Iterable { + + private static final String PREFIX = "META-INF/services/"; +} +``` + +在 debug `PREFIX` 属性的被调用处时, 发现 `ServiceLoader.load` 实际是使用懒加载的方式, 并没有在调用它的时候, 找寻到实际返回类, 而是在遍历时查找. + +它的懒加载具体实现在如下代码: + +```java +public final class ServiceLoader implements Iterable { + + public static ServiceLoader load(Class service) { + // 获取当前的类加载器 (我们自己的通常是弟中弟 AppClassLoader ) + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return ServiceLoader.load(service, cl); + } + + public static ServiceLoader load(Class service, ClassLoader loader) { + // 调用构造器初始化对象 (说明每次调用都使用新的 ServiceLoader 对象) + return new ServiceLoader<>(service, loader); + } + + private ServiceLoader(Class svc, ClassLoader cl) { + service = Objects.requireNonNull(svc, "Service interface cannot be null"); + loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; + acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; + // 上面都是将信息放入对象实例属性中, 这行才是关键调用 + reload(); + } + + public void reload() { + providers.clear(); + // 创建懒加载迭代器, 传入关键的接口 Class 以及加载器 + lookupIterator = new LazyIterator(service, loader); + } +} + +``` + +调用 `ServiceLoader.load` 后关键事情都没干, 仅仅是将接口 class 和加载器传给 LazyIterator 这个迭代器的实现类. + +看到这可以猜测, 真正迭代调用返回的对象时, 肯定需要迭代器完成实现类的搜索和初始化, 而传参是 Class 信息和加载器, 实现类的初始化也明显会是反射了. + +看下 LazyIterator 的实现方式, 先从其最开始会被调用到的 `hasNext()` 开始: + +```java +private class LazyIterator implements Iterator { + + public boolean hasNext() { + if (acc == null) { + return hasNextService(); + } else { + // ... + } + } + + private boolean hasNextService() { + if (nextName != null) { + return true; + } + if (configs == null) { + try { + String fullName = PREFIX + service.getName(); + if (loader == null) + configs = ClassLoader.getSystemResources(fullName); + else + // 加载资源文件 + configs = loader.getResources(fullName); + } catch (IOException x) { + fail(service, "Error locating configuration files", x); + } + } + while ((pending == null) || !pending.hasNext()) { + if (!configs.hasMoreElements()) { + return false; + } + // 解析出资源文件中写入的实现类类名 + pending = parse(service, configs.nextElement()); + } + // 获取一个类名 + nextName = pending.next(); + return true; + } +} +``` + +![image-20210129111231212](/assets/img/blog1/image-20210129111231212.png) + +`hasNext()` 的调用可以获取到我们资源中的类名, 写入到实例属性 `nextName` 中, 并返回 `true`, 让迭代器可以进行 `next()` 的调用 + +```java +public S next() { + if (acc == null) { + return nextService(); + } else { + // ... + } +} + +private S nextService() { + if (!hasNextService()) throw new NoSuchElementException(); + String cn = nextName; + nextName = null; + Class c = null; + try { + // 反射得到 Class 对象 + c = Class.forName(cn, false, loader); + } catch (ClassNotFoundException x) { + fail(service, "Provider " + cn + " not found"); + } + if (!service.isAssignableFrom(c)) { + fail(service, "Provider " + cn + " not a subtype"); + } + try { + // 初始化对象, 并判断是否与接口符合 + S p = service.cast(c.newInstance()); + // 将初始化的对象放入hash缓存 (关键步骤) + providers.put(cn, p); + return p; + } catch (Throwable x) { + fail(service, "Provider " + cn + " could not be instantiated", x); + } + throw new Error(); // This cannot happen +} +``` + +看到这里我们明白了, 在初始化后会将对象放入缓存中, key 就是接口 class 二次调用不会再有反射消耗. + +那么之前我们在测试时的方式为什么会产生不同对象实例呢? 原因就是每次调用 `ServiceLoader.load()` 都会产生新的 `ServiceLoader` 对象. 我们将测试方法改进下: + +```java +public static void main(String[] args) { + // 复用 ServiceLoaders + final ServiceLoader load = ServiceLoader.load(Animal.class); + for (int i = 0; i < 2; i++) { + test(load); + System.out.println("----------"); + } +} + +private static void test(ServiceLoader load) { + for (Animal animal : load) { + System.out.println(animal); + animal.run(); + } +} +``` + +![image-20210129113307494](/assets/img/blog1/image-20210129113307494.png) + +### Java SPI 思考 + +Java SPI 中我们还有很多的细节没有描述到, 但主流程就是这些. 我们之前的两个疑问点, 如何实现以及性能情况也可以得到解答: + +1. 如何实现: 通过 IO 流读取到资源文件, 反射加载对应路径并生成 Class 对象, 初始化后放入缓存中 +2. 性能情况: 首次迭代调用即会有反射调用, 但多次使用时, 只要保证是用同一个 ServiceLoader 对象, 即可避免多次反射, 因为会直接复用缓存中的对象. + +写到这我有个非常疑惑的地方, 之前我觉得它和工厂方法很类似但比它有优势, 因为添加子类后仅需用改动资源文件不用变动工厂类. + +但我尝试用 Java SPI 去真正实现时, 发现并不能达到这个效果, 一个重要的原因是, **资源文件中的各个实现类没有区分度**, 我无法去筛选出某一个我需要的缓存在 `ServiceLoaders` 中的实现类. + +那么它的使用场景在哪呢? + +## JDBC SPI 使用方式 + +经过查阅资料得知, 在 JDBC 中最关键的可插拔式驱动设计就是由 SPI 实现. + +### Mysql 驱动包 SPI + +各个数据库连接包中关于 JDBC 方式实现, 都需要实现其 Driver 接口, 这块其实用的就是 SPI 的方式, 我们看看 `mysql-connector-java.jar` + +![image-20210130202512831](/assets/img/blog1/image-20210130202512831.png) + +那么 JDK 中的 JDBC 相关类, 是如何实现这块的? 关键类就是 DriverManager + +```java +public class DriverManager { + + static { + loadInitialDrivers(); + } + + private static void loadInitialDrivers() { + // ... + + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + + // 这里就是 SPI 的实现, 迭代时实际会 Class.forName() 初始化实现类 + ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class); + Iterator driversIterator = loadedDrivers.iterator(); + try{ + while(driversIterator.hasNext()) { + driversIterator.next(); + } + } catch(Throwable t) { + // Do nothing + } + return null; + } + }); + + // ... + } +} +``` + +如果代码中调用到 DriverManager 的静态方法, 即会触发上面这些代码, 而这些代码的**作用便是将 SPI 资源文件中 Driver 实现类全部初始化**, 那么初始化实现类后又有什么作用呢? 接着看看 `com.mysql.jdbc.Driver` + +```java +public class Driver extends NonRegisteringDriver implements java.sql.Driver { + static { + try { + // 调用 DriverManager 的注册方法, 将此 Driver 实现类注册到 JDBC 的 Driver 管理器中 + java.sql.DriverManager.registerDriver(new Driver()); + } catch (SQLException E) { + throw new RuntimeException("Can't register driver!"); + } + } +} +``` + +DriverManager 的注册方法实现很简单, 即将入参放入静态变量作为全局缓存 + +```java +public class DriverManager { + // 缓存 Driver 实现类 + private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>(); + + public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { + registerDriver(driver, null); + } + + public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { + if(driver != null) { + // 注册到变量中 + registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); + } else { + throw new NullPointerException(); + } + } +} +``` + +### 筛选 Driver: 约定大于配置 + +正常使用时, 我们会直接用 `DriverManager.getConnection(url, user, passwd)` 获取到连接, 但这里就有疑问了, 我们在 DriverManager 中注册了多个 Driver, 为什么这里能确定一个唯一 Driver 呢? + +先找到 DriverManager 的 `getConnection()` 方法: + +```java +public static Connection getConnection(String url, String user, String password) throws SQLException { + // ... + return (getConnection(url, info, Reflection.getCallerClass())); +} + +private static Connection getConnection( + String url, java.util.Properties info, Class caller) throws SQLException { + + // ... + + for(DriverInfo aDriver : registeredDrivers) { + // isDriverAllowed() 仅是通过 Class.forName() 初始化, 没有甄别作用 + if(isDriverAllowed(aDriver.driver, callerCL)) { + try { + // 最关键的点在这行, 筛选工作其实在实现类自身的 connect() 方法中, 会根据传入的 url 筛选 + Connection con = aDriver.driver.connect(url, info); + if (con != null) { + return (con); + } + } catch (SQLException ex) { + } + } else { + } + + } + + // ... +} +``` + +看看最重要的 Mysql 的 Driver 中如何实现筛选 (Driver 继承自 NonRegisteringDriver) + +```java +public class NonRegisteringDriver implements java.sql.Driver { + private static final String URL_PREFIX = "jdbc:mysql://"; + private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://"; + private static final String MXJ_URL_PREFIX = "jdbc:mysql:mxj://"; + public static final String LOADBALANCE_URL_PREFIX = "jdbc:mysql:loadbalance://"; + + public java.sql.Connection connect(String url, Properties info) throws SQLException { + // ... + // parseURL() 会匹配 url 是否符合其所在 Driver 的连接方式 + // 这里就是采用"约定大于配置"的思想, 通过匹配路径头做筛选 + if ((props = parseURL(url, info)) == null) { + return null; + } + + // ... + } + + public Properties parseURL(String url, Properties defaults) throws java.sql.SQLException { + // ... + // 如果 url 不匹配此 Driver 的路径则返回null, 最外层会继续尝试下个 Driver + if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX) + && !StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { + return null; + } + + // ... + } +} +``` + +### 总结 MySQL & JDBC + +看到这里我想你已经了解 MySQL & JDBC 中关于 SPI 的实现方式了, 归纳几点 + +- JDBC 中的 DriverManager 会加载 SPI 资源文件, 将 `java.sql.Driver` 的实现类全部初始化 +- 其实现类初始化时, 会自主创建自身对象并注入到 DriverManager 中进行统一管理 +- DriverManager 对于管理的 Driver 筛选方式是交由 Driver 实现类自身进行的, 它仅负责遍历并取出可用的 Driver +- Driver 实现类通过传入的数据库 url 头, 判断是否该返回自身. 如果判断为否则返回 `null`. JDBC 的 DriverManager 接收到 `null` 会继续下个 Driver 实现类的调用. +- MySql 驱动实选方案是路径头匹配, 是一种 **约定大于配置的思想** + +### JDBC Demo + +写完这些分析我们再来看如果实现个简单的 demo. + +先分享个我以前写的方式 + +```java +static { + try { + // 反射, 该类加载时会在静态块中, 向 DriverManager 注册 Driver + Class.forName("com.mysql.jdbc.Driver"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } +} + +public static void main(String[] args) { + try ( + final Connection conn = DriverManager.getConnection(url, user, passwd); + final Statement stmt = conn.createStatement(); + final ResultSet rs = stmt.executeQuery("select count(1) from test") + ) { + while (rs.next()) { + int count = rs.getInt("count(1)"); + System.out.println(count); + } + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +虽然这样可以使用, 但不觉得有多余的代码吗? 看看我新写的方式 + +```java +public static void main(String[] args) throws ClassNotFoundException { + try ( + final Connection conn = DriverManager.getConnection(url, user, passwd); + final Statement stmt = conn.createStatement(); + final ResultSet rs = stmt.executeQuery("select count(1) from test") + ) { + while (rs.next()) { + int count = rs.getInt("count(1)"); + System.out.println(count); + } + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +仅仅需要这些简单的代码即可, `DriverManager.getConnection()` 被调用时 DriverManager 会自动加载 SPI 中的实现类, 不需要我们再去 `Class.forName()` 手动调用 `java.mysql.Driver` 的初始化. + +**看到这里我想你依然明白 SPI 最最重要的作用了. 无需显式的写出接口对应的实现类** + +那么我们还有个在 "Java SPI 思考" 中的问题也解开了. **如何区分出 SPI 中要使用的实现类呢? 让实现类自己判定即可, 外层调用仅需迭代所有. ** + +## SOUL SPI 实现 + +Java 中 SPI 的使用方式我们已经掰开来了解透彻了, 而 Soul 中的 SPI 是自己设计的, 采用 Dubbo 中 SPI 的设计思想. 在 `org.dromara.soul.spi.SPI` 注释类上可以看到相关注释. + +```java +/** + * SPI Extend the processing. + * All spi system reference the apache implementation of + * https://github.com/apache/dubbo/blob/master/dubbo-common/src/main/java/org/apache/dubbo/common/extension. + */ +``` + +### Java SPI 缺陷 + +在上两个模块中分析 Java SPI 使用时, 发现了些缺点: + +1. 如果使用 ServiceLoader 不当, **没有正确利用到它的缓存机制**, 会导致每次获取具体实现类都要反射出类对象以及初始化实例对象, 性能完蛋不说, 每次得到的对象都不一样可能会引发程序问题. +2. 即每次找寻具体实现类都要迭代一遍才行, 虽然子类少的使用没什么影响, 但这种方式还是很傻. 另外参考 MySQL 驱动中 JDBC 的实现, 还需要自行设计一套比较复杂的筛选机制. + +那么 Soul SPI 的实现, 是如何解决这两个问题的? 关键就在接下来的两个子模块中 + +- 优化的 ExtensionLoader +- 增强型 getJoin() + +### 优化的 ExtensionLoader + +先来看 SPI 实现项目的全貌, 项目为 `soul-spi`: + +![image-20210130214402997](/assets/img/blog1/image-20210130214402997.png) + +其中最核心的类就是 ExtensionLoader, 可以说是 Soul 版的 ServiceLoader, 它也定义了 SPI 资源文件的路径位置 + +```java +public final class ExtensionLoader { + private static final String SOUL_DIRECTORY = "META-INF/soul/"; +} +``` + +通过检查它各个方法的调用处, 我们找到入口方法 `getExtensionLoader()` + +```java +public final class ExtensionLoader { + + private static final Map, ExtensionLoader> LOADERS = new ConcurrentHashMap<>(); + + public static ExtensionLoader getExtensionLoader(final Class clazz) { + // ... + + // 根据加载类对象取出缓存中数据, 如果没有则新建 ExtensionLoader 对象并放入缓存 + ExtensionLoader extensionLoader = (ExtensionLoader) LOADERS.get(clazz); + if (extensionLoader != null) { + return extensionLoader; + } + LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz)); + return (ExtensionLoader) LOADERS.get(clazz); + } +} +``` + +这个方法的作用其实就像是 ServiceLoader 的 `load()` 方法, 会返回一个 ServiceLoader 对象. + +只是 Soul 中的实现改了种方式, 将 ExtensionLoader 对象缓存起来, 这样 **二次调用时传入相同 Class 对象也会返回同样的 ExtensionLoader, 避免了 ServiceLoader 使用时不理解其机制导致没有用到它的缓存, 每次迭代都去反射初始化所有实现类** + +### 增强型搜索 getJoin() + +再来看看 ExtensionLoader 的 `getJoin()` 方法, 我将它理解为 **更优的 ServiceLoader 迭代器版实现**. 它同样是做了两件 ServiceLoader 迭代时做过的事情: + +- 初始化 SPI 中的实现类 + +- 将实现类缓存 -> 缓存为 Key-Value 形式的 Map 集合 + +基于 K-V 缓存模式, 它还做了一件我最期待的改造: + +- 时间复杂度 `O(1)` 的直接匹配实现类方式 + +### 多层缓存 + +ExtensionLoader 之所以能做到这种增强型搜索, 无需每次都迭代所有, 是依靠三种不同类型的缓存. + +这三种缓存我将它分为二层, 它们各有不同用途, 总览如下: + +```java +// 一层缓存 +private final Map> cachedInstances = new ConcurrentHashMap<>(); + +// 二层缓存之一 +private final Holder>> cachedClasses = new Holder<>(); + +// 二层缓存之一 +private final Map, Object> joinInstances = new ConcurrentHashMap<>(); +``` + +#### 第一层缓存: cachedInstances + +首先是第一层缓存, 它是我们搜索接口的具体实现类时最先接触到的, 如果命中它则直接可以得到实现类的对象 + +```java +private final Map> cachedInstances = new ConcurrentHashMap<>(); +``` + +它的 `key` 其实就是 Soul SPI 资源文件中我们配置的信息, 比如 Divide 插件的负载均衡实现类的资源文件 + +![image-20210130230250748](/assets/img/blog1/image-20210130230250748.png) + +而它的 `value` 则是 Holder 对象, 其中存有实现类的对象. 调用 `getJoin()` 时传入标识 (比如 random) 获得实现类对象. + +```java +public T getJoin(final String name) { + // ... + Holder objectHolder = cachedInstances.get(name); + Object value = objectHolder.getValue(); + // ... + return (T) value; +} +``` + +#### 第二层缓存之: cachedClasses + +`cachedClasses` 存放的是 标识(random) 与 类对象 的映射 + +```java +private final Holder>> cachedClasses = new Holder<>(); +``` + +`cachedClasses` 缓存的信息如何填充的呢? 是直接触发到检索 SPI 资源文件, 然后解析成 `cachedClasses` 缓存. 具体方法在 `loadResources()` 中 + +```java +private void loadResources(final Map> classes, final URL url) throws IOException { + Properties properties = new Properties(); + // 解析资源文件 + properties.load(inputStream); + properties.forEach((name, classPath) -> { + // 读出 K-V 结构并组装成 classes, 外层调用会包装到 cachedClasses + loadClass(classes, name, classPath); + }); +} +``` + +#### 第二层缓存之: joinInstances + +`joinInstances` 缓存存放的是 类对象与对象实例 的映射 + +```java +private final Map, Object> joinInstances = new ConcurrentHashMap<>(); +``` + +这一层缓存会借助第二层缓存, 得到对应标识(random) 的类对象, 并通过类对象初始化实例, 缓存到自身中. 对应实现方法为 `createExtension()` + +```java +private T createExtension(final String name) { + Class aClass = getExtensionClasses().get(name); + Object o = joinInstances.get(aClass); + if (o == null) { + joinInstances.putIfAbsent(aClass, aClass.newInstance()); + } + return (T) o; +} +``` + +#### 缓存小结 + +通过 ExtensionLoader 加载某个接口的实现类时, 缓存调用流程图如下: + +![09](/assets/img/blog1/09.png) + +### 详细源码分析 (可跳过) + +```java +// name 理解为标识, 用于甄别 SPI 文件中, 想要获取的某个实现类 +public T getJoin(final String name) { + // ... + // cachedInstances 缓存所有 Holder 对象. Holder 对象的 value 属性存放具体实现类 + // 我将 cachedInstances 理解为第一层缓存, 命中则直接返回要找的类 + Holder objectHolder = cachedInstances.get(name); + if (objectHolder == null) { + cachedInstances.putIfAbsent(name, new Holder<>()); + objectHolder = cachedInstances.get(name); + } + Object value = objectHolder.getValue(); + // 双重锁, 如果没有命中则调用 createExtension() + if (value == null) { + synchronized (cachedInstances) { + value = objectHolder.getValue(); + if (value == null) { + value = createExtension(name); + objectHolder.setValue(value); + } + } + } + return (T) value; +} +``` + +```java +private T createExtension(final String name) { + // 关键代码, 搜索标识对应的类对象 + Class aClass = getExtensionClasses().get(name); + if (aClass == null) { + throw new IllegalArgumentException("name is error"); + } + // joinInstances 理解为第二层缓存, K-V 存放类对象与其初始化对象 + Object o = joinInstances.get(aClass); + if (o == null) { + try { + joinInstances.putIfAbsent(aClass, aClass.newInstance()); + o = joinInstances.get(aClass); + } catch (InstantiationException | IllegalAccessException e) { + // ... + } + } + return (T) o; +} +``` + +```java +public Map> getExtensionClasses() { + // cachedClasses 为第三层缓存, 存放标识与类对象映射 + Map> classes = cachedClasses.getValue(); + if (classes == null) { + synchronized (cachedClasses) { + classes = cachedClasses.getValue(); + if (classes == null) { + // 构造 classes 缓存, classes 的 K-V 结构为 标识-类对象 + classes = loadExtensionClass(); + cachedClasses.setValue(classes); + } + } + } + return classes; +} +``` + +```java +private Map> loadExtensionClass() { + // 拿到接口的 SPI 注解 + SPI annotation = clazz.getAnnotation(SPI.class); + if (annotation != null) { + String value = annotation.value(); + if (StringUtils.isNotBlank(value)) { + cachedDefaultName = value; + } + } + // 构造 classes 缓存, classes 的 K-V 结构为 标识-类对象 + Map> classes = new HashMap<>(16); + loadDirectory(classes); + return classes; +} +``` + +```java +private void loadDirectory(final Map> classes) { + String fileName = SOUL_DIRECTORY + clazz.getName(); + try { + ClassLoader classLoader = ExtensionLoader.class.getClassLoader(); + // 读取 SPI 资源文件 + Enumeration urls = classLoader != null ? classLoader.getResources(fileName) + : ClassLoader.getSystemResources(fileName); + if (urls != null) { + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + // 构造 classes 缓存, classes 的 K-V 结构为 标识-类对象 + loadResources(classes, url); + } + } + } +} +``` + +```java +private void loadResources(final Map> classes, final URL url) throws IOException { + try (InputStream inputStream = url.openStream()) { + Properties properties = new Properties(); + properties.load(inputStream); + // 解析资源文件为 KV 结构 + properties.forEach((k, v) -> { + String name = (String) k; + String classPath = (String) v; + if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(classPath)) { + try { + // 加载路径, 传入 classes 缓存、标识、类路径 + loadClass(classes, name, classPath); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("load extension resources error", e); + } + } + }); + } +} +``` + +```java +private void loadClass(final Map> classes, + final String name, final String classPath) throws ClassNotFoundException { + // 将资源文件中的类路径反射成类对象 + Class subClass = Class.forName(classPath); + // 拿到实现类的 Join 注解 + Join annotation = subClass.getAnnotation(Join.class); + Class oldClass = classes.get(name); + if (oldClass == null) { + // 放入入参 classes 缓存中, K-V 形式为 标识-类对象 + classes.put(name, subClass); + } +} +``` diff --git a/src/zh/blog/soul_source_learning_12_sign.md b/src/zh/blog/soul_source_learning_12_sign.md index 3a5f3e34d1..94d1c87e8d 100644 --- a/src/zh/blog/soul_source_learning_12_sign.md +++ b/src/zh/blog/soul_source_learning_12_sign.md @@ -1,147 +1,147 @@ ---- -title: Soul网关学习Sign插件 -author: 唐甜 -date: 2021-01-29 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 博客 ---- - -## 介绍 - -sign 插件用来对请求进行签名认证的插件 - -## AK/SK 介绍 - -AK/SK(Access Key ID/Secret Access Key)即访问密钥,包含访问密钥 ID(AK)和秘密访问密钥(SK)两部分,主要用于对用户的调用行为进行鉴权和认证。 - -## 插件使用-以(/dubbo/findAll)为例 - -### 在 SoulBootstrap 的 pom.xml 文件中添加  `sign`  的支持 - -```xml - - - org.dromara - soul-spring-boot-starter-plugin-sign - ${last.version} - - -``` - -### 新增 appKey,secretKey - -![image.png](/assets/img/blog4/01.png) -![image.png](/assets/img/blog4/02.png) -![image.png](/assets/img/blog4/03.png) -![image.png](/assets/img/blog4/04.png) - -## 配置选择器和规则器 - -添加选择器 -![image.png](/assets/img/blog4/05.png) -添加规则器 -![image.png](/assets/img/blog4/06.png) - -### 增加获取鉴权服务 - -在自己服务中增加一个对外访问的方法 - -```java - @GetMapping("/authUrl") - public String authUrl() { - Map map = Maps.newHashMapWithExpectedSize(2); - //timestamp为毫秒数的字符串形式 String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()) - String timetamp = String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()) ; - System.out.println(timetamp); - map.put("timestamp",timetamp); //值应该为毫秒数的字符串形式 - map.put("path", "/dubbo/findAll"); - map.put("version", "1.0.0"); - List storedKeys = Arrays.stream(map.keySet() - .toArray(new String[]{})) - .sorted(Comparator.naturalOrder()) - .collect(Collectors.toList()); - final String sign = storedKeys.stream() - .map(key -> String.join("", key, map.get(key))) - .collect(Collectors.joining()).trim() - .concat("D19CF79F647A465AB9C5C66F430CAD28");//SECRETkey - return DigestUtils.md5DigestAsHex(sign.getBytes()).toUpperCase(); - } - -``` - -下面需要注意的 -![image.png](/assets/img/blog4/07.png) - -### 在网关中增加鉴权头信息 - -![image.png](/assets/img/blog4/08.png) - -### 请求的结果演示 - -通过的返回 -![image.png](/assets/img/blog4/09.png) -5min 超时的返回 -![image.png](/assets/img/blog4/10.png) -appKey 填写错误的返回 -![image.png](/assets/img/blog4/11.png) -签名错误的返回 -![image.png](/assets/img/blog4/12.png) -禁用 sign 插件的返回 -![image.png](/assets/img/blog4/13.png) - -## sign 插件的实现分析 - -### java 中 Pair - -简单的说就是 pair 保存的是一对 key value,而 map 可以保存多对 key value。 -SignPlugin 插件调用 DefaultSignService 中 signVerify 方法 -判断 sign 插件是否可用,如果可用获取在 global 插件存入的 soulContext 并调用 verify 方法 - -```java -if (signData != null && signData.getEnabled()) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - return verify(soulContext, exchange); -} -``` - -verify 方法中 -判断请求头信息是否正确 -如果不正确就抛出  log.error("sign parameters are incomplete,{}", soulContext)异常 - -```java -if (StringUtils.isBlank(soulContext.getAppKey()) - || StringUtils.isBlank(soulContext.getSign()) - || StringUtils.isBlank(soulContext.getTimestamp())) { - log.error("sign parameters are incomplete,{}", soulContext); - return Pair.of(Boolean.FALSE, Constants.SIGN_PARAMS_ERROR); -} -``` - -判断请求时间是否超时 - -```java - if (between > delay) { - return Pair.of(Boolean.FALSE, String.format(SoulResultEnum.SING_TIME_IS_TIMEOUT.getMsg(), delay)); - } -``` - -没有超时继续调用 sign 方法 -获取认证数据,这个数据在 soulAdmin 中配置 - -```java -AppAuthData appAuthData = SignAuthDataCache.getInstance().obtainAuthData(soulContext.getAppKey()); -``` - -后面对 appAuthData 数据进行判断,数据有错误就不通过 -对获取的参数再次签名,判断传入的和再次签名的是否一样 - -```java -String sigKey = SignUtils.generateSign(appAuthData.getAppSecret(), buildParamsMap(soulContext)); -``` - -如果都校验都通过就完成认证 访问请求。 +--- +title: Soul网关学习Sign插件 +author: 唐甜 +date: 2021-01-29 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 博客 +--- + +## 介绍 + +sign 插件用来对请求进行签名认证的插件 + +## AK/SK 介绍 + +AK/SK(Access Key ID/Secret Access Key)即访问密钥,包含访问密钥 ID(AK)和秘密访问密钥(SK)两部分,主要用于对用户的调用行为进行鉴权和认证。 + +## 插件使用-以(/dubbo/findAll)为例 + +### 在 SoulBootstrap 的 pom.xml 文件中添加  `sign`  的支持 + +```xml + + + org.dromara + soul-spring-boot-starter-plugin-sign + ${last.version} + + +``` + +### 新增 appKey,secretKey + +![image.png](/assets/img/blog4/01.png) +![image.png](/assets/img/blog4/02.png) +![image.png](/assets/img/blog4/03.png) +![image.png](/assets/img/blog4/04.png) + +## 配置选择器和规则器 + +添加选择器 +![image.png](/assets/img/blog4/05.png) +添加规则器 +![image.png](/assets/img/blog4/06.png) + +### 增加获取鉴权服务 + +在自己服务中增加一个对外访问的方法 + +```java + @GetMapping("/authUrl") + public String authUrl() { + Map map = Maps.newHashMapWithExpectedSize(2); + //timestamp为毫秒数的字符串形式 String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()) + String timetamp = String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()) ; + System.out.println(timetamp); + map.put("timestamp",timetamp); //值应该为毫秒数的字符串形式 + map.put("path", "/dubbo/findAll"); + map.put("version", "1.0.0"); + List storedKeys = Arrays.stream(map.keySet() + .toArray(new String[]{})) + .sorted(Comparator.naturalOrder()) + .collect(Collectors.toList()); + final String sign = storedKeys.stream() + .map(key -> String.join("", key, map.get(key))) + .collect(Collectors.joining()).trim() + .concat("D19CF79F647A465AB9C5C66F430CAD28");//SECRETkey + return DigestUtils.md5DigestAsHex(sign.getBytes()).toUpperCase(); + } + +``` + +下面需要注意的 +![image.png](/assets/img/blog4/07.png) + +### 在网关中增加鉴权头信息 + +![image.png](/assets/img/blog4/08.png) + +### 请求的结果演示 + +通过的返回 +![image.png](/assets/img/blog4/09.png) +5min 超时的返回 +![image.png](/assets/img/blog4/10.png) +appKey 填写错误的返回 +![image.png](/assets/img/blog4/11.png) +签名错误的返回 +![image.png](/assets/img/blog4/12.png) +禁用 sign 插件的返回 +![image.png](/assets/img/blog4/13.png) + +## sign 插件的实现分析 + +### java 中 Pair + +简单的说就是 pair 保存的是一对 key value,而 map 可以保存多对 key value。 +SignPlugin 插件调用 DefaultSignService 中 signVerify 方法 +判断 sign 插件是否可用,如果可用获取在 global 插件存入的 soulContext 并调用 verify 方法 + +```java +if (signData != null && signData.getEnabled()) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + return verify(soulContext, exchange); +} +``` + +verify 方法中 +判断请求头信息是否正确 +如果不正确就抛出  log.error("sign parameters are incomplete,{}", soulContext)异常 + +```java +if (StringUtils.isBlank(soulContext.getAppKey()) + || StringUtils.isBlank(soulContext.getSign()) + || StringUtils.isBlank(soulContext.getTimestamp())) { + log.error("sign parameters are incomplete,{}", soulContext); + return Pair.of(Boolean.FALSE, Constants.SIGN_PARAMS_ERROR); +} +``` + +判断请求时间是否超时 + +```java + if (between > delay) { + return Pair.of(Boolean.FALSE, String.format(SoulResultEnum.SING_TIME_IS_TIMEOUT.getMsg(), delay)); + } +``` + +没有超时继续调用 sign 方法 +获取认证数据,这个数据在 soulAdmin 中配置 + +```java +AppAuthData appAuthData = SignAuthDataCache.getInstance().obtainAuthData(soulContext.getAppKey()); +``` + +后面对 appAuthData 数据进行判断,数据有错误就不通过 +对获取的参数再次签名,判断传入的和再次签名的是否一样 + +```java +String sigKey = SignUtils.generateSign(appAuthData.getAppSecret(), buildParamsMap(soulContext)); +``` + +如果都校验都通过就完成认证 访问请求。 diff --git a/src/zh/blog/soul_source_learning_13_zookeeper_01.md b/src/zh/blog/soul_source_learning_13_zookeeper_01.md index 78ce893b59..741fc5ac62 100644 --- a/src/zh/blog/soul_source_learning_13_zookeeper_01.md +++ b/src/zh/blog/soul_source_learning_13_zookeeper_01.md @@ -1,287 +1,287 @@ ---- -title: Soul网关学习Zookeeper数据同步01 -author: 李权 -date: 2021-01-20 -tag: - - Soul -cover: '/assets/img/architecture/soul-framework.png' -head: - - - meta - - name: 博客 ---- - -#### 启动 soul-admin、soul-bootstrap, 使用 zookeeper 同步数据到网关 - -###### 一、配置环境 - -1、soul-admin 服务配置,需要重启服务 - -soul-admin/src/main/resources/application.yml - -```yaml -soul: - sync: - zookeeper: - url: localhost:2181 - sessionTimeout: 5000 - connectionTimeout: 2000 -``` - -2、soul-bootstrap 网关服务配置,需要重启 - -soul-bootstrap/pom.xml - -```xml - - - org.dromara - soul-spring-boot-starter-sync-data-zookeeper - ${project.version} - -``` - -soul-bootstrap/src/main/resources/application-local.yml - -```yaml -soul: - sync: - zookeeper: - url: localhost:2181 - sessionTimeout: 5000 - connectionTimeout: 2000 -``` - -###### 二、启动服务 - -1、 启动 zookeeper - -``` -zookeeper ./bin/zkServer.sh start -/usr/bin/java -ZooKeeper JMX enabled by default -Using config: /Documents/soft/zookeeper/bin/../conf/zoo.cfg -Starting zookeeper ... STARTED -``` - -2、soul-admin 网关后台服务启动,服务启动后可以看到发起的 ZooKeeper 请求调用 - -``` - -2021-01-20 17:34:48.752 INFO 64500 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Starting ZkClient event thread. -2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:zookeeper.version=3.5.6-c11b7e26bc554b8523dc929761dd28808913f091, built on 10/08/2019 20:18 GMT -2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:host.name=10.7.254.31 -2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.version=1.8.0_261 -2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.vendor=Oracle Corporation -...... -2021-01-20 17:34:48.806 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown error) -2021-01-20 17:34:48.826 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Socket connection established, initiating session, client: /0:0:0:0:0:0:0:1:58214, server: localhost/0:0:0:0:0:0:0:1:2181 -2021-01-20 17:34:48.857 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x1000b5e22f50001, negotiated timeout = 5000 -2021-01-20 17:34:48.861 INFO 64500 --- [ain-EventThread] org.I0Itec.zkclient.ZkClient : zookeeper state changed (SyncConnected) -``` - -3、soul-bootstrap 网关服务启动,服务启动后可以看到发起的 ZooKeeper 请求调用 - -``` -2021-01-20 17:35:58.996 INFO 64583 --- [ main] s.b.s.d.z.ZookeeperSyncDataConfiguration : you use zookeeper sync soul data....... -2021-01-20 17:35:59.003 INFO 64583 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Starting ZkClient event thread. -...... - -2021-01-20 17:35:59.012 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:user.home=/Users/liquan -2021-01-20 17:35:59.012 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:os.memory.total=310MB -2021-01-20 17:35:59.018 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Initiating client connection, connectString=localhost:2181 sessionTimeout=5000 watcher=org.I0Itec.zkclient.ZkClient@114a5e0 -2021-01-20 17:35:59.121 INFO 64583 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x1000b5e22f50002, negotiated timeout = 5000 -2021-01-20 17:35:59.126 INFO 64583 --- [ain-EventThread] org.I0Itec.zkclient.ZkClient : zookeeper state changed (SyncConnected) -``` - -4、查看 zookeeper 上的 soul 网关同步的注册信息 -![在这里插入图片描述](/assets/img/blog5/zk1.png) - -###### 三、Soul 网关 Zookeeper 数据同步原理解析 - -在 soul-admin 启动后在控制台中看到了 org.I0Itec.zkclient.ZkClient,以此为入口进行跟踪调试。 - -1、ZookeeperConfiguration 作用:注册 zkClient 到 Spring 容器。 - -```java -// EnableConfigurationProperties 作用:使用 @ConfigurationProperties 注解的类生效。如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。@EnableConfigurationProperties 相当于把使用@ConfigurationProperties 的类进行了一次注入。 -// @ConditionalOnMissingBean 容器中没有指定的类,就进行注入,@ConditionalOnBean与之相反 -/** - * ZookeeperConfiguration . - * @author xiaoyu(Myth) - */ -@EnableConfigurationProperties(ZookeeperProperties.class) -public class ZookeeperConfiguration { - /** - * register zkClient in spring ioc. - * - * @param zookeeperProp the zookeeper configuration - * @return ZkClient {@linkplain ZkClient} - */ - @Bean - @ConditionalOnMissingBean(ZkClient.class) - public ZkClient zkClient(final ZookeeperProperties zookeeperProp) { - return new ZkClient(zookeeperProp.getUrl(), zookeeperProp.getSessionTimeout(), zookeeperProp.getConnectionTimeout()); - } -} -``` - -soul-admin 启动后,会实读取 zookeeper 配置信息,向容器中注入 zkClient 和 zookeeper 建立连接。 -![在这里插入图片描述](/assets/img/blog5/zk2.png) - -![在这里插入图片描述](/assets/img/blog5/zk3.png) - -2、实例化 ZkClient 的调用栈中会调用 DataChangedEventDispatcher 的 afterPropertiesSet 方法。 - -org.dromara.soul.admin.listener.DataChangedEventDispatcher 作用:事件转发器,将更改的事件转发到每个 ConfigEventListener。 - -此类 实现了 InitializingBean,在 DataChangedEventDispatcher 初始化过程中,会执行 afterPropertiesSet 方法。 - -afterPropertiesSet 方法会在容器中查找类型是 DataChangedListener.class 的 bean。 - -```java -@Component -public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { - private ApplicationContext applicationContext; - private List listeners; - public DataChangedEventDispatcher(final ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } -@Override -@SuppressWarnings("unchecked") -public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - case APP_AUTH: - listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); - break; - ....... - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } - ...... - @Override - public void afterPropertiesSet() { - Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); - this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); - } -} -``` - -3、afterPropertiesSet 方法的执行会查找 DataChangedListener.class 相关类的实例化。 - -org.dromara.soul.admin.config.DataSyncConfiguration 作用:数据同步配置类。 - -ZookeeperDataChangedListener 数据变化监听器,作用:应该是监听元数据变化,然后同步到 zookeeper。 - -ZookeeperDataInit zookeeper 数据初始化,作用:向 zookeeper 同步初始化数据。 - -```java -/** - * The type Zookeeper listener. - */ -@Configuration -@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url") -@Import(ZookeeperConfiguration.class) -static class ZookeeperListener { - /** - * Config event listener data changed listener. - * @param zkClient the zk client - * @return the data changed listener - */ - @Bean - @ConditionalOnMissingBean(ZookeeperDataChangedListener.class) - public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) { - return new ZookeeperDataChangedListener(zkClient); - } - /** - * Zookeeper data init zookeeper data init - * @param zkClient the zk client - * @param syncDataService the sync data service - * @return the zookeeper data init - */ - @Bean - @ConditionalOnMissingBean(ZookeeperDataInit.class) - public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { - return new ZookeeperDataInit(zkClient, syncDataService); - } -} -``` - -4、org.dromara.soul.admin.listener.zookeeper.ZookeeperDataInit 作用:负责向 zookeeper 同步初始化数据。此类实现了 CommandLineRunner。 - -CommandLineRunner:作用:SpringBoot 在项目启动后会遍历所有实现 CommandLineRunner 的实体类并执行 run 方法,如果需要按照一定的顺序去执行,那么就需要在实体类上使用一个@Order 注解(或者实现 Order 接口)来表明顺序。 - -run 方法会调用 syncDataService.syncAll 方法。 - -```java -public class ZookeeperDataInit implements CommandLineRunner { - private final ZkClient zkClient; - private final SyncDataService syncDataService; - /** - * Instantiates a new Zookeeper data init. - * @param zkClient the zk client - * @param syncDataService the sync data service - */ - public ZookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { - this.zkClient = zkClient; - this.syncDataService = syncDataService; - } - @Override - public void run(final String... args) { - String pluginPath = ZkPathConstants.PLUGIN_PARENT; - String authPath = ZkPathConstants.APP_AUTH_PARENT; - String metaDataPath = ZkPathConstants.META_DATA; - if (!zkClient.exists(pluginPath) && !zkClient.exists(authPath) && !zkClient.exists(metaDataPath)) { - syncDataService.syncAll(DataEventTypeEnum.REFRESH); - } - } -} -``` - -5、org.dromara.soul.admin.service.sync.SyncDataServiceImpl - -syncAll 方法会调用事件发布器进行事件发布,事件类型是 DataEventTypeEnum.REFRESH。 - -```java -/** - * The type sync data service. - * @author xiaoyu(Myth) - */ -@Service("syncDataService") -public class SyncDataServiceImpl implements SyncDataService { - // 发布事件,也就是把某个事件告诉的所有与这个事件相关的监听器 - private final ApplicationEventPublisher eventPublisher; - ...... - @Override - public boolean syncAll(final DataEventTypeEnum type) { - appAuthService.syncData(); - List pluginDataList = pluginService.listAll(); - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList)); - List selectorDataList = selectorService.listAll(); - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList)); - List ruleDataList = ruleService.listAll(); - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList)); - metaDataService.syncData(); - return true; - } - ...... -} -``` - -![在这里插入图片描述](/assets/img/blog5//zk4.png) - -6、事件发布后 org.dromara.soul.admin.listener.DataChangedEventDispatcher 类的 onApplicationEvent 方法会监听事件变化,遍历所有的监听者进行数据同步处理,这里的监听者实现类是 ZookeeperDataChangedListener,根据对应的事件类型通过 zkClient 向 -zookeeper 同步数据。 - -![在这里插入图片描述](/assets/img/blog5//zk5.png) - -7、soul-admin 初始化到数据到 zookeeper 思维导图 - -![在这里插入图片描述](/assets/img/blog5//zk6.png) - -###### 四、总结 - -soul-admin 启动就会同步网关数据 rule、metaData、selector、plugin 等到 zookeeper。数据变化会发布 DataChangedEvent 事件,监听事件将数据同步至 zookeeper。 - -[Soul 网关数据同步原理](https://dromara.org/projects/soul/data-sync/) +--- +title: Soul网关学习Zookeeper数据同步01 +author: 李权 +date: 2021-01-20 +tag: + - Soul +cover: '/assets/img/architecture/soul-framework.png' +head: + - - meta + - name: 博客 +--- + +#### 启动 soul-admin、soul-bootstrap, 使用 zookeeper 同步数据到网关 + +###### 一、配置环境 + +1、soul-admin 服务配置,需要重启服务 + +soul-admin/src/main/resources/application.yml + +```yaml +soul: + sync: + zookeeper: + url: localhost:2181 + sessionTimeout: 5000 + connectionTimeout: 2000 +``` + +2、soul-bootstrap 网关服务配置,需要重启 + +soul-bootstrap/pom.xml + +```xml + + + org.dromara + soul-spring-boot-starter-sync-data-zookeeper + ${project.version} + +``` + +soul-bootstrap/src/main/resources/application-local.yml + +```yaml +soul: + sync: + zookeeper: + url: localhost:2181 + sessionTimeout: 5000 + connectionTimeout: 2000 +``` + +###### 二、启动服务 + +1、 启动 zookeeper + +``` +zookeeper ./bin/zkServer.sh start +/usr/bin/java +ZooKeeper JMX enabled by default +Using config: /Documents/soft/zookeeper/bin/../conf/zoo.cfg +Starting zookeeper ... STARTED +``` + +2、soul-admin 网关后台服务启动,服务启动后可以看到发起的 ZooKeeper 请求调用 + +``` + +2021-01-20 17:34:48.752 INFO 64500 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Starting ZkClient event thread. +2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:zookeeper.version=3.5.6-c11b7e26bc554b8523dc929761dd28808913f091, built on 10/08/2019 20:18 GMT +2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:host.name=10.7.254.31 +2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.version=1.8.0_261 +2021-01-20 17:34:48.761 INFO 64500 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.vendor=Oracle Corporation +...... +2021-01-20 17:34:48.806 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown error) +2021-01-20 17:34:48.826 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Socket connection established, initiating session, client: /0:0:0:0:0:0:0:1:58214, server: localhost/0:0:0:0:0:0:0:1:2181 +2021-01-20 17:34:48.857 INFO 64500 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x1000b5e22f50001, negotiated timeout = 5000 +2021-01-20 17:34:48.861 INFO 64500 --- [ain-EventThread] org.I0Itec.zkclient.ZkClient : zookeeper state changed (SyncConnected) +``` + +3、soul-bootstrap 网关服务启动,服务启动后可以看到发起的 ZooKeeper 请求调用 + +``` +2021-01-20 17:35:58.996 INFO 64583 --- [ main] s.b.s.d.z.ZookeeperSyncDataConfiguration : you use zookeeper sync soul data....... +2021-01-20 17:35:59.003 INFO 64583 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Starting ZkClient event thread. +...... + +2021-01-20 17:35:59.012 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:user.home=/Users/liquan +2021-01-20 17:35:59.012 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:os.memory.total=310MB +2021-01-20 17:35:59.018 INFO 64583 --- [ main] org.apache.zookeeper.ZooKeeper : Initiating client connection, connectString=localhost:2181 sessionTimeout=5000 watcher=org.I0Itec.zkclient.ZkClient@114a5e0 +2021-01-20 17:35:59.121 INFO 64583 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x1000b5e22f50002, negotiated timeout = 5000 +2021-01-20 17:35:59.126 INFO 64583 --- [ain-EventThread] org.I0Itec.zkclient.ZkClient : zookeeper state changed (SyncConnected) +``` + +4、查看 zookeeper 上的 soul 网关同步的注册信息 +![在这里插入图片描述](/assets/img/blog5/zk1.png) + +###### 三、Soul 网关 Zookeeper 数据同步原理解析 + +在 soul-admin 启动后在控制台中看到了 org.I0Itec.zkclient.ZkClient,以此为入口进行跟踪调试。 + +1、ZookeeperConfiguration 作用:注册 zkClient 到 Spring 容器。 + +```java +// EnableConfigurationProperties 作用:使用 @ConfigurationProperties 注解的类生效。如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。@EnableConfigurationProperties 相当于把使用@ConfigurationProperties 的类进行了一次注入。 +// @ConditionalOnMissingBean 容器中没有指定的类,就进行注入,@ConditionalOnBean与之相反 +/** + * ZookeeperConfiguration . + * @author xiaoyu(Myth) + */ +@EnableConfigurationProperties(ZookeeperProperties.class) +public class ZookeeperConfiguration { + /** + * register zkClient in spring ioc. + * + * @param zookeeperProp the zookeeper configuration + * @return ZkClient {@linkplain ZkClient} + */ + @Bean + @ConditionalOnMissingBean(ZkClient.class) + public ZkClient zkClient(final ZookeeperProperties zookeeperProp) { + return new ZkClient(zookeeperProp.getUrl(), zookeeperProp.getSessionTimeout(), zookeeperProp.getConnectionTimeout()); + } +} +``` + +soul-admin 启动后,会实读取 zookeeper 配置信息,向容器中注入 zkClient 和 zookeeper 建立连接。 +![在这里插入图片描述](/assets/img/blog5/zk2.png) + +![在这里插入图片描述](/assets/img/blog5/zk3.png) + +2、实例化 ZkClient 的调用栈中会调用 DataChangedEventDispatcher 的 afterPropertiesSet 方法。 + +org.dromara.soul.admin.listener.DataChangedEventDispatcher 作用:事件转发器,将更改的事件转发到每个 ConfigEventListener。 + +此类 实现了 InitializingBean,在 DataChangedEventDispatcher 初始化过程中,会执行 afterPropertiesSet 方法。 + +afterPropertiesSet 方法会在容器中查找类型是 DataChangedListener.class 的 bean。 + +```java +@Component +public class DataChangedEventDispatcher implements ApplicationListener, InitializingBean { + private ApplicationContext applicationContext; + private List listeners; + public DataChangedEventDispatcher(final ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } +@Override +@SuppressWarnings("unchecked") +public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + case APP_AUTH: + listener.onAppAuthChanged((List) event.getSource(), event.getEventType()); + break; + ....... + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } + ...... + @Override + public void afterPropertiesSet() { + Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); + this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); + } +} +``` + +3、afterPropertiesSet 方法的执行会查找 DataChangedListener.class 相关类的实例化。 + +org.dromara.soul.admin.config.DataSyncConfiguration 作用:数据同步配置类。 + +ZookeeperDataChangedListener 数据变化监听器,作用:应该是监听元数据变化,然后同步到 zookeeper。 + +ZookeeperDataInit zookeeper 数据初始化,作用:向 zookeeper 同步初始化数据。 + +```java +/** + * The type Zookeeper listener. + */ +@Configuration +@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url") +@Import(ZookeeperConfiguration.class) +static class ZookeeperListener { + /** + * Config event listener data changed listener. + * @param zkClient the zk client + * @return the data changed listener + */ + @Bean + @ConditionalOnMissingBean(ZookeeperDataChangedListener.class) + public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) { + return new ZookeeperDataChangedListener(zkClient); + } + /** + * Zookeeper data init zookeeper data init + * @param zkClient the zk client + * @param syncDataService the sync data service + * @return the zookeeper data init + */ + @Bean + @ConditionalOnMissingBean(ZookeeperDataInit.class) + public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { + return new ZookeeperDataInit(zkClient, syncDataService); + } +} +``` + +4、org.dromara.soul.admin.listener.zookeeper.ZookeeperDataInit 作用:负责向 zookeeper 同步初始化数据。此类实现了 CommandLineRunner。 + +CommandLineRunner:作用:SpringBoot 在项目启动后会遍历所有实现 CommandLineRunner 的实体类并执行 run 方法,如果需要按照一定的顺序去执行,那么就需要在实体类上使用一个@Order 注解(或者实现 Order 接口)来表明顺序。 + +run 方法会调用 syncDataService.syncAll 方法。 + +```java +public class ZookeeperDataInit implements CommandLineRunner { + private final ZkClient zkClient; + private final SyncDataService syncDataService; + /** + * Instantiates a new Zookeeper data init. + * @param zkClient the zk client + * @param syncDataService the sync data service + */ + public ZookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) { + this.zkClient = zkClient; + this.syncDataService = syncDataService; + } + @Override + public void run(final String... args) { + String pluginPath = ZkPathConstants.PLUGIN_PARENT; + String authPath = ZkPathConstants.APP_AUTH_PARENT; + String metaDataPath = ZkPathConstants.META_DATA; + if (!zkClient.exists(pluginPath) && !zkClient.exists(authPath) && !zkClient.exists(metaDataPath)) { + syncDataService.syncAll(DataEventTypeEnum.REFRESH); + } + } +} +``` + +5、org.dromara.soul.admin.service.sync.SyncDataServiceImpl + +syncAll 方法会调用事件发布器进行事件发布,事件类型是 DataEventTypeEnum.REFRESH。 + +```java +/** + * The type sync data service. + * @author xiaoyu(Myth) + */ +@Service("syncDataService") +public class SyncDataServiceImpl implements SyncDataService { + // 发布事件,也就是把某个事件告诉的所有与这个事件相关的监听器 + private final ApplicationEventPublisher eventPublisher; + ...... + @Override + public boolean syncAll(final DataEventTypeEnum type) { + appAuthService.syncData(); + List pluginDataList = pluginService.listAll(); + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList)); + List selectorDataList = selectorService.listAll(); + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList)); + List ruleDataList = ruleService.listAll(); + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList)); + metaDataService.syncData(); + return true; + } + ...... +} +``` + +![在这里插入图片描述](/assets/img/blog5//zk4.png) + +6、事件发布后 org.dromara.soul.admin.listener.DataChangedEventDispatcher 类的 onApplicationEvent 方法会监听事件变化,遍历所有的监听者进行数据同步处理,这里的监听者实现类是 ZookeeperDataChangedListener,根据对应的事件类型通过 zkClient 向 +zookeeper 同步数据。 + +![在这里插入图片描述](/assets/img/blog5//zk5.png) + +7、soul-admin 初始化到数据到 zookeeper 思维导图 + +![在这里插入图片描述](/assets/img/blog5//zk6.png) + +###### 四、总结 + +soul-admin 启动就会同步网关数据 rule、metaData、selector、plugin 等到 zookeeper。数据变化会发布 DataChangedEvent 事件,监听事件将数据同步至 zookeeper。 + +[Soul 网关数据同步原理](https://dromara.org/projects/soul/data-sync/) diff --git a/src/zh/blog/soul_source_learning_13_zookeeper_02.md b/src/zh/blog/soul_source_learning_13_zookeeper_02.md index 31c025d43a..b118290c4a 100644 --- a/src/zh/blog/soul_source_learning_13_zookeeper_02.md +++ b/src/zh/blog/soul_source_learning_13_zookeeper_02.md @@ -1,199 +1,199 @@ ---- -title: Soul网关学习Zookeeper数据同步02 -author: 李权 -date: 2021-01-21 -tag: - - Soul -cover: '/assets/img/architecture/soul-framework.png' -head: - - - meta - - name: 博客 ---- - -#### 启动 admin,与网关。 admin 操作,使用 zookeeper 同步数据到网关 - -[上一篇](https://dromara.org/blog/soul_source_learning_13_zookeeper_01),通过 soul-admin 启动过程为入口,分析了 soul-admin 启动就会同步网关数据 rule、metaData、selector、plugin 等到 zookeeper。 - -数据变化会发布 DataChangedEvent 事件,监听事件将数据同步至 zookeeper。 -本篇接着上一篇继续跟踪源码分析 zookeeper 同步数据到网关原理: - -- soul-admin 变更网关数据,跟踪数据同步过程。 -- soul-bootstrap 如何获取 zookeeper 数据的,如何感知网关数据变化的。 - -###### 一、soul-admin 变更网关数据,跟踪数据同步过程 - -1、在网关后台尝试更改 divide 插件状态,debug 跟踪。 - -![在这里插入图片描述](/assets/img/blog5/zk7.png) - -2、插件更新后会发布一个 DataChangedEvent 事件 - -![在这里插入图片描述](/assets/img/blog5/zk8.png) - -3、org.dromara.soul.admin.listener.DataChangedEventDispatcher --> onApplicationEvent() 负责监听事件 - -![在这里插入图片描述](/assets/img/blog5/zk9.png) - -4、org.dromara.soul.admin.listener.zookeeper.ZookeeperDataChangedListener 负责同步数据至 zookeeper - -![在这里插入图片描述](/assets/img/blog5/zk10.png) - -###### 二、soul-bootstrap 如何获取 zookeeper 数据的,如何感知网关数据变化的。 - -1、soul-bootstrap 依赖 - -```xml - - org.dromara - soul-spring-boot-starter-sync-data-zookeeper - ${project.version} - -``` - -2、soul-bootstrap 启动后会自动注入 org.dromara.soul.spring.boot.sync.data.zookeeper.ZookeeperSyncDataConfiguration - -读取 Zookeeper 配置向容器中注入 ZkClient。 - -SyncDataService 向容器注入数据同步服务 bean,从 Spring 容器中获取,ZkClient(zookeeper 客户端), pluginSubscriber(插件数据订阅)、metaSubscribers (元数据订阅)、authSubscribers(权限订阅)。 - -```java -public class ZookeeperSyncDataConfiguration { - /** - * Sync data service sync data service. - * @param zkClient the zk client - * @param pluginSubscriber the plugin subscriber - * @param metaSubscribers the meta subscribers - * @param authSubscribers the auth subscribers - * @return the sync data service - */ - @Bean - public SyncDataService syncDataService(final ObjectProvider zkClient, final ObjectProvider pluginSubscriber, - final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { - log.info("you use zookeeper sync soul data......."); - return new ZookeeperSyncDataService(zkClient.getIfAvailable(), pluginSubscriber.getIfAvailable(), - metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); - } - /** - * register zkClient in spring ioc. - * @param zookeeperConfig the zookeeper configuration - * @return ZkClient {@linkplain ZkClient} - */ - @Bean - public ZkClient zkClient(final ZookeeperConfig zookeeperConfig) { - return new ZkClient(zookeeperConfig.getUrl(), zookeeperConfig.getSessionTimeout(), zookeeperConfig.getConnectionTimeout()); - } -} -``` - -3、org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService 初始化,也就是 soul-bootstrap 启动后就会从 zookeeper 获取数据,同步至内存。 - -- watcherData()--> watcherAll() --> watcherPlugin() --> cachePluginData()。 -- zkClient.subscribeDataChanges() 监听 当前节点和子节点的内容修改、删除。 - -```java -public class ZookeeperSyncDataService implements SyncDataService, AutoCloseable { - private final ZkClient zkClient; - private final PluginDataSubscriber pluginDataSubscriber; - private final List metaDataSubscribers; - private final List authDataSubscribers; - /** - * Instantiates a new Zookeeper cache manager. - * @param zkClient the zk client - * @param pluginDataSubscriber the plugin data subscriber - * @param metaDataSubscribers the meta data subscribers - * @param authDataSubscribers the auth data subscribers - */ - public ZookeeperSyncDataService(final ZkClient zkClient, final PluginDataSubscriber pluginDataSubscriber, - final List metaDataSubscribers, final List authDataSubscribers) { - this.zkClient = zkClient; - this.pluginDataSubscriber = pluginDataSubscriber; - this.metaDataSubscribers = metaDataSubscribers; - this.authDataSubscribers = authDataSubscribers; - watcherData(); - watchAppAuth(); - watchMetaData(); - } - ...... - private void watcherData() { - final String pluginParent = ZkPathConstants.PLUGIN_PARENT; - List pluginZKs = zkClientGetChildren(pluginParent); - for (String pluginName : pluginZKs) { - watcherAll(pluginName); - } - zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> { - if (CollectionUtils.isNotEmpty(currentChildren)) { - for (String pluginName : currentChildren) { - watcherAll(pluginName); - } - } - }); - } - ...... - private void watcherPlugin(final String pluginName) { - String pluginPath = ZkPathConstants.buildPluginPath(pluginName); - if (!zkClient.exists(pluginPath)) { - zkClient.createPersistent(pluginPath, true); - } - cachePluginData(zkClient.readData(pluginPath)); - subscribePluginDataChanges(pluginPath, pluginName); - } -} -``` - -4、debug 过程 - -![在这里插入图片描述](/assets/img/blog5/zk11.png) - -###### 三、soul-bootstrap 是如何感知网关数据变化的 - -1、org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService -cacheRuleData 方法上打上断点,更新插件规则,观察是否会进入此断点。 - -```java -private void cacheRuleData(final RuleData ruleData) { - Optional.ofNullable(ruleData) - .ifPresent(data -> Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.onRuleSubscribe(data))); -} -``` - -2、soul-admin 后台操作更改 divide 插件规则,首先 soul-admin 会发布事件,并监听事件同步更新数据至 zookeeper。 - -![在这里插入图片描述](/assets/img/blog5/zk12.png) - -3、soul-bootstrap 确实收到了插件数据的更新,根据 Soul 官网介绍的"zookeeper 的同步原理"这里主要是依赖 zookeeper 的 watch 机制。 - -org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService 类: - -zkClient.subscribeDataChanges() 监听 当前节点和子节点的内容修改、删除。 - -```java -zkClient.subscribeChildChanges(groupParentPath, (parentPath, currentChildren) -> { - if (CollectionUtils.isNotEmpty(currentChildren)) { - List addSubscribePath = addSubscribePath(childrenList, currentChildren); - // Get the newly added node data and subscribe to that node - addSubscribePath.stream().map(addPath -> { - String realPath = buildRealPath(parentPath, addPath); - cacheRuleData(zkClient.readData(realPath)); - return realPath; - }).forEach(this::subscribeRuleDataChanges); - } -}); -private void subscribeRuleDataChanges(final String path) { - zkClient.subscribeDataChanges(path, new IZkDataListener() { - @Override - public void handleDataChange(final String dataPath, final Object data) { - cacheRuleData((RuleData) data); - } - @Override - public void handleDataDeleted(final String dataPath) { - unCacheRuleData(dataPath); - } - }); -} -``` - -![在这里插入图片描述](/assets/img/blog5/zk13.png) - -###### 四、总结 - -![在这里插入图片描述](/assets/img/blog5/zk14.png) +--- +title: Soul网关学习Zookeeper数据同步02 +author: 李权 +date: 2021-01-21 +tag: + - Soul +cover: '/assets/img/architecture/soul-framework.png' +head: + - - meta + - name: 博客 +--- + +#### 启动 admin,与网关。 admin 操作,使用 zookeeper 同步数据到网关 + +[上一篇](https://dromara.org/blog/soul_source_learning_13_zookeeper_01),通过 soul-admin 启动过程为入口,分析了 soul-admin 启动就会同步网关数据 rule、metaData、selector、plugin 等到 zookeeper。 + +数据变化会发布 DataChangedEvent 事件,监听事件将数据同步至 zookeeper。 +本篇接着上一篇继续跟踪源码分析 zookeeper 同步数据到网关原理: + +- soul-admin 变更网关数据,跟踪数据同步过程。 +- soul-bootstrap 如何获取 zookeeper 数据的,如何感知网关数据变化的。 + +###### 一、soul-admin 变更网关数据,跟踪数据同步过程 + +1、在网关后台尝试更改 divide 插件状态,debug 跟踪。 + +![在这里插入图片描述](/assets/img/blog5/zk7.png) + +2、插件更新后会发布一个 DataChangedEvent 事件 + +![在这里插入图片描述](/assets/img/blog5/zk8.png) + +3、org.dromara.soul.admin.listener.DataChangedEventDispatcher --> onApplicationEvent() 负责监听事件 + +![在这里插入图片描述](/assets/img/blog5/zk9.png) + +4、org.dromara.soul.admin.listener.zookeeper.ZookeeperDataChangedListener 负责同步数据至 zookeeper + +![在这里插入图片描述](/assets/img/blog5/zk10.png) + +###### 二、soul-bootstrap 如何获取 zookeeper 数据的,如何感知网关数据变化的。 + +1、soul-bootstrap 依赖 + +```xml + + org.dromara + soul-spring-boot-starter-sync-data-zookeeper + ${project.version} + +``` + +2、soul-bootstrap 启动后会自动注入 org.dromara.soul.spring.boot.sync.data.zookeeper.ZookeeperSyncDataConfiguration + +读取 Zookeeper 配置向容器中注入 ZkClient。 + +SyncDataService 向容器注入数据同步服务 bean,从 Spring 容器中获取,ZkClient(zookeeper 客户端), pluginSubscriber(插件数据订阅)、metaSubscribers (元数据订阅)、authSubscribers(权限订阅)。 + +```java +public class ZookeeperSyncDataConfiguration { + /** + * Sync data service sync data service. + * @param zkClient the zk client + * @param pluginSubscriber the plugin subscriber + * @param metaSubscribers the meta subscribers + * @param authSubscribers the auth subscribers + * @return the sync data service + */ + @Bean + public SyncDataService syncDataService(final ObjectProvider zkClient, final ObjectProvider pluginSubscriber, + final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { + log.info("you use zookeeper sync soul data......."); + return new ZookeeperSyncDataService(zkClient.getIfAvailable(), pluginSubscriber.getIfAvailable(), + metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); + } + /** + * register zkClient in spring ioc. + * @param zookeeperConfig the zookeeper configuration + * @return ZkClient {@linkplain ZkClient} + */ + @Bean + public ZkClient zkClient(final ZookeeperConfig zookeeperConfig) { + return new ZkClient(zookeeperConfig.getUrl(), zookeeperConfig.getSessionTimeout(), zookeeperConfig.getConnectionTimeout()); + } +} +``` + +3、org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService 初始化,也就是 soul-bootstrap 启动后就会从 zookeeper 获取数据,同步至内存。 + +- watcherData()--> watcherAll() --> watcherPlugin() --> cachePluginData()。 +- zkClient.subscribeDataChanges() 监听 当前节点和子节点的内容修改、删除。 + +```java +public class ZookeeperSyncDataService implements SyncDataService, AutoCloseable { + private final ZkClient zkClient; + private final PluginDataSubscriber pluginDataSubscriber; + private final List metaDataSubscribers; + private final List authDataSubscribers; + /** + * Instantiates a new Zookeeper cache manager. + * @param zkClient the zk client + * @param pluginDataSubscriber the plugin data subscriber + * @param metaDataSubscribers the meta data subscribers + * @param authDataSubscribers the auth data subscribers + */ + public ZookeeperSyncDataService(final ZkClient zkClient, final PluginDataSubscriber pluginDataSubscriber, + final List metaDataSubscribers, final List authDataSubscribers) { + this.zkClient = zkClient; + this.pluginDataSubscriber = pluginDataSubscriber; + this.metaDataSubscribers = metaDataSubscribers; + this.authDataSubscribers = authDataSubscribers; + watcherData(); + watchAppAuth(); + watchMetaData(); + } + ...... + private void watcherData() { + final String pluginParent = ZkPathConstants.PLUGIN_PARENT; + List pluginZKs = zkClientGetChildren(pluginParent); + for (String pluginName : pluginZKs) { + watcherAll(pluginName); + } + zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> { + if (CollectionUtils.isNotEmpty(currentChildren)) { + for (String pluginName : currentChildren) { + watcherAll(pluginName); + } + } + }); + } + ...... + private void watcherPlugin(final String pluginName) { + String pluginPath = ZkPathConstants.buildPluginPath(pluginName); + if (!zkClient.exists(pluginPath)) { + zkClient.createPersistent(pluginPath, true); + } + cachePluginData(zkClient.readData(pluginPath)); + subscribePluginDataChanges(pluginPath, pluginName); + } +} +``` + +4、debug 过程 + +![在这里插入图片描述](/assets/img/blog5/zk11.png) + +###### 三、soul-bootstrap 是如何感知网关数据变化的 + +1、org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService +cacheRuleData 方法上打上断点,更新插件规则,观察是否会进入此断点。 + +```java +private void cacheRuleData(final RuleData ruleData) { + Optional.ofNullable(ruleData) + .ifPresent(data -> Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.onRuleSubscribe(data))); +} +``` + +2、soul-admin 后台操作更改 divide 插件规则,首先 soul-admin 会发布事件,并监听事件同步更新数据至 zookeeper。 + +![在这里插入图片描述](/assets/img/blog5/zk12.png) + +3、soul-bootstrap 确实收到了插件数据的更新,根据 Soul 官网介绍的"zookeeper 的同步原理"这里主要是依赖 zookeeper 的 watch 机制。 + +org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService 类: + +zkClient.subscribeDataChanges() 监听 当前节点和子节点的内容修改、删除。 + +```java +zkClient.subscribeChildChanges(groupParentPath, (parentPath, currentChildren) -> { + if (CollectionUtils.isNotEmpty(currentChildren)) { + List addSubscribePath = addSubscribePath(childrenList, currentChildren); + // Get the newly added node data and subscribe to that node + addSubscribePath.stream().map(addPath -> { + String realPath = buildRealPath(parentPath, addPath); + cacheRuleData(zkClient.readData(realPath)); + return realPath; + }).forEach(this::subscribeRuleDataChanges); + } +}); +private void subscribeRuleDataChanges(final String path) { + zkClient.subscribeDataChanges(path, new IZkDataListener() { + @Override + public void handleDataChange(final String dataPath, final Object data) { + cacheRuleData((RuleData) data); + } + @Override + public void handleDataDeleted(final String dataPath) { + unCacheRuleData(dataPath); + } + }); +} +``` + +![在这里插入图片描述](/assets/img/blog5/zk13.png) + +###### 四、总结 + +![在这里插入图片描述](/assets/img/blog5/zk14.png) diff --git a/src/zh/blog/soul_source_learning_14_nacos.md b/src/zh/blog/soul_source_learning_14_nacos.md index 6a087eaeb1..57a5a243c3 100644 --- a/src/zh/blog/soul_source_learning_14_nacos.md +++ b/src/zh/blog/soul_source_learning_14_nacos.md @@ -1,358 +1,358 @@ ---- -title: Soul网关学习Nacos数据同步 -author: 李权 -date: 2021-01-26 -tag: - - Soul -cover: '/assets/img/blog5/ns15.png' -head: - - - meta - - name: 博客 ---- - -本篇分析一下 Nacos 同步数据原理 - -1、先配置一下环境 - -- soul-admin - soul-admin/src/main/resources/application.yml - -```yaml -soul: - sync: - nacos: - url: localhost:8848 - namespace: 1c10d748-af86-43b9-8265-75f487d20c6c - # acm: - # enabled: false - # endpoint: acm.aliyun.com - # namespace: - # accessKey: - # secretKey: -``` - -soul-admin/pom.xml,这里默认配置是有的 - -```xml - - com.alibaba.nacos - nacos-client - ${nacos-client.version} - -``` - -- soul-bootstrap - soul-bootstrap/src/main/resources/application-local.yml - -```yaml -soul: - sync: - nacos: - url: localhost:8848 - namespace: 1c10d748-af86-43b9-8265-75f487d20c6c -# acm: -# enabled: false -# endpoint: acm.aliyun.com -# namespace: -# accessKey: -# secretKey: -``` - -soul-bootstrap/pom.xml,下面的配置默认是没有的,需要手动添加 - -```xml - - org.dromara - soul-spring-boot-starter-sync-data-nacos - ${project.version} - -``` - -- 启动服务 - -``` -1、启动 nacos -2、启动 soul-admin -3、启动 soul-bootstrap -``` - -2、上面看着挺顺利,这个过程遇到了坑,soul-bootstrap 启动不起来报空指针异常,下面详细记录一下。 -首先 soul-admin 启动后不会主动向 nacos 同步网关数据,需要手动同步,官网这一点没有提到。这个问题绊了我好久,最后是看到了群里其他同学遇到了同样的问题,参考了他们的文章才解决,下面记录一下解决过程。 - -1)soul-bootstrap 启动的时候遇到了如下的错误,NullPointerException。 - -soul-bootstrap 启动的时候会去,nacos 获取网关数据,看到下面的断点,拿到的是空数据。 - -``` -Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. -2021-01-25 16:49:06.052 ERROR 5273 --- [ main] o.s.boot.SpringApplication : Application run failed -org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosSyncDataService' defined in class path resource [org/dromara/soul/springboot/starter/sync/data/nacos/NacosSyncDataConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is java.lang.NullPointerException -...... - at org.dromara.soul.bootstrap.SoulBootstrapApplication.main(SoulBootstrapApplication.java:37) [classes/:na] -Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is java.lang.NullPointerException - at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] - at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] - ... 19 common frames omitted -Caused by: java.lang.NullPointerException: null - at org.dromara.soul.sync.data.nacos.handler.NacosCacheHandler.updateMetaDataMap(NacosCacheHandler.java:128) ~[classes/:na] - at org.dromara.soul.sync.data.nacos.handler.NacosCacheHandler.watcherData(NacosCacheHandler.java:167) ~[classes/:na] - at org.dromara.soul.sync.data.nacos.NacosSyncDataService.start(NacosSyncDataService.java:59) ~[classes/:na] - at org.dromara.soul.sync.data.nacos.NacosSyncDataService.(NacosSyncDataService.java:49) ~[classes/:na] - at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration.nacosSyncDataService(NacosSyncDataConfiguration.java:66) ~[classes/:na] - at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7.CGLIB$nacosSyncDataService$0() ~[classes/:na] - at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7$$FastClassBySpringCGLIB$$3830e886.invoke() ~[classes/:na] - at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE] - at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE] - at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7.nacosSyncDataService() ~[classes/:na] -...... - at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] - ... 20 common frames omitted -``` - -![在这里插入图片描述](/assets/img/blog5/ns1.png) - -2)到 nacos 去看一下是否有网关的数据,根据配置的 “namespace: 1c10d748-af86-43b9-8265-75f487d20c6c” 结果是什么都没有。 - -![在这里插入图片描述](/assets/img/blog5/ns2.png) - -![在这里插入图片描述](/assets/img/blog5/ns3.png) - -3、尝试去 soul-admin 手动同步,nacos 也看不到数据,必须需要手动创建命名空间“1c10d748-af86-43b9-8265-75f487d20c6c”,如下图。 - -![在这里插入图片描述](/assets/img/blog5/ns4.png) - -4、去 soul-admin 手动同步数据后,就看到了 nacos 上有了网关的配置信息,这时候 soul-bootstrap 还是启动不起来,因为这里还缺少元数据信息。元数据只有 dubbo、springcloud 服务有数据,http 是没有元数据的,所以还得去启动一下 dubbo 服务。然后在 soul-admin 同步一下元数据。 - -![在这里插入图片描述](/assets/img/blog5/ns5.png) - -soul-admin 点击同步数据,将元数据会同步到 nacos - -![在这里插入图片描述](/assets/img/blog5/ns6.png) - -soul-admin 点击同步数据,将认证数据会同步到 nacos - -![在这里插入图片描述](/assets/img/blog5/ns7.png) - -这时候 nacos 已经看到了全部的网关数据 - -![在这里插入图片描述](/assets/img/blog5/ns8.png) - -5、再去启动 soul-bootstrap,终于启动成功 - -``` -2021-01-25 17:56:54.798 INFO 10051 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[monitor] [org.dromara.soul.plugin.monitor.MonitorPlugin] -2021-01-25 17:56:54.798 INFO 10051 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[response] [org.dromara.soul.plugin.httpclient.response.WebClientResponsePlugin] -2021-01-25 17:56:54.990 INFO 10051 --- [ main] d.s.s.s.s.d.n.NacosSyncDataConfiguration : you use nacos sync soul data....... -2021-01-25 17:56:58.890 INFO 10051 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' -2021-01-25 17:56:59.758 INFO 10051 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195 -2021-01-25 17:56:59.764 INFO 10051 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 8.401 seconds (JVM running for 9.95) -``` - -6、小结: - -配置下来感觉使用 nacos 同步数据不是很友好,配置过程遇到了很多坑,首先 soul-admin 不会主动同步网关数据到 nacos,需要手动同步。soul-bootstrap 必须依赖所有的网关配置数据 soul.plugin、soul.selector、soul.selector、soul.meta、soul.auth,缺一不可。如果网关只代理 http 服务(无元数据),soul-bootstrap 是启动不起来的。官网这一块没有做详细说明,对小白不是很友好。 - -我们知道 soul-admin 启动后不会自动向 nacos 同步数据,需要手动操作。 - -下面分析一下 soul-admin,nacos,soul-bootstrap 同步数据的过程。 - -##### soul-admin 如何同步网关数据? - -1、插件信息更新后会发布一个 DataChangedEvent 事件 - -```java -/** - * create or update plugin - * @param pluginDTO {@linkplain PluginDTO} - * @return rows - */ -@Override -@Transactional(rollbackFor = Exception.class) -public String createOrUpdate(final PluginDTO pluginDTO) { - ...... - // publish change event. - eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType, - Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO)))); - return StringUtils.EMPTY; -} -``` - -2、由监听事件处理类 DataChangedEventDispatcher 负责调用具体的监听实现类对 DataChangedEvent 事件进行处理,这里的具体实现类是 NacosDataChangedListener。 - -> org.dromara.soul.admin.listener.DataChangedEventDispatcher - -> DataChangedEventDispatcher 初始化完成后会执行 afterPropertiesSet(),在容器中获取所有类型是 DataChangedListener.class 的 bean - -```java - -@Override -public void afterPropertiesSet() { - Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); - this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); -} -``` - -> > DataChangedEventDispatcher 监听到变更事件后,会执行 onApplicationEvent,遍历所有的监听类对监听事件进行处理,这里是 NacosDataChangedListener,如下图的 debug。 - -```java - -@Override -@SuppressWarnings("unchecked") -public void onApplicationEvent(final DataChangedEvent event) { - for (DataChangedListener listener : listeners) { - switch (event.getGroupKey()) { - ...... - case RULE: - listener.onRuleChanged((List) event.getSource(), event.getEventType()); - break; - ...... - default: - throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); - } - } -} -``` - -![在这里插入图片描述](/assets/img/blog5/ns9.png) - -3、NacosDataChangedListener 会执行 onRuleChanged,updateRuleMap 先将网关数据同步至内存,在通过 publishConfig 同步至 nacos。 - -> org.dromara.soul.admin.listener.nacos.NacosDataChangedListener - -```java -// 执行监听事件 -@Override -public void onRuleChanged(final List changed, final DataEventTypeEnum eventType) { - updateRuleMap(getConfig(RULE_DATA_ID)); - switch (eventType) { - ...... - default: - changed.forEach(rule -> { - List ls = RULE_MAP - .getOrDefault(rule.getSelectorId(), new ArrayList<>()) - .stream() - .filter(s -> !s.getId().equals(rule.getSelectorId())) - .sorted(RULE_DATA_COMPARATOR) - .collect(Collectors.toList()); - ls.add(rule); - RULE_MAP.put(rule.getSelectorId(), ls); - }); - break; - } - publishConfig(RULE_DATA_ID, RULE_MAP); -} -// 同步至内存 -private void updateRuleMap(final String configInfo) { - JsonObject jo = GsonUtils.getInstance().fromJson(configInfo, JsonObject.class); - Set set = new HashSet<>(RULE_MAP.keySet()); - ...... - RULE_MAP.keySet().removeAll(set); -} -// 同步至nacos -@SneakyThrows -private void publishConfig(final String dataId, final Object data) { - configService.publishConfig(dataId, GROUP, GsonUtils.getInstance().toJson(data)); -} -``` - -![在这里插入图片描述](/assets/img/blog5/ns10.png) - -4、DataChangedEventDispatcher 、NacosDataChangedListener 类继承关系 - -![在这里插入图片描述](/assets/img/blog5/ns11.png) - -![在这里插入图片描述](/assets/img/blog5/ns12.png) - -5、小结 - -> 1、例如 soul-admin 更新网关数据,发布一个 DataChangedEvent 事件,eventPublisher.publishEvent(new DataChangedEvent()) -> 2、DataChangedEventDispatcher --> onApplicationEvent()方法监听事件到事件,判断监听类是 NacosDataChangedListener -> 3、NacosDataChangedListener --> onRuleChanged()处理事件 -> 4、同步至内存 updateRuleMap(getConfig(RULE_DATA_ID)) -> 5、同步至 nacos publishConfig(RULE_DATA_ID, RULE_MAP) - -##### soul-bootstrap 如何同步网关数据? - -1、soul-bootstrap 添加了 nacos 依赖 soul-spring-boot-starter-sync-data-nacos,服务启动后会自动注入 NacosSyncDataConfiguration - -> org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration -> -> > NacosSyncDataService 负责读取和同步 nacos 网关数据 - -```java -@Configuration -@ConditionalOnClass(NacosSyncDataService.class) -@ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url") -@Slf4j -public class NacosSyncDataConfiguration { - // 注入nacos数据同步服务 - @Bean - public SyncDataService nacosSyncDataService(final ObjectProvider configService, final ObjectProvider pluginSubscriber, - final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { - log.info("you use nacos sync soul data......."); - return new NacosSyncDataService(configService.getIfAvailable(), pluginSubscriber.getIfAvailable(), - metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); - } - // 注入nacos客户端配置服务 - @Bean - public ConfigService nacosConfigService(final NacosConfig nacosConfig) throws Exception { - Properties properties = new Properties(); - ...... - return NacosFactory.createConfigService(properties); - } - // 注入nacos配置服务 - @Bean - @ConfigurationProperties(prefix = "soul.sync.nacos") - public NacosConfig nacosConfig() { - return new NacosConfig(); - } -} -``` - -2、org.dromara.soul.sync.data.nacos.NacosSyncDataService - -> 初始化会执行 start -> -> > watcherData 负责监听 nacos 网关数据 -> > -> > > updatePluginMap 同步网关数据到内存 - -```java -public void start() { - ...... - watcherData(RULE_DATA_ID, this::updateRuleMap); - ...... -} -@SneakyThrows -private String getConfigAndSignListener(final String dataId, final Listener listener) { - return configService.getConfigAndSignListener(dataId, GROUP, 6000, listener); -} -protected void watcherData(final String dataId, final OnChange oc) { - Listener listener = new Listener() { - @Override - public void receiveConfigInfo(final String configInfo) { - oc.change(configInfo); - } - ...... - }; - oc.change(getConfigAndSignListener(dataId, listener)); - LISTENERS.getOrDefault(dataId, new ArrayList<>()).add(listener); -} -``` - -![在这里插入图片描述](/assets/img/blog5/ns13.png) - -3、NacosSyncDataService 类关系图 - -![在这里插入图片描述](/assets/img/blog5/ns14.png) - -4、小结 - -> 1、soul-bootstrap 启动向容器自动注入 NacosSyncDataConfiguration -> 2、NacosSyncDataConfiguration 类中会向容器注入 NacosSyncDataService -> 3、NacosSyncDataService --> start() --> watcherData() 监听 nacos,同步网关数据到内存 -> 4、watcherData() --> updatePluginMap() - -##### 总结 - -![在这里插入图片描述](/assets/img/blog5/ns15.png) +--- +title: Soul网关学习Nacos数据同步 +author: 李权 +date: 2021-01-26 +tag: + - Soul +cover: '/assets/img/blog5/ns15.png' +head: + - - meta + - name: 博客 +--- + +本篇分析一下 Nacos 同步数据原理 + +1、先配置一下环境 + +- soul-admin + soul-admin/src/main/resources/application.yml + +```yaml +soul: + sync: + nacos: + url: localhost:8848 + namespace: 1c10d748-af86-43b9-8265-75f487d20c6c + # acm: + # enabled: false + # endpoint: acm.aliyun.com + # namespace: + # accessKey: + # secretKey: +``` + +soul-admin/pom.xml,这里默认配置是有的 + +```xml + + com.alibaba.nacos + nacos-client + ${nacos-client.version} + +``` + +- soul-bootstrap + soul-bootstrap/src/main/resources/application-local.yml + +```yaml +soul: + sync: + nacos: + url: localhost:8848 + namespace: 1c10d748-af86-43b9-8265-75f487d20c6c +# acm: +# enabled: false +# endpoint: acm.aliyun.com +# namespace: +# accessKey: +# secretKey: +``` + +soul-bootstrap/pom.xml,下面的配置默认是没有的,需要手动添加 + +```xml + + org.dromara + soul-spring-boot-starter-sync-data-nacos + ${project.version} + +``` + +- 启动服务 + +``` +1、启动 nacos +2、启动 soul-admin +3、启动 soul-bootstrap +``` + +2、上面看着挺顺利,这个过程遇到了坑,soul-bootstrap 启动不起来报空指针异常,下面详细记录一下。 +首先 soul-admin 启动后不会主动向 nacos 同步网关数据,需要手动同步,官网这一点没有提到。这个问题绊了我好久,最后是看到了群里其他同学遇到了同样的问题,参考了他们的文章才解决,下面记录一下解决过程。 + +1)soul-bootstrap 启动的时候遇到了如下的错误,NullPointerException。 + +soul-bootstrap 启动的时候会去,nacos 获取网关数据,看到下面的断点,拿到的是空数据。 + +``` +Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. +2021-01-25 16:49:06.052 ERROR 5273 --- [ main] o.s.boot.SpringApplication : Application run failed +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosSyncDataService' defined in class path resource [org/dromara/soul/springboot/starter/sync/data/nacos/NacosSyncDataConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is java.lang.NullPointerException +...... + at org.dromara.soul.bootstrap.SoulBootstrapApplication.main(SoulBootstrapApplication.java:37) [classes/:na] +Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dromara.soul.sync.data.api.SyncDataService]: Factory method 'nacosSyncDataService' threw exception; nested exception is java.lang.NullPointerException + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] + ... 19 common frames omitted +Caused by: java.lang.NullPointerException: null + at org.dromara.soul.sync.data.nacos.handler.NacosCacheHandler.updateMetaDataMap(NacosCacheHandler.java:128) ~[classes/:na] + at org.dromara.soul.sync.data.nacos.handler.NacosCacheHandler.watcherData(NacosCacheHandler.java:167) ~[classes/:na] + at org.dromara.soul.sync.data.nacos.NacosSyncDataService.start(NacosSyncDataService.java:59) ~[classes/:na] + at org.dromara.soul.sync.data.nacos.NacosSyncDataService.(NacosSyncDataService.java:49) ~[classes/:na] + at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration.nacosSyncDataService(NacosSyncDataConfiguration.java:66) ~[classes/:na] + at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7.CGLIB$nacosSyncDataService$0() ~[classes/:na] + at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7$$FastClassBySpringCGLIB$$3830e886.invoke() ~[classes/:na] + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE] + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE] + at org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration$$EnhancerBySpringCGLIB$$cce084b7.nacosSyncDataService() ~[classes/:na] +...... + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] + ... 20 common frames omitted +``` + +![在这里插入图片描述](/assets/img/blog5/ns1.png) + +2)到 nacos 去看一下是否有网关的数据,根据配置的 “namespace: 1c10d748-af86-43b9-8265-75f487d20c6c” 结果是什么都没有。 + +![在这里插入图片描述](/assets/img/blog5/ns2.png) + +![在这里插入图片描述](/assets/img/blog5/ns3.png) + +3、尝试去 soul-admin 手动同步,nacos 也看不到数据,必须需要手动创建命名空间“1c10d748-af86-43b9-8265-75f487d20c6c”,如下图。 + +![在这里插入图片描述](/assets/img/blog5/ns4.png) + +4、去 soul-admin 手动同步数据后,就看到了 nacos 上有了网关的配置信息,这时候 soul-bootstrap 还是启动不起来,因为这里还缺少元数据信息。元数据只有 dubbo、springcloud 服务有数据,http 是没有元数据的,所以还得去启动一下 dubbo 服务。然后在 soul-admin 同步一下元数据。 + +![在这里插入图片描述](/assets/img/blog5/ns5.png) + +soul-admin 点击同步数据,将元数据会同步到 nacos + +![在这里插入图片描述](/assets/img/blog5/ns6.png) + +soul-admin 点击同步数据,将认证数据会同步到 nacos + +![在这里插入图片描述](/assets/img/blog5/ns7.png) + +这时候 nacos 已经看到了全部的网关数据 + +![在这里插入图片描述](/assets/img/blog5/ns8.png) + +5、再去启动 soul-bootstrap,终于启动成功 + +``` +2021-01-25 17:56:54.798 INFO 10051 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[monitor] [org.dromara.soul.plugin.monitor.MonitorPlugin] +2021-01-25 17:56:54.798 INFO 10051 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[response] [org.dromara.soul.plugin.httpclient.response.WebClientResponsePlugin] +2021-01-25 17:56:54.990 INFO 10051 --- [ main] d.s.s.s.s.d.n.NacosSyncDataConfiguration : you use nacos sync soul data....... +2021-01-25 17:56:58.890 INFO 10051 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' +2021-01-25 17:56:59.758 INFO 10051 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195 +2021-01-25 17:56:59.764 INFO 10051 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 8.401 seconds (JVM running for 9.95) +``` + +6、小结: + +配置下来感觉使用 nacos 同步数据不是很友好,配置过程遇到了很多坑,首先 soul-admin 不会主动同步网关数据到 nacos,需要手动同步。soul-bootstrap 必须依赖所有的网关配置数据 soul.plugin、soul.selector、soul.selector、soul.meta、soul.auth,缺一不可。如果网关只代理 http 服务(无元数据),soul-bootstrap 是启动不起来的。官网这一块没有做详细说明,对小白不是很友好。 + +我们知道 soul-admin 启动后不会自动向 nacos 同步数据,需要手动操作。 + +下面分析一下 soul-admin,nacos,soul-bootstrap 同步数据的过程。 + +##### soul-admin 如何同步网关数据? + +1、插件信息更新后会发布一个 DataChangedEvent 事件 + +```java +/** + * create or update plugin + * @param pluginDTO {@linkplain PluginDTO} + * @return rows + */ +@Override +@Transactional(rollbackFor = Exception.class) +public String createOrUpdate(final PluginDTO pluginDTO) { + ...... + // publish change event. + eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType, + Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO)))); + return StringUtils.EMPTY; +} +``` + +2、由监听事件处理类 DataChangedEventDispatcher 负责调用具体的监听实现类对 DataChangedEvent 事件进行处理,这里的具体实现类是 NacosDataChangedListener。 + +> org.dromara.soul.admin.listener.DataChangedEventDispatcher + +> DataChangedEventDispatcher 初始化完成后会执行 afterPropertiesSet(),在容器中获取所有类型是 DataChangedListener.class 的 bean + +```java + +@Override +public void afterPropertiesSet() { + Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); + this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); +} +``` + +> > DataChangedEventDispatcher 监听到变更事件后,会执行 onApplicationEvent,遍历所有的监听类对监听事件进行处理,这里是 NacosDataChangedListener,如下图的 debug。 + +```java + +@Override +@SuppressWarnings("unchecked") +public void onApplicationEvent(final DataChangedEvent event) { + for (DataChangedListener listener : listeners) { + switch (event.getGroupKey()) { + ...... + case RULE: + listener.onRuleChanged((List) event.getSource(), event.getEventType()); + break; + ...... + default: + throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); + } + } +} +``` + +![在这里插入图片描述](/assets/img/blog5/ns9.png) + +3、NacosDataChangedListener 会执行 onRuleChanged,updateRuleMap 先将网关数据同步至内存,在通过 publishConfig 同步至 nacos。 + +> org.dromara.soul.admin.listener.nacos.NacosDataChangedListener + +```java +// 执行监听事件 +@Override +public void onRuleChanged(final List changed, final DataEventTypeEnum eventType) { + updateRuleMap(getConfig(RULE_DATA_ID)); + switch (eventType) { + ...... + default: + changed.forEach(rule -> { + List ls = RULE_MAP + .getOrDefault(rule.getSelectorId(), new ArrayList<>()) + .stream() + .filter(s -> !s.getId().equals(rule.getSelectorId())) + .sorted(RULE_DATA_COMPARATOR) + .collect(Collectors.toList()); + ls.add(rule); + RULE_MAP.put(rule.getSelectorId(), ls); + }); + break; + } + publishConfig(RULE_DATA_ID, RULE_MAP); +} +// 同步至内存 +private void updateRuleMap(final String configInfo) { + JsonObject jo = GsonUtils.getInstance().fromJson(configInfo, JsonObject.class); + Set set = new HashSet<>(RULE_MAP.keySet()); + ...... + RULE_MAP.keySet().removeAll(set); +} +// 同步至nacos +@SneakyThrows +private void publishConfig(final String dataId, final Object data) { + configService.publishConfig(dataId, GROUP, GsonUtils.getInstance().toJson(data)); +} +``` + +![在这里插入图片描述](/assets/img/blog5/ns10.png) + +4、DataChangedEventDispatcher 、NacosDataChangedListener 类继承关系 + +![在这里插入图片描述](/assets/img/blog5/ns11.png) + +![在这里插入图片描述](/assets/img/blog5/ns12.png) + +5、小结 + +> 1、例如 soul-admin 更新网关数据,发布一个 DataChangedEvent 事件,eventPublisher.publishEvent(new DataChangedEvent()) +> 2、DataChangedEventDispatcher --> onApplicationEvent()方法监听事件到事件,判断监听类是 NacosDataChangedListener +> 3、NacosDataChangedListener --> onRuleChanged()处理事件 +> 4、同步至内存 updateRuleMap(getConfig(RULE_DATA_ID)) +> 5、同步至 nacos publishConfig(RULE_DATA_ID, RULE_MAP) + +##### soul-bootstrap 如何同步网关数据? + +1、soul-bootstrap 添加了 nacos 依赖 soul-spring-boot-starter-sync-data-nacos,服务启动后会自动注入 NacosSyncDataConfiguration + +> org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration +> +> > NacosSyncDataService 负责读取和同步 nacos 网关数据 + +```java +@Configuration +@ConditionalOnClass(NacosSyncDataService.class) +@ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url") +@Slf4j +public class NacosSyncDataConfiguration { + // 注入nacos数据同步服务 + @Bean + public SyncDataService nacosSyncDataService(final ObjectProvider configService, final ObjectProvider pluginSubscriber, + final ObjectProvider> metaSubscribers, final ObjectProvider> authSubscribers) { + log.info("you use nacos sync soul data......."); + return new NacosSyncDataService(configService.getIfAvailable(), pluginSubscriber.getIfAvailable(), + metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList)); + } + // 注入nacos客户端配置服务 + @Bean + public ConfigService nacosConfigService(final NacosConfig nacosConfig) throws Exception { + Properties properties = new Properties(); + ...... + return NacosFactory.createConfigService(properties); + } + // 注入nacos配置服务 + @Bean + @ConfigurationProperties(prefix = "soul.sync.nacos") + public NacosConfig nacosConfig() { + return new NacosConfig(); + } +} +``` + +2、org.dromara.soul.sync.data.nacos.NacosSyncDataService + +> 初始化会执行 start +> +> > watcherData 负责监听 nacos 网关数据 +> > +> > > updatePluginMap 同步网关数据到内存 + +```java +public void start() { + ...... + watcherData(RULE_DATA_ID, this::updateRuleMap); + ...... +} +@SneakyThrows +private String getConfigAndSignListener(final String dataId, final Listener listener) { + return configService.getConfigAndSignListener(dataId, GROUP, 6000, listener); +} +protected void watcherData(final String dataId, final OnChange oc) { + Listener listener = new Listener() { + @Override + public void receiveConfigInfo(final String configInfo) { + oc.change(configInfo); + } + ...... + }; + oc.change(getConfigAndSignListener(dataId, listener)); + LISTENERS.getOrDefault(dataId, new ArrayList<>()).add(listener); +} +``` + +![在这里插入图片描述](/assets/img/blog5/ns13.png) + +3、NacosSyncDataService 类关系图 + +![在这里插入图片描述](/assets/img/blog5/ns14.png) + +4、小结 + +> 1、soul-bootstrap 启动向容器自动注入 NacosSyncDataConfiguration +> 2、NacosSyncDataConfiguration 类中会向容器注入 NacosSyncDataService +> 3、NacosSyncDataService --> start() --> watcherData() 监听 nacos,同步网关数据到内存 +> 4、watcherData() --> updatePluginMap() + +##### 总结 + +![在这里插入图片描述](/assets/img/blog5/ns15.png) diff --git a/src/zh/blog/soul_source_learning_15_plugin_chain.md b/src/zh/blog/soul_source_learning_15_plugin_chain.md index 05de779094..2b00398610 100644 --- a/src/zh/blog/soul_source_learning_15_plugin_chain.md +++ b/src/zh/blog/soul_source_learning_15_plugin_chain.md @@ -1,271 +1,271 @@ ---- -title: Soul网关学习插件链实现 -author: 沈祥俊 -date: 2021-01-21 -tag: - - Soul -cover: '/assets/img/blog6/mirco.png' -head: - - - meta - - name: 博客 ---- - -### 一、引言 - -**插件是 Soul 的灵魂。** - -Soul 使用了插件化设计思想,实现了插件的热插拔,且极易扩展。内置丰富的插件支持,鉴权,限流,熔断,防火墙等等。 - -![image-20210122021834793](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122021834.png) - -Soul 是如何实现插件化设计的呢? - -在探究插件化设计之前,我们需要先了解下微内核架构(又称插件化架构)。 - -### 二、微内核架构 - -#### 1、架构释义 - -![img](/assets/img/blog6/mirco.png) - -微内核架构也被称为插件化架构,是一种**面向功能进行拆分**的可扩展性架构,通常用于实现基于产品的应用。 - -应用逻辑被分割为独立的**插件模块**和**核心系统**,提供了可扩展性、灵活性、功能隔离和自定义处理逻辑的特性。 - -微内核架构的**本质**,是将变化封装在插件里面,从而达到快速灵活扩展的目的,而又不影响整体系统的稳定。 - -#### 2、设计关键点 - -核心系统设计的关键技术: - -- **插件管理:**当前有哪些插件可用?如何加载这些插件?什么时候加载插件? - - 常见的实现方法是插件注册表机制。 - -- **插件连接:**插件如何连接到核心系统? - - 通常由核心系统制定连接规范,然后插件按照规范实现,核心系统按照规范加载即可。 - - 常见连接机制主要有:OSGi(Eclipse 使用)、消息模式、依赖注入(Spring 使用)。 - -- **插件通信:**插件与插件、插件与核心系统如何通信? - - 通信必须经过核心系统,因此通常由核心系统提供插件通信机制。 - -### 三、Soul 的插件化设计 - -参照微内核架构来看,Soul 的 `soul-web` 模块相当于核心系统,`soul-plugin` 下的子模块相当于插件模块。 - -**插件管理方面:** - -`soul-bootstrap` 模块的 pom 文件充当插件列表, 以硬编码的方式引入各插件。 - -在容器启动阶段,借助 springboot 的 starter 机制自动扫描并注册插件 bean 到 Spring 容器。 - -**插件连接方面:** - -借助 springboot 支持的多实例自动注入能力(ObjectProvider<List> plugins),将插件 Bean 列表注入到网关的**插件链**,实现插件与网关的连接。 - -**插件通信方面:** - -先在插件链初始化阶段完成插件排序,然后在插件处理时,借助贯穿整个插件链的 ServerWebExchange 完成向下游插件的定向传参,即某种意义上的插件通信机制。 - -### 四、Soul 的插件化实现 - -Soul 网关中定义了一条插件链,所有的插件都在这条链上依次处理。 - -在探究插件链之前,我们先来看看插件实现。 - -#### 1、插件实现 - -Soul 中所有插件最终均继承自 SoulPlugin,其完整继承关系如下所示: - -![SoulPlugin](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122022709.jpg) - -可以看到,Soul 的插件生态极其丰富,正是如此丰富的插件支撑起了 Soul 网关强大的扩展能力。 - -我们以常用的 DividePlugin 为例,分析插件内部所做工作。 - -DividePlugin 继承结构: - -![DividePlugin](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122024517.jpg) - -DividePlugin 继承自 AbstractSoulPlugin,最终实现了 SoulPlugin 接口。 - -1)先关注 SoulPlugin,该插件接口结构如下: - -![image-20210122025700589](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122025700.png) - -- execute 方法:处理方法,需要传入 exchange 交换区 和 SoulPluginChain 插件链 -- getOrder 方法:取得序号,用作插件排序 -- named 方法:获得插件名 -- skip 方法:判断是否跳过本次处理 - -每次处理时,将先进行 skip 判断,不跳过则执行 excute 处理方法。 - -2)再来看下 AbstractSoulPlugin,该抽象类结构如下: - -![image-20210122030444704](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122030444.png) - -重点关注 execute 方法,其核心代码如下: - -```java -if (pluginData.getEnable()){ - // 获取插件数据 - final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); - // 获取选择器数据 - final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); - final SelectorData selectorData = matchSelector(exchange, selectors); - // 获取规则 - final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); - RuleData rule; - if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { - //get last - rule = rules.get(rules.size() - 1); - } else { - rule = matchRule(exchange, rules); - } - // 执行具体处理 - return doExecute(exchange, chain, selectorData, rule); -} -// 继续执行后续插件处理 -return chain.execute(exchange); -``` - -获取选择器数据和规则,然后传入 doExecute 方法进行具体处理,doExecute 方法为抽象方法,交由子类具体实现。 - -3)查看插件子类 DividePlugin,其结构如下: - -![image-20210122032336069](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122032336.png) - -重点关注 doExecute 方法,以下是核心代码: - -```java -// 获取网关上下文和规则处理器 -final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); -final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); -// 获取上游列表 -final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); -// 选择待分发的目标上游 -final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); -DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); -// 设置 http url -String domain = buildDomain(divideUpstream); -String realURL = buildRealURL(domain, soulContext, exchange); -exchange.getAttributes().put(Constants.HTTP_URL, realURL); -// 设置 http timeout -exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); -exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); -return chain.execute(exchange); -``` - -很明显,divide 插件只是完成目标上游服务的待分发,即根据选择器和规则找到对应服务,再通过负载均衡策略分配上游服务实例。 - -而调用上游服务的工作是由其他相应的 client 类插件完成。 - -#### 2、插件链实现 - -借由插件链,Soul 将众多插件整合到一起进行统一调度处理。 - -插件链继承结构: - -![SoulPluginChain](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122035121.jpg) - -可以看到,Soul 中插件链 SoulPluginChain 仅有一个默认实现类 DefaultSoulPluginChain。 - -1)DefaultSoulPluginChain 类结构如下: - -![image-20210122040245671](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122040245.png) - -其持有通过构造方法传入的插件链,看看 execute 方法: - -```java -public Mono execute(final ServerWebExchange exchange) { - // 反应式编程语法:Mono.defer - return Mono.defer(() -> { - if (this.index < plugins.size()) { - SoulPlugin plugin = plugins.get(this.index++); - // 判断是否需要调过 - Boolean skip = plugin.skip(exchange); - if (skip) { - return this.execute(exchange); - } - // 依次执行插件处理逻辑 - return plugin.execute(exchange, this); - } - return Mono.empty(); - }); -} -``` - -依次处理插件链上的插件,执行插件处理逻辑。 - -DefaultSoulPluginChain 是 SoulWebHandler 的内部类,看下 SoulWebHandler 的实现。 - -2)SoulWebHandler 结构如下: - -![image-20210122035525261](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122035525.png) - -SoulWebHandler 是 web 请求处理的起点,在此创建并开始插件链的处理。 - -同 DefaultSoulPluginChain 一样,SoulWebHandler 也是持有通过构造方法传入的插件链。 - -看看 handle 方法: - -```java -public Mono handle(@NonNull final ServerWebExchange exchange) { - MetricsTrackerFacade.getInstance().counterInc(MetricsLabelEnum.REQUEST_TOTAL.getName()); - Optional startTimer = MetricsTrackerFacade.getInstance().histogramStartTimer(MetricsLabelEnum.REQUEST_LATENCY.getName()); - return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler) - .doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time))); -} -``` - -handle 方法负责插件链执行指标度量的采集,通过在 DefaultSoulPluginChain 执行时加订阅实现,DefaultSoulPluginChain 在此处完成初始化。 - -全局查找 SoulWebHandler 构造方法,定位到 SoulConfiguration 的 soulWebHandler 方法。 - -3)SoulConfiguration 结构如下: - -![image-20210122042354171](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122042354.png) - -SoulConfiguration 是 Soul 的核心配置类,负责自动装配网关所需的核心 bean 对象。 - -如装配 SoulWebHandler: - -```java -@Bean("webHandler") -public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { - // 获取可用的插件 - List pluginList = plugins.getIfAvailable(Collections::emptyList); - // 插件重排 - final List soulPlugins = pluginList.stream() - .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); - soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); - return new SoulWebHandler(soulPlugins); -} -``` - -注意此处的插件列表经过了一次重排,重排顺序参见 PluginEnum。 - -4)初始化 SoulWebHandler - -soul-bootstrap 启动的过程中,所有插件是怎么形成 ObjectProvider<List<SoulPlugin>> plugins,然后初始化 SoulWebHandler 的呢? - -SoulWebHandler 所在的配置类通过配置 @ComponentScan("org.dromara.soul"),通知 spring 扫描 org.dromara.soul 包。 - -借助 springboot 的 starter 机制,将 spring.factories 里指定的配置类自动加载到容器。 - -![DividePluginConfiguration](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122044810.png) - -最后,借助 spring4.3 开始支持的 ObjectProvider,实现容器内插件 bean 的集合式注入,最终形成我们看到的插件链。 - -### 总结 - -本篇从微内核架构说起,并以此为框架分析 Soul 的插件化设计,再结合源码实现,基本理清了 Soul 中插件式设计的实现。 - -需要注意: - -1)由 SoulConfiguration 自动装配 SoulWebHandler,此时 SoulWebHandler 持有插件列表,但未初始化插件链。 - -2)待调用 handle 方法处理请求时,才初始化插件链进入插件处理。 +--- +title: Soul网关学习插件链实现 +author: 沈祥俊 +date: 2021-01-21 +tag: + - Soul +cover: '/assets/img/blog6/mirco.png' +head: + - - meta + - name: 博客 +--- + +### 一、引言 + +**插件是 Soul 的灵魂。** + +Soul 使用了插件化设计思想,实现了插件的热插拔,且极易扩展。内置丰富的插件支持,鉴权,限流,熔断,防火墙等等。 + +![image-20210122021834793](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122021834.png) + +Soul 是如何实现插件化设计的呢? + +在探究插件化设计之前,我们需要先了解下微内核架构(又称插件化架构)。 + +### 二、微内核架构 + +#### 1、架构释义 + +![img](/assets/img/blog6/mirco.png) + +微内核架构也被称为插件化架构,是一种**面向功能进行拆分**的可扩展性架构,通常用于实现基于产品的应用。 + +应用逻辑被分割为独立的**插件模块**和**核心系统**,提供了可扩展性、灵活性、功能隔离和自定义处理逻辑的特性。 + +微内核架构的**本质**,是将变化封装在插件里面,从而达到快速灵活扩展的目的,而又不影响整体系统的稳定。 + +#### 2、设计关键点 + +核心系统设计的关键技术: + +- **插件管理:**当前有哪些插件可用?如何加载这些插件?什么时候加载插件? + + 常见的实现方法是插件注册表机制。 + +- **插件连接:**插件如何连接到核心系统? + + 通常由核心系统制定连接规范,然后插件按照规范实现,核心系统按照规范加载即可。 + + 常见连接机制主要有:OSGi(Eclipse 使用)、消息模式、依赖注入(Spring 使用)。 + +- **插件通信:**插件与插件、插件与核心系统如何通信? + + 通信必须经过核心系统,因此通常由核心系统提供插件通信机制。 + +### 三、Soul 的插件化设计 + +参照微内核架构来看,Soul 的 `soul-web` 模块相当于核心系统,`soul-plugin` 下的子模块相当于插件模块。 + +**插件管理方面:** + +`soul-bootstrap` 模块的 pom 文件充当插件列表, 以硬编码的方式引入各插件。 + +在容器启动阶段,借助 springboot 的 starter 机制自动扫描并注册插件 bean 到 Spring 容器。 + +**插件连接方面:** + +借助 springboot 支持的多实例自动注入能力(ObjectProvider<List> plugins),将插件 Bean 列表注入到网关的**插件链**,实现插件与网关的连接。 + +**插件通信方面:** + +先在插件链初始化阶段完成插件排序,然后在插件处理时,借助贯穿整个插件链的 ServerWebExchange 完成向下游插件的定向传参,即某种意义上的插件通信机制。 + +### 四、Soul 的插件化实现 + +Soul 网关中定义了一条插件链,所有的插件都在这条链上依次处理。 + +在探究插件链之前,我们先来看看插件实现。 + +#### 1、插件实现 + +Soul 中所有插件最终均继承自 SoulPlugin,其完整继承关系如下所示: + +![SoulPlugin](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122022709.jpg) + +可以看到,Soul 的插件生态极其丰富,正是如此丰富的插件支撑起了 Soul 网关强大的扩展能力。 + +我们以常用的 DividePlugin 为例,分析插件内部所做工作。 + +DividePlugin 继承结构: + +![DividePlugin](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122024517.jpg) + +DividePlugin 继承自 AbstractSoulPlugin,最终实现了 SoulPlugin 接口。 + +1)先关注 SoulPlugin,该插件接口结构如下: + +![image-20210122025700589](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122025700.png) + +- execute 方法:处理方法,需要传入 exchange 交换区 和 SoulPluginChain 插件链 +- getOrder 方法:取得序号,用作插件排序 +- named 方法:获得插件名 +- skip 方法:判断是否跳过本次处理 + +每次处理时,将先进行 skip 判断,不跳过则执行 excute 处理方法。 + +2)再来看下 AbstractSoulPlugin,该抽象类结构如下: + +![image-20210122030444704](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122030444.png) + +重点关注 execute 方法,其核心代码如下: + +```java +if (pluginData.getEnable()){ + // 获取插件数据 + final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); + // 获取选择器数据 + final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); + final SelectorData selectorData = matchSelector(exchange, selectors); + // 获取规则 + final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); + RuleData rule; + if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { + //get last + rule = rules.get(rules.size() - 1); + } else { + rule = matchRule(exchange, rules); + } + // 执行具体处理 + return doExecute(exchange, chain, selectorData, rule); +} +// 继续执行后续插件处理 +return chain.execute(exchange); +``` + +获取选择器数据和规则,然后传入 doExecute 方法进行具体处理,doExecute 方法为抽象方法,交由子类具体实现。 + +3)查看插件子类 DividePlugin,其结构如下: + +![image-20210122032336069](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122032336.png) + +重点关注 doExecute 方法,以下是核心代码: + +```java +// 获取网关上下文和规则处理器 +final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); +final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); +// 获取上游列表 +final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); +// 选择待分发的目标上游 +final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); +DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); +// 设置 http url +String domain = buildDomain(divideUpstream); +String realURL = buildRealURL(domain, soulContext, exchange); +exchange.getAttributes().put(Constants.HTTP_URL, realURL); +// 设置 http timeout +exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); +exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); +return chain.execute(exchange); +``` + +很明显,divide 插件只是完成目标上游服务的待分发,即根据选择器和规则找到对应服务,再通过负载均衡策略分配上游服务实例。 + +而调用上游服务的工作是由其他相应的 client 类插件完成。 + +#### 2、插件链实现 + +借由插件链,Soul 将众多插件整合到一起进行统一调度处理。 + +插件链继承结构: + +![SoulPluginChain](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122035121.jpg) + +可以看到,Soul 中插件链 SoulPluginChain 仅有一个默认实现类 DefaultSoulPluginChain。 + +1)DefaultSoulPluginChain 类结构如下: + +![image-20210122040245671](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122040245.png) + +其持有通过构造方法传入的插件链,看看 execute 方法: + +```java +public Mono execute(final ServerWebExchange exchange) { + // 反应式编程语法:Mono.defer + return Mono.defer(() -> { + if (this.index < plugins.size()) { + SoulPlugin plugin = plugins.get(this.index++); + // 判断是否需要调过 + Boolean skip = plugin.skip(exchange); + if (skip) { + return this.execute(exchange); + } + // 依次执行插件处理逻辑 + return plugin.execute(exchange, this); + } + return Mono.empty(); + }); +} +``` + +依次处理插件链上的插件,执行插件处理逻辑。 + +DefaultSoulPluginChain 是 SoulWebHandler 的内部类,看下 SoulWebHandler 的实现。 + +2)SoulWebHandler 结构如下: + +![image-20210122035525261](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122035525.png) + +SoulWebHandler 是 web 请求处理的起点,在此创建并开始插件链的处理。 + +同 DefaultSoulPluginChain 一样,SoulWebHandler 也是持有通过构造方法传入的插件链。 + +看看 handle 方法: + +```java +public Mono handle(@NonNull final ServerWebExchange exchange) { + MetricsTrackerFacade.getInstance().counterInc(MetricsLabelEnum.REQUEST_TOTAL.getName()); + Optional startTimer = MetricsTrackerFacade.getInstance().histogramStartTimer(MetricsLabelEnum.REQUEST_LATENCY.getName()); + return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler) + .doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time))); +} +``` + +handle 方法负责插件链执行指标度量的采集,通过在 DefaultSoulPluginChain 执行时加订阅实现,DefaultSoulPluginChain 在此处完成初始化。 + +全局查找 SoulWebHandler 构造方法,定位到 SoulConfiguration 的 soulWebHandler 方法。 + +3)SoulConfiguration 结构如下: + +![image-20210122042354171](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122042354.png) + +SoulConfiguration 是 Soul 的核心配置类,负责自动装配网关所需的核心 bean 对象。 + +如装配 SoulWebHandler: + +```java +@Bean("webHandler") +public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { + // 获取可用的插件 + List pluginList = plugins.getIfAvailable(Collections::emptyList); + // 插件重排 + final List soulPlugins = pluginList.stream() + .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); + soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); + return new SoulWebHandler(soulPlugins); +} +``` + +注意此处的插件列表经过了一次重排,重排顺序参见 PluginEnum。 + +4)初始化 SoulWebHandler + +soul-bootstrap 启动的过程中,所有插件是怎么形成 ObjectProvider<List<SoulPlugin>> plugins,然后初始化 SoulWebHandler 的呢? + +SoulWebHandler 所在的配置类通过配置 @ComponentScan("org.dromara.soul"),通知 spring 扫描 org.dromara.soul 包。 + +借助 springboot 的 starter 机制,将 spring.factories 里指定的配置类自动加载到容器。 + +![DividePluginConfiguration](https://gitee.com/stephenshen/pic-bed/raw/master/img/20210122044810.png) + +最后,借助 spring4.3 开始支持的 ObjectProvider,实现容器内插件 bean 的集合式注入,最终形成我们看到的插件链。 + +### 总结 + +本篇从微内核架构说起,并以此为框架分析 Soul 的插件化设计,再结合源码实现,基本理清了 Soul 中插件式设计的实现。 + +需要注意: + +1)由 SoulConfiguration 自动装配 SoulWebHandler,此时 SoulWebHandler 持有插件列表,但未初始化插件链。 + +2)待调用 handle 方法处理请求时,才初始化插件链进入插件处理。 diff --git a/src/zh/blog/soul_source_learning_16_divide_sxj.md b/src/zh/blog/soul_source_learning_16_divide_sxj.md index 4015952974..af750d0fa3 100644 --- a/src/zh/blog/soul_source_learning_16_divide_sxj.md +++ b/src/zh/blog/soul_source_learning_16_divide_sxj.md @@ -1,326 +1,326 @@ ---- -title: Soul网关学习divide插件源码解读 -author: 沈祥俊 -tag: - - Soul -date: 2021-02-01 -cover: /assets/img/activite/soul-xmind.png -head: - - - meta - - name: 博客 ---- - -## 插件概述 - -**插件定位** - -divide 插件是一个 http 正向代理插件,所有的 http 请求都由该插件进行负载均衡处理(具体的负载均衡策略在规则中指定)。 - -**生效时机** - -当请求头的 rpcType = http 且插件开启时,它将根据请求参数匹配规则,最终交由下游插件进行响应式代理调用。 - -## 插件处理流程 - -1)先回顾下请求处理类插件的通用流程(AbstractSoulPlugin # execute): - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - // 获取插件数据 - String pluginName = named(); - final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); - if (pluginData != null && pluginData.getEnabled()) { - // 获取选择器数据 - final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); - ... - // 匹配选择器 - final SelectorData selectorData = matchSelector(exchange, selectors); - ... - // 获取规则数据 - final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); - ... - // 匹配规则 - RuleData rule; - if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { - //get last - rule = rules.get(rules.size() - 1); - } else { - rule = matchRule(exchange, rules); - } - ... - // 执行自定义处理 - return doExecute(exchange, chain, selectorData, rule); - } - // 继续执行插件链处理 - return chain.execute(exchange); -} -``` - -AbstractSoulPlugin 先匹配到对应的选择器和规则,匹配通过则执行插件的自定义处理。 - -2)再来看看 divide 插件的自定义处理流程(DividePlugin # doExecute): - -```java -protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - ... - // 准备规则处理对象(内部持有:负载均衡算法名、重试次数以及超时时间) - final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); - // 获取选择器对应的可用服务列表 - final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); - ... - // 选择具体分发的服务实例ip(负载均衡) - final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); - DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); - ... - //设置 http url、超时时间以及重试次数 - String domain = buildDomain(divideUpstream); - String realURL = buildRealURL(domain, soulContext, exchange); - exchange.getAttributes().put(Constants.HTTP_URL, realURL); - exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); - exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); - // 继续执行插件链处理 - return chain.execute(exchange); -} -``` - -DividePlugin 先获取到选择器对应的可用服务列表,然后进行负载均衡选择即将分发的目标服务器实例 ip,最后设置最终的 url、超时时间以及重试次数并交由插件链下游进行处理。 - -**注意:** - -divide 插件自身只是负责根据选择器、规则和负载均衡策略选出待分发的服务器实例,并不直接向后端服务发起 http 请求。 - -## 主机探活 - -上面提到,divide 需要获取服务列表,看下获取的实现(UpstreamCacheManager # findUpstreamListBySelectorId): - -```java -public List findUpstreamListBySelectorId(final String selectorId) { - return UPSTREAM_MAP_TEMP.get(selectorId); -} -``` - -内部通过 UPSTREAM_MAP_TEMP 获取存活服务列表。 - -UpstreamCacheManager 内部维护了两份散列表: - -- UPSTREAM_MAP: - - 全量服务散列表,负责存放全量的上游服务信息,key 为 选择器 id,value 为使用相同选择器的服务列表。 - -- UPSTREAM_MAP_TEMP: - - 临时服务散列表,负责存放活动的上游服务信息,key 为 选择器 id,value 为使用相同选择器的服务列表。 - -前面章节我们提到,数据同步时,submit 方法同时更新了 UPSTREAM_MAP 和 UPSTREAM_MAP_TEMP,但后续服务下线如何维护 UPSTREAM_MAP_TEMP 呢,一切还得从 ip 探活说起。 - -#### 3.1 探活时机 - -探活时机得从 UpstreamCacheManager 初始化说起: - -```java -private UpstreamCacheManager() { - // 探活开关检查 - boolean check = Boolean.parseBoolean(System.getProperty("soul.upstream.check", "false")); - if (check) { - // 启动定时探活任务 - new ScheduledThreadPoolExecutor(1, SoulThreadFactory.create("scheduled-upstream-task", false)) - .scheduleWithFixedDelay(this::scheduled, - 30, Integer.parseInt(System.getProperty("soul.upstream.scheduledTime", "30")), TimeUnit.SECONDS); - } -} -``` - -UpstreamCacheManager 初始化时,若探活开关打开,则创建定时探活任务,此处默认 30 秒执行一次。 - -此处共涉及到两个配置参数: - -- soul.upstream.check 探活开关:默认为 ture,设置为 false 表示不检测 -- soul.upstream.scheduledTime 探活时间间隔,默认 10 秒 - -#### 3.2 探活任务 - -1)接下来看看探活任务实现(UpstreamCacheManager # scheduled): - -```java -private void scheduled() { - if (UPSTREAM_MAP.size() > 0) { - UPSTREAM_MAP.forEach((k, v) -> { - // 活动检查 - List result = check(v); - if (result.size() > 0) { - UPSTREAM_MAP_TEMP.put(k, result); - } else { - UPSTREAM_MAP_TEMP.remove(k); - } - }); - } -} -``` - -任务负责逐条遍历登记全量服务散列表,检查服务活性: - -- 若存活数大于 0,则更新存活服务散列表 -- 否则,移除存活服务散列表相应内容 - -2)继续看服务列表活性检查处理(UpstreamCacheManager # check): - -```java -private List check(final List upstreamList) { - List resultList = Lists.newArrayListWithCapacity(upstreamList.size()); - for (DivideUpstream divideUpstream : upstreamList) { - // 检查服务活性 - final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl()); - if (pass) { - // 更新服务状态 - if (!divideUpstream.isStatus()) { - divideUpstream.setTimestamp(System.currentTimeMillis()); - divideUpstream.setStatus(true); - ... - } - // 记录存活的服务 - resultList.add(divideUpstream); - } else { - // 更新服务状态 - divideUpstream.setStatus(false); - ... - } - } - return resultList; -} -``` - -负责遍历服务列表,根据 url 检查各服务活性并登记存活的服务。 - -#### 3.3 活性检查 - -1)服务活性检查实现(UpstreamCheckUtils # checkUrl): - -```java -public static boolean checkUrl(final String url) { - ... - // 检查url是否为ip+端口格式 - if (checkIP(url)) { - // 处理 ip 和端口 - String[] hostPort; - if (url.startsWith(HTTP)) { - final String[] http = StringUtils.split(url, "\\/\\/"); - hostPort = StringUtils.split(http[1], Constants.COLONS); - } else { - hostPort = StringUtils.split(url, Constants.COLONS); - } - // 测试主机是否可连通 - return isHostConnector(hostPort[0], Integer.parseInt(hostPort[1])); - } else { - // 测试主机是否可达 - return isHostReachable(url); - } -} -``` - -检查 url 是否为 ip + port 格式: - -- 若为 ip + 端口格式,则测试主机是否可连接 -- 否则,测试主机是否可达 - -2)测试主机是否可连接(UpstreamCheckUtils # isHostConnector): - -```java -private static boolean isHostConnector(final String host, final int port) { - try (Socket socket = new Socket()) { - socket.connect(new InetSocketAddress(host, port)); - } catch (IOException e) { - return false; - } - return true; -} -``` - -通过 socket 的 connection 测试 ip 的连通性。 - -3)测试主机是否可达(UpstreamCheckUtils # isHostReachable): - -```java -private static boolean isHostReachable(final String host) { - try { - return InetAddress.getByName(host).isReachable(1000); - } catch (IOException ignored) { - } - return false; -} -``` - -非 ip + 端口格式 url 尝试使用域名格式测试主机是否可达。 - -整体看下来,divide 插件从缓存里拿到的服务器信息,来源于数据同步,由探活任务定期主动更新。 - -## 负载均衡 - -上面提到,divide 通过负载均衡算法挑选最终分发的服务 ip,看下负载均衡的实现(LoadBalanceUtils # selector): - -```java -public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) { - LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm); - return loadBalance.select(upstreamList, ip); -} -``` - -内部使用 ExtensionLoader 实现 SPI 机制,然后通过算法名加载对应的负载均衡算法,执行负载均衡计算最终分发到的服务 ip。 - -soul 网关里默认支持三种负载均衡策略 - -- HASH(需要计算,可能存在不均衡的情况) -- RANDOM(最简单最快,大量请求下几乎平均) -- ROUND_ROBIN(需要记录状态,有一定的影响,大数据量下随机和轮询并无太大结果上的差异) - -默认为 RANDOM 随机算法,算法处理如下(RandomLoadBalance # doSelect): - -```java -public DivideUpstream doSelect(final List upstreamList, final String ip) { - int totalWeight = calculateTotalWeight(upstreamList); - boolean sameWeight = isAllUpStreamSameWeight(upstreamList); - // 若权重不一致,则按总权重随机 - if (totalWeight > 0 && !sameWeight) { - return random(totalWeight, upstreamList); - } - // 按服务数随机 - return random(upstreamList); -} -``` - -判断服务列表内服务的权重是否一致: - -- 若权重不一致,则按总权重随机 -- 否则,按服务数随机 - -按总权重随机细节(RandomLoadBalance # random): - -```java -private DivideUpstream random(final int totalWeight, final List upstreamList) { - // 按总权重取随机数 - int offset = RANDOM.nextInt(totalWeight); - // 确定随机值落在哪个段上 - for (DivideUpstream divideUpstream : upstreamList) { - offset -= getWeight(divideUpstream); - if (offset < 0) { - return divideUpstream; - } - } - return upstreamList.get(0); -} -``` - -## 总结 - -divide 插件处理流程: - -- 获取可用服务列表 - - - 服务列表最初来自 `soul-admin` 数据同步 - - 可用服务列表默认每 30 秒主动探活更新 - -- 负载均衡 - - 扩展加载器加载目标负载均衡算法 - - 执行具体均衡策略 - - 返回一个最终选择的服务信息 -- 设置最终服务的的 url 信息 -- 交由插件链下游进行处理 +--- +title: Soul网关学习divide插件源码解读 +author: 沈祥俊 +tag: + - Soul +date: 2021-02-01 +cover: /assets/img/activite/soul-xmind.png +head: + - - meta + - name: 博客 +--- + +## 插件概述 + +**插件定位** + +divide 插件是一个 http 正向代理插件,所有的 http 请求都由该插件进行负载均衡处理(具体的负载均衡策略在规则中指定)。 + +**生效时机** + +当请求头的 rpcType = http 且插件开启时,它将根据请求参数匹配规则,最终交由下游插件进行响应式代理调用。 + +## 插件处理流程 + +1)先回顾下请求处理类插件的通用流程(AbstractSoulPlugin # execute): + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + // 获取插件数据 + String pluginName = named(); + final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); + if (pluginData != null && pluginData.getEnabled()) { + // 获取选择器数据 + final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); + ... + // 匹配选择器 + final SelectorData selectorData = matchSelector(exchange, selectors); + ... + // 获取规则数据 + final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); + ... + // 匹配规则 + RuleData rule; + if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { + //get last + rule = rules.get(rules.size() - 1); + } else { + rule = matchRule(exchange, rules); + } + ... + // 执行自定义处理 + return doExecute(exchange, chain, selectorData, rule); + } + // 继续执行插件链处理 + return chain.execute(exchange); +} +``` + +AbstractSoulPlugin 先匹配到对应的选择器和规则,匹配通过则执行插件的自定义处理。 + +2)再来看看 divide 插件的自定义处理流程(DividePlugin # doExecute): + +```java +protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + ... + // 准备规则处理对象(内部持有:负载均衡算法名、重试次数以及超时时间) + final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); + // 获取选择器对应的可用服务列表 + final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); + ... + // 选择具体分发的服务实例ip(负载均衡) + final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); + DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); + ... + //设置 http url、超时时间以及重试次数 + String domain = buildDomain(divideUpstream); + String realURL = buildRealURL(domain, soulContext, exchange); + exchange.getAttributes().put(Constants.HTTP_URL, realURL); + exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); + exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); + // 继续执行插件链处理 + return chain.execute(exchange); +} +``` + +DividePlugin 先获取到选择器对应的可用服务列表,然后进行负载均衡选择即将分发的目标服务器实例 ip,最后设置最终的 url、超时时间以及重试次数并交由插件链下游进行处理。 + +**注意:** + +divide 插件自身只是负责根据选择器、规则和负载均衡策略选出待分发的服务器实例,并不直接向后端服务发起 http 请求。 + +## 主机探活 + +上面提到,divide 需要获取服务列表,看下获取的实现(UpstreamCacheManager # findUpstreamListBySelectorId): + +```java +public List findUpstreamListBySelectorId(final String selectorId) { + return UPSTREAM_MAP_TEMP.get(selectorId); +} +``` + +内部通过 UPSTREAM_MAP_TEMP 获取存活服务列表。 + +UpstreamCacheManager 内部维护了两份散列表: + +- UPSTREAM_MAP: + + 全量服务散列表,负责存放全量的上游服务信息,key 为 选择器 id,value 为使用相同选择器的服务列表。 + +- UPSTREAM_MAP_TEMP: + + 临时服务散列表,负责存放活动的上游服务信息,key 为 选择器 id,value 为使用相同选择器的服务列表。 + +前面章节我们提到,数据同步时,submit 方法同时更新了 UPSTREAM_MAP 和 UPSTREAM_MAP_TEMP,但后续服务下线如何维护 UPSTREAM_MAP_TEMP 呢,一切还得从 ip 探活说起。 + +#### 3.1 探活时机 + +探活时机得从 UpstreamCacheManager 初始化说起: + +```java +private UpstreamCacheManager() { + // 探活开关检查 + boolean check = Boolean.parseBoolean(System.getProperty("soul.upstream.check", "false")); + if (check) { + // 启动定时探活任务 + new ScheduledThreadPoolExecutor(1, SoulThreadFactory.create("scheduled-upstream-task", false)) + .scheduleWithFixedDelay(this::scheduled, + 30, Integer.parseInt(System.getProperty("soul.upstream.scheduledTime", "30")), TimeUnit.SECONDS); + } +} +``` + +UpstreamCacheManager 初始化时,若探活开关打开,则创建定时探活任务,此处默认 30 秒执行一次。 + +此处共涉及到两个配置参数: + +- soul.upstream.check 探活开关:默认为 ture,设置为 false 表示不检测 +- soul.upstream.scheduledTime 探活时间间隔,默认 10 秒 + +#### 3.2 探活任务 + +1)接下来看看探活任务实现(UpstreamCacheManager # scheduled): + +```java +private void scheduled() { + if (UPSTREAM_MAP.size() > 0) { + UPSTREAM_MAP.forEach((k, v) -> { + // 活动检查 + List result = check(v); + if (result.size() > 0) { + UPSTREAM_MAP_TEMP.put(k, result); + } else { + UPSTREAM_MAP_TEMP.remove(k); + } + }); + } +} +``` + +任务负责逐条遍历登记全量服务散列表,检查服务活性: + +- 若存活数大于 0,则更新存活服务散列表 +- 否则,移除存活服务散列表相应内容 + +2)继续看服务列表活性检查处理(UpstreamCacheManager # check): + +```java +private List check(final List upstreamList) { + List resultList = Lists.newArrayListWithCapacity(upstreamList.size()); + for (DivideUpstream divideUpstream : upstreamList) { + // 检查服务活性 + final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl()); + if (pass) { + // 更新服务状态 + if (!divideUpstream.isStatus()) { + divideUpstream.setTimestamp(System.currentTimeMillis()); + divideUpstream.setStatus(true); + ... + } + // 记录存活的服务 + resultList.add(divideUpstream); + } else { + // 更新服务状态 + divideUpstream.setStatus(false); + ... + } + } + return resultList; +} +``` + +负责遍历服务列表,根据 url 检查各服务活性并登记存活的服务。 + +#### 3.3 活性检查 + +1)服务活性检查实现(UpstreamCheckUtils # checkUrl): + +```java +public static boolean checkUrl(final String url) { + ... + // 检查url是否为ip+端口格式 + if (checkIP(url)) { + // 处理 ip 和端口 + String[] hostPort; + if (url.startsWith(HTTP)) { + final String[] http = StringUtils.split(url, "\\/\\/"); + hostPort = StringUtils.split(http[1], Constants.COLONS); + } else { + hostPort = StringUtils.split(url, Constants.COLONS); + } + // 测试主机是否可连通 + return isHostConnector(hostPort[0], Integer.parseInt(hostPort[1])); + } else { + // 测试主机是否可达 + return isHostReachable(url); + } +} +``` + +检查 url 是否为 ip + port 格式: + +- 若为 ip + 端口格式,则测试主机是否可连接 +- 否则,测试主机是否可达 + +2)测试主机是否可连接(UpstreamCheckUtils # isHostConnector): + +```java +private static boolean isHostConnector(final String host, final int port) { + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(host, port)); + } catch (IOException e) { + return false; + } + return true; +} +``` + +通过 socket 的 connection 测试 ip 的连通性。 + +3)测试主机是否可达(UpstreamCheckUtils # isHostReachable): + +```java +private static boolean isHostReachable(final String host) { + try { + return InetAddress.getByName(host).isReachable(1000); + } catch (IOException ignored) { + } + return false; +} +``` + +非 ip + 端口格式 url 尝试使用域名格式测试主机是否可达。 + +整体看下来,divide 插件从缓存里拿到的服务器信息,来源于数据同步,由探活任务定期主动更新。 + +## 负载均衡 + +上面提到,divide 通过负载均衡算法挑选最终分发的服务 ip,看下负载均衡的实现(LoadBalanceUtils # selector): + +```java +public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) { + LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm); + return loadBalance.select(upstreamList, ip); +} +``` + +内部使用 ExtensionLoader 实现 SPI 机制,然后通过算法名加载对应的负载均衡算法,执行负载均衡计算最终分发到的服务 ip。 + +soul 网关里默认支持三种负载均衡策略 + +- HASH(需要计算,可能存在不均衡的情况) +- RANDOM(最简单最快,大量请求下几乎平均) +- ROUND_ROBIN(需要记录状态,有一定的影响,大数据量下随机和轮询并无太大结果上的差异) + +默认为 RANDOM 随机算法,算法处理如下(RandomLoadBalance # doSelect): + +```java +public DivideUpstream doSelect(final List upstreamList, final String ip) { + int totalWeight = calculateTotalWeight(upstreamList); + boolean sameWeight = isAllUpStreamSameWeight(upstreamList); + // 若权重不一致,则按总权重随机 + if (totalWeight > 0 && !sameWeight) { + return random(totalWeight, upstreamList); + } + // 按服务数随机 + return random(upstreamList); +} +``` + +判断服务列表内服务的权重是否一致: + +- 若权重不一致,则按总权重随机 +- 否则,按服务数随机 + +按总权重随机细节(RandomLoadBalance # random): + +```java +private DivideUpstream random(final int totalWeight, final List upstreamList) { + // 按总权重取随机数 + int offset = RANDOM.nextInt(totalWeight); + // 确定随机值落在哪个段上 + for (DivideUpstream divideUpstream : upstreamList) { + offset -= getWeight(divideUpstream); + if (offset < 0) { + return divideUpstream; + } + } + return upstreamList.get(0); +} +``` + +## 总结 + +divide 插件处理流程: + +- 获取可用服务列表 + + - 服务列表最初来自 `soul-admin` 数据同步 + - 可用服务列表默认每 30 秒主动探活更新 + +- 负载均衡 + - 扩展加载器加载目标负载均衡算法 + - 执行具体均衡策略 + - 返回一个最终选择的服务信息 +- 设置最终服务的的 url 信息 +- 交由插件链下游进行处理 diff --git a/src/zh/blog/soul_source_learning_17_http.md b/src/zh/blog/soul_source_learning_17_http.md index 78466bbd21..b56b6e3580 100644 --- a/src/zh/blog/soul_source_learning_17_http.md +++ b/src/zh/blog/soul_source_learning_17_http.md @@ -1,416 +1,416 @@ ---- -title: Soul网关学习Http请求探险 -author: 百钰 -date: 2021-01-26 -tag: - - Soul -cover: https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d89a3b45058846be94aa0b8935ec1868~tplv-k3u1fbpfcp-watermark.image -head: - - - meta - - name: 博客 ---- - -# 回顾 - -在 Soul 请求处理概览概览这篇文章中,我们已经知晓了 Soul 针对于请求的处理入库在**DefaultSoulPluginChain 的 excute**,其中执行了一个插件链的模式来完成了请求的处理。 - -我们大体梳理了注入到**plugins**的插件,但是即使这样依然不能纵观全局,对此特地对 soul 插件所涉及的类进行了相关梳理,整体梳理结果如下图。 - -![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d9c8e69429e4cb1bcc5bd54ad4f6112~tplv-k3u1fbpfcp-watermark.image) - -在梳理文章中可以看到核心类是**SoulPlugin、PluginEnum、PluginDataHandler、MetaDataSubscriber**,在梳理请求的相关文章中我们目前只需要重点关注 SoulPlugin 与 PluginEnum 类。 - -SoulPlugin 类我们已经有了一定的理解,那 PluginEnum 枚举类的主要作用是什么呢? - -PluginEnum:插件的枚举类 - -| 属性 | 作用 | -| ---- | --------------------------- | -| code | 插件的执行顺序 越小越先执行 | -| role | 角色 暂时未发现实际引用地址 | -| name | 插件名称 | - -其实我们不难发现在**DefaultSoulPluginChain 的 plugins**的插件都是有固定的执行顺序的,那这个插件的执行顺序是在哪定义的呢? - -最终可以追溯到**SoulConfiguration**类下 - -```java - public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { - //省略 - final List soulPlugins = pluginList.stream() - .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); - return new SoulWebHandler(soulPlugins); - } -``` - -整理整个 PluginEnum 类相关引用,整理出如下表格,不难看出插件与插件之间的顺序关系 -![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d89a3b45058846be94aa0b8935ec1868~tplv-k3u1fbpfcp-watermark.image) - -| 等级 | 作用 | -| -------------------- | -------------------------------------------------- | -| 第一等级 | 只有 GlobalPlugin 全局插件 | -| 第二等级到第八等级 | 可以理解为在请求发起前的前置处理插件 | -| 第九等级到第十一等级 | 可以理解为针对调用方的方式所针对的不同调用处理 | -| 第十二等级 | 只有 MonitorPlugin 监控插件 | -| 第十三等级 | 是针对于各个调用方返回结果处理的 Response 相关插件 | - -在刚才的回顾中我们已经明白 soul 处理请求的大体流程 - -- 1.GloBalPlugin 插件 进行全局的初始化 -- 2.部分插件根据鉴权、限流、熔断等规则对请求进行处理 -- 3.选择适合自己的调用方式进行拼装参数,发起调用。 -- 4.进行监控 -- 5.对调用的结果进行处理 - -# 请求流程梳理 - -> 以下演示代码截图来自于 soul-examples 下的 http demo,调用的接口地址为http://127.0.0.1:9195/http/test/findByUserId?userId=10 - -在**DefaultSoulPluginChain 的 excute**方法进行埋点,查看一次 http 请求调用经过了哪些类? - -```java -public Mono execute(final ServerWebExchange exchange) { - return Mono.defer(() -> { - if (this.index < plugins.size()) { - SoulPlugin plugin = plugins.get(this.index++); - Boolean skip = plugin.skip(exchange); - if (skip) { - System.out.println("跳过的插件为"+plugin.getClass().getName().replace("org.dromara.soul.plugin.","")); - return this.execute(exchange); - } - System.out.println("未跳过的插件为"+plugin.getClass().getName().replace("org.dromara.soul.plugin.","")); - return plugin.execute(exchange, this); - } - return Mono.empty(); - }); - } -``` - -最终输出的未跳过的插件如下: - -未跳过的插件为 global.GlobalPlugin
-未跳过的插件为 sign.SignPlugin
-未跳过的插件为 waf.WafPlugin
-未跳过的插件为 ratelimiter.RateLimiterPlugin
-未跳过的插件为 hystrix.HystrixPlugin
-未跳过的插件为 resilience4j.Resilience4JPlugin
-未跳过的插件为 divide.DividePlugin
-未跳过的插件为 httpclient.WebClientPlugin
-未跳过的插件为 alibaba.dubbo.param.BodyParamPlugin
-未跳过的插件为 monitor.MonitorPlugin
-未跳过的插件为 httpclient.response.WebClientResponsePlugin
- -> 这里有个小疑惑,为啥这个 alibaba.dubbo.param.BodyParamPlugin 插件会被执行,暂时忽略,后期跟踪。 - -我们发现一次针对于 http 请求的网关调用 所执行的插件的大体流程与我们猜想的处理流程一致。
-目前我们只挑重点来讲,即**GlobalPlugin、DividePlugin、WebClientPlugin、WebClientResponsePlugin**。 - -发起 Debug 调用依次追踪上述四个插件的作用。 - -## GlobalPlugin SoulContext 对象封装插件 - -GlobalPlugin 的插件的 excute 方法如下所示 - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - final ServerHttpRequest request = exchange.getRequest(); - final HttpHeaders headers = request.getHeaders(); - final String upgrade = headers.getFirst("Upgrade"); - SoulContext soulContext; - if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) { - soulContext = builder.build(exchange); - } else { - final MultiValueMap queryParams = request.getQueryParams(); - soulContext = transformMap(queryParams); - } - exchange.getAttributes().put(Constants.CONTEXT, soulContext); - return chain.execute(exchange); - } -``` - -不难看出 在 GlobalPlugin 的 excute 方法中主要目的就是封装一个**SoulContext 对象**,放入 exchange 中(exchange 对象是整个插件链上的共享对象,有一个插件执行完成后传递给下一个插件,本人理解的就是一个类似于 ThreadLocal 对象)。 - -那 SoulContext 对象中又包含哪些属性呢? - -| 属性 | 含义 | -| ------------- | ------------------------------------------------------------ | -| module | 每种 RPCType 针对的值不同 http 调用时指代网关调用的前置地址 | -| method | 切割后的方法名(在 RpcType 为 http 时) | -| rpcType | RPC 调用类型有 Http、dubbo、sofa 等 | -| httpMethod | Http 调用的方式目前只支持 get、post | -| sign | 鉴权的相关属性目前不知道具体作用,可能与 SignPlugin 插件有关 | -| timestamp | 时间戳 | -| appKey | 鉴权的相关属性目前不知道具体作用,可能与 SignPlugin 插件有关 | -| path | 路径指代调用到 soul 网关的全路径(在 RpcType 为 http 时) | -| contextPath | 与 module 取值一致(在 RPCType 为 http 时) | -| realUrl | 与 method 的值一致(在 RpcType 为 http 时) | -| dubboParams | dubbo 的参数? | -| startDateTime | 开始时间怀疑与监控插件和统计指标模块有联用 | - -在执行完 GlobalPlugin 插件后,最终封装完成的 SoulContext 对象如下所示。 -![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d34d9e900a1e4448b8f15302db36a5bb~tplv-k3u1fbpfcp-watermark.image) - -其他 RPCType 的 SoulContext 的参数封装可以查看**DefaultSoulContextBuilder 的 build**方法进行追踪,由于本编文章主要追溯 http 调用,故在这里不在多余讨论。 - -## DividePlugin 路由选择插件 - -在执行完成 GlobalPlugin 插件后,最终封装成了一个**SoulContext 对象**,并将其放在了**ServerWebExchange**中,供下游的调用链使用。 - -接下来让我们看一下**DividePlugin 插件**在整个链式调用过程中到底起了一个什么样的作用? - -### AbstractSoulPlugin - -通过追溯源码得知**DividePlugin 插件继承于 AbstractSoulPlugin 类,而 AbstractSoulPlugin 类实现了 SoulPlugin 接口**。 - -那么**AbstractSoulPlugin**又做了哪些扩展呢?让我们梳理一下该类的方法。 - -| 方法名 | 作用 | -| -------------------- | -------------------------------------------------------------------------- | -| excute | 实现于 SoulPlugin 接口,在 AbstractSoulPlugin 中起到一个**模板方法的作用** | -| doexcute | **抽象方法** 交由各个子类实现 | -| matchSelector | 匹配选择器 | -| filterSelector | 筛选选择器 | -| matchRule | 匹配规则 | -| filterRule | 筛选规则 | -| handleSelectorIsNull | 处理选择器为空情况 | -| handleRuleIsNull | 处理规则为空情况 | -| selectorLog | 选择器日志打印 | -| ruleLog | 规则日志打印 | - -看一下**excute**方法的具体作用 - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - String pluginName = named(); - //获取对应插件 - final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); - //判断插件是否启用 - if (pluginData != null && pluginData.getEnabled()) { - //获取插件下的所有选择器 - final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); - if (CollectionUtils.isEmpty(selectors)) { - return handleSelectorIsNull(pluginName, exchange, chain); - } - //匹配选择器 - final SelectorData selectorData = matchSelector(exchange, selectors); - if (Objects.isNull(selectorData)) { - return handleSelectorIsNull(pluginName, exchange, chain); - } - //打印选择器日志 - selectorLog(selectorData, pluginName); - final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); - if (CollectionUtils.isEmpty(rules)) { - return handleRuleIsNull(pluginName, exchange, chain); - } - RuleData rule; - if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { - rule = rules.get(rules.size() - 1); - } else { - //匹配规则 - rule = matchRule(exchange, rules); - } - if (Objects.isNull(rule)) { - return handleRu![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f523f655f0014d288b7a4502cc6a08d1~tplv-k3u1fbpfcp-watermark.image)leIsNull(pluginName, exchange, chain); - } - //打印规则日志 - ruleLog(rule, pluginName); - //执行子类具体实现 - return doExecute(exchange, chain, selectorData, rule); - } - return chain.execute(exchange); - } -``` - -最终整理的流程图如下所示: -![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1ec8bd02d6546c79a96d67535049aae~tplv-k3u1fbpfcp-watermark.image) - -ps:在上述的流程图中并没有细化到具体的方法级别的处理。 - -但仍有几个点需要着重解释一下: - -- 1.插件数据、选择器数据、规则数据的获取全部来自于**BaseDataCache**,该类是数据同步过程中最终会影响的类。 -- 2.选择器的类型,在使用 SpringMvc 项目进行接口注册时,会有一个 isFull 的选项为 true 代表全局代理,在全局代理模式下只会注册一个选择器\规则(指代代理所有的接口),所以这里的对应处理为 rule.size()-1. -- 3.选择器和规则的选择,实际的处理要复杂的多,考虑到是介绍一次请求流程的大体逻辑,所以这里不展开阐述,有兴趣的可以查看**MatchStrategy、AbstractMatchStrategy 及其相关实现类**(后期会单独开一篇具体讲解),此处对应页面的如下: - ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f624b13f205a44e29b2799718433e0c9~tplv-k3u1fbpfcp-watermark.image) - ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f590c2cf336442f08a52b864c81d41a8~tplv-k3u1fbpfcp-watermark.image) - -梳理一下**AbstractSoulPlugin 的 exeute 方法**作用,经过上述流程图的引导,我们已经知晓该方法的作用是为了选取插件--->选取选择器--->选取规则,最后交由子类的**doexcute**方法。 - -接下来让我们看一下**DividePlugin 的 doexcute**方法具体做了哪些事。 - -### DividePlugin - -```java -protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - //获取规则处理数据 - final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); - //获取该选择器下的注入的地址 - final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); - if (CollectionUtils.isEmpty(upstreamList)) { - log.error("divide upstream configuration error: {}", rule.toString()); - Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); - //通过规则对应的负载均衡策略选择一个地址 - DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); - if (Objects.isNull(divideUpstream)) { - log.error("divide has no upstream"); - Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // set the http url - String domain = buildDomain(divideUpstream); - //拼装真实调用地址 - String realURL = buildRealURL(domain, soulContext, exchange); - exchange.getAttributes().put(Constants.HTTP_URL, realURL); - //设置超时时间 及重试次数 - exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); - exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); - return chain.execute(exchange); - } -``` - -通过上述代码梳理完成后大体逻辑如下: - -- 1.获取选择器对应的注册地址,对应页面数据如下 - ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bea039b5c98040ee80433f785dac85aa~tplv-k3u1fbpfcp-watermark.image) -- 2.根据规则的 handle 字段获取负载均衡策略,并选择真实的调用地址(**LoadBalanceUtils**),重试次数和超时时间,对应页面数据如下。 - ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07db1f8f76fc43b2aec61ee0f9ca4c05~tplv-k3u1fbpfcp-watermark.image) -- 3.将真实调用地址,超时时间,重试次数传递到**ServerWebExchange**中,供下游调用链使用。 - debug 演示: - ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0aa5f7d95f2942169b33029f074f1712~tplv-k3u1fbpfcp-watermark.image) - ps:在上述的主题逻辑中我们没有看到参数在哪里?那这个参数在哪封装的呢?答案在**buildRealURL 方法中**,是从**exchange**上下文中获取到的。 - -## WebClientPlugin Http 请求调用插件 - -接下来让我们看看 Soul 如何发起的请求调用 - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - //获取真实地址 - String urlPath = exchange.getAttribute(Constants.HTTP_URL); - if (StringUtils.isEmpty(urlPath)) { - Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - //获取超时时间 - long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L); - //获取重试次数 - int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0); - log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes); - HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue()); - WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath); - return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain); - } -``` - -在 webClient 的**excute**方法中,主要做了三个事 - -- 1.将从 Divide 插件中放入 exchange 的属性取出来,**调用的真实地址、超时时间、重试次数**。 -- 2.封装了一个**RequestBodySpec**对象(不认识这个响应式编程的东西) -- 3.调用了一个**handleRequestBody**方法 - -先认识**handleRequestBody**方法 - -```java -private Mono handleRequestBody(final WebClient.RequestBodySpec requestBodySpec, - final ServerWebExchange exchange, - final long timeout, - final int retryTimes, - final SoulPluginChain chain) { - return requestBodySpec.headers(httpHeaders -> { - httpHeaders.addAll(exchange.getRequest().getHeaders()); - httpHeaders.remove(HttpHeaders.HOST); - }) - .contentType(buildMediaType(exchange)) - .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody())) - .exchange() - //失败打印日志 - .doOnError(e -> log.error(e.getMessage())) - //设置超时时间 - .timeout(Duration.ofMillis(timeout)) - //设置请求重试实际 - .retryWhen(Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException) - .retryMax(retryTimes) - .backoff(Backoff.exponential(Duration.ofMillis(200), Duration.ofSeconds(20), 2, true))) - //请求结束后对应的处理 - .flatMap(e -> doNext(e, exchange, chain)); - - } -``` - -在这个方法里,大体可以理解为 - -- exchange 中的请求头放到本次调用的请求头中 -- 设置 contentType -- 设置超时时间 -- 设置失败响应 -- 设置重试的场景及重试次数 -- 最终结果的处理。 - 在流程中需要还需要看一个**doNext 方法** - -大体逻辑就是判断请求是否成功,将请求结果放入 exchange 中交给下游插件处理。 - -```java -private Mono doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) { - if (res.statusCode().is2xxSuccessful()) { - exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); - } else { - exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.ERROR.getName()); - } - exchange.getAttributes().put(Constants.CLIENT_RESPONSE_ATTR, res); - return chain.execute(exchange); - } -``` - -ps: 虽然并不懂响应式编程,但并不影响我们阅读代码。 - -## WebClientResponsePlugin Http 结果处理插件 - -该实现的 excute 方法没有什么核心逻辑,就是判断请求状态码,根据状态码返回给前端不同的数据格式。 - -```java -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - return chain.execute(exchange).then(Mono.defer(() -> { - ServerHttpResponse response = exchange.getResponse(); - ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR); - if (Objects.isNull(clientResponse) - || response.getStatusCode() == HttpStatus.BAD_GATEWAY - || response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) { - Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { - Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - response.setStatusCode(clientResponse.statusCode()); - response.getCookies().putAll(clientResponse.cookies()); - response.getHeaders().putAll(clientResponse.headers().asHttpHeaders()); - return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())); - })); - } -``` - -# 总结 - -到此为止,一个基于 Soul 网关发起的 Http 请求调用流程大体已经结束。 - -梳理 http 请求调用流程 - -- Global 插件封装 SoulContext 对象 -- 前置插件处理熔断限流鉴权等操作。 -- Divide 插件选择对应调用的真实地址,重试次数,超时时间。 -- WebClient 插件发起真实的 Http 调用 -- WebClientResponse 插件处理对应结果,返回前台。 - -基于 Http 调用的大体流程,我们可以大体猜测出基于别 RPC 调用的流程,就是替换发起请求的插件和返回结果处理的插件。 - -在上文中我们还提到了路由规则的选择**LoadBalanceUtils**,选择器和规则的处理**MatchStrategy**。 - -之后将会开启新篇章一步步揭开 RPC 泛化调用,路由选择,选择器、规则匹配的神秘面纱。 +--- +title: Soul网关学习Http请求探险 +author: 百钰 +date: 2021-01-26 +tag: + - Soul +cover: https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d89a3b45058846be94aa0b8935ec1868~tplv-k3u1fbpfcp-watermark.image +head: + - - meta + - name: 博客 +--- + +# 回顾 + +在 Soul 请求处理概览概览这篇文章中,我们已经知晓了 Soul 针对于请求的处理入库在**DefaultSoulPluginChain 的 excute**,其中执行了一个插件链的模式来完成了请求的处理。 + +我们大体梳理了注入到**plugins**的插件,但是即使这样依然不能纵观全局,对此特地对 soul 插件所涉及的类进行了相关梳理,整体梳理结果如下图。 + +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d9c8e69429e4cb1bcc5bd54ad4f6112~tplv-k3u1fbpfcp-watermark.image) + +在梳理文章中可以看到核心类是**SoulPlugin、PluginEnum、PluginDataHandler、MetaDataSubscriber**,在梳理请求的相关文章中我们目前只需要重点关注 SoulPlugin 与 PluginEnum 类。 + +SoulPlugin 类我们已经有了一定的理解,那 PluginEnum 枚举类的主要作用是什么呢? + +PluginEnum:插件的枚举类 + +| 属性 | 作用 | +| ---- | --------------------------- | +| code | 插件的执行顺序 越小越先执行 | +| role | 角色 暂时未发现实际引用地址 | +| name | 插件名称 | + +其实我们不难发现在**DefaultSoulPluginChain 的 plugins**的插件都是有固定的执行顺序的,那这个插件的执行顺序是在哪定义的呢? + +最终可以追溯到**SoulConfiguration**类下 + +```java + public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { + //省略 + final List soulPlugins = pluginList.stream() + .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); + return new SoulWebHandler(soulPlugins); + } +``` + +整理整个 PluginEnum 类相关引用,整理出如下表格,不难看出插件与插件之间的顺序关系 +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d89a3b45058846be94aa0b8935ec1868~tplv-k3u1fbpfcp-watermark.image) + +| 等级 | 作用 | +| -------------------- | -------------------------------------------------- | +| 第一等级 | 只有 GlobalPlugin 全局插件 | +| 第二等级到第八等级 | 可以理解为在请求发起前的前置处理插件 | +| 第九等级到第十一等级 | 可以理解为针对调用方的方式所针对的不同调用处理 | +| 第十二等级 | 只有 MonitorPlugin 监控插件 | +| 第十三等级 | 是针对于各个调用方返回结果处理的 Response 相关插件 | + +在刚才的回顾中我们已经明白 soul 处理请求的大体流程 + +- 1.GloBalPlugin 插件 进行全局的初始化 +- 2.部分插件根据鉴权、限流、熔断等规则对请求进行处理 +- 3.选择适合自己的调用方式进行拼装参数,发起调用。 +- 4.进行监控 +- 5.对调用的结果进行处理 + +# 请求流程梳理 + +> 以下演示代码截图来自于 soul-examples 下的 http demo,调用的接口地址为http://127.0.0.1:9195/http/test/findByUserId?userId=10 + +在**DefaultSoulPluginChain 的 excute**方法进行埋点,查看一次 http 请求调用经过了哪些类? + +```java +public Mono execute(final ServerWebExchange exchange) { + return Mono.defer(() -> { + if (this.index < plugins.size()) { + SoulPlugin plugin = plugins.get(this.index++); + Boolean skip = plugin.skip(exchange); + if (skip) { + System.out.println("跳过的插件为"+plugin.getClass().getName().replace("org.dromara.soul.plugin.","")); + return this.execute(exchange); + } + System.out.println("未跳过的插件为"+plugin.getClass().getName().replace("org.dromara.soul.plugin.","")); + return plugin.execute(exchange, this); + } + return Mono.empty(); + }); + } +``` + +最终输出的未跳过的插件如下: + +未跳过的插件为 global.GlobalPlugin
+未跳过的插件为 sign.SignPlugin
+未跳过的插件为 waf.WafPlugin
+未跳过的插件为 ratelimiter.RateLimiterPlugin
+未跳过的插件为 hystrix.HystrixPlugin
+未跳过的插件为 resilience4j.Resilience4JPlugin
+未跳过的插件为 divide.DividePlugin
+未跳过的插件为 httpclient.WebClientPlugin
+未跳过的插件为 alibaba.dubbo.param.BodyParamPlugin
+未跳过的插件为 monitor.MonitorPlugin
+未跳过的插件为 httpclient.response.WebClientResponsePlugin
+ +> 这里有个小疑惑,为啥这个 alibaba.dubbo.param.BodyParamPlugin 插件会被执行,暂时忽略,后期跟踪。 + +我们发现一次针对于 http 请求的网关调用 所执行的插件的大体流程与我们猜想的处理流程一致。
+目前我们只挑重点来讲,即**GlobalPlugin、DividePlugin、WebClientPlugin、WebClientResponsePlugin**。 + +发起 Debug 调用依次追踪上述四个插件的作用。 + +## GlobalPlugin SoulContext 对象封装插件 + +GlobalPlugin 的插件的 excute 方法如下所示 + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + final ServerHttpRequest request = exchange.getRequest(); + final HttpHeaders headers = request.getHeaders(); + final String upgrade = headers.getFirst("Upgrade"); + SoulContext soulContext; + if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) { + soulContext = builder.build(exchange); + } else { + final MultiValueMap queryParams = request.getQueryParams(); + soulContext = transformMap(queryParams); + } + exchange.getAttributes().put(Constants.CONTEXT, soulContext); + return chain.execute(exchange); + } +``` + +不难看出 在 GlobalPlugin 的 excute 方法中主要目的就是封装一个**SoulContext 对象**,放入 exchange 中(exchange 对象是整个插件链上的共享对象,有一个插件执行完成后传递给下一个插件,本人理解的就是一个类似于 ThreadLocal 对象)。 + +那 SoulContext 对象中又包含哪些属性呢? + +| 属性 | 含义 | +| ------------- | ------------------------------------------------------------ | +| module | 每种 RPCType 针对的值不同 http 调用时指代网关调用的前置地址 | +| method | 切割后的方法名(在 RpcType 为 http 时) | +| rpcType | RPC 调用类型有 Http、dubbo、sofa 等 | +| httpMethod | Http 调用的方式目前只支持 get、post | +| sign | 鉴权的相关属性目前不知道具体作用,可能与 SignPlugin 插件有关 | +| timestamp | 时间戳 | +| appKey | 鉴权的相关属性目前不知道具体作用,可能与 SignPlugin 插件有关 | +| path | 路径指代调用到 soul 网关的全路径(在 RpcType 为 http 时) | +| contextPath | 与 module 取值一致(在 RPCType 为 http 时) | +| realUrl | 与 method 的值一致(在 RpcType 为 http 时) | +| dubboParams | dubbo 的参数? | +| startDateTime | 开始时间怀疑与监控插件和统计指标模块有联用 | + +在执行完 GlobalPlugin 插件后,最终封装完成的 SoulContext 对象如下所示。 +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d34d9e900a1e4448b8f15302db36a5bb~tplv-k3u1fbpfcp-watermark.image) + +其他 RPCType 的 SoulContext 的参数封装可以查看**DefaultSoulContextBuilder 的 build**方法进行追踪,由于本编文章主要追溯 http 调用,故在这里不在多余讨论。 + +## DividePlugin 路由选择插件 + +在执行完成 GlobalPlugin 插件后,最终封装成了一个**SoulContext 对象**,并将其放在了**ServerWebExchange**中,供下游的调用链使用。 + +接下来让我们看一下**DividePlugin 插件**在整个链式调用过程中到底起了一个什么样的作用? + +### AbstractSoulPlugin + +通过追溯源码得知**DividePlugin 插件继承于 AbstractSoulPlugin 类,而 AbstractSoulPlugin 类实现了 SoulPlugin 接口**。 + +那么**AbstractSoulPlugin**又做了哪些扩展呢?让我们梳理一下该类的方法。 + +| 方法名 | 作用 | +| -------------------- | -------------------------------------------------------------------------- | +| excute | 实现于 SoulPlugin 接口,在 AbstractSoulPlugin 中起到一个**模板方法的作用** | +| doexcute | **抽象方法** 交由各个子类实现 | +| matchSelector | 匹配选择器 | +| filterSelector | 筛选选择器 | +| matchRule | 匹配规则 | +| filterRule | 筛选规则 | +| handleSelectorIsNull | 处理选择器为空情况 | +| handleRuleIsNull | 处理规则为空情况 | +| selectorLog | 选择器日志打印 | +| ruleLog | 规则日志打印 | + +看一下**excute**方法的具体作用 + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + String pluginName = named(); + //获取对应插件 + final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); + //判断插件是否启用 + if (pluginData != null && pluginData.getEnabled()) { + //获取插件下的所有选择器 + final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); + if (CollectionUtils.isEmpty(selectors)) { + return handleSelectorIsNull(pluginName, exchange, chain); + } + //匹配选择器 + final SelectorData selectorData = matchSelector(exchange, selectors); + if (Objects.isNull(selectorData)) { + return handleSelectorIsNull(pluginName, exchange, chain); + } + //打印选择器日志 + selectorLog(selectorData, pluginName); + final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); + if (CollectionUtils.isEmpty(rules)) { + return handleRuleIsNull(pluginName, exchange, chain); + } + RuleData rule; + if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { + rule = rules.get(rules.size() - 1); + } else { + //匹配规则 + rule = matchRule(exchange, rules); + } + if (Objects.isNull(rule)) { + return handleRu![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f523f655f0014d288b7a4502cc6a08d1~tplv-k3u1fbpfcp-watermark.image)leIsNull(pluginName, exchange, chain); + } + //打印规则日志 + ruleLog(rule, pluginName); + //执行子类具体实现 + return doExecute(exchange, chain, selectorData, rule); + } + return chain.execute(exchange); + } +``` + +最终整理的流程图如下所示: +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1ec8bd02d6546c79a96d67535049aae~tplv-k3u1fbpfcp-watermark.image) + +ps:在上述的流程图中并没有细化到具体的方法级别的处理。 + +但仍有几个点需要着重解释一下: + +- 1.插件数据、选择器数据、规则数据的获取全部来自于**BaseDataCache**,该类是数据同步过程中最终会影响的类。 +- 2.选择器的类型,在使用 SpringMvc 项目进行接口注册时,会有一个 isFull 的选项为 true 代表全局代理,在全局代理模式下只会注册一个选择器\规则(指代代理所有的接口),所以这里的对应处理为 rule.size()-1. +- 3.选择器和规则的选择,实际的处理要复杂的多,考虑到是介绍一次请求流程的大体逻辑,所以这里不展开阐述,有兴趣的可以查看**MatchStrategy、AbstractMatchStrategy 及其相关实现类**(后期会单独开一篇具体讲解),此处对应页面的如下: + ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f624b13f205a44e29b2799718433e0c9~tplv-k3u1fbpfcp-watermark.image) + ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f590c2cf336442f08a52b864c81d41a8~tplv-k3u1fbpfcp-watermark.image) + +梳理一下**AbstractSoulPlugin 的 exeute 方法**作用,经过上述流程图的引导,我们已经知晓该方法的作用是为了选取插件--->选取选择器--->选取规则,最后交由子类的**doexcute**方法。 + +接下来让我们看一下**DividePlugin 的 doexcute**方法具体做了哪些事。 + +### DividePlugin + +```java +protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + //获取规则处理数据 + final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class); + //获取该选择器下的注入的地址 + final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId()); + if (CollectionUtils.isEmpty(upstreamList)) { + log.error("divide upstream configuration error: {}", rule.toString()); + Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress(); + //通过规则对应的负载均衡策略选择一个地址 + DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip); + if (Objects.isNull(divideUpstream)) { + log.error("divide has no upstream"); + Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // set the http url + String domain = buildDomain(divideUpstream); + //拼装真实调用地址 + String realURL = buildRealURL(domain, soulContext, exchange); + exchange.getAttributes().put(Constants.HTTP_URL, realURL); + //设置超时时间 及重试次数 + exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout()); + exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry()); + return chain.execute(exchange); + } +``` + +通过上述代码梳理完成后大体逻辑如下: + +- 1.获取选择器对应的注册地址,对应页面数据如下 + ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bea039b5c98040ee80433f785dac85aa~tplv-k3u1fbpfcp-watermark.image) +- 2.根据规则的 handle 字段获取负载均衡策略,并选择真实的调用地址(**LoadBalanceUtils**),重试次数和超时时间,对应页面数据如下。 + ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07db1f8f76fc43b2aec61ee0f9ca4c05~tplv-k3u1fbpfcp-watermark.image) +- 3.将真实调用地址,超时时间,重试次数传递到**ServerWebExchange**中,供下游调用链使用。 + debug 演示: + ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0aa5f7d95f2942169b33029f074f1712~tplv-k3u1fbpfcp-watermark.image) + ps:在上述的主题逻辑中我们没有看到参数在哪里?那这个参数在哪封装的呢?答案在**buildRealURL 方法中**,是从**exchange**上下文中获取到的。 + +## WebClientPlugin Http 请求调用插件 + +接下来让我们看看 Soul 如何发起的请求调用 + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + //获取真实地址 + String urlPath = exchange.getAttribute(Constants.HTTP_URL); + if (StringUtils.isEmpty(urlPath)) { + Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + //获取超时时间 + long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L); + //获取重试次数 + int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0); + log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes); + HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue()); + WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath); + return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain); + } +``` + +在 webClient 的**excute**方法中,主要做了三个事 + +- 1.将从 Divide 插件中放入 exchange 的属性取出来,**调用的真实地址、超时时间、重试次数**。 +- 2.封装了一个**RequestBodySpec**对象(不认识这个响应式编程的东西) +- 3.调用了一个**handleRequestBody**方法 + +先认识**handleRequestBody**方法 + +```java +private Mono handleRequestBody(final WebClient.RequestBodySpec requestBodySpec, + final ServerWebExchange exchange, + final long timeout, + final int retryTimes, + final SoulPluginChain chain) { + return requestBodySpec.headers(httpHeaders -> { + httpHeaders.addAll(exchange.getRequest().getHeaders()); + httpHeaders.remove(HttpHeaders.HOST); + }) + .contentType(buildMediaType(exchange)) + .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody())) + .exchange() + //失败打印日志 + .doOnError(e -> log.error(e.getMessage())) + //设置超时时间 + .timeout(Duration.ofMillis(timeout)) + //设置请求重试实际 + .retryWhen(Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException) + .retryMax(retryTimes) + .backoff(Backoff.exponential(Duration.ofMillis(200), Duration.ofSeconds(20), 2, true))) + //请求结束后对应的处理 + .flatMap(e -> doNext(e, exchange, chain)); + + } +``` + +在这个方法里,大体可以理解为 + +- exchange 中的请求头放到本次调用的请求头中 +- 设置 contentType +- 设置超时时间 +- 设置失败响应 +- 设置重试的场景及重试次数 +- 最终结果的处理。 + 在流程中需要还需要看一个**doNext 方法** + +大体逻辑就是判断请求是否成功,将请求结果放入 exchange 中交给下游插件处理。 + +```java +private Mono doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) { + if (res.statusCode().is2xxSuccessful()) { + exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); + } else { + exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.ERROR.getName()); + } + exchange.getAttributes().put(Constants.CLIENT_RESPONSE_ATTR, res); + return chain.execute(exchange); + } +``` + +ps: 虽然并不懂响应式编程,但并不影响我们阅读代码。 + +## WebClientResponsePlugin Http 结果处理插件 + +该实现的 excute 方法没有什么核心逻辑,就是判断请求状态码,根据状态码返回给前端不同的数据格式。 + +```java +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + return chain.execute(exchange).then(Mono.defer(() -> { + ServerHttpResponse response = exchange.getResponse(); + ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR); + if (Objects.isNull(clientResponse) + || response.getStatusCode() == HttpStatus.BAD_GATEWAY + || response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) { + Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { + Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + response.setStatusCode(clientResponse.statusCode()); + response.getCookies().putAll(clientResponse.cookies()); + response.getHeaders().putAll(clientResponse.headers().asHttpHeaders()); + return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())); + })); + } +``` + +# 总结 + +到此为止,一个基于 Soul 网关发起的 Http 请求调用流程大体已经结束。 + +梳理 http 请求调用流程 + +- Global 插件封装 SoulContext 对象 +- 前置插件处理熔断限流鉴权等操作。 +- Divide 插件选择对应调用的真实地址,重试次数,超时时间。 +- WebClient 插件发起真实的 Http 调用 +- WebClientResponse 插件处理对应结果,返回前台。 + +基于 Http 调用的大体流程,我们可以大体猜测出基于别 RPC 调用的流程,就是替换发起请求的插件和返回结果处理的插件。 + +在上文中我们还提到了路由规则的选择**LoadBalanceUtils**,选择器和规则的处理**MatchStrategy**。 + +之后将会开启新篇章一步步揭开 RPC 泛化调用,路由选择,选择器、规则匹配的神秘面纱。 diff --git a/src/zh/blog/soul_source_learning_18_ratelimiter.md b/src/zh/blog/soul_source_learning_18_ratelimiter.md index 80ce71816e..9a9df02181 100644 --- a/src/zh/blog/soul_source_learning_18_ratelimiter.md +++ b/src/zh/blog/soul_source_learning_18_ratelimiter.md @@ -1,243 +1,243 @@ ---- -title: Soul网关学习RateLimiter插件原理解析 -author: 百钰 -date: 2021-01-30 -tag: - - Soul -cover: https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9434447ebc674f58b65c26b65f855181~tplv-k3u1fbpfcp-watermark.image -head: - - - meta - - name: 博客 ---- - -## 回顾 - -在之前的 HTTP 请求初探的文章中,大体梳理了 Soul 插件的处理流程,也得知了 DividePlugin、GlobalPlugin,WebClientPlugin,WebCilentResponsePlugin 插件的具体作用,在梳理流程中,发现 Soul 的插件是有**先后顺序**的,在 DividePlugin 插件之前做了很多前置插件的操作,其中包含了我们本章分析的主题**RateLimiterPlugin 限流插件**(其中一种)。 - -## 学习使用 - -### 阅读官方文档 对其有大概认知 - -rateLimiter 插件 - -通过官方文档的阅读我们得知了**RateLimiterPlugin**的两个核心点**速率、容量** - -以下讲解来源于官方文档 - -- 容量:是允许用户在一秒钟内执行的最大请求数。这是令牌桶可以保存的令牌数。 -- 速率:是你允许用户每秒执行多少请求,而丢弃任何请求。这是令牌桶的填充速率。 - -可以看出**RateLimiterPlugin**限流核心在于**令牌桶算法**的实现。 - -ps:关于限流算法常见的有四种实现**令牌桶算法**,**漏斗算法**,**计数器(固定窗口)算法**,**滑动窗口算法**,详情看对应博客介绍 - -### 初步使用 - -#### 启用对应插件 - -在 Soul 网关**系统管理-插件管理**处,将状态更改为启用状态,注意此处需要填写 redis 相关配置,Soul 令牌桶基于 Redis。 - -为什么 Soul 的令牌桶算法要基于 redis? - -在集群部署情况下单机的令牌桶算法无法满足集群状态下的限流功能。 - -![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e25dd524c294b4f9c227e3f2127757f~tplv-k3u1fbpfcp-watermark.image) - -#### 添加限流选择器、规则 - -在 Soul 网关**插件列表**处,选择 rate_limiter 处添加规则及选择器配置,不懂如何添加的可以先阅读选择器\规则的匹配逻辑. -![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9cbbc63ed6214aeda8c70f8e34d7c19c~tplv-k3u1fbpfcp-watermark.image) -![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/25e67268dd5e4aa9a081a51963a03da8~tplv-k3u1fbpfcp-watermark.image) -在此处添加的容量及速率都为 1 主要为了验证插件是否启用。 - -#### 接口对应访问 - -调用*http://127.0.0.1:9195/http/test/findByUserId?userId=10* 进行访问,速率高于 1 的情况下出现如下接口返回结果,代表插件成功使用。 - -```json -{ - "code": 429, - "message": "You have been restricted, please try again later!", - "data": null -} -``` - -### 源码阅读 带着问题读源码 - -#### 如何保证在页面修改 redis 配置后立即生效的,后台对应的 redis 连接立马变更的。 - -答案自然数据同步脱不了干系。 - -在修改插件的配置时,也发布了一个插件数据变更的事件通知,在之前梳理Soul 网关同步数据整体流程时,已经得知修改的插件数据除了更改了 JVM 缓存内的数据外,还对对应的插件进行下发操作,如下图 -![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9434447ebc674f58b65c26b65f855181~tplv-k3u1fbpfcp-watermark.image) -而针对于**RateLimiterPlugin**而言,其主要实现了**handlePlugin**的接口,那这个对应的实现到底做了哪些事呢? - -具体的方法为**RateLimiterPluginDataHandler 的 handlerPlugin**。 - -```java -public void handlerPlugin(final PluginData pluginData) { - if (Objects.nonNull(pluginData) && pluginData.getEnabled()) { - //加载限流插件配置 - RateLimiterConfig rateLimiterConfig = GsonUtils.getInstance().fromJson(pluginData.getConfig(), RateLimiterConfig.class); - //判断是否需要重新加载redis连接值 - if (Objects.isNull(Singleton.INST.get(ReactiveRedisTemplate.class)) - || Objects.isNull(Singleton.INST.get(RateLimiterConfig.class)) - || !rateLimiterConfig.equals(Singleton.INST.get(RateLimiterConfig.class))) { - LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(rateLimiterConfig); - lettuceConnectionFactory.afterPropertiesSet(); - RedisSerializer serializer = new StringRedisSerializer(); - RedisSerializationContext serializationContext = - RedisSerializationContext.newSerializationContext().key(serializer).value(serializer).hashKey(serializer).hashValue(serializer).build(); - ReactiveRedisTemplate reactiveRedisTemplate = new ReactiveRedisTemplate<>(lettuceConnectionFactory, serializationContext); - Singleton.INST.single(ReactiveRedisTemplate.class, reactiveRedisTemplate); - Singleton.INST.single(RateLimiterConfig.class, rateLimiterConfig); - } - } - } -``` - -上述代码有几个较为关键的点: - -在上述代码中将限流插件的配置和对应的 redisTemplate 实例放入了 Singleton.INST 对应 map 中。 - -在插件数据过来时,判断是否存在 redis 连接实例,是否存在限流配置实例,判断当前的限流配置实例是否和传递的限流实例一致,不一致就认为配置是有更改的,就重新初始化限流实例和连接池实例放入 Singleton.INST 的 map 中,由此而言就保证了更改 redis 配置的热部署。 - -if 判断中的代码就是基于 SpringDataRedis 封装成一个对应 redis 连接池。 - -ps:Singleton.INST 是枚举实现的单例模式。 - -### 限流插件是底层是如何实现的呢? - -#### Debug 调用链 - -**RateLimiterPlugin**由于需要对特定规则进行限流,所以依旧实现了**AbstractSoulPlugin**,之前依旧梳理过**AbstractSoulPlugin 的 excute**的方法和作用了,所以这里不重复解释,可观看Http 调用流程梳理,加深对该类的印象。 - -本节重点还是看具体的**doexcute**方法做了哪些事。 - -```java - protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final String handle = rule.getHandle(); - final RateLimiterHandle limiterHandle = GsonUtils.getInstance().fromJson(handle, RateLimiterHandle.class); - return redisRateLimiter.isAllowed(rule.getId(), limiterHandle.getReplenishRate(), limiterHandle.getBurstCapacity()) - .flatMap(response -> { - if (!response.isAllowed()) { - //返回的错误信息 429错误编码 - exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); - Object error = SoulResultWrap.error(SoulResultEnum.TOO_MANY_REQUESTS.getCode(), SoulResultEnum.TOO_MANY_REQUESTS.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - return chain.execute(exchange); - }); - } -``` - -在上述代码中可以看出是通过**redisRateLimiter.isAllowed**来判断是否获取令牌成功的。 -该方法如下 - -```java - public Mono isAllowed(final String id, final double replenishRate, final double burstCapacity) { - if (!this.initialized.get()) { - throw new IllegalStateException("RedisRateLimiter is not initialized"); - } - //获取redis Key - List keys = getKeys(id); - //封装lua脚本执行所需的参数 第一位是速率 第二位是容量 第三位是当前时间戳10位 第四位固定参数值1 代表申请的令牌数 - List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", Instant.now().getEpochSecond() + "", "1"); - //执行lua脚本 - Flux> resultFlux = Singleton.INST.get(ReactiveRedisTemplate.class).execute(this.script, keys, scriptArgs); - return resultFlux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))) - .reduce(new ArrayList(), (longs, l) -> { - longs.addAll(l); - return longs; - }).map(results -> { - //allowed 代表执行的结果 为1 代表执行成功 - boolean allowed = results.get(0) == 1L; - Long tokensLeft = results.get(1); - RateLimiterResponse rateLimiterResponse = new RateLimiterResponse(allowed, tokensLeft); - log.info("RateLimiter response:{}", rateLimiterResponse.toString()); - return rateLimiterResponse; - }).doOnError(throwable -> log.error("Error determining if user allowed from redis:{}", throwable.getMessage())); - } -``` - -#### 方法 getKeys(id) - -该方法是获取 redis 需要操作的 key,一共获取了两个类型的 Key,格式如下: - -![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55ce72f4e044405fbd3b1461905072f2~tplv-k3u1fbpfcp-watermark.image) - -中间那位特别长的数字是**规则 ID**,因为限流的最小粒度是规则。 - -第一个 timestamp 记录的是**上一次调用的时间戳** - -第二个 tokens 记录的是**上一次调用完成后剩余的令牌数量** - -#### execute(this.script, keys, scriptArgs) - -执行 lua 脚本 keys 传递的是 getKeys(id)返回值,scriptArgs 传递的是所需的参数 - -通过阅读上述代码已经知晓 限流规则的具体实现是交给特定的 lua 脚本的。 - -ps:这里需要提醒一下限流算法是令牌桶算法,令牌桶算法一共有两种大体实现,一种是有个线程不断生成令牌,当请求进来时,先从对应的队列中获取令牌,但这种令牌生成方式在设定阈值特别大时,会非常消耗性能,所以有了第二种令牌桶算法,在获取令牌时实时计算令牌数量,而 soul 就是基于第二种实现的。 - -#### Lua 限流算法分析 - -```lua --- 当前规则令牌剩余数量存储key -local tokens_key = KEYS[1] --- 当前规则上次调用时间 -local timestamp_key = KEYS[2] - --- 速率 -local rate = tonumber(ARGV[1]) --- 容量 -local capacity = tonumber(ARGV[2]) --- 时间戳 -local now = tonumber(ARGV[3]) --- 值为1 -local requested = tonumber(ARGV[4]) --- 容量除以速率 计算填充时间 -local fill_time = capacity/rate --- 计算过期时间 取下限 -local ttl = math.floor(fill_time*2) - --- 获取当前存有的令牌数 -local last_tokens = tonumber(redis.call("get", tokens_key)) -if last_tokens == nil then --- 将令牌数量赋值为设定的容量 - last_tokens = capacity -end --- 获取上一次调用时间 -local last_refreshed = tonumber(redis.call("get", timestamp_key)) -if last_refreshed == nil then - last_refreshed = 0 -end --- 计算出上次调用和本次调用之间的时间差 -local delta = math.max(0, now-last_refreshed) --- 计算出当前剩余的令牌数量 -local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) --- 判断当前令牌数量 数量>=1 代表获取成功 -local allowed = filled_tokens >= requested -local new_tokens = filled_tokens -local allowed_num = 0 -if allowed then - -- 申请一个令牌 - new_tokens = filled_tokens - requested - allowed_num = 1 -end - --- setex 设置过期key 过期时间 新值 -redis.call("setex", tokens_key, ttl, new_tokens) -redis.call("setex", timestamp_key, ttl, now) - -return { allowed_num, new_tokens } -``` - -推荐先了解一下 lua**KEYS ARGS**的作用redis lua 中 keys[1] 和 argv[1] 的理解. - -Lua 代码整体逻辑还是非常明朗的,在这里细讲也讲不出个啥来,代码注释已经打全了。 - -本人在这里疑惑的有两点 - -- **ttl**参数的计算 乘 2 的目的是为了怕不是整数?,所以进行的\*2 取最小操作? -- **filled_tokens**参数的计算 核心代码 last_tokens+(delta*rate),其中 delta 参数是两个十位时间戳相减得来 ,但是 rate 是按秒来生成的,难道不应该是 last_tokens+((delta/1000)*rate)吗? +--- +title: Soul网关学习RateLimiter插件原理解析 +author: 百钰 +date: 2021-01-30 +tag: + - Soul +cover: https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9434447ebc674f58b65c26b65f855181~tplv-k3u1fbpfcp-watermark.image +head: + - - meta + - name: 博客 +--- + +## 回顾 + +在之前的 HTTP 请求初探的文章中,大体梳理了 Soul 插件的处理流程,也得知了 DividePlugin、GlobalPlugin,WebClientPlugin,WebCilentResponsePlugin 插件的具体作用,在梳理流程中,发现 Soul 的插件是有**先后顺序**的,在 DividePlugin 插件之前做了很多前置插件的操作,其中包含了我们本章分析的主题**RateLimiterPlugin 限流插件**(其中一种)。 + +## 学习使用 + +### 阅读官方文档 对其有大概认知 + +rateLimiter 插件 + +通过官方文档的阅读我们得知了**RateLimiterPlugin**的两个核心点**速率、容量** + +以下讲解来源于官方文档 + +- 容量:是允许用户在一秒钟内执行的最大请求数。这是令牌桶可以保存的令牌数。 +- 速率:是你允许用户每秒执行多少请求,而丢弃任何请求。这是令牌桶的填充速率。 + +可以看出**RateLimiterPlugin**限流核心在于**令牌桶算法**的实现。 + +ps:关于限流算法常见的有四种实现**令牌桶算法**,**漏斗算法**,**计数器(固定窗口)算法**,**滑动窗口算法**,详情看对应博客介绍 + +### 初步使用 + +#### 启用对应插件 + +在 Soul 网关**系统管理-插件管理**处,将状态更改为启用状态,注意此处需要填写 redis 相关配置,Soul 令牌桶基于 Redis。 + +为什么 Soul 的令牌桶算法要基于 redis? + +在集群部署情况下单机的令牌桶算法无法满足集群状态下的限流功能。 + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e25dd524c294b4f9c227e3f2127757f~tplv-k3u1fbpfcp-watermark.image) + +#### 添加限流选择器、规则 + +在 Soul 网关**插件列表**处,选择 rate_limiter 处添加规则及选择器配置,不懂如何添加的可以先阅读选择器\规则的匹配逻辑. +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9cbbc63ed6214aeda8c70f8e34d7c19c~tplv-k3u1fbpfcp-watermark.image) +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/25e67268dd5e4aa9a081a51963a03da8~tplv-k3u1fbpfcp-watermark.image) +在此处添加的容量及速率都为 1 主要为了验证插件是否启用。 + +#### 接口对应访问 + +调用*http://127.0.0.1:9195/http/test/findByUserId?userId=10* 进行访问,速率高于 1 的情况下出现如下接口返回结果,代表插件成功使用。 + +```json +{ + "code": 429, + "message": "You have been restricted, please try again later!", + "data": null +} +``` + +### 源码阅读 带着问题读源码 + +#### 如何保证在页面修改 redis 配置后立即生效的,后台对应的 redis 连接立马变更的。 + +答案自然数据同步脱不了干系。 + +在修改插件的配置时,也发布了一个插件数据变更的事件通知,在之前梳理Soul 网关同步数据整体流程时,已经得知修改的插件数据除了更改了 JVM 缓存内的数据外,还对对应的插件进行下发操作,如下图 +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9434447ebc674f58b65c26b65f855181~tplv-k3u1fbpfcp-watermark.image) +而针对于**RateLimiterPlugin**而言,其主要实现了**handlePlugin**的接口,那这个对应的实现到底做了哪些事呢? + +具体的方法为**RateLimiterPluginDataHandler 的 handlerPlugin**。 + +```java +public void handlerPlugin(final PluginData pluginData) { + if (Objects.nonNull(pluginData) && pluginData.getEnabled()) { + //加载限流插件配置 + RateLimiterConfig rateLimiterConfig = GsonUtils.getInstance().fromJson(pluginData.getConfig(), RateLimiterConfig.class); + //判断是否需要重新加载redis连接值 + if (Objects.isNull(Singleton.INST.get(ReactiveRedisTemplate.class)) + || Objects.isNull(Singleton.INST.get(RateLimiterConfig.class)) + || !rateLimiterConfig.equals(Singleton.INST.get(RateLimiterConfig.class))) { + LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(rateLimiterConfig); + lettuceConnectionFactory.afterPropertiesSet(); + RedisSerializer serializer = new StringRedisSerializer(); + RedisSerializationContext serializationContext = + RedisSerializationContext.newSerializationContext().key(serializer).value(serializer).hashKey(serializer).hashValue(serializer).build(); + ReactiveRedisTemplate reactiveRedisTemplate = new ReactiveRedisTemplate<>(lettuceConnectionFactory, serializationContext); + Singleton.INST.single(ReactiveRedisTemplate.class, reactiveRedisTemplate); + Singleton.INST.single(RateLimiterConfig.class, rateLimiterConfig); + } + } + } +``` + +上述代码有几个较为关键的点: + +在上述代码中将限流插件的配置和对应的 redisTemplate 实例放入了 Singleton.INST 对应 map 中。 + +在插件数据过来时,判断是否存在 redis 连接实例,是否存在限流配置实例,判断当前的限流配置实例是否和传递的限流实例一致,不一致就认为配置是有更改的,就重新初始化限流实例和连接池实例放入 Singleton.INST 的 map 中,由此而言就保证了更改 redis 配置的热部署。 + +if 判断中的代码就是基于 SpringDataRedis 封装成一个对应 redis 连接池。 + +ps:Singleton.INST 是枚举实现的单例模式。 + +### 限流插件是底层是如何实现的呢? + +#### Debug 调用链 + +**RateLimiterPlugin**由于需要对特定规则进行限流,所以依旧实现了**AbstractSoulPlugin**,之前依旧梳理过**AbstractSoulPlugin 的 excute**的方法和作用了,所以这里不重复解释,可观看Http 调用流程梳理,加深对该类的印象。 + +本节重点还是看具体的**doexcute**方法做了哪些事。 + +```java + protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final String handle = rule.getHandle(); + final RateLimiterHandle limiterHandle = GsonUtils.getInstance().fromJson(handle, RateLimiterHandle.class); + return redisRateLimiter.isAllowed(rule.getId(), limiterHandle.getReplenishRate(), limiterHandle.getBurstCapacity()) + .flatMap(response -> { + if (!response.isAllowed()) { + //返回的错误信息 429错误编码 + exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); + Object error = SoulResultWrap.error(SoulResultEnum.TOO_MANY_REQUESTS.getCode(), SoulResultEnum.TOO_MANY_REQUESTS.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + return chain.execute(exchange); + }); + } +``` + +在上述代码中可以看出是通过**redisRateLimiter.isAllowed**来判断是否获取令牌成功的。 +该方法如下 + +```java + public Mono isAllowed(final String id, final double replenishRate, final double burstCapacity) { + if (!this.initialized.get()) { + throw new IllegalStateException("RedisRateLimiter is not initialized"); + } + //获取redis Key + List keys = getKeys(id); + //封装lua脚本执行所需的参数 第一位是速率 第二位是容量 第三位是当前时间戳10位 第四位固定参数值1 代表申请的令牌数 + List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", Instant.now().getEpochSecond() + "", "1"); + //执行lua脚本 + Flux> resultFlux = Singleton.INST.get(ReactiveRedisTemplate.class).execute(this.script, keys, scriptArgs); + return resultFlux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))) + .reduce(new ArrayList(), (longs, l) -> { + longs.addAll(l); + return longs; + }).map(results -> { + //allowed 代表执行的结果 为1 代表执行成功 + boolean allowed = results.get(0) == 1L; + Long tokensLeft = results.get(1); + RateLimiterResponse rateLimiterResponse = new RateLimiterResponse(allowed, tokensLeft); + log.info("RateLimiter response:{}", rateLimiterResponse.toString()); + return rateLimiterResponse; + }).doOnError(throwable -> log.error("Error determining if user allowed from redis:{}", throwable.getMessage())); + } +``` + +#### 方法 getKeys(id) + +该方法是获取 redis 需要操作的 key,一共获取了两个类型的 Key,格式如下: + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55ce72f4e044405fbd3b1461905072f2~tplv-k3u1fbpfcp-watermark.image) + +中间那位特别长的数字是**规则 ID**,因为限流的最小粒度是规则。 + +第一个 timestamp 记录的是**上一次调用的时间戳** + +第二个 tokens 记录的是**上一次调用完成后剩余的令牌数量** + +#### execute(this.script, keys, scriptArgs) + +执行 lua 脚本 keys 传递的是 getKeys(id)返回值,scriptArgs 传递的是所需的参数 + +通过阅读上述代码已经知晓 限流规则的具体实现是交给特定的 lua 脚本的。 + +ps:这里需要提醒一下限流算法是令牌桶算法,令牌桶算法一共有两种大体实现,一种是有个线程不断生成令牌,当请求进来时,先从对应的队列中获取令牌,但这种令牌生成方式在设定阈值特别大时,会非常消耗性能,所以有了第二种令牌桶算法,在获取令牌时实时计算令牌数量,而 soul 就是基于第二种实现的。 + +#### Lua 限流算法分析 + +```lua +-- 当前规则令牌剩余数量存储key +local tokens_key = KEYS[1] +-- 当前规则上次调用时间 +local timestamp_key = KEYS[2] + +-- 速率 +local rate = tonumber(ARGV[1]) +-- 容量 +local capacity = tonumber(ARGV[2]) +-- 时间戳 +local now = tonumber(ARGV[3]) +-- 值为1 +local requested = tonumber(ARGV[4]) +-- 容量除以速率 计算填充时间 +local fill_time = capacity/rate +-- 计算过期时间 取下限 +local ttl = math.floor(fill_time*2) + +-- 获取当前存有的令牌数 +local last_tokens = tonumber(redis.call("get", tokens_key)) +if last_tokens == nil then +-- 将令牌数量赋值为设定的容量 + last_tokens = capacity +end +-- 获取上一次调用时间 +local last_refreshed = tonumber(redis.call("get", timestamp_key)) +if last_refreshed == nil then + last_refreshed = 0 +end +-- 计算出上次调用和本次调用之间的时间差 +local delta = math.max(0, now-last_refreshed) +-- 计算出当前剩余的令牌数量 +local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) +-- 判断当前令牌数量 数量>=1 代表获取成功 +local allowed = filled_tokens >= requested +local new_tokens = filled_tokens +local allowed_num = 0 +if allowed then + -- 申请一个令牌 + new_tokens = filled_tokens - requested + allowed_num = 1 +end + +-- setex 设置过期key 过期时间 新值 +redis.call("setex", tokens_key, ttl, new_tokens) +redis.call("setex", timestamp_key, ttl, now) + +return { allowed_num, new_tokens } +``` + +推荐先了解一下 lua**KEYS ARGS**的作用redis lua 中 keys[1] 和 argv[1] 的理解. + +Lua 代码整体逻辑还是非常明朗的,在这里细讲也讲不出个啥来,代码注释已经打全了。 + +本人在这里疑惑的有两点 + +- **ttl**参数的计算 乘 2 的目的是为了怕不是整数?,所以进行的\*2 取最小操作? +- **filled_tokens**参数的计算 核心代码 last_tokens+(delta*rate),其中 delta 参数是两个十位时间戳相减得来 ,但是 rate 是按秒来生成的,难道不应该是 last_tokens+((delta/1000)*rate)吗? diff --git a/src/zh/blog/soul_source_learning_19_redirect.md b/src/zh/blog/soul_source_learning_19_redirect.md index 7b91f62193..3ae26838d0 100644 --- a/src/zh/blog/soul_source_learning_19_redirect.md +++ b/src/zh/blog/soul_source_learning_19_redirect.md @@ -1,133 +1,133 @@ ---- -title: Soul网关学习Redirect插件原理解析 -author: 阿行 -date: 2021-03-16 -tag: - - Soul -cover: /assets/img/blog6/01.jpg -head: - - - meta - - name: 博客 ---- - -# 介绍 - -`Soul` 网关在对目标服务进行代理调用的时候,可以使用 `redirect` 插件来重定向请求。其中包含两种场景:一种把 `redirectUrl` 配置为第三方 URL 地址,直接使用 `308` 进行转发跳转,另一种是把 `redirectUrl` 配置以 `/` 开头的转发到网关自身。 - -## 插件配置 - -- 在 `soul-admin` –> 插件管理 –> `redirect`,设置为开启。 -- 在 `soul-bootstrap` 项目的 `pom.xml` 文件中添加 `redirect` 的 `maven` 依赖。 -- 在 `soul- admin` 后台设置选择器规则,只有匹配的请求,才会进行转发和重定向,请详细看:[选择器规则](https://dromara.org/zh/projects/soul/selector-and-rule)。 - -## Maven 依赖 - -在 `soul-bootstrap` 工程的 `pom.xml` 文件中添加插件依赖。 - -```xml - - org.dromara - soul-spring-boot-starter-plugin-redirect - ${last.version} - -``` - -## 场景 - -> 顾名思义,`redirect` 插件就是对 `uri` 的重新转发和重定向。 - -### 重定向 - -- 我们在 `Rule` 配置自定义路径时,应该为一个可达的服务路径。 -- 当匹配到请求后,根据自定义的路径,`Soul 网关`会进行 `308` 服务跳转。 - -![重定向配置](https://dromara.org/img/soul/plugin/redirect/redirect-01.png) - -### 网关自身接口转发 - -- 当满足匹配规则时,服务内部会使用 `DispatcherHandler` 内部接口转发。 -- 要实现网关自身接口转发,我们需要在配置路径使用 `/` 作为前缀开始,具体配置如下图。 - -![自身接口转发](https://dromara.org/img/soul/plugin/redirect/redirect-02.png) - -## 源码解析 - -在解析 `redirect` 重定向源码之前,有必要说一些大前提,我们明白 Soul 网关基于 SpringBoot WebFlux 实现,其中对于 `WebFlux` 如果默认什么都不配置,请求会默认执行 `DispatcherHandler` 处理,这个是响应式 `MVC` 的处理核心,可以看一下初始化: - -```java -protected void initStrategies(ApplicationContext context) { - Map mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); - ArrayList mappings = new ArrayList(mappingBeans.values()); - AnnotationAwareOrderComparator.sort(mappings); - // handlerMapping 相关 - this.handlerMappings = Collections.unmodifiableList(mappings); - Map adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); - // handlerAdapter 相关 - this.handlerAdapters = new ArrayList(adapterBeans.values()); - AnnotationAwareOrderComparator.sort(this.handlerAdapters); - Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false); - // resultHandler 相关 - this.resultHandlers = new ArrayList(beans.values()); - AnnotationAwareOrderComparator.sort(this.resultHandlers); -} -``` - -再之后就是我们熟悉的 `MVC` 核心处理 `DispatcherHandler#handle` 方法 - -```java -public Mono handle(ServerWebExchange exchange) { - return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { - return mapping.getHandler(exchange); - }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { - return this.invokeHandler(exchange, handler); - }).flatMap((result) -> { - return this.handleResult(exchange, result); - }); -} -``` - -搞清楚默认 `DispatcherHandler` 如何处理,我们再来说一下 Soul 网关,`SoulWebHandler` 实现了 `WebHandler` 接口,再把 `BeanName` 声明为 `webHandler` 替代了之前 `DispatcherHandler` 注册成默认处理 `handler`。 - -```java -@Bean("webHandler") -public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { - List pluginList = plugins.getIfAvailable(Collections::emptyList); - List soulPlugins = pluginList.stream() - .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); - soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); - return new SoulWebHandler(soulPlugins); -} -``` - -到此为止我们明白了,默认请求都通过了 `SoulWebHandler#handle` 处理,如果我们需要转发到网关自身的 `MVC` 如何做呢?下面通过初始化`RedirectPlugin` 的时候把 `DispatcherHandler` 注入,根据具体请求再由 `DispatcherHandler` 分发,具体核心代码如下: - -```java -@Override -protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, - final SelectorData selector, final RuleData rule) { - final String handle = rule.getHandle(); - final RedirectHandle redirectHandle = GsonUtils.getInstance().fromJson(handle, RedirectHandle.class); - if (Objects.isNull(redirectHandle) || StringUtils.isBlank(redirectHandle.getRedirectURI())) { - log.error("uri redirect rule can not configuration: {}", handle); - return chain.execute(exchange); - } - // 处理以 / 开头自身转发 - if (redirectHandle.getRedirectURI().startsWith(ROOT_PATH_PREFIX)) { - ServerHttpRequest request = exchange.getRequest().mutate() - .uri(Objects.requireNonNull(UriUtils.createUri(redirectHandle.getRedirectURI()))).build(); - ServerWebExchange mutated = exchange.mutate().request(request).build(); - return dispatcherHandler.handle(mutated); - } else { - // 否则就 308 跳转 - ServerHttpResponse response = exchange.getResponse(); - response.setStatusCode(HttpStatus.PERMANENT_REDIRECT); - response.getHeaders().add(HttpHeaders.LOCATION, redirectHandle.getRedirectURI()); - return response.setComplete(); - } -} -``` - -### 参考链接: - -- [Spring WebFlux 的设计及工作原理剖析](https://learnku.com/articles/30263#replies) -- [Spring WebFlux 工作原理](https://www.processon.com/view/link/5d0763ede4b039f39f3b5a8a) +--- +title: Soul网关学习Redirect插件原理解析 +author: 阿行 +date: 2021-03-16 +tag: + - Soul +cover: /assets/img/blog6/01.jpg +head: + - - meta + - name: 博客 +--- + +# 介绍 + +`Soul` 网关在对目标服务进行代理调用的时候,可以使用 `redirect` 插件来重定向请求。其中包含两种场景:一种把 `redirectUrl` 配置为第三方 URL 地址,直接使用 `308` 进行转发跳转,另一种是把 `redirectUrl` 配置以 `/` 开头的转发到网关自身。 + +## 插件配置 + +- 在 `soul-admin` –> 插件管理 –> `redirect`,设置为开启。 +- 在 `soul-bootstrap` 项目的 `pom.xml` 文件中添加 `redirect` 的 `maven` 依赖。 +- 在 `soul- admin` 后台设置选择器规则,只有匹配的请求,才会进行转发和重定向,请详细看:[选择器规则](https://dromara.org/zh/projects/soul/selector-and-rule)。 + +## Maven 依赖 + +在 `soul-bootstrap` 工程的 `pom.xml` 文件中添加插件依赖。 + +```xml + + org.dromara + soul-spring-boot-starter-plugin-redirect + ${last.version} + +``` + +## 场景 + +> 顾名思义,`redirect` 插件就是对 `uri` 的重新转发和重定向。 + +### 重定向 + +- 我们在 `Rule` 配置自定义路径时,应该为一个可达的服务路径。 +- 当匹配到请求后,根据自定义的路径,`Soul 网关`会进行 `308` 服务跳转。 + +![重定向配置](https://dromara.org/img/soul/plugin/redirect/redirect-01.png) + +### 网关自身接口转发 + +- 当满足匹配规则时,服务内部会使用 `DispatcherHandler` 内部接口转发。 +- 要实现网关自身接口转发,我们需要在配置路径使用 `/` 作为前缀开始,具体配置如下图。 + +![自身接口转发](https://dromara.org/img/soul/plugin/redirect/redirect-02.png) + +## 源码解析 + +在解析 `redirect` 重定向源码之前,有必要说一些大前提,我们明白 Soul 网关基于 SpringBoot WebFlux 实现,其中对于 `WebFlux` 如果默认什么都不配置,请求会默认执行 `DispatcherHandler` 处理,这个是响应式 `MVC` 的处理核心,可以看一下初始化: + +```java +protected void initStrategies(ApplicationContext context) { + Map mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); + ArrayList mappings = new ArrayList(mappingBeans.values()); + AnnotationAwareOrderComparator.sort(mappings); + // handlerMapping 相关 + this.handlerMappings = Collections.unmodifiableList(mappings); + Map adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); + // handlerAdapter 相关 + this.handlerAdapters = new ArrayList(adapterBeans.values()); + AnnotationAwareOrderComparator.sort(this.handlerAdapters); + Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false); + // resultHandler 相关 + this.resultHandlers = new ArrayList(beans.values()); + AnnotationAwareOrderComparator.sort(this.resultHandlers); +} +``` + +再之后就是我们熟悉的 `MVC` 核心处理 `DispatcherHandler#handle` 方法 + +```java +public Mono handle(ServerWebExchange exchange) { + return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { + return mapping.getHandler(exchange); + }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { + return this.invokeHandler(exchange, handler); + }).flatMap((result) -> { + return this.handleResult(exchange, result); + }); +} +``` + +搞清楚默认 `DispatcherHandler` 如何处理,我们再来说一下 Soul 网关,`SoulWebHandler` 实现了 `WebHandler` 接口,再把 `BeanName` 声明为 `webHandler` 替代了之前 `DispatcherHandler` 注册成默认处理 `handler`。 + +```java +@Bean("webHandler") +public SoulWebHandler soulWebHandler(final ObjectProvider> plugins) { + List pluginList = plugins.getIfAvailable(Collections::emptyList); + List soulPlugins = pluginList.stream() + .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); + soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); + return new SoulWebHandler(soulPlugins); +} +``` + +到此为止我们明白了,默认请求都通过了 `SoulWebHandler#handle` 处理,如果我们需要转发到网关自身的 `MVC` 如何做呢?下面通过初始化`RedirectPlugin` 的时候把 `DispatcherHandler` 注入,根据具体请求再由 `DispatcherHandler` 分发,具体核心代码如下: + +```java +@Override +protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, + final SelectorData selector, final RuleData rule) { + final String handle = rule.getHandle(); + final RedirectHandle redirectHandle = GsonUtils.getInstance().fromJson(handle, RedirectHandle.class); + if (Objects.isNull(redirectHandle) || StringUtils.isBlank(redirectHandle.getRedirectURI())) { + log.error("uri redirect rule can not configuration: {}", handle); + return chain.execute(exchange); + } + // 处理以 / 开头自身转发 + if (redirectHandle.getRedirectURI().startsWith(ROOT_PATH_PREFIX)) { + ServerHttpRequest request = exchange.getRequest().mutate() + .uri(Objects.requireNonNull(UriUtils.createUri(redirectHandle.getRedirectURI()))).build(); + ServerWebExchange mutated = exchange.mutate().request(request).build(); + return dispatcherHandler.handle(mutated); + } else { + // 否则就 308 跳转 + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.PERMANENT_REDIRECT); + response.getHeaders().add(HttpHeaders.LOCATION, redirectHandle.getRedirectURI()); + return response.setComplete(); + } +} +``` + +### 参考链接: + +- [Spring WebFlux 的设计及工作原理剖析](https://learnku.com/articles/30263#replies) +- [Spring WebFlux 工作原理](https://www.processon.com/view/link/5d0763ede4b039f39f3b5a8a) diff --git a/src/zh/blog/soul_source_learning_20_sentinel.md b/src/zh/blog/soul_source_learning_20_sentinel.md index 737c4ade94..b196d83e64 100644 --- a/src/zh/blog/soul_source_learning_20_sentinel.md +++ b/src/zh/blog/soul_source_learning_20_sentinel.md @@ -1,250 +1,250 @@ ---- -title: Soul网关学习Sentinel插件原理解析 -author: 骆潇龙 -date: 2021-03-19 -tag: - - Soul -cover: /assets/img/blog6/02.jpg -head: - - - meta - - name: 博客 ---- - -# 概述 - -在业务网关中熔断和流量控制都是非常必要的功能。soul 在实现这部分功能时使用了不同的成熟组件,用户可以根据自己的喜好选择。本文将介绍如何在 soul 中使用阿里的 Sentinel 组件实现熔断及流控功能。本文首先会介绍熔断和流控的场景及意义。然后介绍如何在 soul 上配置使用 sentinel 插件做流控和熔断。最后从源码的层面简略分析 soul 是如何使用 Sentinel 组件的。 - -# 熔断和流量控制 - -## 场景描述 - -业务网关作为流量的入口,有保护后继服务的职责。以下两个对服务有严重危害的场景在生产中经常会遇到,也是业务网关必须要关注处理的问题。一种情况是在比如双 11 或双 12 这些大型促销时,接口的请求量是平时是数倍,如果没有评估好容量,这种激增的请求很容易导致整个服务完全不可用。这种宕机往往不是因为业务逻辑的漏洞而是因为请求过多资源不够导致的。另一种情况是在整个服务体系中有一些核心服务,多个业务流程都依赖该服务。然而是服务都有出现处理不稳定或者服务损坏的情况,导致请求处理时间长或者老是频繁抛出异常。排除业务 BUG 的情况,可能就是突发的非常随机的阻塞,一般减缓请求量就会自动修复,但是如果不加保护就有出现多米诺效应导致整个服务不可用。此场景和第一种场景有略微不同,第一种场景是实际流量确实出现了不可处理的峰值,而第二种场景主要考虑的是服务本身出现了不可避免、不可预测的抖动而引发的连锁反应。 - -## 流量控制 - -针对第一种场景我们通常的做法是进行流量控制,核心思路是业务网关保证打到后面的请求是业务可以承受的量,多余的请求直接拒绝或者加入等待队列,保证服务不会宕掉,大部分请求还是可以正常处理。在考虑流量控制的策略时,我们应该主要思考以下几个问题: - -1. 通过什么角度控制流量? -2. 阈值是多少? -3. 流量控制的策略是什么? - -对于第一个问题,正常思路是通过 QPS 来监控流量,即每秒钟请求的数量超过某限额时进行流控。但其实还有一种思路是从并发数来监控流量。这种控制场景也是非常有意义的,例如当下游应用由于某种原因导致服务不稳定、响应延迟增加,对于网关来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。从某种意义上讲通过并发进行流控可以一定程度上保护网关服务本身。对于第二个问题阈值来说比较好理解,就是触发流控的边界,如果从 QPS 来考虑就是每秒达到多少时开始流控,从并发数来考量的话就是请求上下文的线程数目超过多少进行流控。对于第三个问题,我们一般有以下 3 中处理方案: - -1. 直接拒绝,这种策略非常好理解就是当 QPS 高于阈值时直接拒接服务,不把请求传输到后面的服务中。 -2. 预热启动,这个策略所针对的场景是系统长期处于低水位的情况下,可能出现流量突然增加时,而直接把系统拉升到高水位可能瞬间把系统压垮。预热启动的方式是让阈值缓慢增加,在一定时间内逐渐增加阈值直至达到设置,给冷系统一个预热的时间,避免冷系统被压垮。对于超出阈值的请求也是触发拒绝。 -3. 匀速排队,此策略核心思路是以固定间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。 - -## 熔断 - -针对第二种场景通常的处理方式是设置服务熔断。简单的说就是当我们探测的一个服务出现了异常,则不再访问它以免更多的请求对它造成更大的压力。一段时间后如果探测到服务恢复了再将流量发送过去。我们首先需要判断出这个服务是否出现了不稳定\抖动的情况。然后思考如果发现了抖动的服务我们应该怎么办。如何判断服务是否恢复正常了。对于服务是否不稳定这一点我们一般可以通过一下 3 个方式进行判断。 - -1. 慢调用比例:当单位统计时长内请求数目大于设置的最小请求数目,并且超过最大忍受时间的请求大于阈值,则判断服务异常,触发熔断; -2. 异常比例:当单位统计时长内异常请求的比例大于阈值则我们判定服务异常,触发熔断; -3. 异常数:当单位时长内出现异常的请求的数量的达到阈值则判定服务异常,触发熔断; - -当我们通过以上 3 个指标判断服务为异常并熔断服务后,对于一定时间内(熔断时长内)的请求我们可以选择直接报错,不阻塞上游服务,让请求方来自行决定如何处理。或者直接触发服务降级。服务降级粗略的可以理解为请求此业务的简版,该简版省掉了很多非核心流程,并且只是最终保证流程处理完(最终一致性)。和现实中的熔断一样服务熔断是会自动恢复的。一般是触发熔断后的一段时间内服务处于熔断状态不提供服务,然后进入半开状态,若接下来的少量请求没有报错且响应时间合理则服务恢复,如果还是异常则继续熔断。 - -# soul 中的 Sentinel 插件 - -Sentinel 是阿里开源的面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。Soul 作为国内优秀的开源网关,将 Sentinel 整合为插件融入了自己的体系中,使用户通过简单的配置就可以使用 Sentinel 提供的流量控制和服务熔断功能。下面将简要介绍在 soul 中如何配置使用 sentinel 插件。 - -首先登陆 soul 管理平台在"插件列表" --> "sentinel"中配置插件。其中"选择器"的配置不是本文的重点不再介绍,点击"增加规则"来进行具体设置如下图。 - -![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-02.png) - -在这个配置页面中"名称"、"匹配方式"、"条件"、"日志打印"、"是否开启"、"执行顺序"属于 soul 插件的常规配置这里也不再赘述。我们重点需要关注的是"处理"中的配置项。这些配置项主要可以分为 2 组,前 4 个选项是关于熔断的配置,后 4 个选项是关于流量控制的配置。在 soul 中我们可以针对某一组请求同时设置它的流量控制和熔断策略。下面来重点分析下各个配置项如何使用。 - -## 熔断 - -首先来看熔断相关的配置,它有四个配置项"熔断阈值"、"是否开启熔断"、"熔断窗口大小"以及没有注名字的是服务异常判断方式。熔断开关表示是否开启熔断(1 开\0 不开)。熔断窗口大小指的是触发熔断后经过多少秒后进入半开状态,在半开状态如果请求正常则会进入正常状态如果请求依然不正常则继续熔断。熔断判定方式和熔断阈值需要结合来看。soul 中使用了 sentinel 的 3 种服务异常判定方式。分别是: - -1. 慢调用比例,在此模式下阈值指的是判定为慢调用的毫秒数。慢调用的比例默认是 1 不能更改即单位统计时长内全部超过阈值则触发熔断。该模式是 sentinel 的默认模式。 -2. 异常比例,在此模式下阈值指的是单位统计时长内异常请求的比例上限,需要填写 1 个[0.0, 1.0]的数,表示 0%-100% -3. 异常数策略,在该模式下阈值指的是单位统计时间内异常请求个数的上限。 - -需要注意的是 soul 对于单位统计时长(statIntervalMs)和熔断最小请求数(minRequestAmount)使用的是 sentinel 的默认参数。分别是 1 秒钟和 5 次。单位时长指定的是异常判断以是 1 秒钟为统计范围,下一秒重新开始计数。最小请求数指的是 1 秒钟内如果请求的次数少于 5 那么即使达到阈值也不会触发熔断。 - -![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-03.png) - -如上图配置表示的意思是,开启熔断配置,如果此服务在 1 秒钟内有 5 个请求都出现了异常那么则熔断 10 秒,10 秒后进入如半开状态,如果请求都正常则变为正常状态,如果还不正常则继续熔断。熔断期间如果请求该服务则 soul 网关会直接返回请求错误,保护后端服务不会再接到请求。 - -## 流量控制 - -流量控制的相关配置有 5 个,从上到下从左到右分别是"流控效果","限流阈值","流控开关","限流阈值类型"。首先是限流类型,我们可以选择"QPS"或"并发线程数",这个参数规定了我们从哪个角度来设置限流的阈值。阈值则是 QPS 的上限或者是线程数量,达到此阈值则会启动限流策略。具体的限流策略在"流控效果"中配置,流控策略里我们可以选择"直接拒绝"、"warm up(预热)"、"匀速排队"、"预热+匀速排队"。直接拒绝比较好理解,就是 QPS 或线程数达到阈值后,多余的请求直接报错返回。预热指的是在 10 秒钟内阈值逐步增长到指定阈值,即头 2-3 秒的阈值是低于设置阈值的,但阈值是逐步增长的,10 秒后达到指定阈值,这样可以使系统有个预热过程。超过阈值的请求 soul 网关会直接报错返回。匀速队列这种模式会严格控制每个请求的时间间隔,如果流控类型是 QPS 阈值是 10,那么 soul 会控制每 100ms 将 1 个请求传导到后端服务上。多余的请求首先会进入等待队列,每个请求最多等待 500ms,如果请求预计等待时间超过 500ms 则直接报错返回。需要注意的是如果限流类型是并发线程数,那么流控效果只能是"直接拒绝"。如下图所示该配置表示的是 soul 网关会保证该服务的 QPS 不超过 10,多余的请求将会直接报错。 - -![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-04.png) - -需要注意的是 Sentinel 组件独立运行于 soul 的每个网关中,如果网关是集群,那么在做流控时,实际传到后面服务中的量是需要乘上 soul 网关服务的数量的。即如果我们的 soul 网关部署了 3 个节点,通过 nginx 将所有请求平均负载到了每个节点上。对应 1 个接口我们配置的流控是 10 qps,那么实际后向服务需要处理的 QPS 是 10\*3。熔断同样需要考虑这种情况,只有 3 个节点上某个服务都触发熔断时,那么该服务才不会再收到任何请求。 - -# Sentinel 插件源码阅读 - -soul 中 Sentinel 插件的源码主要有 3 块,"SentinelRuleHandle"负责处理当有 Sentinel 规则从管理节点同步过来时的处理逻辑,"SentinelPlugin"插件的处理逻辑,"SentinelFallbackHandler"对于触发了流控或熔断的处理逻辑。下面我一个个来看一下。首先是"SentinelRuleHandle",源码如下: - -```java -public class SentinelRuleHandle implements PluginDataHandler { - - @Override - public void handlerRule(final RuleData ruleData) { - // 处理新的sentinel配置 - SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(ruleData.getHandle(), SentinelHandle.class); - sentinelHandle.checkData(sentinelHandle); - // 获取所有现有流控配置,删除与新配置同resourceName的配置 - List flowRules = FlowRuleManager.getRules() - .stream() - .filter(r -> !r.getResource().equals(getResourceName(ruleData))) - .collect(Collectors.toList()); - if (sentinelHandle.getFlowRuleEnable() == Constants.SENTINEL_ENABLE_FLOW_RULE) { - // 如果开启了流控 - // 根据配置设置sentinel流控规则 - FlowRule rule = new FlowRule(getResourceName(ruleData)); - // 配置阈值 - rule.setCount(sentinelHandle.getFlowRuleCount()); - // 流控方式 QPS or 线程 - rule.setGrade(sentinelHandle.getFlowRuleGrade()); - // 流控行为: 0. default(reject directly), 1. warm up, 2. rate limiter, 3. warm up + rate limiter - rule.setControlBehavior(sentinelHandle.getFlowRuleControlBehavior()); - flowRules.add(rule); - } - // 更新全部流控配置 - FlowRuleManager.loadRules(flowRules); - // 获取所有现有熔断配置,删除与新配置同resourceName的配置 - List degradeRules = DegradeRuleManager.getRules() - .stream() - .filter(r -> !r.getResource().equals(getResourceName(ruleData))) - .collect(Collectors.toList()); - if (sentinelHandle.getDegradeRuleEnable() == Constants.SENTINEL_ENABLE_DEGRADE_RULE) { - // 如果开启了流控 - // 根据配置设置sentinel熔断规则 - DegradeRule rule = new DegradeRule(getResourceName(ruleData)); - // 熔断阈值 - rule.setCount(sentinelHandle.getDegradeRuleCount()); - // 熔断判断的依据 0: average RT, 1: exception ratio, 2: exception count - rule.setGrade(sentinelHandle.getDegradeRuleGrade()); - // 熔断时间窗口 - rule.setTimeWindow(sentinelHandle.getDegradeRuleTimeWindow()); - degradeRules.add(rule); - } - // 更新全部熔断配置 - DegradeRuleManager.loadRules(degradeRules); - } - - @Override - public void removeRule(final RuleData ruleData) { - // 删除指定规则 - FlowRuleManager.loadRules(FlowRuleManager.getRules() - .stream() - .filter(r -> !r.getResource().equals(getResourceName(ruleData))) - .collect(Collectors.toList())); - DegradeRuleManager.loadRules(DegradeRuleManager.getRules() - .stream() - .filter(r -> !r.getResource().equals(getResourceName(ruleData))) - .collect(Collectors.toList())); - } - - @Override - public String pluginNamed() { - return PluginEnum.SENTINEL.getName(); - } - - /** - * return sentinel resource name. - * - * @param ruleData ruleData - * @return string string - */ - public static String getResourceName(final RuleData ruleData) { - return ruleData.getSelectorId() + "_" + ruleData.getName(); - } - -} -``` - -插件执行逻辑代码"SentinelPlugin"如下 - -```java -public class SentinelPlugin extends AbstractSoulPlugin { - // 异常处理的handler - private final SentinelFallbackHandler sentinelFallbackHandler; - - public SentinelPlugin(final SentinelFallbackHandler sentinelFallbackHandler) { - this.sentinelFallbackHandler = sentinelFallbackHandler; - } - - @Override - protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - // 从插件配置中生成sentinel使用的资源名称,该名称对应1个流控或熔断策略 - String resourceName = SentinelRuleHandle.getResourceName(rule); - // 验证sentinel插件的配置信息 - SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), SentinelHandle.class); - sentinelHandle.checkData(sentinelHandle); - // 引入sentinel官方的Transformer,将请求交给sentinel处理 - return chain.execute(exchange).transform(new SentinelReactorTransformer<>(resourceName)) - .doOnSuccess(v -> { - HttpStatus status = exchange.getResponse().getStatusCode(); - if (status == null || !status.is2xxSuccessful()) { - exchange.getResponse().setStatusCode(null); - throw new SentinelFallbackException(status == null ? HttpStatus.INTERNAL_SERVER_ERROR : status); - } - }) - //sentinel 触发了流控或熔断而报错调用sentinelFallbackHandler返回错误信息 - .onErrorResume(throwable -> sentinelFallbackHandler.fallback(exchange, UriUtils.createUri(sentinelHandle.getFallbackUri()), throwable)); - } - // 插件名sentinel - @Override - public String named() { - return PluginEnum.SENTINEL.getName(); - } - // 顺序 45 - @Override - public int getOrder() { - return PluginEnum.SENTINEL.getCode(); - } - - public static class SentinelFallbackException extends HttpStatusCodeException { - public SentinelFallbackException(final HttpStatus statusCode) { - super(statusCode); - } - } -} -``` - -异常处理"SentinelFallbackHandler",在 soul 中不管是熔断后请求的处理还是被流控的请求,都是有 soul 直接返回报错 - -```java -public class SentinelFallbackHandler implements FallbackHandler { - - @Override - public Mono generateError(final ServerWebExchange exchange, final Throwable throwable) { - Object error; - - if (throwable instanceof DegradeException) { - // 触发熔断 - // http status 设为500 - exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); - // request body 设置 - error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); - } else if (throwable instanceof FlowException) { - // 流控报错 该错提示客户端再次尝试 - // http status 设为429 - exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); - // request body 设置 - error = SoulResultWrap.error(SoulResultEnum.TOO_MANY_REQUESTS.getCode(), SoulResultEnum.TOO_MANY_REQUESTS.getMsg(), null); - } else if (throwable instanceof BlockException) { - // FlowException的父类 该错提示服务已阻塞 - // http status 设为429 - exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); - // request body 设置 - error = SoulResultWrap.error(SoulResultEnum.SENTINEL_BLOCK_ERROR.getCode(), SoulResultEnum.SENTINEL_BLOCK_ERROR.getMsg(), null); - } else { - return Mono.error(throwable); - } - return WebFluxResultUtils.result(exchange, error); - } -} -``` - -# 总结 - -soul 网关封装了优秀的流控组件——sentinel,为用户提供了好用的流量控制和熔断功能。需要注意的是 soul 在使用 sentinel 时部分参数是默认配置,如果有修改的需求则需要自行调整源码。其次 soul 网关可以分布式部署,但是使用 sentinel 时并没有用分布式流控,每个 soul 网关节点对于同一个资源的流控是独立但相同的。 +--- +title: Soul网关学习Sentinel插件原理解析 +author: 骆潇龙 +date: 2021-03-19 +tag: + - Soul +cover: /assets/img/blog6/02.jpg +head: + - - meta + - name: 博客 +--- + +# 概述 + +在业务网关中熔断和流量控制都是非常必要的功能。soul 在实现这部分功能时使用了不同的成熟组件,用户可以根据自己的喜好选择。本文将介绍如何在 soul 中使用阿里的 Sentinel 组件实现熔断及流控功能。本文首先会介绍熔断和流控的场景及意义。然后介绍如何在 soul 上配置使用 sentinel 插件做流控和熔断。最后从源码的层面简略分析 soul 是如何使用 Sentinel 组件的。 + +# 熔断和流量控制 + +## 场景描述 + +业务网关作为流量的入口,有保护后继服务的职责。以下两个对服务有严重危害的场景在生产中经常会遇到,也是业务网关必须要关注处理的问题。一种情况是在比如双 11 或双 12 这些大型促销时,接口的请求量是平时是数倍,如果没有评估好容量,这种激增的请求很容易导致整个服务完全不可用。这种宕机往往不是因为业务逻辑的漏洞而是因为请求过多资源不够导致的。另一种情况是在整个服务体系中有一些核心服务,多个业务流程都依赖该服务。然而是服务都有出现处理不稳定或者服务损坏的情况,导致请求处理时间长或者老是频繁抛出异常。排除业务 BUG 的情况,可能就是突发的非常随机的阻塞,一般减缓请求量就会自动修复,但是如果不加保护就有出现多米诺效应导致整个服务不可用。此场景和第一种场景有略微不同,第一种场景是实际流量确实出现了不可处理的峰值,而第二种场景主要考虑的是服务本身出现了不可避免、不可预测的抖动而引发的连锁反应。 + +## 流量控制 + +针对第一种场景我们通常的做法是进行流量控制,核心思路是业务网关保证打到后面的请求是业务可以承受的量,多余的请求直接拒绝或者加入等待队列,保证服务不会宕掉,大部分请求还是可以正常处理。在考虑流量控制的策略时,我们应该主要思考以下几个问题: + +1. 通过什么角度控制流量? +2. 阈值是多少? +3. 流量控制的策略是什么? + +对于第一个问题,正常思路是通过 QPS 来监控流量,即每秒钟请求的数量超过某限额时进行流控。但其实还有一种思路是从并发数来监控流量。这种控制场景也是非常有意义的,例如当下游应用由于某种原因导致服务不稳定、响应延迟增加,对于网关来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。从某种意义上讲通过并发进行流控可以一定程度上保护网关服务本身。对于第二个问题阈值来说比较好理解,就是触发流控的边界,如果从 QPS 来考虑就是每秒达到多少时开始流控,从并发数来考量的话就是请求上下文的线程数目超过多少进行流控。对于第三个问题,我们一般有以下 3 中处理方案: + +1. 直接拒绝,这种策略非常好理解就是当 QPS 高于阈值时直接拒接服务,不把请求传输到后面的服务中。 +2. 预热启动,这个策略所针对的场景是系统长期处于低水位的情况下,可能出现流量突然增加时,而直接把系统拉升到高水位可能瞬间把系统压垮。预热启动的方式是让阈值缓慢增加,在一定时间内逐渐增加阈值直至达到设置,给冷系统一个预热的时间,避免冷系统被压垮。对于超出阈值的请求也是触发拒绝。 +3. 匀速排队,此策略核心思路是以固定间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。 + +## 熔断 + +针对第二种场景通常的处理方式是设置服务熔断。简单的说就是当我们探测的一个服务出现了异常,则不再访问它以免更多的请求对它造成更大的压力。一段时间后如果探测到服务恢复了再将流量发送过去。我们首先需要判断出这个服务是否出现了不稳定\抖动的情况。然后思考如果发现了抖动的服务我们应该怎么办。如何判断服务是否恢复正常了。对于服务是否不稳定这一点我们一般可以通过一下 3 个方式进行判断。 + +1. 慢调用比例:当单位统计时长内请求数目大于设置的最小请求数目,并且超过最大忍受时间的请求大于阈值,则判断服务异常,触发熔断; +2. 异常比例:当单位统计时长内异常请求的比例大于阈值则我们判定服务异常,触发熔断; +3. 异常数:当单位时长内出现异常的请求的数量的达到阈值则判定服务异常,触发熔断; + +当我们通过以上 3 个指标判断服务为异常并熔断服务后,对于一定时间内(熔断时长内)的请求我们可以选择直接报错,不阻塞上游服务,让请求方来自行决定如何处理。或者直接触发服务降级。服务降级粗略的可以理解为请求此业务的简版,该简版省掉了很多非核心流程,并且只是最终保证流程处理完(最终一致性)。和现实中的熔断一样服务熔断是会自动恢复的。一般是触发熔断后的一段时间内服务处于熔断状态不提供服务,然后进入半开状态,若接下来的少量请求没有报错且响应时间合理则服务恢复,如果还是异常则继续熔断。 + +# soul 中的 Sentinel 插件 + +Sentinel 是阿里开源的面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。Soul 作为国内优秀的开源网关,将 Sentinel 整合为插件融入了自己的体系中,使用户通过简单的配置就可以使用 Sentinel 提供的流量控制和服务熔断功能。下面将简要介绍在 soul 中如何配置使用 sentinel 插件。 + +首先登陆 soul 管理平台在"插件列表" --> "sentinel"中配置插件。其中"选择器"的配置不是本文的重点不再介绍,点击"增加规则"来进行具体设置如下图。 + +![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-02.png) + +在这个配置页面中"名称"、"匹配方式"、"条件"、"日志打印"、"是否开启"、"执行顺序"属于 soul 插件的常规配置这里也不再赘述。我们重点需要关注的是"处理"中的配置项。这些配置项主要可以分为 2 组,前 4 个选项是关于熔断的配置,后 4 个选项是关于流量控制的配置。在 soul 中我们可以针对某一组请求同时设置它的流量控制和熔断策略。下面来重点分析下各个配置项如何使用。 + +## 熔断 + +首先来看熔断相关的配置,它有四个配置项"熔断阈值"、"是否开启熔断"、"熔断窗口大小"以及没有注名字的是服务异常判断方式。熔断开关表示是否开启熔断(1 开\0 不开)。熔断窗口大小指的是触发熔断后经过多少秒后进入半开状态,在半开状态如果请求正常则会进入正常状态如果请求依然不正常则继续熔断。熔断判定方式和熔断阈值需要结合来看。soul 中使用了 sentinel 的 3 种服务异常判定方式。分别是: + +1. 慢调用比例,在此模式下阈值指的是判定为慢调用的毫秒数。慢调用的比例默认是 1 不能更改即单位统计时长内全部超过阈值则触发熔断。该模式是 sentinel 的默认模式。 +2. 异常比例,在此模式下阈值指的是单位统计时长内异常请求的比例上限,需要填写 1 个[0.0, 1.0]的数,表示 0%-100% +3. 异常数策略,在该模式下阈值指的是单位统计时间内异常请求个数的上限。 + +需要注意的是 soul 对于单位统计时长(statIntervalMs)和熔断最小请求数(minRequestAmount)使用的是 sentinel 的默认参数。分别是 1 秒钟和 5 次。单位时长指定的是异常判断以是 1 秒钟为统计范围,下一秒重新开始计数。最小请求数指的是 1 秒钟内如果请求的次数少于 5 那么即使达到阈值也不会触发熔断。 + +![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-03.png) + +如上图配置表示的意思是,开启熔断配置,如果此服务在 1 秒钟内有 5 个请求都出现了异常那么则熔断 10 秒,10 秒后进入如半开状态,如果请求都正常则变为正常状态,如果还不正常则继续熔断。熔断期间如果请求该服务则 soul 网关会直接返回请求错误,保护后端服务不会再接到请求。 + +## 流量控制 + +流量控制的相关配置有 5 个,从上到下从左到右分别是"流控效果","限流阈值","流控开关","限流阈值类型"。首先是限流类型,我们可以选择"QPS"或"并发线程数",这个参数规定了我们从哪个角度来设置限流的阈值。阈值则是 QPS 的上限或者是线程数量,达到此阈值则会启动限流策略。具体的限流策略在"流控效果"中配置,流控策略里我们可以选择"直接拒绝"、"warm up(预热)"、"匀速排队"、"预热+匀速排队"。直接拒绝比较好理解,就是 QPS 或线程数达到阈值后,多余的请求直接报错返回。预热指的是在 10 秒钟内阈值逐步增长到指定阈值,即头 2-3 秒的阈值是低于设置阈值的,但阈值是逐步增长的,10 秒后达到指定阈值,这样可以使系统有个预热过程。超过阈值的请求 soul 网关会直接报错返回。匀速队列这种模式会严格控制每个请求的时间间隔,如果流控类型是 QPS 阈值是 10,那么 soul 会控制每 100ms 将 1 个请求传导到后端服务上。多余的请求首先会进入等待队列,每个请求最多等待 500ms,如果请求预计等待时间超过 500ms 则直接报错返回。需要注意的是如果限流类型是并发线程数,那么流控效果只能是"直接拒绝"。如下图所示该配置表示的是 soul 网关会保证该服务的 QPS 不超过 10,多余的请求将会直接报错。 + +![](https://rfc2616.oss-cn-beijing.aliyuncs.com/blog/soul16-04.png) + +需要注意的是 Sentinel 组件独立运行于 soul 的每个网关中,如果网关是集群,那么在做流控时,实际传到后面服务中的量是需要乘上 soul 网关服务的数量的。即如果我们的 soul 网关部署了 3 个节点,通过 nginx 将所有请求平均负载到了每个节点上。对应 1 个接口我们配置的流控是 10 qps,那么实际后向服务需要处理的 QPS 是 10\*3。熔断同样需要考虑这种情况,只有 3 个节点上某个服务都触发熔断时,那么该服务才不会再收到任何请求。 + +# Sentinel 插件源码阅读 + +soul 中 Sentinel 插件的源码主要有 3 块,"SentinelRuleHandle"负责处理当有 Sentinel 规则从管理节点同步过来时的处理逻辑,"SentinelPlugin"插件的处理逻辑,"SentinelFallbackHandler"对于触发了流控或熔断的处理逻辑。下面我一个个来看一下。首先是"SentinelRuleHandle",源码如下: + +```java +public class SentinelRuleHandle implements PluginDataHandler { + + @Override + public void handlerRule(final RuleData ruleData) { + // 处理新的sentinel配置 + SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(ruleData.getHandle(), SentinelHandle.class); + sentinelHandle.checkData(sentinelHandle); + // 获取所有现有流控配置,删除与新配置同resourceName的配置 + List flowRules = FlowRuleManager.getRules() + .stream() + .filter(r -> !r.getResource().equals(getResourceName(ruleData))) + .collect(Collectors.toList()); + if (sentinelHandle.getFlowRuleEnable() == Constants.SENTINEL_ENABLE_FLOW_RULE) { + // 如果开启了流控 + // 根据配置设置sentinel流控规则 + FlowRule rule = new FlowRule(getResourceName(ruleData)); + // 配置阈值 + rule.setCount(sentinelHandle.getFlowRuleCount()); + // 流控方式 QPS or 线程 + rule.setGrade(sentinelHandle.getFlowRuleGrade()); + // 流控行为: 0. default(reject directly), 1. warm up, 2. rate limiter, 3. warm up + rate limiter + rule.setControlBehavior(sentinelHandle.getFlowRuleControlBehavior()); + flowRules.add(rule); + } + // 更新全部流控配置 + FlowRuleManager.loadRules(flowRules); + // 获取所有现有熔断配置,删除与新配置同resourceName的配置 + List degradeRules = DegradeRuleManager.getRules() + .stream() + .filter(r -> !r.getResource().equals(getResourceName(ruleData))) + .collect(Collectors.toList()); + if (sentinelHandle.getDegradeRuleEnable() == Constants.SENTINEL_ENABLE_DEGRADE_RULE) { + // 如果开启了流控 + // 根据配置设置sentinel熔断规则 + DegradeRule rule = new DegradeRule(getResourceName(ruleData)); + // 熔断阈值 + rule.setCount(sentinelHandle.getDegradeRuleCount()); + // 熔断判断的依据 0: average RT, 1: exception ratio, 2: exception count + rule.setGrade(sentinelHandle.getDegradeRuleGrade()); + // 熔断时间窗口 + rule.setTimeWindow(sentinelHandle.getDegradeRuleTimeWindow()); + degradeRules.add(rule); + } + // 更新全部熔断配置 + DegradeRuleManager.loadRules(degradeRules); + } + + @Override + public void removeRule(final RuleData ruleData) { + // 删除指定规则 + FlowRuleManager.loadRules(FlowRuleManager.getRules() + .stream() + .filter(r -> !r.getResource().equals(getResourceName(ruleData))) + .collect(Collectors.toList())); + DegradeRuleManager.loadRules(DegradeRuleManager.getRules() + .stream() + .filter(r -> !r.getResource().equals(getResourceName(ruleData))) + .collect(Collectors.toList())); + } + + @Override + public String pluginNamed() { + return PluginEnum.SENTINEL.getName(); + } + + /** + * return sentinel resource name. + * + * @param ruleData ruleData + * @return string string + */ + public static String getResourceName(final RuleData ruleData) { + return ruleData.getSelectorId() + "_" + ruleData.getName(); + } + +} +``` + +插件执行逻辑代码"SentinelPlugin"如下 + +```java +public class SentinelPlugin extends AbstractSoulPlugin { + // 异常处理的handler + private final SentinelFallbackHandler sentinelFallbackHandler; + + public SentinelPlugin(final SentinelFallbackHandler sentinelFallbackHandler) { + this.sentinelFallbackHandler = sentinelFallbackHandler; + } + + @Override + protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + // 从插件配置中生成sentinel使用的资源名称,该名称对应1个流控或熔断策略 + String resourceName = SentinelRuleHandle.getResourceName(rule); + // 验证sentinel插件的配置信息 + SentinelHandle sentinelHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), SentinelHandle.class); + sentinelHandle.checkData(sentinelHandle); + // 引入sentinel官方的Transformer,将请求交给sentinel处理 + return chain.execute(exchange).transform(new SentinelReactorTransformer<>(resourceName)) + .doOnSuccess(v -> { + HttpStatus status = exchange.getResponse().getStatusCode(); + if (status == null || !status.is2xxSuccessful()) { + exchange.getResponse().setStatusCode(null); + throw new SentinelFallbackException(status == null ? HttpStatus.INTERNAL_SERVER_ERROR : status); + } + }) + //sentinel 触发了流控或熔断而报错调用sentinelFallbackHandler返回错误信息 + .onErrorResume(throwable -> sentinelFallbackHandler.fallback(exchange, UriUtils.createUri(sentinelHandle.getFallbackUri()), throwable)); + } + // 插件名sentinel + @Override + public String named() { + return PluginEnum.SENTINEL.getName(); + } + // 顺序 45 + @Override + public int getOrder() { + return PluginEnum.SENTINEL.getCode(); + } + + public static class SentinelFallbackException extends HttpStatusCodeException { + public SentinelFallbackException(final HttpStatus statusCode) { + super(statusCode); + } + } +} +``` + +异常处理"SentinelFallbackHandler",在 soul 中不管是熔断后请求的处理还是被流控的请求,都是有 soul 直接返回报错 + +```java +public class SentinelFallbackHandler implements FallbackHandler { + + @Override + public Mono generateError(final ServerWebExchange exchange, final Throwable throwable) { + Object error; + + if (throwable instanceof DegradeException) { + // 触发熔断 + // http status 设为500 + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + // request body 设置 + error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); + } else if (throwable instanceof FlowException) { + // 流控报错 该错提示客户端再次尝试 + // http status 设为429 + exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); + // request body 设置 + error = SoulResultWrap.error(SoulResultEnum.TOO_MANY_REQUESTS.getCode(), SoulResultEnum.TOO_MANY_REQUESTS.getMsg(), null); + } else if (throwable instanceof BlockException) { + // FlowException的父类 该错提示服务已阻塞 + // http status 设为429 + exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); + // request body 设置 + error = SoulResultWrap.error(SoulResultEnum.SENTINEL_BLOCK_ERROR.getCode(), SoulResultEnum.SENTINEL_BLOCK_ERROR.getMsg(), null); + } else { + return Mono.error(throwable); + } + return WebFluxResultUtils.result(exchange, error); + } +} +``` + +# 总结 + +soul 网关封装了优秀的流控组件——sentinel,为用户提供了好用的流量控制和熔断功能。需要注意的是 soul 在使用 sentinel 时部分参数是默认配置,如果有修改的需求则需要自行调整源码。其次 soul 网关可以分布式部署,但是使用 sentinel 时并没有用分布式流控,每个 soul 网关节点对于同一个资源的流控是独立但相同的。 diff --git a/src/zh/blog/soul_source_learning_21_resilience4j.md b/src/zh/blog/soul_source_learning_21_resilience4j.md index 251a65eaa9..cd1e504722 100644 --- a/src/zh/blog/soul_source_learning_21_resilience4j.md +++ b/src/zh/blog/soul_source_learning_21_resilience4j.md @@ -1,344 +1,344 @@ ---- -title: Soul网关学习Resilience4j插件原理解析 -author: yanbing -tag: - - Soul -date: 2021-03-22 -cover: /assets/img/blog6/03.jpg -head: - - - meta - - name: 博客 ---- - -## 目标 - -- 什么是 Resilience4J -- soul 的 Resilience4j 体验 - - 限流 - - 熔断 -- Resilience4J 插件源码解读 - -## 什么是 Resilience4j - -- Resilience4J 是 Spring Cloud Gateway 推荐的容错方案,它是一个轻量级的容错库 -- 借鉴了 Hystrix 而设计,并且采用 JDK8 这个函数式编程,即 lambda 表达式 -- 相比之下, Netflix Hystrix 对 Archaius 具有编译依赖性,Resilience4j 你无需引用全部依赖,可以根据自己需要的功能引用相关的模块即可 - Hystrix 不更新了,Spring 提供 Netflix Hystrix 的替换方案,即 Resilence4J -- Resilience4J 提供了一系列增强微服务的可用性功能: - - - 断路器 CircuitBreaker - - 限流 RateLimiter - - 基于信号量的隔离 - - 缓存 - - 限时 Timelimiter - - 请求重启 Retry - -- 官方提供的依赖包 - -```Java - - io.github.resilience4j - resilience4j-circuitbreaker - ${resilience.version} - -``` - -## soul 的 Resilience4j 体验 - -- 首先在 soul-admin 控制台插件管理开启 Resilience4j - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210321112151395.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODY5MjQz,size_16,color_FFFFFF,t_70) - -- 在 soul 网关添加依赖 - -```Java - - org.dromara - soul-spring-boot-starter-plugin-ratelimiter - ${project.version} - -``` - -- 启动三个服务,分别是一个 soul-admin,一个 soul-bootstrap,一个 soul-examples-http - -- 在 soul-admin 控制台找到插件列表的 Resilience4j,自定义配置,如下图, - ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210321112202189.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODY5MjQz,size_16,color_FFFFFF,t_70) - -- [soul 官网的配置介绍](https://dromara.org/zh/projects/soul/resilience4j-plugin/) - -``` -* Resilience4j处理详解: - - * timeoutDurationRate:等待获取令牌的超时时间,单位ms,默认值:5000。 - - * limitRefreshPeriod:刷新令牌的时间间隔,单位ms,默认值:500。 - - * limitForPeriod:每次刷新令牌的数量,默认值:50。 - - * circuitEnable:是否开启熔断,0:关闭,1:开启,默认值:0。 - - * timeoutDuration:熔断超时时间,单位ms,默认值:30000。 - - * fallbackUri:降级处理的uri。 - - * slidingWindowSize:滑动窗口大小,默认值:100。 - - * slidingWindowType:滑动窗口类型,0:基于计数,1:基于时间,默认值:0。 - - * minimumNumberOfCalls:开启熔断的最小请求数,超过这个请求数才开启熔断统计,默认值:100。 - - * waitIntervalFunctionInOpenState:熔断器开启持续时间,单位ms,默认值:10。 - - * permittedNumberOfCallsInHalfOpenState:半开状态下的环形缓冲区大小,必须达到此数量才会计算失败率,默认值:10。 - - * failureRateThreshold:错误率百分比,达到这个阈值,熔断器才会开启,默认值50。 - - * automaticTransitionFromOpenToHalfOpenEnabled:是否自动从open状态转换为half-open状态,,true:是,false:否,默认值:false。 -``` - -### 限流 - -- 参数配置 - 如下是参数配置校验,参数值小于默认值,会直接赋值默认值,因此方便测试效果直接修改源码的配置 - : 每次刷新令牌的数量为 2 ,刷新令牌的时间间隔为 1s,超时时间为 1s - -```java - /** - * check filed default value. - * - * @param resilience4JHandle {@linkplain Resilience4JHandle} - * @return {@linkplain Resilience4JHandle} - */ - public Resilience4JHandle checkData(final Resilience4JHandle resilience4JHandle) { - resilience4JHandle.setTimeoutDurationRate(Math.max(resilience4JHandle.getTimeoutDurationRate(), Constants.TIMEOUT_DURATION_RATE)); - //resilience4JHandle.setLimitRefreshPeriod(Math.max(resilience4JHandle.getLimitRefreshPeriod(), Constants.LIMIT_REFRESH_PERIOD)); - //resilience4JHandle.setLimitForPeriod(Math.max(resilience4JHandle.getLimitForPeriod(), Constants.LIMIT_FOR_PERIOD)); - //每次刷新令牌的数量为2 ,刷新令牌的时间间隔为1s - resilience4JHandle.setLimitRefreshPeriod(1000); - resilience4JHandle.setLimitForPeriod(2); - resilience4JHandle.setTimeoutDuration(1000); - resilience4JHandle.setCircuitEnable(Math.max(resilience4JHandle.getCircuitEnable(), Constants.CIRCUIT_ENABLE)); - //resilience4JHandle.setTimeoutDuration(Math.max(resilience4JHandle.getTimeoutDuration(), Constants.TIMEOUT_DURATION)); - resilience4JHandle.setFallbackUri(!"0".equals(resilience4JHandle.getFallbackUri()) ? resilience4JHandle.getFallbackUri() : ""); - resilience4JHandle.setSlidingWindowSize(Math.max(resilience4JHandle.getSlidingWindowSize(), Constants.SLIDING_WINDOW_SIZE)); - resilience4JHandle.setSlidingWindowType(Math.max(resilience4JHandle.getSlidingWindowType(), Constants.SLIDING_WINDOW_TYPE)); - resilience4JHandle.setMinimumNumberOfCalls(Math.max(resilience4JHandle.getMinimumNumberOfCalls(), Constants.MINIMUM_NUMBER_OF_CALLS)); - resilience4JHandle.setWaitIntervalFunctionInOpenState(Math.max(resilience4JHandle.getWaitIntervalFunctionInOpenState(), Constants.WAIT_INTERVAL_FUNCTION_IN_OPEN_STATE)); - resilience4JHandle.setPermittedNumberOfCallsInHalfOpenState(Math.max(resilience4JHandle.getPermittedNumberOfCallsInHalfOpenState(), Constants.PERMITTED_NUMBER_OF_CALLS_IN_HALF_OPEN_STATE)); - resilience4JHandle.setFailureRateThreshold(Math.max(resilience4JHandle.getFailureRateThreshold(), Constants.FAILURE_RATE_THRESHOLD)); - return resilience4JHandle; - } -``` - -- 使用 SuperBenchmarker 工具,4 个线程,执行 10s - -```java -C:\Users\v-yanb07>sb -u http://localhost:9195/http/test/findByUserId?userId=1 -c 4 -N 10 -Starting at 2021-03-14 15:46:28 -[Press C to stop the test] -23 (RPS: 1) ----------------Finished!---------------- -Finished at 2021-03-14 15:46:51 (took 00:00:23.0477097) -24 (RPS: 1) Status 200: 25 - -RPS: 2.2 (requests/second) -Max: 2020ms -Min: 472ms -Avg: 1677ms - - 50% below 1994ms - 60% below 1997ms - 70% below 1999ms - 80% below 1999ms - 90% below 2001ms - 95% below 2019ms - 98% below 2020ms - 99% below 2020ms -99.9% below 2020ms -``` - -- 输出日志 - -```java -2021-03-14 12:16:35.252 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 -2021-03-14 12:16:36.249 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : 限流测试 -2021-03-14 12:16:36.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 -2021-03-14 12:16:37.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 -2021-03-14 12:16:37.250 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : 限流测试 -2021-03-14 12:16:38.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 -2021-03-14 12:16:38.250 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : 限流测试 -2021-03-14 12:16:39.252 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 -2021-03-14 12:16:39.252 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : 限流测试 -``` - -控制台日志每秒输出两条,由此验证限流生效 - -### 熔断 - -- 从配置信息我们知道熔断器默认是关闭,我们需要开打 -- soul-examples-http 调用接口处添加休眠时间 - -```java - @GetMapping("/findByUserId") - public UserDTO findByUserId(@RequestParam("userId") final String userId) throws Exception{ - UserDTO userDTO = new UserDTO(); - userDTO.setUserId(userId); - userDTO.setUserName("hello world"); - log.info("限流测试"); - - int i = RandomUtils.nextInt(1,3); - if(i %2==0){ - //throw new Exception("异常抛出"); - Thread.currentThread().sleep(2000); - } - return userDTO; - } -``` - -- Resilience4JHandle#checkData 手动设置超时时间为 1s - -```java - resilience4JHandle.setTimeoutDuration(1000); -``` - -- pos 接口调用 - > http://localhost:9195/http/test/findByUserId?userId=1 - -多次请求时,有的请求返回正常数据,有的请求返回如下数据,表示超时熔断生效 - -```java -{ - "code": 500, - "message": "Internal Server Error", - "data": "404 NOT_FOUND" -} -``` - -## Resilience4J 插件源码解读 - -soul 网关 Resilience4j 插件源码大量使用了[响应式编程](https://developer.ibm.com/zh/languages/java/articles/j-cn-with-reactor-response-encode/)方式,首先需要对响应式编程了解 - -- Resilience4J 插件目录结构 - -``` -└─resilience4j - │ Resilience4JPlugin.java //插件处理,核心类 - │ - ├─build - │ Resilience4JBuilder.java //构建Resilience4JConf对象 - │ - ├─conf - │ Resilience4JConf.java - │ - ├─executor - │ CombinedExecutor.java //限流和熔断执行器 - │ Executor.java - │ RateLimiterExecutor.java //限流执行器 - │ - ├─factory - │ Resilience4JRegistryFactory.java //限流和熔断对象构建 - │ - └─handler - Resilience4JHandler.java -``` - -- Resilience4JPlugn#doExecute - Resilience4JPlugn 其他 soul 中插件一样继承 AbstractSoulPlugin,只要开启了,通过链式机制执行,都会走到核心方法 doExecute - -```java - @Override - protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - //获取配置信息对象 - Resilience4JHandle resilience4JHandle = GsonUtils.getGson().fromJson(rule.getHandle(), Resilience4JHandle.class); - //校验配置信息,如果小于默认值,则赋值默认值 - resilience4JHandle = resilience4JHandle.checkData(resilience4JHandle); - //circuitEnable配置:1 开启熔断组件 ,否则走限流组件 - if (resilience4JHandle.getCircuitEnable() == 1) { - return combined(exchange, chain, rule); - } - - return rateLimiter(exchange, chain, rule); - } -``` - -- 限流 Resilience4JPlugin#rateLimiter - -```java - private Mono rateLimiter(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) { - return ratelimiterExecutor.run( - // chain.execute(exchange) 后续插件执行 - chain.execute(exchange), fallback(ratelimiterExecutor, exchange, null), Resilience4JBuilder.build(rule)) - .onErrorResume(throwable -> ratelimiterExecutor.withoutFallback(exchange, throwable)) - - - //ratelimiterExecutor.run调用 - @Override -public Mono run(final Mono toRun, final Function> fallback, final Resilience4JConf conf) { - //限流器组件 - RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(conf.getId(), conf.getRateLimiterConfig()); - //限流执行 - Mono to = toRun.transformDeferred(RateLimiterOperator.of(rateLimiter)); - if (fallback != null) { - //回调的执行 - return to.onErrorResume(fallback); - } - return to; -} - - - // to.onErrorResume(fallback); - default Mono fallback(ServerWebExchange exchange, String uri, Throwable t) { - if (StringUtils.isBlank(uri)) { - return withoutFallback(exchange, t); - } - DispatcherHandler dispatcherHandler = SpringBeanUtils.getInstance().getBean(DispatcherHandler.class); - ServerHttpRequest request = exchange.getRequest().mutate().uri(Objects.requireNonNull(UriUtils.createUri(uri))).build(); - ServerWebExchange mutated = exchange.mutate().request(request).build(); - //回调的执行地方 - return dispatcherHandler.handle(mutated); -} -``` - -- 熔断 Resilience4JPlugin#combined - -```java - private Mono combined(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) { - Resilience4JConf conf = Resilience4JBuilder.build(rule); - return combinedExecutor.run( - chain.execute(exchange).doOnSuccess(v -> { - HttpStatus status = exchange.getResponse().getStatusCode(); - if (status == null || !status.is2xxSuccessful()) { - exchange.getResponse().setStatusCode(null); - throw new CircuitBreakerStatusCodeException(status == null ? HttpStatus.INTERNAL_SERVER_ERROR : status); - } - }), fallback(combinedExecutor, exchange, conf.getFallBackUri()), conf); - } - - - //combinedExecutor#run执行的内容 - public Mono run(final Mono run, final Function> fallback, final Resilience4JConf resilience4JConf) { - RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(resilience4JConf.getId(), resilience4JConf.getRateLimiterConfig()); - CircuitBreaker circuitBreaker = Resilience4JRegistryFactory.circuitBreaker(resilience4JConf.getId(), resilience4JConf.getCircuitBreakerConfig()); - //断路器的操作 - Mono to = run.transformDeferred(CircuitBreakerOperator.of(circuitBreaker)) - //限流操作 - .transformDeferred(RateLimiterOperator.of(rateLimiter)) - //设置超时时间 - .timeout(resilience4JConf.getTimeLimiterConfig().getTimeoutDuration()) - //如果超时了抛出超时异常 - .doOnError(TimeoutException.class, t -> circuitBreaker.onError( - resilience4JConf.getTimeLimiterConfig().getTimeoutDuration().toMillis(), - TimeUnit.MILLISECONDS, - t)); - if (fallback != null) { - to = to.onErrorResume(fallback); - } - return to; - } -``` - -## 总结 - -- soul 网关提供限流和熔断,熔断默认是关闭的 -- 参数值小于默认值,会直接使用默认值 +--- +title: Soul网关学习Resilience4j插件原理解析 +author: yanbing +tag: + - Soul +date: 2021-03-22 +cover: /assets/img/blog6/03.jpg +head: + - - meta + - name: 博客 +--- + +## 目标 + +- 什么是 Resilience4J +- soul 的 Resilience4j 体验 + - 限流 + - 熔断 +- Resilience4J 插件源码解读 + +## 什么是 Resilience4j + +- Resilience4J 是 Spring Cloud Gateway 推荐的容错方案,它是一个轻量级的容错库 +- 借鉴了 Hystrix 而设计,并且采用 JDK8 这个函数式编程,即 lambda 表达式 +- 相比之下, Netflix Hystrix 对 Archaius 具有编译依赖性,Resilience4j 你无需引用全部依赖,可以根据自己需要的功能引用相关的模块即可 + Hystrix 不更新了,Spring 提供 Netflix Hystrix 的替换方案,即 Resilence4J +- Resilience4J 提供了一系列增强微服务的可用性功能: + + - 断路器 CircuitBreaker + - 限流 RateLimiter + - 基于信号量的隔离 + - 缓存 + - 限时 Timelimiter + - 请求重启 Retry + +- 官方提供的依赖包 + +```Java + + io.github.resilience4j + resilience4j-circuitbreaker + ${resilience.version} + +``` + +## soul 的 Resilience4j 体验 + +- 首先在 soul-admin 控制台插件管理开启 Resilience4j + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210321112151395.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODY5MjQz,size_16,color_FFFFFF,t_70) + +- 在 soul 网关添加依赖 + +```Java + + org.dromara + soul-spring-boot-starter-plugin-ratelimiter + ${project.version} + +``` + +- 启动三个服务,分别是一个 soul-admin,一个 soul-bootstrap,一个 soul-examples-http + +- 在 soul-admin 控制台找到插件列表的 Resilience4j,自定义配置,如下图, + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210321112202189.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODY5MjQz,size_16,color_FFFFFF,t_70) + +- [soul 官网的配置介绍](https://dromara.org/zh/projects/soul/resilience4j-plugin/) + +``` +* Resilience4j处理详解: + + * timeoutDurationRate:等待获取令牌的超时时间,单位ms,默认值:5000。 + + * limitRefreshPeriod:刷新令牌的时间间隔,单位ms,默认值:500。 + + * limitForPeriod:每次刷新令牌的数量,默认值:50。 + + * circuitEnable:是否开启熔断,0:关闭,1:开启,默认值:0。 + + * timeoutDuration:熔断超时时间,单位ms,默认值:30000。 + + * fallbackUri:降级处理的uri。 + + * slidingWindowSize:滑动窗口大小,默认值:100。 + + * slidingWindowType:滑动窗口类型,0:基于计数,1:基于时间,默认值:0。 + + * minimumNumberOfCalls:开启熔断的最小请求数,超过这个请求数才开启熔断统计,默认值:100。 + + * waitIntervalFunctionInOpenState:熔断器开启持续时间,单位ms,默认值:10。 + + * permittedNumberOfCallsInHalfOpenState:半开状态下的环形缓冲区大小,必须达到此数量才会计算失败率,默认值:10。 + + * failureRateThreshold:错误率百分比,达到这个阈值,熔断器才会开启,默认值50。 + + * automaticTransitionFromOpenToHalfOpenEnabled:是否自动从open状态转换为half-open状态,,true:是,false:否,默认值:false。 +``` + +### 限流 + +- 参数配置 + 如下是参数配置校验,参数值小于默认值,会直接赋值默认值,因此方便测试效果直接修改源码的配置 + : 每次刷新令牌的数量为 2 ,刷新令牌的时间间隔为 1s,超时时间为 1s + +```java + /** + * check filed default value. + * + * @param resilience4JHandle {@linkplain Resilience4JHandle} + * @return {@linkplain Resilience4JHandle} + */ + public Resilience4JHandle checkData(final Resilience4JHandle resilience4JHandle) { + resilience4JHandle.setTimeoutDurationRate(Math.max(resilience4JHandle.getTimeoutDurationRate(), Constants.TIMEOUT_DURATION_RATE)); + //resilience4JHandle.setLimitRefreshPeriod(Math.max(resilience4JHandle.getLimitRefreshPeriod(), Constants.LIMIT_REFRESH_PERIOD)); + //resilience4JHandle.setLimitForPeriod(Math.max(resilience4JHandle.getLimitForPeriod(), Constants.LIMIT_FOR_PERIOD)); + //每次刷新令牌的数量为2 ,刷新令牌的时间间隔为1s + resilience4JHandle.setLimitRefreshPeriod(1000); + resilience4JHandle.setLimitForPeriod(2); + resilience4JHandle.setTimeoutDuration(1000); + resilience4JHandle.setCircuitEnable(Math.max(resilience4JHandle.getCircuitEnable(), Constants.CIRCUIT_ENABLE)); + //resilience4JHandle.setTimeoutDuration(Math.max(resilience4JHandle.getTimeoutDuration(), Constants.TIMEOUT_DURATION)); + resilience4JHandle.setFallbackUri(!"0".equals(resilience4JHandle.getFallbackUri()) ? resilience4JHandle.getFallbackUri() : ""); + resilience4JHandle.setSlidingWindowSize(Math.max(resilience4JHandle.getSlidingWindowSize(), Constants.SLIDING_WINDOW_SIZE)); + resilience4JHandle.setSlidingWindowType(Math.max(resilience4JHandle.getSlidingWindowType(), Constants.SLIDING_WINDOW_TYPE)); + resilience4JHandle.setMinimumNumberOfCalls(Math.max(resilience4JHandle.getMinimumNumberOfCalls(), Constants.MINIMUM_NUMBER_OF_CALLS)); + resilience4JHandle.setWaitIntervalFunctionInOpenState(Math.max(resilience4JHandle.getWaitIntervalFunctionInOpenState(), Constants.WAIT_INTERVAL_FUNCTION_IN_OPEN_STATE)); + resilience4JHandle.setPermittedNumberOfCallsInHalfOpenState(Math.max(resilience4JHandle.getPermittedNumberOfCallsInHalfOpenState(), Constants.PERMITTED_NUMBER_OF_CALLS_IN_HALF_OPEN_STATE)); + resilience4JHandle.setFailureRateThreshold(Math.max(resilience4JHandle.getFailureRateThreshold(), Constants.FAILURE_RATE_THRESHOLD)); + return resilience4JHandle; + } +``` + +- 使用 SuperBenchmarker 工具,4 个线程,执行 10s + +```java +C:\Users\v-yanb07>sb -u http://localhost:9195/http/test/findByUserId?userId=1 -c 4 -N 10 +Starting at 2021-03-14 15:46:28 +[Press C to stop the test] +23 (RPS: 1) +---------------Finished!---------------- +Finished at 2021-03-14 15:46:51 (took 00:00:23.0477097) +24 (RPS: 1) Status 200: 25 + +RPS: 2.2 (requests/second) +Max: 2020ms +Min: 472ms +Avg: 1677ms + + 50% below 1994ms + 60% below 1997ms + 70% below 1999ms + 80% below 1999ms + 90% below 2001ms + 95% below 2019ms + 98% below 2020ms + 99% below 2020ms +99.9% below 2020ms +``` + +- 输出日志 + +```java +2021-03-14 12:16:35.252 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 +2021-03-14 12:16:36.249 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : 限流测试 +2021-03-14 12:16:36.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 +2021-03-14 12:16:37.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 +2021-03-14 12:16:37.250 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : 限流测试 +2021-03-14 12:16:38.250 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 +2021-03-14 12:16:38.250 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : 限流测试 +2021-03-14 12:16:39.252 INFO 379336 --- [ctor-http-nio-7] o.d.s.e.h.controller.HttpTestController : 限流测试 +2021-03-14 12:16:39.252 INFO 379336 --- [ctor-http-nio-4] o.d.s.e.h.controller.HttpTestController : 限流测试 +``` + +控制台日志每秒输出两条,由此验证限流生效 + +### 熔断 + +- 从配置信息我们知道熔断器默认是关闭,我们需要开打 +- soul-examples-http 调用接口处添加休眠时间 + +```java + @GetMapping("/findByUserId") + public UserDTO findByUserId(@RequestParam("userId") final String userId) throws Exception{ + UserDTO userDTO = new UserDTO(); + userDTO.setUserId(userId); + userDTO.setUserName("hello world"); + log.info("限流测试"); + + int i = RandomUtils.nextInt(1,3); + if(i %2==0){ + //throw new Exception("异常抛出"); + Thread.currentThread().sleep(2000); + } + return userDTO; + } +``` + +- Resilience4JHandle#checkData 手动设置超时时间为 1s + +```java + resilience4JHandle.setTimeoutDuration(1000); +``` + +- pos 接口调用 + > http://localhost:9195/http/test/findByUserId?userId=1 + +多次请求时,有的请求返回正常数据,有的请求返回如下数据,表示超时熔断生效 + +```java +{ + "code": 500, + "message": "Internal Server Error", + "data": "404 NOT_FOUND" +} +``` + +## Resilience4J 插件源码解读 + +soul 网关 Resilience4j 插件源码大量使用了[响应式编程](https://developer.ibm.com/zh/languages/java/articles/j-cn-with-reactor-response-encode/)方式,首先需要对响应式编程了解 + +- Resilience4J 插件目录结构 + +``` +└─resilience4j + │ Resilience4JPlugin.java //插件处理,核心类 + │ + ├─build + │ Resilience4JBuilder.java //构建Resilience4JConf对象 + │ + ├─conf + │ Resilience4JConf.java + │ + ├─executor + │ CombinedExecutor.java //限流和熔断执行器 + │ Executor.java + │ RateLimiterExecutor.java //限流执行器 + │ + ├─factory + │ Resilience4JRegistryFactory.java //限流和熔断对象构建 + │ + └─handler + Resilience4JHandler.java +``` + +- Resilience4JPlugn#doExecute + Resilience4JPlugn 其他 soul 中插件一样继承 AbstractSoulPlugin,只要开启了,通过链式机制执行,都会走到核心方法 doExecute + +```java + @Override + protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + //获取配置信息对象 + Resilience4JHandle resilience4JHandle = GsonUtils.getGson().fromJson(rule.getHandle(), Resilience4JHandle.class); + //校验配置信息,如果小于默认值,则赋值默认值 + resilience4JHandle = resilience4JHandle.checkData(resilience4JHandle); + //circuitEnable配置:1 开启熔断组件 ,否则走限流组件 + if (resilience4JHandle.getCircuitEnable() == 1) { + return combined(exchange, chain, rule); + } + + return rateLimiter(exchange, chain, rule); + } +``` + +- 限流 Resilience4JPlugin#rateLimiter + +```java + private Mono rateLimiter(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) { + return ratelimiterExecutor.run( + // chain.execute(exchange) 后续插件执行 + chain.execute(exchange), fallback(ratelimiterExecutor, exchange, null), Resilience4JBuilder.build(rule)) + .onErrorResume(throwable -> ratelimiterExecutor.withoutFallback(exchange, throwable)) + + + //ratelimiterExecutor.run调用 + @Override +public Mono run(final Mono toRun, final Function> fallback, final Resilience4JConf conf) { + //限流器组件 + RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(conf.getId(), conf.getRateLimiterConfig()); + //限流执行 + Mono to = toRun.transformDeferred(RateLimiterOperator.of(rateLimiter)); + if (fallback != null) { + //回调的执行 + return to.onErrorResume(fallback); + } + return to; +} + + + // to.onErrorResume(fallback); + default Mono fallback(ServerWebExchange exchange, String uri, Throwable t) { + if (StringUtils.isBlank(uri)) { + return withoutFallback(exchange, t); + } + DispatcherHandler dispatcherHandler = SpringBeanUtils.getInstance().getBean(DispatcherHandler.class); + ServerHttpRequest request = exchange.getRequest().mutate().uri(Objects.requireNonNull(UriUtils.createUri(uri))).build(); + ServerWebExchange mutated = exchange.mutate().request(request).build(); + //回调的执行地方 + return dispatcherHandler.handle(mutated); +} +``` + +- 熔断 Resilience4JPlugin#combined + +```java + private Mono combined(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) { + Resilience4JConf conf = Resilience4JBuilder.build(rule); + return combinedExecutor.run( + chain.execute(exchange).doOnSuccess(v -> { + HttpStatus status = exchange.getResponse().getStatusCode(); + if (status == null || !status.is2xxSuccessful()) { + exchange.getResponse().setStatusCode(null); + throw new CircuitBreakerStatusCodeException(status == null ? HttpStatus.INTERNAL_SERVER_ERROR : status); + } + }), fallback(combinedExecutor, exchange, conf.getFallBackUri()), conf); + } + + + //combinedExecutor#run执行的内容 + public Mono run(final Mono run, final Function> fallback, final Resilience4JConf resilience4JConf) { + RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(resilience4JConf.getId(), resilience4JConf.getRateLimiterConfig()); + CircuitBreaker circuitBreaker = Resilience4JRegistryFactory.circuitBreaker(resilience4JConf.getId(), resilience4JConf.getCircuitBreakerConfig()); + //断路器的操作 + Mono to = run.transformDeferred(CircuitBreakerOperator.of(circuitBreaker)) + //限流操作 + .transformDeferred(RateLimiterOperator.of(rateLimiter)) + //设置超时时间 + .timeout(resilience4JConf.getTimeLimiterConfig().getTimeoutDuration()) + //如果超时了抛出超时异常 + .doOnError(TimeoutException.class, t -> circuitBreaker.onError( + resilience4JConf.getTimeLimiterConfig().getTimeoutDuration().toMillis(), + TimeUnit.MILLISECONDS, + t)); + if (fallback != null) { + to = to.onErrorResume(fallback); + } + return to; + } +``` + +## 总结 + +- soul 网关提供限流和熔断,熔断默认是关闭的 +- 参数值小于默认值,会直接使用默认值 diff --git a/src/zh/blog/soul_source_learning_22_apache_dubbo.md b/src/zh/blog/soul_source_learning_22_apache_dubbo.md index 04683857c7..7b0b4898e4 100644 --- a/src/zh/blog/soul_source_learning_22_apache_dubbo.md +++ b/src/zh/blog/soul_source_learning_22_apache_dubbo.md @@ -1,502 +1,502 @@ ---- -title: Soul网关学习Apache Dubbo插件原理解析 -author: nuo-promise -date: 2021-03-23 -tag: - - Soul -cover: /assets/img/blog8/08.jpg -head: - - - meta - - name: 博客 ---- - -## 目标 - -- Apache Dubbo 插件介绍 - - 元数据介绍 -- Apache Dubbo 插件配置 - - Bootstrap pom 配置 - - soul-admin 配置 - - dubbo 服务 pom 配置 -- Apache Dubbo 泛化调用介绍 - - 通过 API 方式使用泛化调用 - - 通过 spring 使用泛化调用 - - 泛化调用实现流程 -- Soul Dubbo 插件调用解析 - - ApachDubboPlugin 泛化调用准备 - - ApacheDubboProxySerivce - - DubboResponsePlugin - - WebFluxResultUtils 返回结果 -- Dubbo 泛化调用介绍 -- 总结 -- 参考 - -### Apache Dubbo 插件介绍 - -Apache Dubbo 是一款高性能、轻量级的开源 Java 服务框架,主要提供了六大核心能力,面向接口代理的高性能 RPC 调用,智能容错和负载均衡,服务自动注册与发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。 -网关中 Dubbo 插件主要是将 `Http协议`  转换成 `Dubbo协议`  ,也是网关实现 Dubbo 泛化调用的关键。而 Dubbo 插件需要配合 `元数据`  才能实现 Dubbo 调用。 - -#### 元数据介绍 - -元数据作用就是在进行协议转换时候要获取真实的请求 `path` 、`methodName` 、 `parameterTypes`  为泛化调用做好准备 - -![image.png](/assets/img/blog8/01.png) - -- 在数据库中,我们有一张表单独存储 Dubbo 元信息,通过数据同步方案,会把这张表的数据同步到网关的 JVM 内存中 -- 表结构如下 - -```sql -CREATE TABLE IF NOT EXISTS `meta_data` ( -`id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'id', -`app_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名称', -`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '路径,不能重复', -`path_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '路径描述', -`rpc_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'rpc类型', -`service_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '服务名称', -`method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '方法名称', -`parameter_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '参数类型 多个参数类型 逗号隔开', -`rpc_ext` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'rpc的扩展信息,json格式', -`date_created` datetime(0) NOT NULL COMMENT '创建时间', -`date_updated` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间', -`enabled` tinyint(4) NOT NULL DEFAULT 0 COMMENT '启用状态', -PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; -``` - -- `path`  字段主要是在请求网关的时候,会根据你的 `path`  字段来匹配到一条数据,然后进行后续的处理流程 -- `rpc_ext`  字段如果代理的接口是 `Dubbo` 类型的服务接口,同时设置了 `group` `version`  字段时候,那么信息就会存储到 `rpc_ext`  中 -- 每一个 `Dubbo`  接口方法会应对一条元数据,对比 SpringCloud、http 分别是只存储一条/contextPath/\*\* 和不存储 - -### Apache Dubbo 插件配置 - -#### soul-bootstrap pom 配置 - -```java - - org.dromara - soul-spring-boot-starter-plugin-apache-dubbo - ${project.version} - - - org.apache.dubbo - dubbo - 2.7.5 - - - org.apache.curator - curator-client - ${curator.version} - - - org.apache.curator - curator-framework - ${curator.version} - - - org.apache.curator - curator-recipes - ${curator.version} - -``` - -#### soul-admin 配置 - -![image.png](/assets/img/blog8/02.png) - -> 登录 soul-admin 后台在插件管理页面打开 Dubbo 配置选项的开关,和填写注册中心的连接地址 - -#### dubbo 服务 pom 配置 - -```java - - org.dromara - soul-spring-boot-starter-client-apache-dubbo - ${soul.version} - -``` - -```java -@SoulDubboClient(path = "/insert", desc = "Insert a row of data") -public DubboTest insert(final DubboTest dubboTest) { - dubboTest.setName("hello world Soul Apache Dubbo: " + dubboTest.getName()); - return dubboTest; -} -``` - -> 被代理的服务使用提供的 `soul-spring-boot-starter-client-apache-dubbo`  客户端依赖,同时使用`@SoulDubboClient`  注解,在启动时候将接口的名称,参数类型,参数内容注册到 `soul-admin`  端,然后 `admin`  端将数据同步到 `bootstrap`  端。 - -### Apache Dubbo 泛化调用介绍 - -泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 `Map`  表示, 通常用于框架集成,可通过 GenericSerivce 调用所有服务实现。 - -#### 通过 API 方式使用泛化调用(网关目前使用方式) - -```java -ReferenceConfig reference = new ReferenceConfig<>(); -reference.setGeneric(true); -reference.setApplication(applicationConfig); -reference.setRegistry(registryConfig); -reference.setInterface(metaData.getServiceName()); -reference.setProtocol("dubbo"); -``` - -> 网关通过 API 方式声明注册使用泛化调用 - -#### 通过 Spring 使用泛化调用 - -```java - -``` - -#### 泛化调用实现流程 - -```java -+-------------------------------------------+ +-------------------------------------------+ -| consumer 端 | | provider 端 | -| | | | -| | | | -| | | | -| | | | -| +------------------+ | | +--------------+ | -| |GenericImplFilter | | Invocation | |GenericFilter | | -| +----> | +-------------------------> | | | -| | +------------------+ | | +--------------+ | -| +-----------+ | | | +-----------+ | -| | | | | | | | | -| |Client | | | +--> | Service | | -| | | | | | | | -| +-----------+ | | +-------+---+ | -| | | | | -| ^ +------------------+ | | +--------------+ | | -| | |GenericImplFilter | | | |GenericFilter | <----------+ | -| +-------------+ | <-------------------------+ | | -| +------------------+ | | +--------------+ | -| | | | -| | | | -| | | | -| | | | -+-------------------------------------------+ +-------------------------------------------+ -``` - -> `GenericService`  这个接口和 Java 的反射调用非常像,只需提供调用的方法名称,参数的类型以及参数的值就可以直接调用对应方法了。 -> -> - GenericFilter : 负责 provider 端参数的转换 -> - 调用时,将 hashMap 结构的参数转换成对应 Pojo -> - 返回结果是,将 Pojo 转换成 hashMap -> -> ![image.png](/assets/img/blog8/03.png) -> -> - GenericImplFilter : 负责 consumer 端参数的转换,将 Pojo 转换成 hashMap 接口 -> -> ![image.png](/assets/img/blog8/04.png) - -```java -/** - * Generic service interface - * - * @export - */ -public interface GenericService { - - /** - * Generic invocation - * - * @param method 方法名,如:findPerson,如果有重载方法,需带上参数列表,如:findPerson(java.lang.String) - * @param parameterTypes 参数类型 - * @param args 参数列表 - * @return invocation 返回值 - * @throws GenericException 方法抛出的异常 - */ - Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; - - default CompletableFuture $invokeAsync(String method, String[] parameterTypes, Object[] args) throws GenericException { - Object object = $invoke(method, parameterTypes, args); - if (object instanceof CompletableFuture) { - return (CompletableFuture) object; - } - return CompletableFuture.completedFuture(object); - } - -} -``` - -### Soul Dubbo 插件调用解析 - -当业务请求发起时候,首先进入 `SoulWebHandler` (至于为什么成为请求入口自行查询,本文不作解释)  类的 `Handle`  方法,下面就带了 `plugins`  从 `DefaultSoulPluginChain`  类开始进入插件链调用。 - -```java -@Override - public Mono handle(@NonNull final ServerWebExchange exchange) { - return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler); - } -``` - -```java -@Override -public Mono execute(final ServerWebExchange exchange) { - // 响应式编程 - return Mono.defer(() -> { - // 判断当前index 是否 < 插件数量 - if (this.index < plugins.size()) { - // 依次从plugins 中获取一种插件进行调用 - SoulPlugin plugin = plugins.get(this.index++); - // 判断此插件是否未打开 - Boolean skip = plugin.skip(exchange); - if (skip) { - return this.execute(exchange); - } - return plugin.execute(exchange, this); - } - return Mono.empty(); - }); -} -``` - -> 本章只关注 Apache Dubbo 所以我们重点放到 Dubbo 插件的调用。 -> ![image.png](/assets/img/blog8/05.png) -> 经过 Debug 网关程序我们知道其实是按照上面的顺序一个一个的进行判断调用。下面我们关注 `ApacheDubboPlugin` - -#### ApachDubboPlugin 泛化调用准备 - -```java -@Override - protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { - // 获取 dubbo_params 数据 - String body = exchange.getAttribute(Constants.DUBBO_PARAMS); - // 获取 exchange context的属性值 - SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); - assert soulContext != null; - // 获取 exchange metaData 属性值 - MetaData metaData = exchange.getAttribute(Constants.META_DATA); - // 判断metaData是否有误,如果有误直接返回 metaData 有误的返回信息 - if (!checkMetaData(metaData)) { - assert metaData != null; - log.error(" path is :{}, meta data have error.... {}", soulContext.getPath(), metaData.toString()); - exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); - Object error = SoulResultWrap.error(SoulResultEnum.META_DATA_ERROR.getCode(), SoulResultEnum.META_DATA_ERROR.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // 判断 metaData的parameterTypes 和 body 是否为空,如果有误则返回Body错误信息 - if (StringUtils.isNoneBlank(metaData.getParameterTypes()) && StringUtils.isBlank(body)) { - exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); - Object error = SoulResultWrap.error(SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getCode(), SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - // 带着exchange、body、metaData 进行 Dubbo GenericsService的异步调用 - final Mono result = dubboProxyService.genericInvoker(body, metaData, exchange); - return result.then(chain.execute(exchange)); - } -``` - -> 首先对泛化调用所需要的参数进行检查 - -#### ApacheDubboProxyService - -```java -public Mono genericInvoker(final String body, final MetaData metaData, final ServerWebExchange exchange) throws SoulException { - // issue(https://github.com/dromara/soul/issues/471), add dubbo tag route - String dubboTagRouteFromHttpHeaders = exchange.getRequest().getHeaders().getFirst(Constants.DUBBO_TAG_ROUTE); - if (StringUtils.isNotBlank(dubboTagRouteFromHttpHeaders)) { - RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, dubboTagRouteFromHttpHeaders); - } - // 根据metaData路径获取ferference - ReferenceConfig reference = ApplicationConfigCache.getInstance().get(metaData.getPath()); - if (Objects.isNull(reference) || StringUtils.isEmpty(reference.getInterface())) { - ApplicationConfigCache.getInstance().invalidate(metaData.getPath()); - reference = ApplicationConfigCache.getInstance().initRef(metaData); - } - // 根据ferference 获取泛化调用的实例 GenericService - GenericService genericService = reference.get(); - Pair pair; - if (ParamCheckUtils.dubboBodyIsEmpty(body)) { - pair = new ImmutablePair<>(new String[]{}, new Object[]{}); - } else { - // 根据body 和 parameterTypes 组织Dubbo 泛化调用的参数类型和参数值 - pair = dubboParamResolveService.buildParameter(body, metaData.getParameterTypes()); - } - // 下面使用GenericSerice 默认方法$invokeAsync进行异步调用 - CompletableFuture future = genericService.$invokeAsync(metaData.getMethodName(), pair.getLeft(), pair.getRight()); - return Mono.fromFuture(future.thenApply(ret -> { - if (Objects.isNull(ret)) { - ret = Constants.DUBBO_RPC_RESULT_EMPTY; - } - // 等调用成功之后 将结果和类型复制到exchagne 对应的属性上 - exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, ret); - exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); - return ret; - })).onErrorMap(exception -> exception instanceof GenericException ? new SoulException(((GenericException) exception).getExceptionMessage()) : new SoulException(exception)); -} -``` - -#### DubboResponsePlugin - -```java -@Override -public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { - return chain.execute(exchange).then(Mono.defer(() -> { - final Object result = exchange.getAttribute(Constants.DUBBO_RPC_RESULT); - if (Objects.isNull(result)) { - Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); - return WebFluxResultUtils.result(exchange, error); - } - Object success = SoulResultWrap.success(SoulResultEnum.SUCCESS.getCode(), SoulResultEnum.SUCCESS.getMsg(), JsonUtils.removeClass(result)); - return WebFluxResultUtils.result(exchange, success); - })); -} -``` - -#### ![image.png](/assets/img/blog8/06.png) - -#### WebFluxResultUtils 返回结果 - -![image.png](/assets/img/blog8/07.png) - -### Dubbo 泛化调用介绍 - -Dubbo 泛化调用主要就分为两块分别是消费端如何使用 `GenericImplFilter`  拦截泛化调用、服务提供端如何使用 `GenericFilter`  拦截请求后把泛化参数序列化然后请求给具体服务。 - -#### 服务消费端 org.apache.dubbo.rpc.filter.GenericImplFilter 是如何拦截泛化调用 - -```java -@Activate(group = CommonConstants.CONSUMER, value = GENERIC_KEY, order = 20000) -public class GenericImplFilter implements Filter, Filter.Listener { -@Override - public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { - // ... 省略非核心代码 - // 判断是否为泛化调用 - if (isMakingGenericCall(generic, invocation)) { - // 获取泛化参数 - Object[] args = (Object[]) invocation.getArguments()[2]; - // 如果泛化为nativeJava - if (ProtocolUtils.isJavaGenericSerialization(generic)) { - for (Object arg : args) { - if (!(byte[].class == arg.getClass())) { - error(generic, byte[].class.getName(), arg.getClass().getName()); - } - } - // 如果泛化方式为bean - } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { - for (Object arg : args) { - if (!(arg instanceof JavaBeanDescriptor)) { - error(generic, JavaBeanDescriptor.class.getName(), arg.getClass().getName()); - } - } - } - - // 设置attachment ,以便与服务端调用 - invocation.setAttachment( - GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY)); - } - // 发起远程调用 - return invoker.invoke(invocation); - } - private boolean isMakingGenericCall(String generic, Invocation invocation) { - return (invocation.getMethodName().equals($INVOKE) || invocation.getMethodName().equals($INVOKE_ASYNC)) - && invocation.getArguments() != null - && invocation.getArguments().length == 3 - && ProtocolUtils.isGeneric(generic); - } -} -``` - -> GenericImplFilter 实现接口 Filter(关于 Dubbo 中的 Filter,不做介绍)然后执行 Invoke 方法,invoke 方法主要做如下事情: -> -> - 参数校验,检查这个调用是否是泛化调用 -> - 获取泛化参数 -> - 判断泛化调用方式:遍历每个参数,然后依次判断参数的泛化方式是 nativejava 还是 bean 方式 -> - 发起远程调用 - -#### 服务提供端通过 GenericFilter 拦截泛化请求 - -```java -@Activate(group = CommonConstants.PROVIDER, order = -20000) -public class GenericFilter implements Filter, Filter.Listener { - @Override - public Result invoke(Invoker invoker, Invocation inv) throws RpcException { - // 参数校验 - if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC)) - && inv.getArguments() != null - && inv.getArguments().length == 3 - && !GenericService.class.isAssignableFrom(invoker.getInterface())) { - // 获取参数名称、参数类型、参数值 - String name = ((String) inv.getArguments()[0]).trim(); - String[] types = (String[]) inv.getArguments()[1]; - Object[] args = (Object[]) inv.getArguments()[2]; - try { - // 使用反射获取调用的方法 - Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types); - Class[] params = method.getParameterTypes(); - if (args == null) { - args = new Object[params.length]; - } - // 获取泛化引用使用的泛化类型,true or bean or nativejava - String generic = inv.getAttachment(GENERIC_KEY); - if (StringUtils.isBlank(generic)) { - generic = RpcContext.getContext().getAttachment(GENERIC_KEY); - } - // 如果generic=true 则使用true方式对入参进行反序列化 - if (StringUtils.isEmpty(generic) - || ProtocolUtils.isDefaultGenericSerialization(generic) - || ProtocolUtils.isGenericReturnRawResult(generic)) { - args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); - // 如果 generic=nativejava,则使用nativejava方式对入参进行反序列化 - } else if (ProtocolUtils.isJavaGenericSerialization(generic)) { - for (int i = 0; i < args.length; i++) { - if (byte[].class == args[i].getClass()) { - try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) { - args[i] = ExtensionLoader.getExtensionLoader(Serialization.class) - .getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA) - .deserialize(null, is).readObject(); - } catch (Exception e) { - throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e); - } - } else { - throw new RpcException(...); - } - } - // 如果 generic=bean 则使用bean方式对入参进行反序列化 - } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { - for (int i = 0; i < args.length; i++) { - if (args[i] instanceof JavaBeanDescriptor) { - args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]); - } else { - throw new RpcException(...); - } - } - } ... - // 将本次请求传递到FilterChain的下一个Filter中,并返回结果result - RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), args, inv.getAttachments(), inv.getAttributes()); - rpcInvocation.setInvoker(inv.getInvoker()); - rpcInvocation.setTargetServiceUniqueName(inv.getTargetServiceUniqueName()); - - return invoker.invoke(rpcInvocation); - } catch (NoSuchMethodException e) { - throw new RpcException(e.getMessage(), e); - } catch (ClassNotFoundException e) { - throw new RpcException(e.getMessage(), e); - } - } - // 如果不是泛化调用,直接把请求传给FilterChain的下一个Filter - return invoker.invoke(inv); - } -} -``` - -> 以上就是 Dubbo 服务提供端如何拦截泛化请求,并进行处理的大体流程: -> -> - 参数校验,判断此次请求是不是泛化调用 -> - 获取参数名称、参数类型、参数值 -> - 使用反射获取调用的方法,和使用的泛化方式 `true`  or `nativejava`  or `bean` -> - 根据泛化方式,反序列化泛化参数 -> - 将本次请求,包括调用的方法,参数和上下文信息传递给 FilterChain 的下一个 Filter 中,并返回 Result 结果 -> - 根据泛化方式,反序列化 Result 结果返回给服务消费端 - -### 总结 - -以上从如何配置 Dubbo 插件到整个调用流程的分析,然后分别介绍服务消费端与服务提供端如何拦截泛化调用流程对参数进行序列化细节,希望对你有所帮助 - -### 参考 - -[https://my.oschina.net/u/4564034/blog/4409382](https://my.oschina.net/u/4564034/blog/4409382) - -[https://qsli.github.io/2018/05/02/dubbo-generic-invoke/](https://qsli.github.io/2018/05/02/dubbo-generic-invoke/) +--- +title: Soul网关学习Apache Dubbo插件原理解析 +author: nuo-promise +date: 2021-03-23 +tag: + - Soul +cover: /assets/img/blog8/08.jpg +head: + - - meta + - name: 博客 +--- + +## 目标 + +- Apache Dubbo 插件介绍 + - 元数据介绍 +- Apache Dubbo 插件配置 + - Bootstrap pom 配置 + - soul-admin 配置 + - dubbo 服务 pom 配置 +- Apache Dubbo 泛化调用介绍 + - 通过 API 方式使用泛化调用 + - 通过 spring 使用泛化调用 + - 泛化调用实现流程 +- Soul Dubbo 插件调用解析 + - ApachDubboPlugin 泛化调用准备 + - ApacheDubboProxySerivce + - DubboResponsePlugin + - WebFluxResultUtils 返回结果 +- Dubbo 泛化调用介绍 +- 总结 +- 参考 + +### Apache Dubbo 插件介绍 + +Apache Dubbo 是一款高性能、轻量级的开源 Java 服务框架,主要提供了六大核心能力,面向接口代理的高性能 RPC 调用,智能容错和负载均衡,服务自动注册与发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。 +网关中 Dubbo 插件主要是将 `Http协议`  转换成 `Dubbo协议`  ,也是网关实现 Dubbo 泛化调用的关键。而 Dubbo 插件需要配合 `元数据`  才能实现 Dubbo 调用。 + +#### 元数据介绍 + +元数据作用就是在进行协议转换时候要获取真实的请求 `path` 、`methodName` 、 `parameterTypes`  为泛化调用做好准备 + +![image.png](/assets/img/blog8/01.png) + +- 在数据库中,我们有一张表单独存储 Dubbo 元信息,通过数据同步方案,会把这张表的数据同步到网关的 JVM 内存中 +- 表结构如下 + +```sql +CREATE TABLE IF NOT EXISTS `meta_data` ( +`id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'id', +`app_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名称', +`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '路径,不能重复', +`path_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '路径描述', +`rpc_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'rpc类型', +`service_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '服务名称', +`method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '方法名称', +`parameter_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '参数类型 多个参数类型 逗号隔开', +`rpc_ext` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'rpc的扩展信息,json格式', +`date_created` datetime(0) NOT NULL COMMENT '创建时间', +`date_updated` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间', +`enabled` tinyint(4) NOT NULL DEFAULT 0 COMMENT '启用状态', +PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; +``` + +- `path`  字段主要是在请求网关的时候,会根据你的 `path`  字段来匹配到一条数据,然后进行后续的处理流程 +- `rpc_ext`  字段如果代理的接口是 `Dubbo` 类型的服务接口,同时设置了 `group` `version`  字段时候,那么信息就会存储到 `rpc_ext`  中 +- 每一个 `Dubbo`  接口方法会应对一条元数据,对比 SpringCloud、http 分别是只存储一条/contextPath/\*\* 和不存储 + +### Apache Dubbo 插件配置 + +#### soul-bootstrap pom 配置 + +```java + + org.dromara + soul-spring-boot-starter-plugin-apache-dubbo + ${project.version} + + + org.apache.dubbo + dubbo + 2.7.5 + + + org.apache.curator + curator-client + ${curator.version} + + + org.apache.curator + curator-framework + ${curator.version} + + + org.apache.curator + curator-recipes + ${curator.version} + +``` + +#### soul-admin 配置 + +![image.png](/assets/img/blog8/02.png) + +> 登录 soul-admin 后台在插件管理页面打开 Dubbo 配置选项的开关,和填写注册中心的连接地址 + +#### dubbo 服务 pom 配置 + +```java + + org.dromara + soul-spring-boot-starter-client-apache-dubbo + ${soul.version} + +``` + +```java +@SoulDubboClient(path = "/insert", desc = "Insert a row of data") +public DubboTest insert(final DubboTest dubboTest) { + dubboTest.setName("hello world Soul Apache Dubbo: " + dubboTest.getName()); + return dubboTest; +} +``` + +> 被代理的服务使用提供的 `soul-spring-boot-starter-client-apache-dubbo`  客户端依赖,同时使用`@SoulDubboClient`  注解,在启动时候将接口的名称,参数类型,参数内容注册到 `soul-admin`  端,然后 `admin`  端将数据同步到 `bootstrap`  端。 + +### Apache Dubbo 泛化调用介绍 + +泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 `Map`  表示, 通常用于框架集成,可通过 GenericSerivce 调用所有服务实现。 + +#### 通过 API 方式使用泛化调用(网关目前使用方式) + +```java +ReferenceConfig reference = new ReferenceConfig<>(); +reference.setGeneric(true); +reference.setApplication(applicationConfig); +reference.setRegistry(registryConfig); +reference.setInterface(metaData.getServiceName()); +reference.setProtocol("dubbo"); +``` + +> 网关通过 API 方式声明注册使用泛化调用 + +#### 通过 Spring 使用泛化调用 + +```java + +``` + +#### 泛化调用实现流程 + +```java ++-------------------------------------------+ +-------------------------------------------+ +| consumer 端 | | provider 端 | +| | | | +| | | | +| | | | +| | | | +| +------------------+ | | +--------------+ | +| |GenericImplFilter | | Invocation | |GenericFilter | | +| +----> | +-------------------------> | | | +| | +------------------+ | | +--------------+ | +| +-----------+ | | | +-----------+ | +| | | | | | | | | +| |Client | | | +--> | Service | | +| | | | | | | | +| +-----------+ | | +-------+---+ | +| | | | | +| ^ +------------------+ | | +--------------+ | | +| | |GenericImplFilter | | | |GenericFilter | <----------+ | +| +-------------+ | <-------------------------+ | | +| +------------------+ | | +--------------+ | +| | | | +| | | | +| | | | +| | | | ++-------------------------------------------+ +-------------------------------------------+ +``` + +> `GenericService`  这个接口和 Java 的反射调用非常像,只需提供调用的方法名称,参数的类型以及参数的值就可以直接调用对应方法了。 +> +> - GenericFilter : 负责 provider 端参数的转换 +> - 调用时,将 hashMap 结构的参数转换成对应 Pojo +> - 返回结果是,将 Pojo 转换成 hashMap +> +> ![image.png](/assets/img/blog8/03.png) +> +> - GenericImplFilter : 负责 consumer 端参数的转换,将 Pojo 转换成 hashMap 接口 +> +> ![image.png](/assets/img/blog8/04.png) + +```java +/** + * Generic service interface + * + * @export + */ +public interface GenericService { + + /** + * Generic invocation + * + * @param method 方法名,如:findPerson,如果有重载方法,需带上参数列表,如:findPerson(java.lang.String) + * @param parameterTypes 参数类型 + * @param args 参数列表 + * @return invocation 返回值 + * @throws GenericException 方法抛出的异常 + */ + Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; + + default CompletableFuture $invokeAsync(String method, String[] parameterTypes, Object[] args) throws GenericException { + Object object = $invoke(method, parameterTypes, args); + if (object instanceof CompletableFuture) { + return (CompletableFuture) object; + } + return CompletableFuture.completedFuture(object); + } + +} +``` + +### Soul Dubbo 插件调用解析 + +当业务请求发起时候,首先进入 `SoulWebHandler` (至于为什么成为请求入口自行查询,本文不作解释)  类的 `Handle`  方法,下面就带了 `plugins`  从 `DefaultSoulPluginChain`  类开始进入插件链调用。 + +```java +@Override + public Mono handle(@NonNull final ServerWebExchange exchange) { + return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler); + } +``` + +```java +@Override +public Mono execute(final ServerWebExchange exchange) { + // 响应式编程 + return Mono.defer(() -> { + // 判断当前index 是否 < 插件数量 + if (this.index < plugins.size()) { + // 依次从plugins 中获取一种插件进行调用 + SoulPlugin plugin = plugins.get(this.index++); + // 判断此插件是否未打开 + Boolean skip = plugin.skip(exchange); + if (skip) { + return this.execute(exchange); + } + return plugin.execute(exchange, this); + } + return Mono.empty(); + }); +} +``` + +> 本章只关注 Apache Dubbo 所以我们重点放到 Dubbo 插件的调用。 +> ![image.png](/assets/img/blog8/05.png) +> 经过 Debug 网关程序我们知道其实是按照上面的顺序一个一个的进行判断调用。下面我们关注 `ApacheDubboPlugin` + +#### ApachDubboPlugin 泛化调用准备 + +```java +@Override + protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { + // 获取 dubbo_params 数据 + String body = exchange.getAttribute(Constants.DUBBO_PARAMS); + // 获取 exchange context的属性值 + SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); + assert soulContext != null; + // 获取 exchange metaData 属性值 + MetaData metaData = exchange.getAttribute(Constants.META_DATA); + // 判断metaData是否有误,如果有误直接返回 metaData 有误的返回信息 + if (!checkMetaData(metaData)) { + assert metaData != null; + log.error(" path is :{}, meta data have error.... {}", soulContext.getPath(), metaData.toString()); + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + Object error = SoulResultWrap.error(SoulResultEnum.META_DATA_ERROR.getCode(), SoulResultEnum.META_DATA_ERROR.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // 判断 metaData的parameterTypes 和 body 是否为空,如果有误则返回Body错误信息 + if (StringUtils.isNoneBlank(metaData.getParameterTypes()) && StringUtils.isBlank(body)) { + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + Object error = SoulResultWrap.error(SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getCode(), SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + // 带着exchange、body、metaData 进行 Dubbo GenericsService的异步调用 + final Mono result = dubboProxyService.genericInvoker(body, metaData, exchange); + return result.then(chain.execute(exchange)); + } +``` + +> 首先对泛化调用所需要的参数进行检查 + +#### ApacheDubboProxyService + +```java +public Mono genericInvoker(final String body, final MetaData metaData, final ServerWebExchange exchange) throws SoulException { + // issue(https://github.com/dromara/soul/issues/471), add dubbo tag route + String dubboTagRouteFromHttpHeaders = exchange.getRequest().getHeaders().getFirst(Constants.DUBBO_TAG_ROUTE); + if (StringUtils.isNotBlank(dubboTagRouteFromHttpHeaders)) { + RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, dubboTagRouteFromHttpHeaders); + } + // 根据metaData路径获取ferference + ReferenceConfig reference = ApplicationConfigCache.getInstance().get(metaData.getPath()); + if (Objects.isNull(reference) || StringUtils.isEmpty(reference.getInterface())) { + ApplicationConfigCache.getInstance().invalidate(metaData.getPath()); + reference = ApplicationConfigCache.getInstance().initRef(metaData); + } + // 根据ferference 获取泛化调用的实例 GenericService + GenericService genericService = reference.get(); + Pair pair; + if (ParamCheckUtils.dubboBodyIsEmpty(body)) { + pair = new ImmutablePair<>(new String[]{}, new Object[]{}); + } else { + // 根据body 和 parameterTypes 组织Dubbo 泛化调用的参数类型和参数值 + pair = dubboParamResolveService.buildParameter(body, metaData.getParameterTypes()); + } + // 下面使用GenericSerice 默认方法$invokeAsync进行异步调用 + CompletableFuture future = genericService.$invokeAsync(metaData.getMethodName(), pair.getLeft(), pair.getRight()); + return Mono.fromFuture(future.thenApply(ret -> { + if (Objects.isNull(ret)) { + ret = Constants.DUBBO_RPC_RESULT_EMPTY; + } + // 等调用成功之后 将结果和类型复制到exchagne 对应的属性上 + exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, ret); + exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); + return ret; + })).onErrorMap(exception -> exception instanceof GenericException ? new SoulException(((GenericException) exception).getExceptionMessage()) : new SoulException(exception)); +} +``` + +#### DubboResponsePlugin + +```java +@Override +public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) { + return chain.execute(exchange).then(Mono.defer(() -> { + final Object result = exchange.getAttribute(Constants.DUBBO_RPC_RESULT); + if (Objects.isNull(result)) { + Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); + return WebFluxResultUtils.result(exchange, error); + } + Object success = SoulResultWrap.success(SoulResultEnum.SUCCESS.getCode(), SoulResultEnum.SUCCESS.getMsg(), JsonUtils.removeClass(result)); + return WebFluxResultUtils.result(exchange, success); + })); +} +``` + +#### ![image.png](/assets/img/blog8/06.png) + +#### WebFluxResultUtils 返回结果 + +![image.png](/assets/img/blog8/07.png) + +### Dubbo 泛化调用介绍 + +Dubbo 泛化调用主要就分为两块分别是消费端如何使用 `GenericImplFilter`  拦截泛化调用、服务提供端如何使用 `GenericFilter`  拦截请求后把泛化参数序列化然后请求给具体服务。 + +#### 服务消费端 org.apache.dubbo.rpc.filter.GenericImplFilter 是如何拦截泛化调用 + +```java +@Activate(group = CommonConstants.CONSUMER, value = GENERIC_KEY, order = 20000) +public class GenericImplFilter implements Filter, Filter.Listener { +@Override + public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { + // ... 省略非核心代码 + // 判断是否为泛化调用 + if (isMakingGenericCall(generic, invocation)) { + // 获取泛化参数 + Object[] args = (Object[]) invocation.getArguments()[2]; + // 如果泛化为nativeJava + if (ProtocolUtils.isJavaGenericSerialization(generic)) { + for (Object arg : args) { + if (!(byte[].class == arg.getClass())) { + error(generic, byte[].class.getName(), arg.getClass().getName()); + } + } + // 如果泛化方式为bean + } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { + for (Object arg : args) { + if (!(arg instanceof JavaBeanDescriptor)) { + error(generic, JavaBeanDescriptor.class.getName(), arg.getClass().getName()); + } + } + } + + // 设置attachment ,以便与服务端调用 + invocation.setAttachment( + GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY)); + } + // 发起远程调用 + return invoker.invoke(invocation); + } + private boolean isMakingGenericCall(String generic, Invocation invocation) { + return (invocation.getMethodName().equals($INVOKE) || invocation.getMethodName().equals($INVOKE_ASYNC)) + && invocation.getArguments() != null + && invocation.getArguments().length == 3 + && ProtocolUtils.isGeneric(generic); + } +} +``` + +> GenericImplFilter 实现接口 Filter(关于 Dubbo 中的 Filter,不做介绍)然后执行 Invoke 方法,invoke 方法主要做如下事情: +> +> - 参数校验,检查这个调用是否是泛化调用 +> - 获取泛化参数 +> - 判断泛化调用方式:遍历每个参数,然后依次判断参数的泛化方式是 nativejava 还是 bean 方式 +> - 发起远程调用 + +#### 服务提供端通过 GenericFilter 拦截泛化请求 + +```java +@Activate(group = CommonConstants.PROVIDER, order = -20000) +public class GenericFilter implements Filter, Filter.Listener { + @Override + public Result invoke(Invoker invoker, Invocation inv) throws RpcException { + // 参数校验 + if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC)) + && inv.getArguments() != null + && inv.getArguments().length == 3 + && !GenericService.class.isAssignableFrom(invoker.getInterface())) { + // 获取参数名称、参数类型、参数值 + String name = ((String) inv.getArguments()[0]).trim(); + String[] types = (String[]) inv.getArguments()[1]; + Object[] args = (Object[]) inv.getArguments()[2]; + try { + // 使用反射获取调用的方法 + Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types); + Class[] params = method.getParameterTypes(); + if (args == null) { + args = new Object[params.length]; + } + // 获取泛化引用使用的泛化类型,true or bean or nativejava + String generic = inv.getAttachment(GENERIC_KEY); + if (StringUtils.isBlank(generic)) { + generic = RpcContext.getContext().getAttachment(GENERIC_KEY); + } + // 如果generic=true 则使用true方式对入参进行反序列化 + if (StringUtils.isEmpty(generic) + || ProtocolUtils.isDefaultGenericSerialization(generic) + || ProtocolUtils.isGenericReturnRawResult(generic)) { + args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); + // 如果 generic=nativejava,则使用nativejava方式对入参进行反序列化 + } else if (ProtocolUtils.isJavaGenericSerialization(generic)) { + for (int i = 0; i < args.length; i++) { + if (byte[].class == args[i].getClass()) { + try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) { + args[i] = ExtensionLoader.getExtensionLoader(Serialization.class) + .getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA) + .deserialize(null, is).readObject(); + } catch (Exception e) { + throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e); + } + } else { + throw new RpcException(...); + } + } + // 如果 generic=bean 则使用bean方式对入参进行反序列化 + } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof JavaBeanDescriptor) { + args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]); + } else { + throw new RpcException(...); + } + } + } ... + // 将本次请求传递到FilterChain的下一个Filter中,并返回结果result + RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), args, inv.getAttachments(), inv.getAttributes()); + rpcInvocation.setInvoker(inv.getInvoker()); + rpcInvocation.setTargetServiceUniqueName(inv.getTargetServiceUniqueName()); + + return invoker.invoke(rpcInvocation); + } catch (NoSuchMethodException e) { + throw new RpcException(e.getMessage(), e); + } catch (ClassNotFoundException e) { + throw new RpcException(e.getMessage(), e); + } + } + // 如果不是泛化调用,直接把请求传给FilterChain的下一个Filter + return invoker.invoke(inv); + } +} +``` + +> 以上就是 Dubbo 服务提供端如何拦截泛化请求,并进行处理的大体流程: +> +> - 参数校验,判断此次请求是不是泛化调用 +> - 获取参数名称、参数类型、参数值 +> - 使用反射获取调用的方法,和使用的泛化方式 `true`  or `nativejava`  or `bean` +> - 根据泛化方式,反序列化泛化参数 +> - 将本次请求,包括调用的方法,参数和上下文信息传递给 FilterChain 的下一个 Filter 中,并返回 Result 结果 +> - 根据泛化方式,反序列化 Result 结果返回给服务消费端 + +### 总结 + +以上从如何配置 Dubbo 插件到整个调用流程的分析,然后分别介绍服务消费端与服务提供端如何拦截泛化调用流程对参数进行序列化细节,希望对你有所帮助 + +### 参考 + +[https://my.oschina.net/u/4564034/blog/4409382](https://my.oschina.net/u/4564034/blog/4409382) + +[https://qsli.github.io/2018/05/02/dubbo-generic-invoke/](https://qsli.github.io/2018/05/02/dubbo-generic-invoke/) diff --git a/src/zh/blog/springboot-forest-deepseek-integration.md b/src/zh/blog/springboot-forest-deepseek-integration.md new file mode 100644 index 0000000000..593bebc511 --- /dev/null +++ b/src/zh/blog/springboot-forest-deepseek-integration.md @@ -0,0 +1,359 @@ +--- +title: 如何使用Forest方便快捷地在SpringBoot项目中对接DeepSeek +author: 2025年03月07日 09:58 +date: 2025-03-07 +cover: /assets/img/blog/springboot-forest-deepseek-integration-0.png +head: + - - meta + - name: 博客 +--- + +## 一. 环境要求 + +* JDK 8 / 17 + +* SpringBoot 2.x / 3.x + +* Forest 1.6.4+ + +* Fastjson2 + + +### 依赖配置 + +除了 SpringBoot 和 Lombok 等基础框架之外,再加上 Forest 和 Fastjson2 的依赖 + +``` + + +    com.dtflys.forest +    forest-spring-boot-starter +    1.6.4 + + + + +    com.alibaba +    fastjson +    2.0.53 + +``` + +## 二. 申请 DeepSeek 的 API Key + +打开 DeepSeek 官网,进入到 API Key 的管理页面(https://platform.deepseek.com/api\_keys),就能找到您的 API Key。 + +如果还没有 KEY,可以点击页面下方的`创建API Key`按钮 + + + + + +![](/assets/img/blog/springboot-forest-deepseek-integration-0.png) + +API Keys 页面 + +创建完之后,会弹出一个对话框告诉您新生成的 API Key 字符串,然后要及时把它复制下来保存到一个安全的地方。 + +## 三. 配置项目 + +进入 SpringBoot 的配置文件`application.yml`,加入以下代码: + +``` +# Forest 框架配置 +forest: +  connect-timeout: 10000      # 请求连接超时时间 +  read-timeout: 3600000       # 请求数据读取超时时间,越长越好 +  variables: +    apiKey: YOUR_API_KEY      # 替换为您申请到的 API Key +    model: deepseek-reasoner  # DeepSeek 支持的模型,R1 模型 +``` + +## 四. 创建声名式接口 + +Forest 支持以声名式的方式发送 HTTP 请求,以下代码就是将 DeepSeek API 请求以声名式接口的方式进行定义 + +``` +public interface DeepSeek { +     +    @Post( +            url = "https://api.deepseek.com/chat/completions", +            contentType = "application/json", +            headers = "Authorization: Bearer {apiKey}", +            data = "{\"messages\":[{\"content\":\"{content}\",\"role\":\"user\"}],\"model\":\"{model}\",\"stream\":true}") +    ForestSSE completions(@Var("content") String content); +} +``` + +以上的代码意思也很明显,调用该接口方法就会发送一个`POST`请求,URL 地址为 https://api.deepseek.com/chat/completions + +其中 {apiKey} 和 {model} 的意思为读取配置文件中的 apiKey 字段,{content} 则是读取 @Var("content") 注解修饰的参数。 并且请求体数据为官网文档提供的 JSON 字符串,然后通过`{变量名}`这种字符串模板占位符的形式拼接出您想要的参数。 + +接口方法的返回类型为`ForestSSE`,这是 Forest 框架提供的内置类型,主要用于接受和处理 SSE 事件流消息。 + +## 五. 调用接口 + +在声名式接口创建完之后,可以通过 Spring 的`@Resouce`注解将此接口实例注入到启动类中,Forest框架会利用动态代理模式自动生成相应的接口代理类实例,并将其自动注入到您所需要调用的类中。 + +``` +@Resource +private DeepSeek deepSeek; +``` + +然后就可以调用接口进行发送请求的操作了,并设置Lambda表达式来接收和处理返回的 SSE 流式事件消息 + +``` +@SpringBootApplication +publicclass DeepSeekExampleApplication implements CommandLineRunner { + +    // DeepSeek 声名式接口 +    @Resource +    private DeepSeek deepSeek;  + +    @Override +    public void run(String... args) { +        // 调用声明式接口方法 +        deepSeek.completions("你好,你是谁?") +                .setOnMessage(event -> { +                    // 接受和处理 SSE 事件 +                    try { +                        // 获取消息数据,并反序列化为 DeepSeekResult 类 +                        DeepSeekResult result = event.value(DeepSeekResult.class); +                        // 打印 DeepSeekResult 对象中的消息内容 +                        System.out.print(result.content()); +                    } catch (Exception e) { +                    } +                }) +                .listen(SSELinesMode.SINGLE_LINE); // 监听 SSE,并设置为单行消息模式 +    } + +    public static void main(String[] args) { +        try { +            SpringApplication.run(DeepSeekExampleApplication.class, args); +        } catch (Throwable th) { +            th.printStackTrace(); +        } +    } +} +``` + +其中,DeepSeekResult 是根据返回的消息格式定义的数据类,具体代码如下 + +``` +@Data +publicclass DeepSeekResult { + +    private String id; + +    private String object; + +    private Integer created; + +    private String model; + +    @JSONField(name = "system_fingerprint") +    private String systemFingerprint; + +    private List choices; + +    // 获取消息中的 choices[0].delta.content +    public String content() { +        List choices = getChoices(); +        if (CollectionUtil.isNotEmpty(choices)) { +            JSONObject chooseJson = choices.get(0); +            DeepSeekResultChoice choice = chooseJson.toJavaObject(DeepSeekResultChoice.class); +            return choice.getDelta().getContent(); +        } +        return""; +    } +} +``` + +其他的数据类包括 DeepSeekResultChoice 类也都类似。如果要看具体代码,在文章末尾会提供代码仓库地址。 + +## 六. 应答测试 + +调用方法写完之后,我们就可以跑一下代码看看了,点击 Run 之后可以看到控制台日志会打印以下内容 + + + + + +![](/assets/img/blog/springboot-forest-deepseek-integration-1.png) + +测试日志 + +日志上半部分`POST https://api.deepseek.com/chat/completions HTTPS [SSE]`这类信息为 Forest 的请求日志,会告诉您发出去的 HTTP 请求信息中有些什么数据和参数。 + +而下半部分 “您好!我是由中国的深度求索(DeepSeek)公司开发的智能助手DeepSeek-R1...” 自然就是 DeepSeek 的回答了。 + +## 七. 思维链 + +以上的代码案例,只会返回 DeepSeek 的回答内容,不包含他的思考过程,哪怕模型是`DeepSeek-R1`也一样。如果要打印出思维链,就要修改一下代码 + +首先要修改 DeepSeekResult 类中的 content() 方法 + +``` +@Data +publicclass DeepSeekResult { + +    private String id; + +    private String object; + +    private Integer created; + +    private String model; + +    @JSONField(name = "system_fingerprint") +    private String systemFingerprint; + +    private List choices; + +    // 获取消息中的 choices[0].delta.reasoning_content +    // 或 choices[0].delta.content +    // 是否为思维内容,通过 DeepSeekContent.isReasoning 来标识 +    public DeepSeekContent content() { +        List choices = getChoices(); +        if (CollectionUtil.isNotEmpty(choices)) { +            JSONObject chooseJson = choices.get(0); +            DeepSeekResultChoice choice = chooseJson.toJavaObject(DeepSeekResultChoice.class); +            String reasoningContent = choice.getDelta().getReasoningContent(); +            // 判断是否存在 reasoningContent,存在就是思维链内容,否则就是存粹的回答内容 +            if (StringUtils.isNotEmpty(reasoningContent)) { +                returnnew DeepSeekContent(true, reasoningContent); +            } +            returnnew DeepSeekContent(false, choice.getDelta().getContent()); +        } +        returnnew DeepSeekContent(); +    } +} +``` + +添加 DeepSeekContent 类 + +``` +@Data +publicclass DeepSeekContent { +    // 是否为思考过程内容 +    privateboolean reasoning = false; +    // DeepSeek 回答的具体内容 +    private String content = ""; + +    public DeepSeekContent() { +    } + +    public DeepSeekContent(boolean reasoning, String content) { +        this.reasoning = reasoning; +        this.content = content; +    } +} +``` + +最后,修改接口的调用部分 + +``` +@SpringBootApplication +publicclass DeepSeekExampleApplication implements CommandLineRunner { + +    // DeepSeek 声名式接口 +    @Resource +    private DeepSeek deepSeek;  + +    @Override +    public void run(String... args) { +        // 标志位:是否为第一次接收到到思维链内容 +        AtomicBoolean isFirstReasoning = new AtomicBoolean(false); +        // 调用声明式接口方法 +        deepSeek.completions("1+1等于几?") +                .setOnMessage(event -> { +                    try { +                        DeepSeekResult result = event.value(DeepSeekResult.class); +                        DeepSeekContent content = result.content(); +                        // 通过 CAS 判断是否第一次接收到到思维链内容 +                        // 如果是,则打印出<思维链>标签 +                        if (content.isReasoning() && isFirstReasoning.compareAndSet(false, true)) { +                            System.out.println("<思维链>"); +                            System.out.print(content.getContent()); +                        } elseif (!content.isReasoning() && isFirstReasoning.compareAndSet(true, false)) { +                            // 当 isFirstReasoning 由 true 转为 false +                            // 则表明消息从思维链内容转向正式回答内容 +                            System.out.print(content.getContent()); +                            System.out.println("\n\n"); +                        } else { +                            // 打印正常的思维链或正式回答内容 +                            System.out.print(Opt.ofBlankAble(content.getContent()).orElse("")); +                        } +                    } catch (Exception e) { +                    } +                }) +                .listen(SSELinesMode.SINGLE_LINE); +    } + +    public static void main(String[] args) { +        try { +            SpringApplication.run(DeepSeekExampleApplication.class, args); +        } catch (Throwable th) { +            th.printStackTrace(); +        } +    } +} +``` + +## 八. 思维链消息测试 + +接下来就可以运行程序测试了,看看日志中是否包含了思维链的过程 + + + +![](/assets/img/blog/springboot-forest-deepseek-integration-2.png) + +思维链日志.png + +从日志中可以看出,程序正常运行了,其中被包裹在`<思维链>`和``标签中间的部分就是 DeepSeek 告诉我们的思维过程。 而在``结束标签之后的文字就是他的正式回答内容。 + +## 九. 错误处理 + +本文案例调用的是 DeepSeek 官方的 API。由于众所周知的原因,调用接口时极有可能发生`401`等网络错误。 + +遇到这种请求,加一个拦截器就完事了 + +``` +// Forest 的 SSE 请求拦截器 +publicclass DeepSeekInterceptor implements SSEInterceptor { + +    // 接受到请求响应时会自动调用该方法 +    @Override +    public ResponseResult onResponse(ForestRequest request, ForestResponse response) { +        // 判断请求是否发生错误,如 401、404 等等 +        if (response.isError()) { +            // 如有错,就打印“服务端繁忙,请稍后再试” +            System.out.println("服务端繁忙,请稍后再试"); +            return success(); +        } +        return proceed(); +    } +} +``` + +然后,将拦截器绑定到接口上 + +``` +// 为整个接口绑定拦截器 +@BaseRequest(interceptor = DeepSeekInterceptor.class) +public interface DeepSeek { +     +    @Post( +            url = "https://api.deepseek.com/chat/completions", +            contentType = "application/json", +            headers = "Authorization: Bearer {apiKey}", +            data = "{\"messages\":[{\"content\":\"{content}\",\"role\":\"user\"}],\"model\":\"{model}\",\"stream\":true}") +    ForestSSE completions(@Var("content") String content); +} +``` + +## 十. 总结 + +可以看到,通过 Forest 这种声名式的形式来对接 DeepSeek API,相比于 OkHttp 和 HttpClient 有很多明显的好处。除了代码简洁,容易实现之外,更重要的是声名式代码天然更容易解耦。文本代码很自然的就实现了在参数配置、HTTP请求参数、以及接口调用的业务逻辑之间实现了代码解耦。如果要修改 API Key 或者模型,直接该配置文件就行。如果要修改 HTTP 的 URL 或参数,可以直接改声名式接口,而不会影响到调用接口的业务代码。而且可以很自然地将 DeepSeek API 的 HTTP 代码统一放到一个接口类中,方便管理,而且请求中的 URL、请求头、请求体参数都都一目了然。 + +**代码仓库地址**:https://gitee.com/dromara/forest/tree/master/forest-examples/example-deepseek \ No newline at end of file diff --git a/src/zh/blog/warm-flow-1.3.0.md b/src/zh/blog/warm-flow-1.3.0.md new file mode 100644 index 0000000000..a8c05e0724 --- /dev/null +++ b/src/zh/blog/warm-flow-1.3.0.md @@ -0,0 +1,157 @@ +--- +title: 一个自带流程设计器的工作流引擎 +author: warm-flow +date: 2024-10-23 +cover: /assets/img/blog/warm-flow-1.3.0-0.png +head: + - - meta + - name: 博客 +--- + +# 一个自带流程设计器的工作流引擎 + + + +**终于迎来了这个激动人心的版本1.3.0,不需要在为引入设计器而烦恼了,按照以下前四点,可以快速接入业务系统,下面介绍如何使用设计器** + + + +## 1\. 引入依赖 + +``` + +    io.github.minliuhua +    warm-flow-plugin-ui-sb-web +    1.3.0 + +``` + +## 2\. 后端放行部分路径 + +> 1、这两个路径需要放行,否则无法访问,`/warm-flow-ui/**`, `/warm-flow/**` +> +> 2、以下是spring-security放行配置示例 + +``` +@Bean +protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception +{ +    return httpSecurity +            ....... +            // 注解标记允许匿名访问的url +            .authorizeHttpRequests((requests) -> { +                // 后端请求,静态资源,可匿名访问 +                requests.antMatchers("/warm-flow-ui/**", "/warm-flow/**").permitAll() +                        // 除上面外的所有请求全部需要鉴权认证 +                        .anyRequest().authenticated(); +            }) +            ...... +            .build(); +} +``` + +## 3\. 前端加载设计器 + +>  1、设计器页面入口地址为:`/warm-flow-ui/${definitionId}?disabled=${disabled}` +> 2、总体思路就是把前端接口(比如80)代理成后端接口(8080),去访问该地址,其他不变 +> 3、或者直接通过后端接口访问该地址,可能需要处理跨域问题 4、以下是nginx代理示例: + +``` +server { +    listen       80; +    server_name  localhost; + +    location /warm-flow-ui/ { +        proxy_pass http://localhost:8080/warm-flow-ui/; +    } +} +``` + +## 4\. 设计器办理人权限数据接入 + +> \[!IMPORTANT\] 给任务节点设置哪些权限的人可以办理,实现接口提供给设计器 + +### 4.1 办理人权限选择弹框页面 + +![](/assets/img/blog/warm-flow-1.3.0-0.png) + +### 4.2 实现接口获取以上页面办理人权限数据 + +#### 4.2.1 HandlerSelectService接口 + +``` +/** + * 流程设计器-获取办理人权限设置列表接口 + * + * @author warm + */ +public interface HandlerSelectService { + +    /** +     * 获取办理人权限设置列表tabs页签, 如:用户、角色和部门等 +     * @return tabs页签 +     */ +    List getHandlerType(); + +    /** +     * 获取办理人权限设置列表结果,如:用户列表、角色列表、部门列表等 +     * @param query 查询参数 +     * @return 结果 +     */ +    List getHandlerSelect(HandlerQuery query); +} +``` + +## 5、项目介绍 + +> \[!IMPORTANT\] Warm-Flow国产工作流引擎🎉,其特点简洁轻量,五脏俱全,可扩展,是一个可通过jar引入设计器的工作流。 + +1. 简洁易用:只有7张表,代码量少,可快速上手和集成 + +2. 审批功能:支持通过、退回、任意跳转、转办、终止、会签、票签、委派和加减签、互斥和并行网关 + +3. 监听器与流程变量:支持四种监听器,可应对不同场景,灵活可扩展,参数传递,动态权限 + +4. 流程图:流程引擎自带流程图,可在不集成流程设计器情况下使用 + +5. 流程设计器:可通过jar包形式快速集成到项目,减少繁琐代码搬运和适配 + +6. 条件表达式:内置常见的和spel条件表达式,并且支持自定义扩展 + +7. 办理人变量表达式:内置${handler}和spel格式的表达式,可满足不同场景,灵活可扩展 + +8. orm框架扩展:目前支持MyBatis、Mybatis-Plus、Mybatis-Flex和Jpa,后续会由社区提供其他支持,扩展方便 + +9. 数据库支持:目前支持MySQL 、Oracle 和PostgreSQL,后续会继续支持其他数据库或者国产数据库 + +10. 多租户与软删除:流程引擎自身维护多租户和软删除实现,也可使用对应orm框架的实现方式 + +11. 同时支持spring和solon + +12. 兼容java8和java17,理论11也可以 + +13. 官方提供基于ruoyi-vue封装实战项目,很实用 + + +## 6、演示地址 + +* admin/admin123 + + +演示地址:http://www.hhzai.top + +## 7、官网 + +http://warm-flow.cn + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/blog/warm-flow-1.3.0-1.webp) \ No newline at end of file diff --git a/src/zh/donation/README.md b/src/zh/donation/README.md index 0e1fde7766..86fbd8a0ba 100644 --- a/src/zh/donation/README.md +++ b/src/zh/donation/README.md @@ -1,377 +1,378 @@ ---- -title: 捐赠 -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - -## 项目捐赠 - -### 基本条件 - -Dromara 开源社区组织对捐赠项目有以下最基本条件要求: - -- **项目必须是原创且不能是 Fork 版本。** -- 项目必须是完整的应用解决方案。 -- 项目必须有良好的代码注释和不断完善的使用文档。 -- 项目必须在 Gitee 平台托管且 Stars 关注量大于 100。 -- 项目至少在近 2 个月内有实际有效的代码提交记录。 -- **项目必须得到 dromara 社区组织评委会的认可。** -- 优先考虑 Gitee 推荐项目或已获得 GVP 的项目。(GVP 项目需联系 Gitee 官方人员取消 GVP 后才能转移到组织,转移成功后恢复 GVP) -- 优选考虑 容器工具、微服务框架和工具、分布式事务、分布式中间件、大数据处理、人工智能、IoT 物联网、开发/测试/运维相关工具链等领域项目。 - -### 源仓库影响 - -目前捐赠项目采用转移到 dromara 组织仓库方式进行处理,项目转移后会有以下影响: - -- gitee :`https://gitee.com/dromara/仓库名` -- github:`https://github.com/dromara/仓库名` - -项目源码中如果存在绝对路径的引用(图片,文件等)则需改为相对路径。 - -### 仓库权限 - -- dromara 组织负责人会给新加项目负责人设置超级管理员权限。 -- 项目 PMC/Committers 会被邀请加入 dromara 组织,设置相关读写权限。项目独立提交与运营。 -- 项目 PMC/Committers 会被分配`@dromara.org` 后缀邮箱。 - -### 仓库文档 - -- 原则上需要联系 dromara 官网负责人,在 `https://dromara.org` 进行更新转移文档 - -### 版本发布 - -- 原则上要求下一个版本,需要更改包名前缀为`org.dromara`, 并且发布到 MAVEN 仓库 - -### 组织/社区 - -- dromara 开源组织负责人或者秘书,邀请进入 dromara 社区群,并进行全员公告。 - -- dromara 开源社区公众号,发布欢迎加入文章,并同步到技术社区。 - -- 原仓库社区群加上 `dromara-xxx` 前缀。 - -### 捐赠项目退出事宜(初版) - -dromara 社区组织始终抱有积极开放宽容的态度,如果捐赠项目作者加入 dromara 组织后无法认同组织的发展理念或想转移到其他组织/个人,则可向 dromara 评委会申请退出。申请退出流程: - -1. 项目作者需书面或邮件编写项目退出事由并电邮至 [pmc@dromara.org](mailto:pmc@dromara.org) 。 -2. dromara 评委会收到申请后第一时间与项目作者沟通并确认退出意向,若作者执意退出,则批准处理。 -3. 特别注意,如果申请退出的项目是 GVP 项目,则需联系 Gitee 官方人员取消 GVP 后转回个人名下,转回成功后联系 Gitee 官方人员恢复即可。非 GVP 项目可直接操作。 -4. 项目退出后,dromara 组织会在各大平台和社区群进行公告推送告知,并将该项目历史记录进行归档保存。 - -最后,希望项目作者在考虑退出 dromara 组织之前可以先和我们沟通,指出 dromara 组织哪里做的不好的地方,以便我们能够改进,共同发展。 - -### 权责与组织形式 - -1. 社区内各个子项目团队,共享所有开源资源,包括且不限于组织品牌、社区公众号、官方社群、主流技术网站和媒体的专属账号和频道等。 -2. 原则上,每个项目内部管理自治,组织委员会由每个项目的主要负责人组成。 -3. dromara 组织相关决策事务(指定和修改规则、加入或者退出项目等),需要由组织委员会投票通过后执行。日常事务由组织的常任负责人处理。 -4. 在组织层面做出决策以后,每个项目的管理团队必须无条件服从组织,或者配合其他项目的一些工作和任务,做到整个组织在宣传活动、任务安排等方面口径一致。 -5. dromara 开源组织从 2021 年 3 月到 2022 年 3 月进入试运行阶段,本原则为组织的试运行指导方案,各个子项目在此期间,在不违反法律、道德原则,不涉及抄袭侵权,以及不伤害组织,不对社区和用户造成恶劣影响的前提下,拥有完整的自治和管理权限、以及项目子品牌的著作权和实际拥有权。 -6. 在试运行期内,社区将成立正式的委员会,对于后续社区发展方式,子项目的管理和权限约束,出台正式的规则,并经由投票通过后生效。 - -### 以上规定 dromara 组委会有最终解释权 - -## 赞助/支持 - -加入知识星球 - - - -**Dromara** 社区能够持续运营和提供更加优质的服务离不开大家的支持。如果想成为 **Dromara** 社区赞助商或支持者,请考虑联系: - -[xiaoyu@dromara.org](mailto:xiaoyu%40dromara.org) - - - -**Dromara** 社区承诺将收到的所有赞助支持资金完全公开化,且后续资金用途仅限于 **Dromara** 社区运营支出。赞助任何事情,可以联系 **** 目前赞助商有(没有显示名称的赞助者,默认:无名): - -| 赞助商 | 赞助金额(元) | 附语 | 赞助日期 | -| ------------------- | -------------- | --------------------------------------------------------- | ---------- | -| 椰子 | 100 | | 2024-10-14 | -| 无名 | 200 | 一如既往的支持!感恩相遇! | 2024-10-08 | -| 岳秋林 | 1 | 用过 hutool,超过 sa-token,支持开源 | 2024-08-23 | -| 无名 | 200 | 喝杯咖啡 | 2024-08-05 | -| one-fit | 100 | 希望 dromara 组织能成为中国的 apache | 2024-07-19 | -| 无名 | 100 | 加油!朋友 | 2024-06-04 | -| 无名 | 100 | 朋友,你好! | 2024-05-06 | -| 无名 | 100 | | 2024-04-14 | -| KoKo | 20 | 开源万岁 | 2024-03-13 | -| 无名 | 100 | 朋友们,元宵节快乐 | 2024-02-24 | -| IILee | 20 | 感谢为中国开源做出的贡献 | 2024-01-25 | -| 无名 | 5 | 加油 | 2023-11-06 | -| 无名 | 100 | 感恩相遇 | 2023-10-18 | -| 无名 | 6.66 | 封神之作 | 2023-07-28 | -| 无名 | 50 | | 2023-07-24 | -| JacKey | 10 | | 2023-07-06 | -| 呼噜 | 1 | 买个卫龙 | 2023-06-14 | -| 连洁 | 20 | 小小赞赏 | 2023-05-26 | -| 一夏千晨 | 50 | 一包辣条奉上 | 2023-05-22 | -| 无名 | 50 | | 2023-05-22 | -| 无名 | 100 | 五一快乐(勿回应) | 2023-04-29 | -| meta.木予 | 10 | | 2023-04-26 | -| 无名 | 100 | 纯粹赞助(无需回应) | 2023-04-24 | -| Java 开发小王 | 1 | 很强 | 2023-04-20 | -| 令狐冲 | 10 | 加油 | 2023-04-12 | -| 如愿 | 10 | 猫大人帅哥,祝社区越来越好 | 2023-04-04 | -| cszj | 100 | 帮助很大,希望越来越好 | 2023-04-03 | -| 海子 | 50 | 感谢诸位无私的奉献 | 2023-04-02 | -| 西岭千秋雪 | 50 | | 2023-03-29 | -| 是白菜 | 1 | | 2023-03-28 | -| 泛微网络刘启成 | 50 | 学习 ee,对我非常有帮助,感谢 | 2023-03-27 | -| 无名 | 10 | 加油! | 2023-03-19 | -| 无名 | 100 | 加油! | 2023-03-16 | -| Yyy | 1 | | 2023-03-01 | -| 无名 | 100 | | 2023-02-28 | -| Evan | 20 | | 2023-02-26 | -| 朔 | 10 | 钱不多,但是心意是满满的,加油! | 2023-02-24 | -| 无名 | 200 | | 2023-02-05 | -| 方 | 28.88 | 新年快乐,感谢开源 | 2023-01-21 | -| 无名 | 200 | 祝社区的兄弟姐妹们新年快乐 | 2023-01-21 | -| 无名 | 0.26 | | 2023-01-11 | -| 无名 | 50 | | 2023-01-06 | -| 紫气东来 | 50 | 感谢开源,坚持信念 | 2022-12-14 | -| 枫原万耶 | 16.66 | | 2022-11-13 | -| 岁月无声 | 10.24 | 希望越来越好 | 2022-11-10 | -| 邵昌明 | 20 | 买雪糕吃 | 2022-11-02 | -| 无名 | 1 | | 2022-10-27 | -| 大熊同学 | 9.9 | 希望我们开源社区长长久久! | 2022-10-26 | -| 曙光 | 50 | | 2022-10-25 | -| 无名 | 1 | Zdp 奉上 | 2022-10-04 | -| 无名 | 200 | | 2022-09-28 | -| 无名 | 200 | | 2022-09-28 | -| 无名 | 200 | | 2022-09-28 | -| 晚风 | 100 | 从接触 hutool 开始就喜欢上了,太好用了 | 2022-09-08 | -| 无名 | 100 | 支持开源,加油! | 2022-09-07 | -| 陈乐辉 | 100 | 祝开源的路上越走越远 | 2022-08-12 | -| 阿超 | 50 | 开源就是人人为我,我为人人 | 2022-07-27 | -| QZWML | 10 | | 2022-07-22 | -| 无名 | 100 | | 2022-06-30 | -| Eclipse | 20 | 支持! | 2022-06-28 | -| 朋云 | 10 | | 2022-06-24 | -| 红豆生南国 | 10 | 吃个雪糕 | 2022-06-22 | -| 修理工 | 80 | 代码规范,值得学习,用的方便,强烈推荐 | 2022-05-27 | -| 苗对我很重要 | 10 | | 2022-05-19 | -| 王磊 | 50 | | 2022-05-19 | -| Hi | 10.24 | 感谢开发 | 2022-04-24 | -| 哈哈 | 66.66 | 支持猫大人,祝社区越来越好~ | 2022-04-14 | -| liming | 50 | 祝社区越来越好! | 2022-04-14 | -| 胡泰室 | 50 | 祝社区越来越好 | 2022-04-14 | -| 金卫信曾小燕 | 10.24 | | 2022-04-14 | -| Chris | 150 | | 2022-04-12 | -| 无名 | 100 | | 2022-04-05 | -| 青衫烟雨客 | 10 | | 2022-03-20 | -| Zack | 20 | 省了我好多事,太 nb 了!!支持下 | 2022-03-10 | -| 许伟超 | 10 | 感恩,尽我绵薄之力 | 2022-03-09 | -| 无名 | 10 | 虽然钱不多,但是我会一直打赏 | 2022-03-09 | -| ^\_^ | 20 | 虎年吉祥 | 2022-02-03 | -| 无名 | 166 | 祝社区能做的越来越好 | 2022-01-17 | -| 吴南方 | 20 | 希望 hutool 能做的越来越好 | 2022-01-09 | -| 忘忧 | 50 | | 2021-12-30 | -| 达 | 5 | hutool 省了很多重复造轮子的时间,特别棒,小小心意赞助一下 | 2021-12-24 | -| 吃瓜群众丙 | 100 | 为往圣继绝学。加油吧,少年。 | 2021-12-23 | -| 早点下班吧 | 10 | | 2021-12-14 | -| Amiron 今天早睡了吗 | 20 | | 2021-12-02 | -| phantom | 100 | | 2021-11-30 | -| 无名 | 11.27 | 感谢,表示支持 | 2021-11-27 | -| 夏和顺顺 | 50 | 为往圣继绝学 | 2021-11-25 | -| Chris | 100 | | 2021-11-24 | -| 光明星辰 | 50 | 辣条钱 | 2021-11-04 | -| 无名 | 3 | 辣条钱 | 2021-10-29 | -| 奥利弗 | 15 | | 2021-10-13 | -| XXXL | 50 | 聊表心意,祝越来越好 | 2021-09-30 | -| 用用 | 20 | | 2021-09-17 | -| 宋不醒 | 10 | 加油 | 2021-09-10 | -| 🐶 | 20 | 聊表心意,有大用了 | 2021-08-24 | -| Coffee | 10 | 冲冲 | 2021-07-29 | -| aliyu | 10 | aly | 2021-07-22 | -| 段大志 | 20 | 国产开源蒸蒸日上 | 2021-07-14 | -| 喜世 | 20 | 感谢铁锚助教辛勤解答问题 | 2021-06-28 | -| Group | 1.00 | 蚊子腿也是肉 | 2021-06-08 | -| 大大的时间小小的我 | 20 | | 2021-05-26 | -| Leo | 15 | 真心不错,加油! | 2021-05-20 | -| 辉辉同学 | 66.66 | hutool 工具个人认为是封神佳作,顶礼膜拜! | 2021-05-19 | -| 0X17 | 100 | | 2021-05-16 | -| 鑫爷 | 16.8 | fans99 | 2021-05-11 | -| こうう | 3 | | 2021-05-06 | -| 一只想要奋斗的咸鱼 | 66 | | 2021-04-29 | -| 锄禾 | 20 | 能力有限,聊表心意 | 2021-04-27 | -| 然而 | 64 | 越开源越进步 | 2021-04-23 | -| 路,还很远 | 20 | 国产开源牛逼 | 2021-04-23 | -| 纳兰-曼孚 | 20 | | 2021-04-15 | -| Stone | 200 | 跟着猫大人搞开源,芜湖起飞 | 2021-04-14 | -| 疯子 | 200 | 国产开源,加油! | 2021-04-14 | -| tom | 50 | 祝越来越好 | 2021-04-14 | -| 子豪 sirius | 50 | 支持猫大人 | 2021-04-14 | -| Sissie | 50 | 感谢开源,支持喵大人 | 2021-04-14 | -| WilliamSky | 50 | 感谢开源,支持喵大人 | 2021-04-14 | -| Sink | 10 | 向大佬致敬 | 2021-04-14 | -| 石启蒙 | 10 | 祝开源社区越来越好 | 2021-04-14 | -| 吴俊杰\_ken | 66.88 | 学习加交流,社区越来越好 | 2021-04-13 | -| 正是在下李某人 | 20 | 猫大人牛批 | 2021-04-13 | -| Zhw | 400 | 做大做强,再创辉煌 | 2021-04-13 | -| 彭伟伦 | 20 | 猫大人的华子 | 2021-04-13 | -| 心中的明月 | 30 | Tshirt 好漂亮,好想要 | 2021-04-13 | -| 刘进 | 20 | 撸下胸毛 | 2021-04-13 | -| 黄灿达 | 20 | 支持 | 2021-04-13 | -| 赵镇 | 33 | 做大做强,再创辉煌 | 2021-04-13 | -| kimmking | 2000 | 祝福社区越来越好 | 2021-04-12 | -| John | 400 | 猫大人抽华子 | 2021-04-12 | -| Charlyfeng | 10 | 为往圣继绝学 | 2021-04-12 | -| 梁胜芳 | 6.66 | 祝组织越来越好 | 2021-04-12 | -| Chris | 200 | 祝福社区越来越好 | 2021-04-11 | -| Jtiag | 20 | 做大做强 | 2021-04-02 | -| Nelson | 66 | 做大做强,走向辉煌 | 2021-04-02 | -| 好方 | 60 | 我们一起学猫叫,一起喵喵喵喵喵~ | 2021-04-02 | -| 哼干嘛 | 20 | 加入的第一个组织,感谢猫大人 | 2021-04-01 | -| 静晓晨曦 | 20 | 支持猫大人,祝福社区越来越好 | 2021-03-31 | -| Attract | 20 | 聊表心意,祝福社区越来越好 | 2021-03-31 | -| 杨大侠 | 60 | 跟着猫大人搞开源 | 2021-03-31 | -| ian | 200 | Dromara 最棒 | 2021-03-31 | -| 白 | 20 | | 2021-03-31 | -| 静静 | 80 | 强 | 2021-03-31 | -| blizzard | 20 | 感谢猫大人花费巨大的心血 | 2021-03-31 | -| Jenkins | 100 | 参加开源,共建社区 | 2021-03-31 | -| Sean | 128 | 祝社区越走越远 | 2021-03-31 | -| 肖邦 | 20 | 全力支持参与开源贡献自己的微博力量 | 2021-03-31 | -| Ted 丶 L | 100 | 祝社区越办越好 | 2021-03-31 | -| hb | 60 | 支持开源 | 2021-03-31 | -| 大黄蜂 | 60 | 感谢猫大人,请猫大人抽根华子 | 2021-03-30 | -| 半盏清茶 | 20 | 越来越好 | 2021-03-30 | -| 小旭 | 20 | | 2021-03-30 | -| 阳有白 | 60 | 请猫大人喝几吨白开水 | 2021-03-30 | -| 韩小波 | 20 | 中国开源加油 | 2021-03-30 | -| Vilochen | 60 | 希望猫大人的开源组织越来越壮大 | 2021-03-30 | -| 滴流乱转的小胖子 | 60 | 向猫大人老师致敬 | 2021-03-30 | -| 泊 | 20 | 支持猫大人的开源 | 2021-03-30 | -| 赵瑞 | 20 | | 2021-03-30 | -| 心想事成 | 1 | | 2021-03-30 | -| 李 | 20 | 支持一下 | 2021-03-30 | -| Yul | 20 | | 2021-03-30 | -| bing | 20 | | 2021-03-30 | -| 榕 | 20 | | 2021-03-30 | -| keepCarry | 160 | dromara 永存 | 2021-03-30 | -| xiaochun | 20 | 猫大人加油,看好你哦 | 2021-03-30 | -| 梁超-ISAAC | 20 | 支持支持 | 2021-03-30 | -| 子豪 sirius | 20 | 支持开源 | 2021-03-30 | -| 瓦让 | 80 | 大家一起加油 | 2021-03-30 | -| wyj | 20 | 支持支持 | 2021-03-30 | -| 小小的太阳 | 20 | 猫大人牛批 | 2021-03-30 | -| 小雨淅淅 | 20 | 请猫大人喝奶茶 | 2021-03-30 | -| 苦了芥末 | 20 | | 2021-03-30 | -| 成龙 | 50 | 谢谢猫大人对于开源的贡献,感谢引路 | 2021-03-30 | -| Chasen | 80 | 哈哈,支持支持 | 2021-03-30 | -| Menglg | 20 | | 2021-03-30 | -| 闫兵 | 66.66 | | 2021-03-30 | -| 王一飞 | 60 | | 2021-03-30 | -| 张民鹏 | 60 | 祝 Dromara 社区越办越好 | 2021-03-30 | -| Jasper | 60 | 猫大人牛逼! | 2021-03-30 | -| 子不语 | 60 | 一直想做开源,这也是离我最近的社区,社区加油! | 2021-03-30 | -| 阿行 | 20 | 猫大人的小迷弟 | 2021-03-30 | -| Wincher | 10.24 | 为往圣继绝学 | 2021-03-30 | -| Marcus | 20 | 一个人可能走的更快,但一群人会走得更远 | 2021-03-30 | -| 欧葵 | 60 | 猫大人加油 | 2021-03-30 | -| 有。无。 | 20 | | 2021-03-30 | -| 哲一 | 20 | | 2021-03-30 | -| 刘耿华 | 66 | 猫大人牛逼 | 2021-03-30 | -| 乔帝鸽 | 20 | 做大做强,再创辉煌 | 2021-03-30 | -| 和尘同光 | 20 | 请猫大人喝咖啡 | 2021-03-30 | -| ~zZZachary | 60 | | 2021-03-30 | -| 唐甜 | 20 | 跟着猫大人做开源 | 2021-03-30 | -| 小程故事多 | 60 | 加油,一如既往的支持 | 2021-03-30 | -| 东东 | 60 | 做大做强 | 2021-03-30 | -| Truing | 20 | 猫大人牛逼 | 2021-03-30 | -| sikm | 20 | 为往圣继绝学,大风起不可当石立 | 2021-03-30 | -| One Day | 299 | | 2021-03-30 | -| jihe | 20 | 开源冲 | 2021-03-30 | -| 豆豆 | 99.99 | 支持开源 | 2021-03-30 | -| 斯普特尼克 | 60 | 祝社区越来越繁荣 | 2021-03-30 | -| orange | 20 | 猫大人帅气 | 2021-03-30 | -| 钟如雷 | 200 | 请老板抽和天下 | 2021-03-30 | -| Goku | 20 | 加油,愿社区越来越好 | 2021-03-30 | -| Try Everything | 20 | 支持猫大人 | 2021-03-30 | -| 郡鸿 | 20 | 支持猫大人,支持开源 | 2021-03-30 | -| FangQ | 20 | 猫大人加油,Dromara 加油 | 2021-03-30 | -| cycle | 120 | 愿中国的开源事业越来越好,猫大人及其同仁们威武 | 2021-03-30 | -| 夜羽 | 20 | 向开源精神,脱帽致敬 | 2021-03-30 | -| SUDONG | 80 | | 2021-03-30 | -| 婷 | 20 | 猫大人牛批,超大声! | 2021-03-30 | -| Tusi | 20 | | 2021-03-30 | -| 任富飞 | 88 | 越来越好 | 2021-03-30 | -| 时间会咬人 | 60 | 再接再励,做大做强 | 2021-03-30 | -| sakila | 60 | 猫大人抽华子 | 2021-03-30 | -| 虎宝 | 66.66 | 婷宝&虎宝,崔猫求疵 | 2021-03-30 | -| 一条小青龙 | 20 | 请猫大人吃鸡排 | 2021-03-30 | -| 徐培 | 60 | 请猫大人抽华子 | 2021-03-30 | -| pengshao | 20 | 猫大人加油 | 2021-03-30 | -| F.C | 20 | 猫大人威武,祝 Dromara 社区越办越好 | 2021-03-30 | -| hellboy0621 | 66.66 | 为往圣继绝学 | 2021-03-30 | -| 吴哭哭 | 60 | 精神股东 | 2021-03-30 | -| 不忘初心 | 120 | | 2021-03-29 | -| 陈宏 | 20 | | 2021-03-29 | -| 恬恬好甜 | 20 | | 2021-03-29 | -| 瑞 | 120 | | 2021-03-29 | -| Uncle George | 10.24 | | 2021-03-29 | -| Tree | 20 | | 2021-03-29 | -| Shallwetalk | 20 | | 2021-03-29 | -| 莫生 | 60 | 请猫大人抽包华子 | 2021-03-29 | -| YEZJ | 20 | | 2021-03-29 | -| Silent | 60 | | 2021-03-29 | -| SIGN | 20 | 猫大人的狸猫 | 2021-03-29 | -| 张\* | 20 | | 2021-03-29 | -| 荼九 | 66 | | 2021-03-29 | -| ? | 20 | | 2021-03-29 | -| 睡觉大王小汤 | 1 | | 2021-03-29 | -| 守候 | 20 | 猫大人牛逼 | 2021-03-29 | -| Bruce | 10 | | 2021-03-29 | -| 晏鹏 | 10.24 | 猫大人加油 | 2021-03-29 | -| 一条小路的距离 | 20 | 猫大人的华子 | 2021-03-29 | -| 嘉宝 | 80 | 请师父喝咖啡 | 2021-03-29 | -| PeiXy | 60 | 猫大人的胸毛 | 2021-03-29 | -| L3nvy | 13 | 猫大人 lol 分队第一塞拉斯 | 2021-03-29 | -| comforyo | 20 | | 2021-03-29 | -| 莫白 | 9.99 | 感恩 | 2021-03-29 | -| 做成一件小事 | 20 | 猫大人的小学徒 | 2021-03-29 | -| zendwang | 20 | | 2021-03-29 | -| 星尘 | 9.99 | 气氛搞起来 | 2021-03-29 | -| 萧 | 60 | 为往圣继绝学 | 2021-03-29 | -| 陈龙 | 20 | 开源无敌,拒绝 996 | 2021-03-29 | -| \*\*涛 | 50 | | 2021-03-29 | -| yarne | 66.66 | 共同的事业,深深的友情 | 2021-03-29 | -| E.S. | 60 | 大锅,喝阔落! | 2021-03-29 | -| 东江 DJ | 20 | | 2021-03-29 | -| ZhW | 20 | | 2021-03-29 | -| Mr...犯 | 20 | | 2021-03-29 | -| 林 | 20 | | 2021-03-29 | -| 天地 | 20 | | 2021-03-29 | -| 项峥 | 20 | 猫大人牛逼 | 2021-03-29 | -| Easley | 200 | 猫大人牛逼 | 2021-03-29 | -| 秦旭 | 60 | | 2021-03-29 | -| Neal | 20 | 猫大人的错别字检查员 | 2021-03-29 | -| Chovi.Wu | 10.24 | 表哥加油 | 2021-03-29 | -| Niverkk | 20 | | 2021-03-29 | -| ~阿槑~ | 20 | | 2021-03-29 | -| Witt | 20 | 支持猫大人 | 2021-03-29 | -| 屈仁能 | 218.88 | 猫大人家的菜鸟 | 2021-03-29 | -| 侯瑞哲 | 20 | 请猫大人喝咖啡 | 2021-03-29 | -| Phoenix | 20 | 感谢猫大人带我们认识开源 | 2021-03-29 | -| 黄放 | 20 | 为往圣继绝学 | 2021-03-29 | -| 那会 | 20 | 感谢猫大人 | 2021-03-29 | -| Liquid | 20 | | 2021-03-29 | -| audi | 15 | 喵小喵 | 2021-03-29 | -| KaitoShy | 6.66 | | 2021-03-29 | -| 氺 | 20 | | 2021-03-29 | -| Ncy | 20 | 喵喵喵 | 2021-03-29 | -| HelloWorld | 20 | 靡不有初,鲜克有终 | 2021-03-29 | -| 孙龙 | 60 | | 2021-03-29 | -| maybe | 10.24 | | 2021-03-29 | -| biao | 20 | | 2021-03-29 | -| BetterWp | 66.66 | 跟着猫大人冲 | 2021-03-29 | -| 502819 | 20 | 猫大人威武霸气 | 2021-03-29 | -| Z | 10 | 猫大人抽华子 | 2021-03-29 | -| 陈曦 | 66.66 | | 2021-03-29 | -| 一朵云 | 20 | | 2021-03-29 | -| 姜吉宁 | 60 | 做大做强 | 2021-03-29 | -| 飞鸿雪泥 | 60 | 开源万岁 | 2021-03-29 | +--- +title: 捐赠 +pageInfo: false +contributors: false +editLink: false +sidebar: false +lastUpdated: false +--- + +## 项目捐赠 + +### 基本条件 + +Dromara 开源社区组织对捐赠项目有以下最基本条件要求: + +- **项目必须是原创且不能是 Fork 版本。** +- 项目必须是完整的应用解决方案。 +- 项目必须有良好的代码注释和不断完善的使用文档。 +- 项目必须在 Gitee 平台托管且 Stars 关注量大于 100。 +- 项目至少在近 2 个月内有实际有效的代码提交记录。 +- **项目必须得到 dromara 社区组织评委会的认可。** +- 优先考虑 Gitee 推荐项目或已获得 GVP 的项目。(GVP 项目需联系 Gitee 官方人员取消 GVP 后才能转移到组织,转移成功后恢复 GVP) +- 优选考虑 容器工具、微服务框架和工具、分布式事务、分布式中间件、大数据处理、人工智能、IoT 物联网、开发/测试/运维相关工具链等领域项目。 + +### 源仓库影响 + +目前捐赠项目采用转移到 dromara 组织仓库方式进行处理,项目转移后会有以下影响: + +- gitee :`https://gitee.com/dromara/仓库名` +- github:`https://github.com/dromara/仓库名` + +项目源码中如果存在绝对路径的引用(图片,文件等)则需改为相对路径。 + +### 仓库权限 + +- dromara 组织负责人会给新加项目负责人设置超级管理员权限。 +- 项目 PMC/Committers 会被邀请加入 dromara 组织,设置相关读写权限。项目独立提交与运营。 +- 项目 PMC/Committers 会被分配`@dromara.org` 后缀邮箱。 + +### 仓库文档 + +- 原则上需要联系 dromara 官网负责人,在 `https://dromara.org` 进行更新转移文档 + +### 版本发布 + +- 原则上要求下一个版本,需要更改包名前缀为`org.dromara`, 并且发布到 MAVEN 仓库 + +### 组织/社区 + +- dromara 开源组织负责人或者秘书,邀请进入 dromara 社区群,并进行全员公告。 + +- dromara 开源社区公众号,发布欢迎加入文章,并同步到技术社区。 + +- 原仓库社区群加上 `dromara-xxx` 前缀。 + +### 捐赠项目退出事宜(初版) + +dromara 社区组织始终抱有积极开放宽容的态度,如果捐赠项目作者加入 dromara 组织后无法认同组织的发展理念或想转移到其他组织/个人,则可向 dromara 评委会申请退出。申请退出流程: + +1. 项目作者需书面或邮件编写项目退出事由并电邮至 [pmc@dromara.org](mailto:pmc@dromara.org) 。 +2. dromara 评委会收到申请后第一时间与项目作者沟通并确认退出意向,若作者执意退出,则批准处理。 +3. 特别注意,如果申请退出的项目是 GVP 项目,则需联系 Gitee 官方人员取消 GVP 后转回个人名下,转回成功后联系 Gitee 官方人员恢复即可。非 GVP 项目可直接操作。 +4. 项目退出后,dromara 组织会在各大平台和社区群进行公告推送告知,并将该项目历史记录进行归档保存。 + +最后,希望项目作者在考虑退出 dromara 组织之前可以先和我们沟通,指出 dromara 组织哪里做的不好的地方,以便我们能够改进,共同发展。 + +### 权责与组织形式 + +1. 社区内各个子项目团队,共享所有开源资源,包括且不限于组织品牌、社区公众号、官方社群、主流技术网站和媒体的专属账号和频道等。 +2. 原则上,每个项目内部管理自治,组织委员会由每个项目的主要负责人组成。 +3. dromara 组织相关决策事务(指定和修改规则、加入或者退出项目等),需要由组织委员会投票通过后执行。日常事务由组织的常任负责人处理。 +4. 在组织层面做出决策以后,每个项目的管理团队必须无条件服从组织,或者配合其他项目的一些工作和任务,做到整个组织在宣传活动、任务安排等方面口径一致。 +5. dromara 开源组织从 2021 年 3 月到 2022 年 3 月进入试运行阶段,本原则为组织的试运行指导方案,各个子项目在此期间,在不违反法律、道德原则,不涉及抄袭侵权,以及不伤害组织,不对社区和用户造成恶劣影响的前提下,拥有完整的自治和管理权限、以及项目子品牌的著作权和实际拥有权。 +6. 在试运行期内,社区将成立正式的委员会,对于后续社区发展方式,子项目的管理和权限约束,出台正式的规则,并经由投票通过后生效。 + +### 以上规定 dromara 组委会有最终解释权 + +## 赞助/支持 + +加入知识星球 + + + +**Dromara** 社区能够持续运营和提供更加优质的服务离不开大家的支持。如果想成为 **Dromara** 社区赞助商或支持者,请考虑联系: + +[xiaoyu@dromara.org](mailto:xiaoyu%40dromara.org) + + + +**Dromara** 社区承诺将收到的所有赞助支持资金完全公开化,且后续资金用途仅限于 **Dromara** 社区运营支出。赞助任何事情,可以联系 **** 目前赞助商有(没有显示名称的赞助者,默认:无名): + +| 赞助商 | 赞助金额(元) | 附语 | 赞助日期 | +| ------------------- | -------------- | --------------------------------------------------------- | ---------- | +| 椰子 | 100 | | 2024-10-14 | +| 无名 | 200 | 一如既往的支持!感恩相遇! | 2024-10-08 | +| 岳秋林 | 1 | 用过 hutool,超过 sa-token,支持开源 | 2024-08-23 | +| 无名 | 200 | 喝杯咖啡 | 2024-08-05 | +| one-fit | 100 | 希望 dromara 组织能成为中国的 apache | 2024-07-19 | +| 无名 | 100 | 加油!朋友 | 2024-06-04 | +| 无名 | 100 | 朋友,你好! | 2024-05-06 | +| 无名 | 100 | | 2024-04-14 | +| KoKo | 20 | 开源万岁 | 2024-03-13 | +| 无名 | 100 | 朋友们,元宵节快乐 | 2024-02-24 | +| IILee | 20 | 感谢为中国开源做出的贡献 | 2024-01-25 | +| 无名 | 5 | 加油 | 2023-11-06 | +| 无名 | 100 | 感恩相遇 | 2023-10-18 | +| 无名 | 6.66 | 封神之作 | 2023-07-28 | +| 无名 | 50 | | 2023-07-24 | +| JacKey | 10 | | 2023-07-06 | +| 呼噜 | 1 | 买个卫龙 | 2023-06-14 | +| 连洁 | 20 | 小小赞赏 | 2023-05-26 | +| 一夏千晨 | 50 | 一包辣条奉上 | 2023-05-22 | +| 无名 | 50 | | 2023-05-22 | +| 无名 | 100 | 五一快乐(勿回应) | 2023-04-29 | +| meta.木予 | 10 | | 2023-04-26 | +| 无名 | 100 | 纯粹赞助(无需回应) | 2023-04-24 | +| Java 开发小王 | 1 | 很强 | 2023-04-20 | +| 令狐冲 | 10 | 加油 | 2023-04-12 | +| 如愿 | 10 | 猫大人帅哥,祝社区越来越好 | 2023-04-04 | +| cszj | 100 | 帮助很大,希望越来越好 | 2023-04-03 | +| 海子 | 50 | 感谢诸位无私的奉献 | 2023-04-02 | +| 西岭千秋雪 | 50 | | 2023-03-29 | +| 是白菜 | 1 | | 2023-03-28 | +| 泛微网络刘启成 | 50 | 学习 ee,对我非常有帮助,感谢 | 2023-03-27 | +| 无名 | 10 | 加油! | 2023-03-19 | +| 无名 | 100 | 加油! | 2023-03-16 | +| Yyy | 1 | | 2023-03-01 | +| 无名 | 100 | | 2023-02-28 | +| Evan | 20 | | 2023-02-26 | +| 朔 | 10 | 钱不多,但是心意是满满的,加油! | 2023-02-24 | +| 无名 | 200 | | 2023-02-05 | +| 方 | 28.88 | 新年快乐,感谢开源 | 2023-01-21 | +| 无名 | 200 | 祝社区的兄弟姐妹们新年快乐 | 2023-01-21 | +| 无名 | 0.26 | | 2023-01-11 | +| 无名 | 50 | | 2023-01-06 | +| 紫气东来 | 50 | 感谢开源,坚持信念 | 2022-12-14 | +| 枫原万耶 | 16.66 | | 2022-11-13 | +| 岁月无声 | 10.24 | 希望越来越好 | 2022-11-10 | +| 邵昌明 | 20 | 买雪糕吃 | 2022-11-02 | +| 无名 | 1 | | 2022-10-27 | +| 大熊同学 | 9.9 | 希望我们开源社区长长久久! | 2022-10-26 | +| 曙光 | 50 | | 2022-10-25 | +| 无名 | 1 | Zdp 奉上 | 2022-10-04 | +| 无名 | 200 | | 2022-09-28 | +| 无名 | 200 | | 2022-09-28 | +| 无名 | 200 | | 2022-09-28 | +| 晚风 | 100 | 从接触 hutool 开始就喜欢上了,太好用了 | 2022-09-08 | +| 无名 | 100 | 支持开源,加油! | 2022-09-07 | +| 陈乐辉 | 100 | 祝开源的路上越走越远 | 2022-08-12 | +| 阿超 | 50 | 开源就是人人为我,我为人人 | 2022-07-27 | +| QZWML | 10 | | 2022-07-22 | +| 无名 | 100 | | 2022-06-30 | +| Eclipse | 20 | 支持! | 2022-06-28 | +| 朋云 | 10 | | 2022-06-24 | +| 红豆生南国 | 10 | 吃个雪糕 | 2022-06-22 | +| 修理工 | 80 | 代码规范,值得学习,用的方便,强烈推荐 | 2022-05-27 | +| 苗对我很重要 | 10 | | 2022-05-19 | +| 王磊 | 50 | | 2022-05-19 | +| Hi | 10.24 | 感谢开发 | 2022-04-24 | +| 哈哈 | 66.66 | 支持猫大人,祝社区越来越好~ | 2022-04-14 | +| liming | 50 | 祝社区越来越好! | 2022-04-14 | +| 胡泰室 | 50 | 祝社区越来越好 | 2022-04-14 | +| 金卫信曾小燕 | 10.24 | | 2022-04-14 | +| Chris | 150 | | 2022-04-12 | +| 无名 | 100 | | 2022-04-05 | +| 青衫烟雨客 | 10 | | 2022-03-20 | +| Zack | 20 | 省了我好多事,太 nb 了!!支持下 | 2022-03-10 | +| 许伟超 | 10 | 感恩,尽我绵薄之力 | 2022-03-09 | +| 无名 | 10 | 虽然钱不多,但是我会一直打赏 | 2022-03-09 | +| ^\_^ | 20 | 虎年吉祥 | 2022-02-03 | +| 无名 | 166 | 祝社区能做的越来越好 | 2022-01-17 | +| 吴南方 | 20 | 希望 hutool 能做的越来越好 | 2022-01-09 | +| 忘忧 | 50 | | 2021-12-30 | +| 达 | 5 | hutool 省了很多重复造轮子的时间,特别棒,小小心意赞助一下 | 2021-12-24 | +| 吃瓜群众丙 | 100 | 为往圣继绝学。加油吧,少年。 | 2021-12-23 | +| 早点下班吧 | 10 | | 2021-12-14 | +| Amiron 今天早睡了吗 | 20 | | 2021-12-02 | +| phantom | 100 | | 2021-11-30 | +| 无名 | 11.27 | 感谢,表示支持 | 2021-11-27 | +| 夏和顺顺 | 50 | 为往圣继绝学 | 2021-11-25 | +| Chris | 100 | | 2021-11-24 | +| 光明星辰 | 50 | 辣条钱 | 2021-11-04 | +| 无名 | 3 | 辣条钱 | 2021-10-29 | +| 奥利弗 | 15 | | 2021-10-13 | +| XXXL | 50 | 聊表心意,祝越来越好 | 2021-09-30 | +| 用用 | 20 | | 2021-09-17 | +| 宋不醒 | 10 | 加油 | 2021-09-10 | +| 🐶 | 20 | 聊表心意,有大用了 | 2021-08-24 | +| Coffee | 10 | 冲冲 | 2021-07-29 | +| aliyu | 10 | aly | 2021-07-22 | +| 段大志 | 20 | 国产开源蒸蒸日上 | 2021-07-14 | +| 喜世 | 20 | 感谢铁锚助教辛勤解答问题 | 2021-06-28 | +| Group | 1.00 | 蚊子腿也是肉 | 2021-06-08 | +| 大大的时间小小的我 | 20 | | 2021-05-26 | +| Leo | 15 | 真心不错,加油! | 2021-05-20 | +| 辉辉同学 | 66.66 | hutool 工具个人认为是封神佳作,顶礼膜拜! | 2021-05-19 | +| 0X17 | 100 | | 2021-05-16 | +| 鑫爷 | 16.8 | fans99 | 2021-05-11 | +| こうう | 3 | | 2021-05-06 | +| 一只想要奋斗的咸鱼 | 66 | | 2021-04-29 | +| 锄禾 | 20 | 能力有限,聊表心意 | 2021-04-27 | +| 然而 | 64 | 越开源越进步 | 2021-04-23 | +| 路,还很远 | 20 | 国产开源牛逼 | 2021-04-23 | +| 纳兰-曼孚 | 20 | | 2021-04-15 | +| Stone | 200 | 跟着猫大人搞开源,芜湖起飞 | 2021-04-14 | +| 疯子 | 200 | 国产开源,加油! | 2021-04-14 | +| tom | 50 | 祝越来越好 | 2021-04-14 | +| 子豪 sirius | 50 | 支持猫大人 | 2021-04-14 | +| Sissie | 50 | 感谢开源,支持喵大人 | 2021-04-14 | +| WilliamSky | 50 | 感谢开源,支持喵大人 | 2021-04-14 | +| Sink | 10 | 向大佬致敬 | 2021-04-14 | +| 石启蒙 | 10 | 祝开源社区越来越好 | 2021-04-14 | +| 吴俊杰\_ken | 66.88 | 学习加交流,社区越来越好 | 2021-04-13 | +| 正是在下李某人 | 20 | 猫大人牛批 | 2021-04-13 | +| Zhw | 400 | 做大做强,再创辉煌 | 2021-04-13 | +| 彭伟伦 | 20 | 猫大人的华子 | 2021-04-13 | +| 心中的明月 | 30 | Tshirt 好漂亮,好想要 | 2021-04-13 | +| 刘进 | 20 | 撸下胸毛 | 2021-04-13 | +| 黄灿达 | 20 | 支持 | 2021-04-13 | +| 赵镇 | 33 | 做大做强,再创辉煌 | 2021-04-13 | +| kimmking | 2000 | 祝福社区越来越好 | 2021-04-12 | +| John | 400 | 猫大人抽华子 | 2021-04-12 | +| Charlyfeng | 10 | 为往圣继绝学 | 2021-04-12 | +| 梁胜芳 | 6.66 | 祝组织越来越好 | 2021-04-12 | +| Chris | 200 | 祝福社区越来越好 | 2021-04-11 | +| Jtiag | 20 | 做大做强 | 2021-04-02 | +| Nelson | 66 | 做大做强,走向辉煌 | 2021-04-02 | +| 好方 | 60 | 我们一起学猫叫,一起喵喵喵喵喵~ | 2021-04-02 | +| 哼干嘛 | 20 | 加入的第一个组织,感谢猫大人 | 2021-04-01 | +| 静晓晨曦 | 20 | 支持猫大人,祝福社区越来越好 | 2021-03-31 | +| Attract | 20 | 聊表心意,祝福社区越来越好 | 2021-03-31 | +| 杨大侠 | 60 | 跟着猫大人搞开源 | 2021-03-31 | +| ian | 200 | Dromara 最棒 | 2021-03-31 | +| 白 | 20 | | 2021-03-31 | +| 静静 | 80 | 强 | 2021-03-31 | +| blizzard | 20 | 感谢猫大人花费巨大的心血 | 2021-03-31 | +| Jenkins | 100 | 参加开源,共建社区 | 2021-03-31 | +| Sean | 128 | 祝社区越走越远 | 2021-03-31 | +| 肖邦 | 20 | 全力支持参与开源贡献自己的微博力量 | 2021-03-31 | +| Ted 丶 L | 100 | 祝社区越办越好 | 2021-03-31 | +| hb | 60 | 支持开源 | 2021-03-31 | +| 大黄蜂 | 60 | 感谢猫大人,请猫大人抽根华子 | 2021-03-30 | +| 半盏清茶 | 20 | 越来越好 | 2021-03-30 | +| 小旭 | 20 | | 2021-03-30 | +| 阳有白 | 60 | 请猫大人喝几吨白开水 | 2021-03-30 | +| 韩小波 | 20 | 中国开源加油 | 2021-03-30 | +| Vilochen | 60 | 希望猫大人的开源组织越来越壮大 | 2021-03-30 | +| 滴流乱转的小胖子 | 60 | 向猫大人老师致敬 | 2021-03-30 | +| 泊 | 20 | 支持猫大人的开源 | 2021-03-30 | +| 赵瑞 | 20 | | 2021-03-30 | +| 心想事成 | 1 | | 2021-03-30 | +| 李 | 20 | 支持一下 | 2021-03-30 | +| Yul | 20 | | 2021-03-30 | +| bing | 20 | | 2021-03-30 | +| 榕 | 20 | | 2021-03-30 | +| keepCarry | 160 | dromara 永存 | 2021-03-30 | +| xiaochun | 20 | 猫大人加油,看好你哦 | 2021-03-30 | +| 梁超-ISAAC | 20 | 支持支持 | 2021-03-30 | +| 子豪 sirius | 20 | 支持开源 | 2021-03-30 | +| 瓦让 | 80 | 大家一起加油 | 2021-03-30 | +| wyj | 20 | 支持支持 | 2021-03-30 | +| 小小的太阳 | 20 | 猫大人牛批 | 2021-03-30 | +| 小雨淅淅 | 20 | 请猫大人喝奶茶 | 2021-03-30 | +| 苦了芥末 | 20 | | 2021-03-30 | +| 成龙 | 50 | 谢谢猫大人对于开源的贡献,感谢引路 | 2021-03-30 | +| Chasen | 80 | 哈哈,支持支持 | 2021-03-30 | +| Menglg | 20 | | 2021-03-30 | +| 闫兵 | 66.66 | | 2021-03-30 | +| 王一飞 | 60 | | 2021-03-30 | +| 张民鹏 | 60 | 祝 Dromara 社区越办越好 | 2021-03-30 | +| Jasper | 60 | 猫大人牛逼! | 2021-03-30 | +| 子不语 | 60 | 一直想做开源,这也是离我最近的社区,社区加油! | 2021-03-30 | +| 阿行 | 20 | 猫大人的小迷弟 | 2021-03-30 | +| Wincher | 10.24 | 为往圣继绝学 | 2021-03-30 | +| Marcus | 20 | 一个人可能走的更快,但一群人会走得更远 | 2021-03-30 | +| 欧葵 | 60 | 猫大人加油 | 2021-03-30 | +| 有。无。 | 20 | | 2021-03-30 | +| 哲一 | 20 | | 2021-03-30 | +| 刘耿华 | 66 | 猫大人牛逼 | 2021-03-30 | +| 乔帝鸽 | 20 | 做大做强,再创辉煌 | 2021-03-30 | +| 和尘同光 | 20 | 请猫大人喝咖啡 | 2021-03-30 | +| ~zZZachary | 60 | | 2021-03-30 | +| 唐甜 | 20 | 跟着猫大人做开源 | 2021-03-30 | +| 小程故事多 | 60 | 加油,一如既往的支持 | 2021-03-30 | +| 东东 | 60 | 做大做强 | 2021-03-30 | +| Truing | 20 | 猫大人牛逼 | 2021-03-30 | +| sikm | 20 | 为往圣继绝学,大风起不可当石立 | 2021-03-30 | +| One Day | 299 | | 2021-03-30 | +| jihe | 20 | 开源冲 | 2021-03-30 | +| 豆豆 | 99.99 | 支持开源 | 2021-03-30 | +| 斯普特尼克 | 60 | 祝社区越来越繁荣 | 2021-03-30 | +| orange | 20 | 猫大人帅气 | 2021-03-30 | +| 钟如雷 | 200 | 请老板抽和天下 | 2021-03-30 | +| Goku | 20 | 加油,愿社区越来越好 | 2021-03-30 | +| Try Everything | 20 | 支持猫大人 | 2021-03-30 | +| 郡鸿 | 20 | 支持猫大人,支持开源 | 2021-03-30 | +| FangQ | 20 | 猫大人加油,Dromara 加油 | 2021-03-30 | +| cycle | 120 | 愿中国的开源事业越来越好,猫大人及其同仁们威武 | 2021-03-30 | +| 夜羽 | 20 | 向开源精神,脱帽致敬 | 2021-03-30 | +| SUDONG | 80 | | 2021-03-30 | +| 婷 | 20 | 猫大人牛批,超大声! | 2021-03-30 | +| Tusi | 20 | | 2021-03-30 | +| 任富飞 | 88 | 越来越好 | 2021-03-30 | +| 时间会咬人 | 60 | 再接再励,做大做强 | 2021-03-30 | +| sakila | 60 | 猫大人抽华子 | 2021-03-30 | +| 虎宝 | 66.66 | 婷宝&虎宝,崔猫求疵 | 2021-03-30 | +| 一条小青龙 | 20 | 请猫大人吃鸡排 | 2021-03-30 | +| 徐培 | 60 | 请猫大人抽华子 | 2021-03-30 | +| pengshao | 20 | 猫大人加油 | 2021-03-30 | +| F.C | 20 | 猫大人威武,祝 Dromara 社区越办越好 | 2021-03-30 | +| hellboy0621 | 66.66 | 为往圣继绝学 | 2021-03-30 | +| 吴哭哭 | 60 | 精神股东 | 2021-03-30 | +| 不忘初心 | 120 | | 2021-03-29 | +| 陈宏 | 20 | | 2021-03-29 | +| 恬恬好甜 | 20 | | 2021-03-29 | +| 瑞 | 120 | | 2021-03-29 | +| Uncle George | 10.24 | | 2021-03-29 | +| Tree | 20 | | 2021-03-29 | +| Shallwetalk | 20 | | 2021-03-29 | +| 莫生 | 60 | 请猫大人抽包华子 | 2021-03-29 | +| YEZJ | 20 | | 2021-03-29 | +| Silent | 60 | | 2021-03-29 | +| SIGN | 20 | 猫大人的狸猫 | 2021-03-29 | +| 张\* | 20 | | 2021-03-29 | +| 荼九 | 66 | | 2021-03-29 | +| ? | 20 | | 2021-03-29 | +| 睡觉大王小汤 | 1 | | 2021-03-29 | +| 守候 | 20 | 猫大人牛逼 | 2021-03-29 | +| Bruce | 10 | | 2021-03-29 | +| 晏鹏 | 10.24 | 猫大人加油 | 2021-03-29 | +| 一条小路的距离 | 20 | 猫大人的华子 | 2021-03-29 | +| 嘉宝 | 80 | 请师父喝咖啡 | 2021-03-29 | +| PeiXy | 60 | 猫大人的胸毛 | 2021-03-29 | +| L3nvy | 13 | 猫大人 lol 分队第一塞拉斯 | 2021-03-29 | +| comforyo | 20 | | 2021-03-29 | +| 莫白 | 9.99 | 感恩 | 2021-03-29 | +| 做成一件小事 | 20 | 猫大人的小学徒 | 2021-03-29 | +| zendwang | 20 | | 2021-03-29 | +| 星尘 | 9.99 | 气氛搞起来 | 2021-03-29 | +| 萧 | 60 | 为往圣继绝学 | 2021-03-29 | +| 陈龙 | 20 | 开源无敌,拒绝 996 | 2021-03-29 | +| \*\*涛 | 50 | | 2021-03-29 | +| yarne | 66.66 | 共同的事业,深深的友情 | 2021-03-29 | +| E.S. | 60 | 大锅,喝阔落! | 2021-03-29 | +| 东江 DJ | 20 | | 2021-03-29 | +| ZhW | 20 | | 2021-03-29 | +| Mr...犯 | 20 | | 2021-03-29 | +| 林 | 20 | | 2021-03-29 | +| 天地 | 20 | | 2021-03-29 | +| 项峥 | 20 | 猫大人牛逼 | 2021-03-29 | +| Easley | 200 | 猫大人牛逼 | 2021-03-29 | +| 秦旭 | 60 | | 2021-03-29 | +| Neal | 20 | 猫大人的错别字检查员 | 2021-03-29 | +| Chovi.Wu | 10.24 | 表哥加油 | 2021-03-29 | +| Niverkk | 20 | | 2021-03-29 | +| ~阿槑~ | 20 | | 2021-03-29 | +| Witt | 20 | 支持猫大人 | 2021-03-29 | +| 屈仁能 | 218.88 | 猫大人家的菜鸟 | 2021-03-29 | +| 侯瑞哲 | 20 | 请猫大人喝咖啡 | 2021-03-29 | +| Phoenix | 20 | 感谢猫大人带我们认识开源 | 2021-03-29 | +| 黄放 | 20 | 为往圣继绝学 | 2021-03-29 | +| 那会 | 20 | 感谢猫大人 | 2021-03-29 | +| Liquid | 20 | | 2021-03-29 | +| audi | 15 | 喵小喵 | 2021-03-29 | +| KaitoShy | 6.66 | | 2021-03-29 | +| 氺 | 20 | | 2021-03-29 | +| Ncy | 20 | 喵喵喵 | 2021-03-29 | +| HelloWorld | 20 | 靡不有初,鲜克有终 | 2021-03-29 | +| 孙龙 | 60 | | 2021-03-29 | +| maybe | 10.24 | | 2021-03-29 | +| biao | 20 | | 2021-03-29 | +| BetterWp | 66.66 | 跟着猫大人冲 | 2021-03-29 | +| 502819 | 20 | 猫大人威武霸气 | 2021-03-29 | +| Z | 10 | 猫大人抽华子 | 2021-03-29 | +| 陈曦 | 66.66 | | 2021-03-29 | +| 一朵云 | 20 | | 2021-03-29 | +| 姜吉宁 | 60 | 做大做强 | 2021-03-29 | +| 飞鸿雪泥 | 60 | 开源万岁 | 2021-03-29 | diff --git a/src/zh/members/README.md b/src/zh/members/README.md index 1c9114bb32..516153de0b 100644 --- a/src/zh/members/README.md +++ b/src/zh/members/README.md @@ -4,24 +4,8 @@ pageInfo: false contributors: false editLink: false lastUpdated: false +sidebar: false +layout: members --- - - - - diff --git a/src/zh/news/Akali-0.md b/src/zh/news/Akali-0.md index d90bd624d4..a8685cac84 100644 --- a/src/zh/news/Akali-0.md +++ b/src/zh/news/Akali-0.md @@ -1,104 +1,106 @@ ---- -title: Dromara社区新晋开源项目-Akali(阿卡丽),轻量化的热点&降级处理框架! -author: 铂赛东 -date: 2023-11-17 -cover: /assets/img/news/Akali-0-0.png -head: - - - meta - - name: 新闻 ---- - -![](/assets/img/news/Akali-0-0.png) - -## 前言 - -Dromara 社区再添一个成员项目! - -今天为大家介绍的是——Akali。 - -它轻量小巧,来无影去无踪,不足 500 行代码,却能解决高流量场景中主要的问题:热点处理和降级处理。 - -## 介绍 - -Akali(阿卡丽)是一个轻量级本地化热点检测/降级框架,适用于大流量场景,可轻松解决业务中超高流量的并发查询等场景。并且接入和使用极其简单,10 秒钟即可接入使用! - -Akali 框架的理念就是小巧,实用,丝血团战,满血退场,所到之处,皆为虚无。 - -Gitee:https://gitee.com/dromara/Akali - -Github:https://github.com/bryan31/Akali - -官方网站:https://akali.yomahub.com/ - -## 使用 - -引入依赖: - - - com.yomahub - akali - 1.0.1 - - -## 对任意方法进行热点处理 - -只需要加上`@AkaliHot`这个标注,任意方法均可以获得热点检测,并在热点期间用热点数据进行返回,在热点过后,又会自动调用原本业务逻辑。 - -举例:比如有一个商品查询的业务,传入 SkuCode,返回商品信息。当某个商品进行促销时,访问的量就会增加,但是对于相同的 SkuCode 而言,其短时间窗口内返回的 SkuInfo 是一致的,我们的目标是当某个商品 sku 被大量查询时,框架能够在短时间内把这个商品 sku 提为热点数据,并通过对其进行缓存返回来降低对下游业务的压力。而当热点值过后,框架又能够自动摘除这个热点值,使其按照原有方式进行查询。 - -其本质相当于实时的监测了热点,并对其热点数据做了一个短时间内的缓存。 - -以下示例代表了:当相同的 skuCode 在 5 秒内超过 50 次调用时,会自动把这个 skuCode 的值提为热点,并用最后一次的返回值直接返回。当调用低于 5 秒 50 次调用时,框架会自动的摘除掉这个热点。使其正常的调用你原有代码进行逻辑计算并返回。这一切都是自动的。 - -@AkaliHot(grade = FlowGradeEnum.FLOW_GRADE_QPS, count = 50, duration = 5) -public SkuInfo getSkuInfo(String skuCode){ - //do your biz and return sku info -} - -其中`grade`参数除了有以`QPS`作为维度统计,还有以`Thread`个数作为维度统计。比如: - -@AkaliHot(grade = FlowGradeEnum.FLOW_GRADE_THREAD, count = 50, duration = 5) -public SkuInfo getSkuInfo(String skuCode){ - //do your biz and return sku info -} - -这就代表了,如果某个 skuCode 在 5 秒之内有超过 50 个线程正在运行,那么就提为热点,并用热点数据直接返回。 - -对开源项目比较熟悉的同学看到这肯定想到了京东的框架-`hotkey`,`Akali`不同于`hotkey`,完全是本地运行的,不依赖于服务端,而且接入比`hotkey` 方便多了。性能完全相当于`hotkey`。 - -## 对任意方法进行降级 - -只需要加上`@AkaliFallback`注解。任意方法均可获得降级功能。 - -举例:某一个方法需要调用外部的接口,但是外部的接口性能不佳,耗时高。当并发一高时,线程池就会吃满,线程池队列也会逐渐堆积从而导致超时,或者丢弃,严重时会拖垮整个系统。 - -这时,我们只要对这个方法加上`@AkaliFallback`标注,即可解决。 - -@AkaliFallback(grade = FlowGradeEnum.FLOW_GRADE_THREAD, count = 100) -public String sayHi(String name){ - return "hi,"+name; -} - -public String sayHiFallback(String name){ - return "fallback str"; -} - -以上注解表示了,当这个方法的同时运行的线程超过 100 个时,触发降级,降级会自动调用`原方法名+Fallback`方法名(并且参数要一致),当降级触发后会直接返回`fallback str`,当线程数小于 100 时,框架也会自动摘除降级,还是输出`hi,xxxx`。 - -如果你的类中没有定义 fallback 方法,那么触发降级时会报错,当然你可以在降级方法中去抛错,来让上游系统知道你这个方法已经达到了瓶颈。 - -## 注意事项 - -Akali 只针对于 Springboot,Spring 环境,并且所有标注了`@AkaliHot`或者`@AkaliFallback`的类一定得注册到 spring 上下文中。 - -Akali 在 springboot 中会自动扫描所有标注的类,您无需做任何配置,在 spring 中,你需要配置: - - - - - -## 最后 - -如果大家感兴趣的话,请在 Gitee 上为 Akali 点上小星星哦。 - -Gitee:https://gitee.com/dromara/Akali +--- +title: Dromara社区新晋开源项目-Akali(阿卡丽),轻量化的热点&降级处理框架! +author: 铂赛东 +date: 2023-11-17 +cover: /assets/img/news/Akali-0-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/Akali-0-0.png) + +## 前言 + +Dromara 社区再添一个成员项目! + +今天为大家介绍的是——Akali。 + +它轻量小巧,来无影去无踪,不足 500 行代码,却能解决高流量场景中主要的问题:热点处理和降级处理。 + +## 介绍 + +Akali(阿卡丽)是一个轻量级本地化热点检测/降级框架,适用于大流量场景,可轻松解决业务中超高流量的并发查询等场景。并且接入和使用极其简单,10 秒钟即可接入使用! + +Akali 框架的理念就是小巧,实用,丝血团战,满血退场,所到之处,皆为虚无。 + +Gitee:https://gitee.com/dromara/Akali + +Github:https://github.com/bryan31/Akali + +官方网站:https://akali.yomahub.com/ + +## 使用 + +引入依赖: +```xml + + com.yomahub + akali + 1.0.1 + +``` + +## 对任意方法进行热点处理 + +只需要加上`@AkaliHot`这个标注,任意方法均可以获得热点检测,并在热点期间用热点数据进行返回,在热点过后,又会自动调用原本业务逻辑。 + +举例:比如有一个商品查询的业务,传入 SkuCode,返回商品信息。当某个商品进行促销时,访问的量就会增加,但是对于相同的 SkuCode 而言,其短时间窗口内返回的 SkuInfo 是一致的,我们的目标是当某个商品 sku 被大量查询时,框架能够在短时间内把这个商品 sku 提为热点数据,并通过对其进行缓存返回来降低对下游业务的压力。而当热点值过后,框架又能够自动摘除这个热点值,使其按照原有方式进行查询。 + +其本质相当于实时的监测了热点,并对其热点数据做了一个短时间内的缓存。 + +以下示例代表了:当相同的 skuCode 在 5 秒内超过 50 次调用时,会自动把这个 skuCode 的值提为热点,并用最后一次的返回值直接返回。当调用低于 5 秒 50 次调用时,框架会自动的摘除掉这个热点。使其正常的调用你原有代码进行逻辑计算并返回。这一切都是自动的。 + +@AkaliHot(grade = FlowGradeEnum.FLOW_GRADE_QPS, count = 50, duration = 5) +public SkuInfo getSkuInfo(String skuCode){ + //do your biz and return sku info +} + +其中`grade`参数除了有以`QPS`作为维度统计,还有以`Thread`个数作为维度统计。比如: + +@AkaliHot(grade = FlowGradeEnum.FLOW_GRADE_THREAD, count = 50, duration = 5) +public SkuInfo getSkuInfo(String skuCode){ + //do your biz and return sku info +} + +这就代表了,如果某个 skuCode 在 5 秒之内有超过 50 个线程正在运行,那么就提为热点,并用热点数据直接返回。 + +对开源项目比较熟悉的同学看到这肯定想到了京东的框架-`hotkey`,`Akali`不同于`hotkey`,完全是本地运行的,不依赖于服务端,而且接入比`hotkey` 方便多了。性能完全相当于`hotkey`。 + +## 对任意方法进行降级 + +只需要加上`@AkaliFallback`注解。任意方法均可获得降级功能。 + +举例:某一个方法需要调用外部的接口,但是外部的接口性能不佳,耗时高。当并发一高时,线程池就会吃满,线程池队列也会逐渐堆积从而导致超时,或者丢弃,严重时会拖垮整个系统。 + +这时,我们只要对这个方法加上`@AkaliFallback`标注,即可解决。 + +@AkaliFallback(grade = FlowGradeEnum.FLOW_GRADE_THREAD, count = 100) +public String sayHi(String name){ + return "hi,"+name; +} + +public String sayHiFallback(String name){ + return "fallback str"; +} + +以上注解表示了,当这个方法的同时运行的线程超过 100 个时,触发降级,降级会自动调用`原方法名+Fallback`方法名(并且参数要一致),当降级触发后会直接返回`fallback str`,当线程数小于 100 时,框架也会自动摘除降级,还是输出`hi,xxxx`。 + +如果你的类中没有定义 fallback 方法,那么触发降级时会报错,当然你可以在降级方法中去抛错,来让上游系统知道你这个方法已经达到了瓶颈。 + +## 注意事项 + +Akali 只针对于 Springboot,Spring 环境,并且所有标注了`@AkaliHot`或者`@AkaliFallback`的类一定得注册到 spring 上下文中。 + +Akali 在 springboot 中会自动扫描所有标注的类,您无需做任何配置,在 spring 中,你需要配置: + +```xml + + + +``` +## 最后 + +如果大家感兴趣的话,请在 Gitee 上为 Akali 点上小星星哦。 + +Gitee:https://gitee.com/dromara/Akali diff --git a/src/zh/news/Apache ShenYu-2.7.0.md b/src/zh/news/Apache ShenYu-2.7.0.md new file mode 100644 index 0000000000..db0578ada3 --- /dev/null +++ b/src/zh/news/Apache ShenYu-2.7.0.md @@ -0,0 +1,864 @@ +--- +title: Apache ShenYu 2.7.0 Released!Apache ShenYu 2.7.0 发布! +author: 刘宏宇 +date: 2025-02-13 +cover: /assets/img/news/Apache ShenYu-2.7.0-0.png +head: + - - meta + - name: 新闻 +--- + + + + + +关于Apache ShenYu  关于 Apache ShenYu + +**Apache ShenYu** 一款使用 **Java Reactor** 开发的响应式**API** 网关。以其高性能,动态灵活的流量管控,热插拔,易部署等特性,开箱即用为用户提供整套全生命周期的 **API**网关,包含 **API**注册、服务代理、协议转换、**API**文档与 **API**治理等功能。 + + + +官网: https://shenyu.apache.org +官网: https://shenyu.apache.org + +GitHub: https://github.com/apache/shenyu +GitHub: https://github.com/apache/shenyu + + + + + + +版本预览 + +时隔一年,**Apache ShenYu**发布了2.7.0版本,该版本共计提交了254+个 Pull Request,新增约17+个新特性,新增了若干增强,重构了若干功能,并且修复了若干个bug。共计61位贡献者参与其中,累计贡献者达350+位。 + + + +版本记录: + +https://github.com/apache/shenyu/compare/v2.6.0...v2.7.0 + + + + + +新特性 + + + +1\. 升级 Dockerfile java 版本从 jdk8 升级到 jdk17 + +pr: https://github.com/apache/shenyu/pull/5374 +公关: https://github.com/apache/shenyu/pull/5374 + + + +2\. 升级 SpringBoot 版本到 3.x + +pr: https://github.com/apache/shenyu/pull/5583 +公关: https://github.com/apache/shenyu/pull/5583 + + + +3\. 支持 ShenYu Admin 集群模式 + +pr:   公关: + +https://github.com/apache/shenyu/pull/5544 + +https://github.com/apache/shenyu/pull/5592 + + + +4\. 升级 checkstyle 插件版本到 3.4.0 + +pr: https://github.com/apache/shenyu/pull/5614 +公关: https://github.com/apache/shenyu/pull/5614 + + + +5\. 数据源支持 OceanBase + +pr: https://github.com/apache/shenyu/pull/5617 +公关: https://github.com/apache/shenyu/pull/5617 + + + +6\. 支持批量修改 选择器\\规则 的状态 + +pr: https://github.com/apache/shenyu/pull/5499 +公关: https://github.com/apache/shenyu/pull/5499 + + + +7\. 支持批量修改 AppAuth 的状态 + +pr: https://github.com/apache/shenyu/pull/5488 +公关: https://github.com/apache/shenyu/pull/5488 + + + +8\. 升级 apache dubbo 的版本 + +pr: https://github.com/apache/shenyu/pull/5527 +公关: https://github.com/apache/shenyu/pull/5527 + + + +9\. 支持 Gitpod 开发 + +pr: https://github.com/apache/shenyu/pull/5610 +公关: https://github.com/apache/shenyu/pull/5610 + + + +10\. 支持配置的导入和导出 + +pr: https://github.com/apache/shenyu/pull/5474 +公关: https://github.com/apache/shenyu/pull/5474 + + + +11\. 增加 shenyu 客户端心跳上报功能 + +pr: https://github.com/apache/shenyu/pull/5659 +公关: https://github.com/apache/shenyu/pull/5659 + + + +12\. 增加 Namespace 功能 + +pr:   公关: + +https://github.com/apache/shenyu/pull/5584 + +https://github.com/apache/shenyu/pull/5715 + +https://github.com/apache/shenyu/pull/5716 + +https://github.com/apache/shenyu/pull/5719 + +https://github.com/apache/shenyu/pull/5729 + +https://github.com/apache/shenyu/pull/5734 + +https://github.com/apache/shenyu/pull/5735 + +https://github.com/apache/shenyu/pull/5740 + +https://github.com/apache/shenyu/pull/5746 + +https://github.com/apache/shenyu/pull/5757 + +https://github.com/apache/shenyu/pull/5760 + +https://github.com/apache/shenyu/pull/5765 + +https://github.com/apache/shenyu/pull/5769 + +https://github.com/apache/shenyu/pull/5771 + +https://github.com/apache/shenyu/pull/5779 + +https://github.com/apache/shenyu/pull/5786 + +https://github.com/apache/shenyu/pull/5787 + +https://github.com/apache/shenyu/pull/5790 + +https://github.com/apache/shenyu/pull/5798 + +https://github.com/apache/shenyu/pull/5799 + +https://github.com/apache/shenyu/pull/5823 + +https://github.com/apache/shenyu/pull/5847 + +https://github.com/apache/shenyu/pull/5857 + + + +13\. 支持 k8s 动态扩缩容 + +pr: https://github.com/apache/shenyu/pull/5686 + + + +14\. 从新登录之后使之前的token失效 + +pr: https://github.com/apache/shenyu/pull/5600 + + + +15\. divide 插件支持灰度发布功能 + +pr: https://github.com/apache/shenyu/pull/5763 + + + +16\. 支持 Kubernetes 注册中心 + +pr: https://github.com/apache/shenyu/pull/5679 + + + + + +增强 + + + +1\. 添加 RocketMQ 日志 e2e 测试 + +pr: https://github.com/apache/shenyu/pull/5439 + + + +2\. 增强速率限制器的指标收集 + +pr: https://github.com/apache/shenyu/pull/5461 + + + +3\. 增强 Sentinel、Resilience4j 和 Hystrix 的指标收集 + +pr: https://github.com/apache/shenyu/pull/5468 + + + +4\. 整理 sofa-common-tools 的依赖 + +pr: https://github.com/apache/shenyu/pull/5609 + + + +5\. 添加缺失的许可证 + +pr: https://github.com/apache/shenyu/pull/5503 + + + +6\. 为 Kafka 消息发送设置回调 + +pr: https://github.com/apache/shenyu/pull/5748 + + + +7\. 使用 Dubbo 元数据中的负载均衡配置 + +pr: https://github.com/apache/shenyu/pull/5806 + + + +8\. 为从选择出获取的Upstream添加非空验证 + +pr: https://github.com/apache/shenyu/pull/5804 + + + +9\. 将规则处理中的超时设置到 Dubbo RPC 上下文 + +pr: https://github.com/apache/shenyu/pull/5778 + + + +10\. 在启用选择器和规则时发布事件 + +pr: https://github.com/apache/shenyu/pull/5762 + + + +11\. 从命名空间会话映射中移除已关闭的会话 + +pr: https://github.com/apache/shenyu/pull/5734 + + + +12\. 为 ShenyuClientURIExecutorSubscriber 添加测试用例 + +pr: https://github.com/apache/shenyu/pull/5413 + + + +13\. 为 ShenyuClientIllegalArgumentException 添加测试用例 + +pr: https://github.com/apache/shenyu/pull/5408 + + + +14\. 为 ShenyuClientRegisterEventPublisher 添加测试用例 + +pr: https://github.com/apache/shenyu/pull/5417 + + + +15\. 为 ShenyuClientMetadataExecutorSubscriber 添加测试用例 + +pr: https://github.com/apache/shenyu/pull/5404 + + + +16\. 为 AbstractWasmPluginDataHandler 添加测试用例 + +pr: https://github.com/apache/shenyu/pull/5451 + + + +17\. 为 ShenyuClientRegisterRepositoryFactoryTest 添加测试用例 + +pr: https://github.com/apache/shenyu/pull/5443 + + + +18\. 为 AbstractWasmDiscoveryHandler 添加测试用例 + +pr: https://github.com/apache/shenyu/pull/5453 + + + +19\. 升级 SOFA RPC 版本支持 + +pr: https://github.com/apache/shenyu/pull/5526 + + + +20\. 将签名插件的请求头键添加到跨域过滤器配置中 + +pr: https://github.com/apache/shenyu/pull/5627 + + + +21\. 加密密码 + +pr: https://github.com/apache/shenyu/pull/5436 + + + +22\. 添加 AbstractShenyuWasmPlugin 测试用例 + +pr: https://github.com/apache/shenyu/pull/5450 + + + +23\. 重写插件/上下文路径插件支持跨应用和插件 + +pr: https://github.com/apache/shenyu/pull/5438 + + + +24\. 移除重复路径检查 + +pr: https://github.com/apache/shenyu/pull/5514 + + + +25\. 移除 Alibaba Dubbo 相关依赖 + +pr: https://github.com/apache/shenyu/pull/5500 + + + +26\. 支持通过 Docker 环境变量设置 HTTP 路径 + +pr: https://github.com/apache/shenyu/pull/5833 + + + +27\. 添加代码重构改进 + +pr: https://github.com/apache/shenyu/pull/5613 + + + +28\. 支持从 cookie、header、param 中获取令牌 + +pr: https://github.com/apache/shenyu/pull/5547 + + + +29\. 调整使得ShenyuDubboService注解的默认值与 DubboService 注解保持一致 + +pr: https://github.com/apache/shenyu/pull/5816 + + + +30\. 将数据库脚本添加到管理包中 + +pr: https://github.com/apache/shenyu/pull/5724 + + + +31\. 清理无用代码并进行改进 + +pr:  + +https://github.com/apache/shenyu/pull/5849 + + + +https://github.com/apache/shenyu/pull/5803 + + + +https://github.com/apache/shenyu/pull/5789 + + + + + +32\. 优化 MotanServiceEventListener 测试用例 + +pr: https://github.com/apache/shenyu/pull/5745 + + + +33\. 删除 shenyu-registry-eureka.xml 中重复的 Maven 配置 + +pr: https://github.com/apache/shenyu/pull/5836 + + + +34\. 更新 JWT 依赖 + +pr: https://github.com/apache/shenyu/pull/5480 + + + +35\. 打印插件执行时间 + +pr: https://github.com/apache/shenyu/pull/5437 + + + +36\. Admin 中的本地发现支持 upstream 健康检查 + +pr: https://github.com/apache/shenyu/pull/5596 + + + +37\. 关闭规则缓存 + +pr: https://github.com/apache/shenyu/pull/5589 + + + +38\. 减少并发 + +pr: https://github.com/apache/shenyu/pull/5587 + + + +39\. 优化逻辑以避免 "orElse" 执行,更新 VersionTwoExtractor + +pr: https://github.com/apache/shenyu/pull/5415 + + + + + +重构 + + + + + +1\. 使用 spring-integration-jdbc 实现 Admin 分布式锁 + +pr: https://github.com/apache/shenyu/pull/5457 + + + +2\. 重构 beanUtils + +pr: https://github.com/apache/shenyu/pull/5497 + + + +3\. 移除 macOS CI + +pr: https://github.com/apache/shenyu/pull/5559 + + + +4\. 更新日志插件中已弃用的 DataBuffer 方法 + +pr: https://github.com/apache/shenyu/pull/5620 + + + +5\. 将 e2e k8s 测试修改为 docker compose + +pr: https://github.com/apache/shenyu/pull/5710 + + + +6\. 将 Admin swagger 从 springfox 迁移到 springdoc + +pr: https://github.com/apache/shenyu/pull/5630 + + + +7\. 重构 springcloud 插件 + +pr: https://github.com/apache/shenyu/pull/5695 + + + +8\. 重构部分代码 + +pr: https://github.com/apache/shenyu/pull/5568 + + + +9\. 删除 SO\_SNDBUF 和 SO\_RCVBUF + +pr: https://github.com/apache/shenyu/pull/5502 + + + +10\. 将日志 %s 替换为 {} + +pr: https://github.com/apache/shenyu/pull/5465 + + + +11\. 优化节点类型监听器 + +pr: https://github.com/apache/shenyu/pull/5435 + + + +12\. 重构插件生命周期 + +pr: https://github.com/apache/shenyu/pull/5432 + + + +13\. 调整代码顺序并移除无效的输入参数 + +pr: https://github.com/apache/shenyu/pull/5397 + + + + + +修复 + + + + + +1\. 修复请求插件的重复请求头问题 + +pr: https://github.com/apache/shenyu/pull/5846 + + + +2\. 修复删除 divide 选择器时代理选择器和发现未删除的问题 + +pr: https://github.com/apache/shenyu/pull/5845 + + + +3\. 修复日志插件错误日志捕获问题 + +pr: https://github.com/apache/shenyu/pull/5842 + + + +4\. 修复日志插件示例 bug + +pr: https://github.com/apache/shenyu/pull/5429 + + + +5\. 修复内存溢出问题 + +pr: https://github.com/apache/shenyu/pull/5407 + + + +6\. 修复rewrite集成测试 + +pr: https://github.com/apache/shenyu/pull/5445 + + + +7\. 修复 AbstractWasmPluginDataHandlerTest + +pr: https://github.com/apache/shenyu/pull/5464 + + + +8\. 修复 sql-script/h2/schema.sql 中缺少主键的问题 + +pr: https://github.com/apache/shenyu/pull/5481 + + + +9\. 修复数据字典页面数据排序异常 + +pr: https://github.com/apache/shenyu/pull/5483 + + + +10\. 修复文档错误 + +pr: https://github.com/apache/shenyu/pull/5505 + + + +11\. 解决仪表盘路由与上下文路径更新不匹配的问题 + +pr: https://github.com/apache/shenyu/pull/5510 + + + +12\. 修复 etcd 同步配置问题 + +pr: https://github.com/apache/shenyu/pull/5535 + + + +13\. 修复 consul 同步问题 + +pr: https://github.com/apache/shenyu/pull/5546 + + + +14\. 修复未注册无法查询的错误 + +pr: https://github.com/apache/shenyu/pull/5578 + + + +15\. 修正插件 ID 查询和更新数据类型 + +pr: https://github.com/apache/shenyu/pull/5622 + + + +16\. 修复 AdminConstants 类拼写错误 + +pr: https://github.com/apache/shenyu/pull/5637 + + + +17\. 修复 shenyu-examples-springmvc 启动失败问题 + +pr: https://github.com/apache/shenyu/pull/5664 + + + +18\. 修复仪表盘菜单子项排序不生效问题 + +pr: https://github.com/apache/shenyu/pull/5691 + + + +19\. ShenyuApacheDubboXmlProviderApplication 配置修复 + +pr: https://github.com/apache/shenyu/pull/5811 + + + +20\. 修复proxy\_selector和discovery的数据同步 ID 不唯一问题 + +pr: https://github.com/apache/shenyu/pull/5783 + + + +21\. 过滤禁用的字典选项 + +pr: https://github.com/apache/shenyu/pull/5776 + + + +22\. 修复 SpringCloudParser 元数据空数据问题 + +pr: https://github.com/apache/shenyu/pull/5737 + + + +23\. 修复客户端注册验证 + +pr: https://github.com/apache/shenyu/pull/5764 + + + +24\. 配置 dubbo 序列化检查状态为禁用 + +pr: https://github.com/apache/shenyu/pull/5756 + + + +25\. 修复示例 TestApacheDubboXmlApplication 启动失败问题 + +pr: https://github.com/apache/shenyu/pull/5754 + + + +26\. 修复 nacos 数据同步模型缺少上下文路径配置 + +pr: https://github.com/apache/shenyu/pull/5722 + + + +27\. 修复 SPI 在多线程场景下创建非单例对象问题 + +pr: https://github.com/apache/shenyu/pull/5713 + + + +28\. 修复错误的 SQL 语法异常 + +pr: https://github.com/apache/shenyu/pull/5707 + + + +29\. 修复 ListUtil->merge 异常 + +pr: https://github.com/apache/shenyu/pull/5642 + + + +30\. 修复元数据禁用未过滤问题 + +pr: https://github.com/apache/shenyu/pull/5638 + + + +31\. 修复 divide 日志请求方法 + +pr: https://github.com/apache/shenyu/pull/5607 + + + +32\. 修复 e2e chunk header 错误 + +pr: https://github.com/apache/shenyu/pull/5593 + + + +33\. 修复 cookie 错误和 SQL 检查 + +pr: https://github.com/apache/shenyu/pull/5567 + + + +34\. 修复空指针异常问题 + +pr:  + +https://github.com/apache/shenyu/pull/5539 + + + +https://github.com/apache/shenyu/pull/5530 + + + +35\. 修复无效路径错误 + +pr: https://github.com/apache/shenyu/pull/5533 + + + +36\. 修复热加载问题 + +pr: https://github.com/apache/shenyu/pull/5509 + + + +37\. 修复 e2e 测试用例无法运行 wget 命令 + +pr: https://github.com/apache/shenyu/pull/5519 + + + +38\. 修复降级问题 + +pr: https://github.com/apache/shenyu/pull/5496 + + + +39\. 解决 rule-sqlmap.xml 中的 SQL 错误 + +pr: https://github.com/apache/shenyu/pull/5644 + + + +40\. 修复 readYmlBuildRepository 空指针异常 + +pr: https://github.com/apache/shenyu/pull/5819 + + + +41\. nacos 无法在 Shenyu-examples-SpringCloud 项目中注册的问题修复 + +pr: https://github.com/apache/shenyu/pull/5825 + + + +42\. 修复 springCloud 规则数据路径设置未使用问题 + +pr:  + +https://github.com/apache/shenyu/pull/5841 + + + +https://github.com/apache/shenyu/pull/5843 + + + +43\. 修复 shenyu-plugin-logging-elasticsearch:修改 ElasticSearchLogConfig 的 setIndexName + +pr: https://github.com/apache/shenyu/pull/5830 + + + +44\. 修复停止服务时未首先从网关下线问题 + +pr: https://github.com/apache/shenyu/pull/5507 + + + +45\. 修复 k8s 存活探针无法运行 wget 命令错误 + +pr: https://github.com/apache/shenyu/pull/5513 + + + +46\. 修复 AbstractNodeDataSyncService 启动时加载discoveryUpstream的问题 + +pr: https://github.com/apache/shenyu/pull/5473 + + + + + +贡献者 + +特别感谢以下贡献者对 **2.7.0** 版本的支持和参与(排名不分先后)。 + + + +**0xmkzt**,**Divyansh200102**,**IceFoxs**,**JJellyfish**,**Misaya295**,**KerwinBryant**,**M.G.Ting**,**NanMu**,**QiXu**,**RayayChung**,**RiccoChen**,**Sinsy**,**VampireAchao**,**WindSearcher**,**Wweiei**,**YuSiheng**,**aias00**,**caaaaaat**,**crazyStar**,**crudboy**,**dragon-zhang**,**dyjxg4xygary**,**dyp314417995**,**eye-gu**,**frank**,**hdgaadd**,**hql0312**,**j@ckzh0u**,**jerbo99**,**loongs-zhang**,**mmengLong**,**moremind**,**po-168**,**tomsun28**,**ttfont**,**wlngo**,**wyfvsfy**,**xcsnx**,**xiangqianZ**,**xiaoyu**,**yunlongn**,**ywwana**,**zhengke zhou**,**zhengpeng**,**ywj1352** + + + + + +成为贡献者 + + + +我们欢迎每一位贡献者的加入ShenYu,欢迎贡献者以Apache Way的精神参与ShenYu! + + + +贡献者指南请参考: + +https://shenyu.apache.org/zh/community/contributor-guide \ No newline at end of file diff --git a/src/zh/news/Apache-Hertzbeat-1.6.1.md b/src/zh/news/Apache-Hertzbeat-1.6.1.md new file mode 100644 index 0000000000..afc4d200a8 --- /dev/null +++ b/src/zh/news/Apache-Hertzbeat-1.6.1.md @@ -0,0 +1,131 @@ +--- +title: Apache Hertzbeat 1.6.1 版本发布公告 +author: 2024年11月01日 08:54 +date: 2024-11-01 +cover: /assets/img/news/Apache-Hertzbeat-1.6.1-0.png +head: + - - meta + - name: 新闻 +--- + + + +# Apache Hertzbeat 1.6.1 版本发布公告 + +亲爱的社区小伙伴们!‍ + +我们很高兴地宣布,Apache Hertzbeat 1.6.1 版本正式发布!此次发布合并了468个PR,带来了众多新功能和改进。本文将详细介绍1.6.1版本的关键更新,欢迎更多开发者和用户加入我们的开源社区! + +![](/assets/img/news/Apache-Hertzbeat-1.6.1-0.png) + +什么是Hertzbeat? + +![](/assets/img/news/Apache-Hertzbeat-1.6.1-1.png) + +## 下载与文档 + + + +* • **Apache Hertzbeat 1.6.1 下载地址**:https://hertzbeat.apache.org/zh-cn/docs/download + +* • **Apache Hertzbeat 文档地址**:https://hertzbeat.apache.org/zh-cn/docs/ + + +## 主要更新 + +### 新功能与增强 + +* • **新增监控功能**:支持Apache HBase、InfluxDB、VictoriaMetrics集群、HDFS、Yarn、Linux进程、HBase RegionServer、OpenAI账号、Redfish协议等监控。 + +* • **Prometheus支持**:新增Prometheus解析器和Prometheus-like推送模式。 + +* • **国际化支持**:为ClickHouse、DynamicTp、Airflow、IoTDB、RocketMQ等监控指标名称提供国际化支持。 + +* • **自定义监控菜单**:监控模板现在支持自定义主菜单。 + +* • **NebulaGraph支持**:新增对`ngql`查询NebulaGraph监控数据的支持。 + +* • **短信功能**:支持通过阿里云发送短信。 + +* • **Docker支持**:提供通过Docker Compose运行Hertzbeat的支持。 + + +### Bug 修复 + +* • **启动问题**:修复了Collector无法单独启动和MySQL依赖问题,以及MongoDB监控在Spring Boot 3中不可用的问题。 + +* • **数据问题**:修复了JPA数据保存逻辑错误、Redis集群节点测试错误、旧数据解码错误等问题。 + +* • **空指针异常修复**:修复了多个与空指针异常(NPE)相关的问题。 + +* • **其它Bug修复**:包括命令窗口数据丢失、MongoDB模板命令错误等问题。 + + +### 代码重构与优化 + +* • **代码简化**:优化代码结构,使用Assert类简化null判断,移除不必要的if-else语句,采用Java 17的新语法。 + +* • **依赖管理优化**:删除不必要的依赖,并将一些包重构为独立模块。 + +* • **性能提升**:通过优化WebSocket连接、Redis URI构建等方面提升性能。 + +* • **日志与配置更新**:更新Collector和Manager的logback配置。 + + +### 文档翻译与改进 + +* • **翻译工作**:将多个类描述、博客文章和监控模板文档从中文翻译为英文。 + +* • **帮助文档增加**:为ClickHouse、DNS、Flink等监控项目增加了帮助文档。 + +* • **文档结构更新**:更新官网文档、贡献指南、首页介绍等。 + + +### 安全更新 + +* • **依赖升级**:升级H2数据库依赖库,修复相关安全漏洞。 + +* • **其他安全改进**:修复SSL证书剩余天数和Jexlespression的安全匹配问题。 + + +### 测试用例添加 + +* • **测试覆盖率提升**:新增Redis、Nginx、Telnet等监控功能的测试用例,提升测试覆盖率。 + + +## 致谢 + +感谢 **@zqr10159** 对本次发版工作的支持,同时感谢以下社区成员的共同努力,使得本次发布顺利完成: + +> LinuxSuRen, transactional, JavaProgrammerLB, westboy, xuziyang, makechoicenow, crossoverJie, xfl12345, boatrainlsz, lw-yang, tomsun28, Alanxtl, Aias00, Clownsw, zhangshenghang, zqr10159, LiuTianyou, handy-git, hudongdong129, dukbong, 15613060203, yqxxgh, miki-hmt, PeixyJ, allcontributors, Ceilzcx, lwjxy, starmilkxin, leo-934, zuobiao-zhou, tomorrowshipyltm, LLP2333, lwqzz, wang1027-wqh, gjjjj0101, ZY945, yuluo-yx, HeartLinked, alpha951, Hi-Mr-Wind, TJxiaobao, YxYL6125, MananPoojara, a-little-fool, Pzz-2021, Yanshuming1, Thespica, Calvin979, WinterKi1ler +> +> + +# Apache Hertzbeat + +**仓库地址:** + +https://github.com/apache/hertzbeat + +**网址:** + +https://hertzbeat.apache.org/ + +**Apache Hertzbeat 下载地址:** + +https://hertzbeat.apache.org/zh-cn/docs/download + +**Apache Hertzbeat Docker 镜像版本:** + +> Apache HertzBeat 为每个版本制作了 Docker 镜像. 你可以从 Docker Hub 拉取使用. +> +> + +* • HertzBeat https://hub.docker.com/r/apache/hertzbeat + +* • HertzBeat Collector https://hub.docker.com/r/apache/hertzbeat-collector + + +**Apache Hertzbeat 开源社区如何参与?** + +https://hertzbeat.apache.org/zh-cn/docs/community/contribution \ No newline at end of file diff --git a/src/zh/news/Apache-ShenYu-2.6.0.md b/src/zh/news/Apache-ShenYu-2.6.0.md index e2678748e8..9fb22a7d0e 100644 --- a/src/zh/news/Apache-ShenYu-2.6.0.md +++ b/src/zh/news/Apache-ShenYu-2.6.0.md @@ -1,823 +1,823 @@ ---- -title: Apache ShenYu 2.6.0 Released! -author: 何凤恩 -date: 2023-08-16 -cover: /assets/img/news/Apache-ShenYu-2.6.0.jpg -head: - - - meta - - name: 新闻 ---- - -## 关于 Apache ShenYu - -`Apache ShenYu` 一款使用 `Java Reactor` 开发的响应式`API` 网关。以其高性能,动态灵活的流量管控,热插拔,易部署等特性,开箱即用为用户提供整套全生命周期的 `API`网关,包含 `API`注册、服务代理、协议转换、`API`文档与 `API`治理等功能。Apache ShenYu 于`2022年7月`毕业成为`Apache`顶级项目。 - -> 官网: https://shenyu.apache.org -> GitHub: https://github.com/apache/shenyu - -## 版本预览 - -时隔半年,`Apache ShenYu`发布了 2.6.0 版本,该版本共计提交了`280+个 Pull Request`,新增约`20+个新特性`,新增了若干增强,重构了若干功能,并且修复了若干个 bug。共计`78位`贡献者参与其中,累计贡献者达 350+位。 - -> 版本记录:https://github.com/apache/shenyu/compare/v2.5.1…v2.6.0 - -### 新特性 - -1.支持插件上传功能,支持网关热加载插件 - -> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/developer/custom-plugin -> -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4392 - -2.支持使用 Apollo 作为数据同步和注册中心 - -``` -sheneyu: - sync: - apollo: - appId: shenyu - meta: http://localhost:8080 - env: dev - clusterName: test - namespace: application -``` - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4532 - -3.支持 springboot client 在 shenyu client 中动态配置 - -4.添加 TCP 插件 - -> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/plugin-center/proxy/tcp-plugin -> -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4607 -> -> https://github.com/apache/shenyu/pull/4766 - -![](/assets/img/news/Apache-ShenYu-2.6.0-1.png) -![](/assets/img/news/Apache-ShenYu-2.6.0-2.png) - -5.支持 springmvn(boot)在 shenyu client 中收集 api-meta data - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4600 - -6.添加 shenyu ingress controller 的支持 - -> 具体使用请查看:https://shenyu.apache.org/zh/docs/user-guide/kubernetes-controller/build-deploy -> -> https://shenyu.apache.org/zh/docs/user-guide/kubernetes-controller/config -> -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4620 -> -> 配置如下: -> -> ``` -> shenyu: -> netty: -> http: -> sni: -> enabled: true -> mod: k8s #k8s模式适用 -> defaultK8sSecretNamespace: shenyu-ingress #默认secret资源的namespace -> defaultK8sSecretName: default-cert #默认secret资源名字 -> ``` - -7.添加 zookeeper,naocs,apollo,HttpLongPolling,consul 作为 shenyu 服务发现 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4636 -> -> https://github.com/apache/shenyu/pull/4657 -> -> https://github.com/apache/shenyu/pull/4802 -> -> https://github.com/apache/shenyu/pull/4795 -> -> https://github.com/apache/shenyu/pull/4800 -> -> https://github.com/apache/shenyu/issues/4562 - -8.添加华为云 lts 日志收集 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4812 - -9.添加 opengauss 数据库支持 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4856 - -10.添加 polaris 作为 shenyu 的数据同步和注册中心 - -``` -shenyu: - sync: - polaris: - url: 127.0.0.1:8093 - namespace: - fileGroup: -``` - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4410 -> -> https://github.com/apache/shenyu/pull/4897 - -11.添加 shenyu 匹配缓存 - -``` -shenyu: - selectorMatchCache: - ## selector L1 cache - cache: - enabled: false - initialCapacity: 10000 # initial capacity in cache - maximumSize: 10000 # max size in cache - ## selector L2 cache, use trie as L2 cache - trie: - enabled: false - cacheSize: 128 # the number of plug-ins - matchMode: antPathMatch - ruleMatchCache: - ## rule L1 cache - cache: - enabled: true - initialCapacity: 10000 # initial capacity in cache - maximumSize: 65536 # max size in cache - ## rule L2 cache, use trie as L2 cache - trie: - enabled: false - cacheSize: 1024 # the number of selectors - matchMode: antPathMatch -``` - -> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/user-guide/property-config/client-property-config -> -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4417 -> -> https://github.com/apache/shenyu/pull/4536 - -12.新增 shenyu admin 对 prometheus 的支持 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4336 - -13.暴露 shenyu actuator 端点 - -> 说明:可通过 pr 查看 shenyu 网关的内存数据 -> -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4637 -> -> 如何关闭请查看 actuator 配置: -> -> ``` -> management: -> endpoints: -> web: -> exposure: -> include: "*" # or health,info -> ``` - -## 增强 - -1.对 API doc client 增加 tags 属性 - -> 具体使用请查看:https://shenyu.apache.org/docs/user-guide/api-doc/shenyu-annotation-apidoc -> -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4362 - -2.添加 Brpc 的集成测试 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4319 - -3.Brpc 支持共享线程池 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4402 - -4.为加密插件(cryptorRequst 和 cryptorResponse)增加映射类型 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4418 - -5.加密插件支持多个个字段加密 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4435 - -6.添加 p2c 负载均衡算法 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4451 - -7.使用 base64 生成插件字符串,并存储到插件数据中 - -> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/developer/custom-plugin -> -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4473 - -8.添加最短响应负载均衡算法 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4488 - -9.添加 hash 负载均衡测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4383 - -10.添加`DetailSerivice`测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4450 - -11.提供宽泛的路径策略 - -> 具体配置如下: -> -> ``` -> shenyu: ->     switchConfig: ->        local: true ->        collapseSlashes: false #true表示开启宽泛路径支持 -> ``` -> -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4522 - -12.添加 shenyu-common 的 enums 包测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4541 - -13.添加 shenyu-common 的 dto 包测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4549/ - -14.添加 Add shenyu-admin 的 model 包测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/issues/4540 - -15.添加 shenyu match cache 测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4557 - -16.支持 k8s 探针 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4567 - -17.添加 shenyu-admin 的 service 包测试 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4579 - -18.在 API 文档中添加 json 支持 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4591 - -19.mock 插件的 SPEL 默认为安全的 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4606 - -20.添加`ShenyuClientApiDocExecutorSubscriber`的测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4632 - -21.为 shenyu-client-sofa 模块添加测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4688 - -22.为`shenyu api doc`添加`tag relation` - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4362 - -23.添加 windows 下的启动、停止脚本 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4673 - -24.添加`ShenyuSdkClientFactory`的测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4645 - -25.添加 shenyu e2e springcloud plugin 的 websocket 同步支持 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4698 - -26.支持 divide 插件自动下线 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4702 - -27.添加 springcloud service instance 缓存 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4705 -> -> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/plugin-center/proxy/spring-cloud-plugin -> -> ``` -> shenyu: -> springCloudCache: -> enabled: false # 为true是开启springcloud缓存 -> ``` - -28.更改密码支持 i18n - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4758 - -29.shenyu discovery 支持 websocket 同步 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4768 - -30.升级`springboot`版本到`2.7.13` - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4783 - -31.为 e2e-springcloud 添加 nacos,zookeeper 同步测试 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4747 - -32.添加`api doc client`注解生成属性 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4845 - -33.支持`zookeeper`客户端自动下线 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4806 - -34.支持`Apollo client`自动下线 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4855 - -35.支持 swagger 文档,并将文档存储到数据库 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4849 - -36.支持`nacos client`自动下线 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4890 - -37.添加 alibaba dubbo e2e 测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4859 - -38.添加 apache dubbo e2e 测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4899 - -39.添加 shenyu spring sdk 测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4913 - -40.添加 sofa e2e 测试 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4919 - -41.添加 Apollo 数据同步的测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4918 - -42.添加数据库的连接池配置(hakari) - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4938 - -43.为 shenyu 添加`idea icon` - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4951 - -## 重构 - -1.重构 shenyu admin - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4355 - -2.优化 least active balance 算法 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4342 - -3.优化 shenyu sign 插件的第一个版本的兼容性 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4332 -> -> 具体使用请查看:https://shenyu.apache.org/docs/plugin-center/security/sign-plugin - -4.优化 shenyu upstream check 逻辑 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4386 - -5.优化项目的全局版本 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4394 - -6.优化`ShenyuConsulConfigWatch`的代码 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4400 - -7.优化 shenyu 前缀树匹配逻辑 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4414 - -8.优化 rule condition 提交时的校验 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4403 - -9.优化 shenyu-client-websocket 的客户端注册代码 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4462 - -10.添加 shenyu admin 依赖 Micrometer 的许可证 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4409 - -11.更新 maven-assembly-plugin 打包插件到 3.5.0 版本 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4673 - -12.优化全局插件的排序 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4429 - -13.在 shenyu admin 中使用 BearerToken 替代 StatelessToken - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4516 - -14.重构 shenyu-logging 模块 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4526 - -15.对 api doc 支持校验 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4564 - -16.优化 shenyu 前缀树,并支持`*`匹配 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4569 - -17.优化插件的热加载 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4392 - -18.优化`ShenyuWebHandler`的 putPlugin 方法 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4598 - -19.重构 Shenyu webfilter - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4614 - -20.重构 oauth2 plguin 插件 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4624 - -21.重构 shenyu 选择器的 continued 字段 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4635 - -22.重构 shenyu 选择和规则的匹配缓存 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4578 - -23.删除了 shenyu 客户端中未使用的泛型 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4653 - -24.重构 shenyu 对 sentinel 插件的支持 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4669 - -25.将缓存数据通过 actuator 端点暴露 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4637 -> -> https://github.com/apache/shenyu/pull/4658 - -26.重构`checkUserPassword`方法,启动时不打印已知错误日志 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4697 - -27.添加打印日志的参数 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4637 - -28.重构 shenyu 全局异常处理 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4709 - -29.添加了 shenyu 插件上传的集成测试 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4679 - -30.优化语法糖 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4700 - -31.优化 discovery_upstream 的 discovery_handler_id 字段 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4710 - -32.重构 shenyu-plugin 模块,将 proxy 插件分类归档 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4765 - -33.重构`AlibabaDubboConfigCache`的缓存 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4772 - -34.移除 hutool 的依赖 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4773 - -35.重构`ShenyuClientShutdownHook` - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4780 - -36.Extractor 添加 BaseAnnotationApiBeansExtractor - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4787 - -37.支持多客户端注册 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4790 - -38.重构 Shenyu-e2e 支持 Shenyu 的 check style - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4799 - -39.优化 shenyu 客户端注册逻辑 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4809 - -40.添加 shenyu divide 插件的域名测试 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4803 - -41.更新 rpc_ext 字段的扩展 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4821 - -42.优化 consul 的连接操作 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4832 - -43.重构 shenyu e2e 的 springcloud 的 yaml 添加方式 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4837 - -44.为 k8s ingress controller 添加集成测试 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4820 - -45.拆分 apidoc 明细接口的 document 字段,增加 requestHeaders、responseParameters 等字段 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4865 - -46.加 swagger 示例项目,测试 API 文档的相关功能 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4825 - -47.优化 shenyu admin 的 json 格式表单字段的显示 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4873 - -48.重构 shenyu 日志可观测性 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4874 - -49.添加 bootstrap 启动日志 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4879 - -50.重构 swagger 的 api 文档 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4892 - -51.升级 grpc 版本至 1.53.0 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4841 - -52.重构 api 元数据处理函数 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4948 - -53.优化代码和 pom 依赖 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4945 - -## Bug 修复 - -1.优化 h2 的路径 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4351 - -2.修复加密响应插件的调用错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4331 - -3.修复 jdk8 Map computeIfAbsent 性能 bug - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4338 - -4.修复 zombieRemovalTimes 代码 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4368 - -5.修复升级后的 sql 错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4374 - -6.删除 detectorOfflineLinks 标签 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4382 - -7.忽略扁平化的 pom - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4390 - -8.修复 LOG 调用方法 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4387 - -9.使用 nacos 修复 sheyu-example-springcloud 的 NPE - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4396 - -10.修复 Shenyu-admin 名称的类型争论 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4340 - -11.修复负载平衡 spi 资源 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4411 - -12.修复 sql 脚本错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4412 - -13.修复 jackson 的 24 小时格式和时区 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4413 - -14.修复 JwtUtils 错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4420 - -15.修复 dubbo 调用者缓存 bug - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4433 - -16.修复丢失 HOST 的删除操作 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4425 - -17.修复 SpringMvcClientEventListener 测试用例 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4252 - -18.修复 zombie 更新 PENDING_SYNC 的错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4430 - -19.修复 windlfu 的内存泄漏 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4486 - -20.修复因规则过多导致规则查询失败的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4499 - -21.修复示例 http 中缺少执行器依赖项和端口错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4506 - -22.修复 UpstreamCheckUtils 的 http 和 https 错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4509 - -23.修复 FileFilter 造成内存泄漏的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4507 - -24.修复 zookeeper 同步错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4906 - -25.修复 MemorySafeWindowTinyLFUMap 内存泄漏错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4524 - -26.修复 ApiDoc 路径缺少分隔符的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4528 - -27.修复 shenyu trie 的 NPE - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4533 - -28.修复插件跳过错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4589 - -29.修复 oracle sql 错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4595 - -30.修复 shenyu admin 中无法加载 shenyu 图标的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4605 - -31.修复 hystrix fallback 的 bug - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4593 - -32.修复 divide 和 springcloud 的预热时间 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4619 - -33.修复 springcloud 服务选择器 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4639 - -34.修复 shenyu-spring-boot-starter-plugin-mock 添加 spring.factories - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4644 - -35.修复 shenyu-client-mvc 和 shenyu-client-springcloud 丢失 ip - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4681 - -36.修复缓存中规则数据和选择器数据为空的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4716 - -37.修复 api 文档模块更新 api 详情错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4720 - -38.修复从 KafkaLogCollectClient 中的配置获取 topic - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4756 - -39.修复 loggingConsole 插件的线程安全问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4763 - -40.修复 brpc 集成测试响应大小 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4784 - -41.修复 plugn-dubbo-common 的选择器更新灰色发布删除缓存的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4762 - -42.修复 shenyu admin 菜单名称 bug - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4805 - -43.修复 shenyu admin 无法配置 consul 端口的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4843 - -44.修复 shenyu 客户端元数据和 uri 无法与 apollo 同步到 admin 的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4851 - -45.修复 PathVariable 注解 url 无法匹配的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4852 - -46.修复 PathPattern 模式下无法更新 uri 的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4854 - -47.修复客户端关闭方法调用两次 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4867 - -48.修复 shenyu 错误处理 consul 配置 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4872 - -49.从 Request、modifyResponse 插件中删除未使用的配置 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4882 - -50.修复 http 注册元数据错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4889 - -51.修复 websocket 丢失用户自定义关闭状态的问题 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4844 - -52.修复 consul 寄存器在特殊符号时丢失元路径的属性 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4885 - -53.修复 etcd 同步错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4911 - -54.修复 shenyu admin 多次同步事件错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4941 - -55.修复 Shenyu motan 插件执行错误 - -> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4934 - -## 贡献者 - -特别感谢以下贡献者对 `2.6.0`版本的支持和参与(排名不分先后)。 - -midnight2104,koonchen,847850277,balloon72,yu199195,iwangjie,damonxue,tian-pengfei,caojiajun,dragon-zhang,u3breeze,li-keguo,SuperMonkeyC,mahaitao617,tomsun28,moremind,liaolzy,Ceilzcx,misaya295,BoyuLi4,HaiqiQin,starlight2003,stulzq,ywj1352,yunlongn,aFlyBird0,dengliming,plutokaito,xuyicheng1995,lan-dian,sachin10fi,zuobiao-zhou, hudongdong129,crudboy,aoshiguchen,VampireAchao,JooKS-me,Redick01,huanccwang,lijay7674,omernaci,peng-heng,December-Pb,6freeair2016,jieyangxchen,lianjunwei,u3breeze,eurecalulu,wanyaoasiainfo,wanyaoasiainfo,Kakk22,xuziyang,menglujing,xcsnx,yu1183688986,lahmXu,fabian4,ileonli,VampireAchao,GOODBOY008,TeslaCN - -## 成为贡献者 - -我们欢迎每一位贡献者的加入 ShenYu,欢迎贡献者以 Apache Way 的精神参与 ShenYu! - -贡献者指南请参考: - -> https://shenyu.apache.org/zh/community/contributor-guide +--- +title: Apache ShenYu 2.6.0 Released! +author: 何凤恩 +date: 2023-08-16 +cover: /assets/img/news/Apache-ShenYu-2.6.0.jpg +head: + - - meta + - name: 新闻 +--- + +## 关于 Apache ShenYu + +`Apache ShenYu` 一款使用 `Java Reactor` 开发的响应式`API` 网关。以其高性能,动态灵活的流量管控,热插拔,易部署等特性,开箱即用为用户提供整套全生命周期的 `API`网关,包含 `API`注册、服务代理、协议转换、`API`文档与 `API`治理等功能。Apache ShenYu 于`2022年7月`毕业成为`Apache`顶级项目。 + +> 官网: https://shenyu.apache.org +> GitHub: https://github.com/apache/shenyu + +## 版本预览 + +时隔半年,`Apache ShenYu`发布了 2.6.0 版本,该版本共计提交了`280+个 Pull Request`,新增约`20+个新特性`,新增了若干增强,重构了若干功能,并且修复了若干个 bug。共计`78位`贡献者参与其中,累计贡献者达 350+位。 + +> 版本记录:https://github.com/apache/shenyu/compare/v2.5.1…v2.6.0 + +### 新特性 + +1.支持插件上传功能,支持网关热加载插件 + +> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/developer/custom-plugin +> +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4392 + +2.支持使用 Apollo 作为数据同步和注册中心 + +``` +sheneyu: + sync: + apollo: + appId: shenyu + meta: http://localhost:8080 + env: dev + clusterName: test + namespace: application +``` + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4532 + +3.支持 springboot client 在 shenyu client 中动态配置 + +4.添加 TCP 插件 + +> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/plugin-center/proxy/tcp-plugin +> +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4607 +> +> https://github.com/apache/shenyu/pull/4766 + +![](/assets/img/news/Apache-ShenYu-2.6.0-1.png) +![](/assets/img/news/Apache-ShenYu-2.6.0-2.png) + +5.支持 springmvn(boot)在 shenyu client 中收集 api-meta data + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4600 + +6.添加 shenyu ingress controller 的支持 + +> 具体使用请查看:https://shenyu.apache.org/zh/docs/user-guide/kubernetes-controller/build-deploy +> +> https://shenyu.apache.org/zh/docs/user-guide/kubernetes-controller/config +> +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4620 +> +> 配置如下: +> +> ``` +> shenyu: +> netty: +> http: +> sni: +> enabled: true +> mod: k8s #k8s模式适用 +> defaultK8sSecretNamespace: shenyu-ingress #默认secret资源的namespace +> defaultK8sSecretName: default-cert #默认secret资源名字 +> ``` + +7.添加 zookeeper,naocs,apollo,HttpLongPolling,consul 作为 shenyu 服务发现 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4636 +> +> https://github.com/apache/shenyu/pull/4657 +> +> https://github.com/apache/shenyu/pull/4802 +> +> https://github.com/apache/shenyu/pull/4795 +> +> https://github.com/apache/shenyu/pull/4800 +> +> https://github.com/apache/shenyu/issues/4562 + +8.添加华为云 lts 日志收集 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4812 + +9.添加 opengauss 数据库支持 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4856 + +10.添加 polaris 作为 shenyu 的数据同步和注册中心 + +``` +shenyu: + sync: + polaris: + url: 127.0.0.1:8093 + namespace: + fileGroup: +``` + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4410 +> +> https://github.com/apache/shenyu/pull/4897 + +11.添加 shenyu 匹配缓存 + +``` +shenyu: + selectorMatchCache: + ## selector L1 cache + cache: + enabled: false + initialCapacity: 10000 # initial capacity in cache + maximumSize: 10000 # max size in cache + ## selector L2 cache, use trie as L2 cache + trie: + enabled: false + cacheSize: 128 # the number of plug-ins + matchMode: antPathMatch + ruleMatchCache: + ## rule L1 cache + cache: + enabled: true + initialCapacity: 10000 # initial capacity in cache + maximumSize: 65536 # max size in cache + ## rule L2 cache, use trie as L2 cache + trie: + enabled: false + cacheSize: 1024 # the number of selectors + matchMode: antPathMatch +``` + +> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/user-guide/property-config/client-property-config +> +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4417 +> +> https://github.com/apache/shenyu/pull/4536 + +12.新增 shenyu admin 对 prometheus 的支持 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4336 + +13.暴露 shenyu actuator 端点 + +> 说明:可通过 pr 查看 shenyu 网关的内存数据 +> +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4637 +> +> 如何关闭请查看 actuator 配置: +> +> ``` +> management: +> endpoints: +> web: +> exposure: +> include: "*" # or health,info +> ``` + +## 增强 + +1.对 API doc client 增加 tags 属性 + +> 具体使用请查看:https://shenyu.apache.org/docs/user-guide/api-doc/shenyu-annotation-apidoc +> +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4362 + +2.添加 Brpc 的集成测试 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4319 + +3.Brpc 支持共享线程池 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4402 + +4.为加密插件(cryptorRequst 和 cryptorResponse)增加映射类型 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4418 + +5.加密插件支持多个个字段加密 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4435 + +6.添加 p2c 负载均衡算法 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4451 + +7.使用 base64 生成插件字符串,并存储到插件数据中 + +> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/developer/custom-plugin +> +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4473 + +8.添加最短响应负载均衡算法 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4488 + +9.添加 hash 负载均衡测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4383 + +10.添加`DetailSerivice`测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4450 + +11.提供宽泛的路径策略 + +> 具体配置如下: +> +> ``` +> shenyu: +>     switchConfig: +>        local: true +>        collapseSlashes: false #true表示开启宽泛路径支持 +> ``` +> +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4522 + +12.添加 shenyu-common 的 enums 包测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4541 + +13.添加 shenyu-common 的 dto 包测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4549/ + +14.添加 Add shenyu-admin 的 model 包测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/issues/4540 + +15.添加 shenyu match cache 测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4557 + +16.支持 k8s 探针 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4567 + +17.添加 shenyu-admin 的 service 包测试 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4579 + +18.在 API 文档中添加 json 支持 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4591 + +19.mock 插件的 SPEL 默认为安全的 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4606 + +20.添加`ShenyuClientApiDocExecutorSubscriber`的测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4632 + +21.为 shenyu-client-sofa 模块添加测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4688 + +22.为`shenyu api doc`添加`tag relation` + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4362 + +23.添加 windows 下的启动、停止脚本 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4673 + +24.添加`ShenyuSdkClientFactory`的测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4645 + +25.添加 shenyu e2e springcloud plugin 的 websocket 同步支持 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4698 + +26.支持 divide 插件自动下线 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4702 + +27.添加 springcloud service instance 缓存 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4705 +> +> 具体使用请查看:https://shenyu.apache.org/zh/docs/next/plugin-center/proxy/spring-cloud-plugin +> +> ``` +> shenyu: +> springCloudCache: +> enabled: false # 为true是开启springcloud缓存 +> ``` + +28.更改密码支持 i18n + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4758 + +29.shenyu discovery 支持 websocket 同步 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4768 + +30.升级`springboot`版本到`2.7.13` + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4783 + +31.为 e2e-springcloud 添加 nacos,zookeeper 同步测试 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4747 + +32.添加`api doc client`注解生成属性 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4845 + +33.支持`zookeeper`客户端自动下线 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4806 + +34.支持`Apollo client`自动下线 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4855 + +35.支持 swagger 文档,并将文档存储到数据库 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4849 + +36.支持`nacos client`自动下线 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4890 + +37.添加 alibaba dubbo e2e 测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4859 + +38.添加 apache dubbo e2e 测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4899 + +39.添加 shenyu spring sdk 测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4913 + +40.添加 sofa e2e 测试 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4919 + +41.添加 Apollo 数据同步的测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4918 + +42.添加数据库的连接池配置(hakari) + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4938 + +43.为 shenyu 添加`idea icon` + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4951 + +## 重构 + +1.重构 shenyu admin + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4355 + +2.优化 least active balance 算法 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4342 + +3.优化 shenyu sign 插件的第一个版本的兼容性 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4332 +> +> 具体使用请查看:https://shenyu.apache.org/docs/plugin-center/security/sign-plugin + +4.优化 shenyu upstream check 逻辑 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4386 + +5.优化项目的全局版本 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4394 + +6.优化`ShenyuConsulConfigWatch`的代码 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4400 + +7.优化 shenyu 前缀树匹配逻辑 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4414 + +8.优化 rule condition 提交时的校验 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4403 + +9.优化 shenyu-client-websocket 的客户端注册代码 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4462 + +10.添加 shenyu admin 依赖 Micrometer 的许可证 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4409 + +11.更新 maven-assembly-plugin 打包插件到 3.5.0 版本 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4673 + +12.优化全局插件的排序 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4429 + +13.在 shenyu admin 中使用 BearerToken 替代 StatelessToken + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4516 + +14.重构 shenyu-logging 模块 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4526 + +15.对 api doc 支持校验 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4564 + +16.优化 shenyu 前缀树,并支持`*`匹配 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4569 + +17.优化插件的热加载 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4392 + +18.优化`ShenyuWebHandler`的 putPlugin 方法 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4598 + +19.重构 Shenyu webfilter + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4614 + +20.重构 oauth2 plguin 插件 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4624 + +21.重构 shenyu 选择器的 continued 字段 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4635 + +22.重构 shenyu 选择和规则的匹配缓存 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4578 + +23.删除了 shenyu 客户端中未使用的泛型 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4653 + +24.重构 shenyu 对 sentinel 插件的支持 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4669 + +25.将缓存数据通过 actuator 端点暴露 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4637 +> +> https://github.com/apache/shenyu/pull/4658 + +26.重构`checkUserPassword`方法,启动时不打印已知错误日志 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4697 + +27.添加打印日志的参数 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4637 + +28.重构 shenyu 全局异常处理 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4709 + +29.添加了 shenyu 插件上传的集成测试 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4679 + +30.优化语法糖 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4700 + +31.优化 discovery_upstream 的 discovery_handler_id 字段 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4710 + +32.重构 shenyu-plugin 模块,将 proxy 插件分类归档 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4765 + +33.重构`AlibabaDubboConfigCache`的缓存 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4772 + +34.移除 hutool 的依赖 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4773 + +35.重构`ShenyuClientShutdownHook` + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4780 + +36.Extractor 添加 BaseAnnotationApiBeansExtractor + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4787 + +37.支持多客户端注册 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4790 + +38.重构 Shenyu-e2e 支持 Shenyu 的 check style + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4799 + +39.优化 shenyu 客户端注册逻辑 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4809 + +40.添加 shenyu divide 插件的域名测试 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4803 + +41.更新 rpc_ext 字段的扩展 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4821 + +42.优化 consul 的连接操作 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4832 + +43.重构 shenyu e2e 的 springcloud 的 yaml 添加方式 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4837 + +44.为 k8s ingress controller 添加集成测试 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4820 + +45.拆分 apidoc 明细接口的 document 字段,增加 requestHeaders、responseParameters 等字段 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4865 + +46.加 swagger 示例项目,测试 API 文档的相关功能 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4825 + +47.优化 shenyu admin 的 json 格式表单字段的显示 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4873 + +48.重构 shenyu 日志可观测性 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4874 + +49.添加 bootstrap 启动日志 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4879 + +50.重构 swagger 的 api 文档 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4892 + +51.升级 grpc 版本至 1.53.0 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4841 + +52.重构 api 元数据处理函数 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4948 + +53.优化代码和 pom 依赖 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4945 + +## Bug 修复 + +1.优化 h2 的路径 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4351 + +2.修复加密响应插件的调用错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4331 + +3.修复 jdk8 Map computeIfAbsent 性能 bug + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4338 + +4.修复 zombieRemovalTimes 代码 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4368 + +5.修复升级后的 sql 错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4374 + +6.删除 detectorOfflineLinks 标签 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4382 + +7.忽略扁平化的 pom + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4390 + +8.修复 LOG 调用方法 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4387 + +9.使用 nacos 修复 sheyu-example-springcloud 的 NPE + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4396 + +10.修复 Shenyu-admin 名称的类型争论 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4340 + +11.修复负载平衡 spi 资源 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4411 + +12.修复 sql 脚本错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4412 + +13.修复 jackson 的 24 小时格式和时区 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4413 + +14.修复 JwtUtils 错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4420 + +15.修复 dubbo 调用者缓存 bug + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4433 + +16.修复丢失 HOST 的删除操作 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4425 + +17.修复 SpringMvcClientEventListener 测试用例 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4252 + +18.修复 zombie 更新 PENDING_SYNC 的错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4430 + +19.修复 windlfu 的内存泄漏 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4486 + +20.修复因规则过多导致规则查询失败的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4499 + +21.修复示例 http 中缺少执行器依赖项和端口错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4506 + +22.修复 UpstreamCheckUtils 的 http 和 https 错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4509 + +23.修复 FileFilter 造成内存泄漏的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4507 + +24.修复 zookeeper 同步错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4906 + +25.修复 MemorySafeWindowTinyLFUMap 内存泄漏错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4524 + +26.修复 ApiDoc 路径缺少分隔符的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4528 + +27.修复 shenyu trie 的 NPE + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4533 + +28.修复插件跳过错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4589 + +29.修复 oracle sql 错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4595 + +30.修复 shenyu admin 中无法加载 shenyu 图标的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4605 + +31.修复 hystrix fallback 的 bug + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4593 + +32.修复 divide 和 springcloud 的预热时间 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4619 + +33.修复 springcloud 服务选择器 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4639 + +34.修复 shenyu-spring-boot-starter-plugin-mock 添加 spring.factories + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4644 + +35.修复 shenyu-client-mvc 和 shenyu-client-springcloud 丢失 ip + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4681 + +36.修复缓存中规则数据和选择器数据为空的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4716 + +37.修复 api 文档模块更新 api 详情错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4720 + +38.修复从 KafkaLogCollectClient 中的配置获取 topic + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4756 + +39.修复 loggingConsole 插件的线程安全问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4763 + +40.修复 brpc 集成测试响应大小 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4784 + +41.修复 plugn-dubbo-common 的选择器更新灰色发布删除缓存的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4762 + +42.修复 shenyu admin 菜单名称 bug + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4805 + +43.修复 shenyu admin 无法配置 consul 端口的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4843 + +44.修复 shenyu 客户端元数据和 uri 无法与 apollo 同步到 admin 的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4851 + +45.修复 PathVariable 注解 url 无法匹配的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4852 + +46.修复 PathPattern 模式下无法更新 uri 的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4854 + +47.修复客户端关闭方法调用两次 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4867 + +48.修复 shenyu 错误处理 consul 配置 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4872 + +49.从 Request、modifyResponse 插件中删除未使用的配置 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4882 + +50.修复 http 注册元数据错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4889 + +51.修复 websocket 丢失用户自定义关闭状态的问题 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4844 + +52.修复 consul 寄存器在特殊符号时丢失元路径的属性 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4885 + +53.修复 etcd 同步错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4911 + +54.修复 shenyu admin 多次同步事件错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4941 + +55.修复 Shenyu motan 插件执行错误 + +> 具体 pr 请查看:https://github.com/apache/shenyu/pull/4934 + +## 贡献者 + +特别感谢以下贡献者对 `2.6.0`版本的支持和参与(排名不分先后)。 + +midnight2104,koonchen,847850277,balloon72,yu199195,iwangjie,damonxue,tian-pengfei,caojiajun,dragon-zhang,u3breeze,li-keguo,SuperMonkeyC,mahaitao617,tomsun28,moremind,liaolzy,Ceilzcx,misaya295,BoyuLi4,HaiqiQin,starlight2003,stulzq,ywj1352,yunlongn,aFlyBird0,dengliming,plutokaito,xuyicheng1995,lan-dian,sachin10fi,zuobiao-zhou, hudongdong129,crudboy,aoshiguchen,VampireAchao,JooKS-me,Redick01,huanccwang,lijay7674,omernaci,peng-heng,December-Pb,6freeair2016,jieyangxchen,lianjunwei,u3breeze,eurecalulu,wanyaoasiainfo,wanyaoasiainfo,Kakk22,xuziyang,menglujing,xcsnx,yu1183688986,lahmXu,fabian4,ileonli,VampireAchao,GOODBOY008,TeslaCN + +## 成为贡献者 + +我们欢迎每一位贡献者的加入 ShenYu,欢迎贡献者以 Apache Way 的精神参与 ShenYu! + +贡献者指南请参考: + +> https://shenyu.apache.org/zh/community/contributor-guide diff --git a/src/zh/news/Apache-ShenYu-2.6.1.md b/src/zh/news/Apache-ShenYu-2.6.1.md index 48e36f496c..a965a508b8 100644 --- a/src/zh/news/Apache-ShenYu-2.6.1.md +++ b/src/zh/news/Apache-ShenYu-2.6.1.md @@ -1,875 +1,875 @@ ---- -title: Apache ShenYu 2.6.1 Released -author: 何凤恩 -date: 2024-01-24 -cover: /assets/img/news/Apache-ShenYu-2.6.1-0.png -head: - - - meta - - name: 新闻 ---- - -## 关于 Apache ShenYu - -官网: https://shenyu.apache.org - -GitHub: https://github.com/apache/shenyu - -## 版本预览 - -版本记录:https://github.com/apache/shenyu/compare/v2.6.0...v2.6.1 - -### 新特性 - -1.为 shenyu ingress controller 添加 Dubbo 注解元数据 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5000 - -具体文档请查看: - -https://shenyu.apache.org/zh/docs/user-guide/kubernetes-controller/config - -2.支持插件生命周期 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5031 - -3.添加 shenyu-sdk-openfeign 模块 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5041 - -4.添加 Motan 和 Spring Cloud 添加 ingress controller 支持 - -5.shenyu 支持告警功能 - -![](/assets/img/news/Apache-ShenYu-2.6.1-0.png) - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4782 - -具体文档请查看: - -https://shenyu.apache.org/zh/docs/next/developer/notice-alert - -6.shenyu client 添加 discovery 的注册中心 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5153 - -7.添加 shenyu context-path Ingress controller - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5167 - -8.添加 shenyu grpc Ingress controller - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5240 - -9.添加 shenyu sofa Ingress controller - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5253 - -10.添加 nacos, etcd, eureka 作为 shenyu discovery 服务注册中心 - -![](/assets/img/news/Apache-ShenYu-2.6.1-1.png) - -![](/assets/img/news/Apache-ShenYu-2.6.1-2.png) - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5193 - -11.添加新的插件:basic-plugin - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5258 - -12.添加新插件以及集成测试:shenyu-rabbitmq-logging plugin - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5283 - -https://github.com/apache/shenyu/pull/5312 - -13.通过 shenyu-discovery 绑定 selector - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5261 - -### API 变化 - -1.重构 shenyu 数据同步的数据结构 - -![](/assets/img/news/Apache-ShenYu-2.6.1-3.png) - -2.使用 netty 作为默认的 httpclient - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5200 - -3.重构 shenyu-admin-listener 来支持 shenyu admin 数据同步 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5294 - -https://github.com/apache/shenyu/pull/5347 - -4.删除 shenyu 对 brpc 的支持,包括 brpc 插件,brpc 示例,brpc 集成测试 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5305 - -https://github.com/apache/shenyu/pull/5358 - -5.移除 Apollo 的依赖以便支持 Java 17(自行添加依赖) - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5308 - -具体文档请查看: - -https://shenyu.apache.org/docs/next/user-guide/property-config/use-data-sync/#apollo-synchronization-config - -6.删除 shenyu 的中间件 register center - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5352 - -### 增强 - -1.添加 shenyu model event 的单元测试 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4965 - -2.添加 shenyu admin 测试用例 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4971 - -https://github.com/apache/shenyu/pull/5231 - -https://github.com/apache/shenyu/pull/5263 - -3.添加 motan 的端到端测试用例 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4957 - -4.支持 motan 插件选择协议 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5003 - -5.添加 Grpc 的端到端测试用例 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4966 - -6.升级 apache-rat-plugin 版本到 0.15 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5029 - -7.在匹配时地址 isBlank 条件匹配 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4983 - -8.Clickhouse 支持 ttl 字段 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5032 - -9.支持 HttpUtils 的日志级别判断 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4976 - -10.为 Ingress Reconciler 添加单元测试 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5051 - -https://github.com/apache/shenyu/pull/5169 - -11.当软件包分发时自动 checksum - -具体 pr‘请查看: - -https://github.com/apache/shenyu/pull/5049 - -12.在 tcp 插件中实现零拷贝 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5066 - -13.shenyu-client-springmvc 支持默认的 appname 和 context-path - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5050 - -14.添加 sdk-feign 的示例和集成测试 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5099 - -15.es log 插件支持用户自定义的索引 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5102 - -16.增强 grpc 插件支持 shenyu-loadbalancer 负载均衡算法 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5115 - -17.支持 http2 协议的下游服务 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5125 - -18.重构增强 dubbo 插件支持 shenyu-loadbalancer 负载均衡算法 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5131 - -19.添加 ingress controller 的 springcloud 集成测试 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5139 - -20.添加 WebSocket 插件代理 ping 的功能 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5144 - -21.添加 ingress controller 的 websocket 集成测试 - -22.Rewrite 插件支持按照百分比重写 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5119 - -23.Admin 使用 discovery config 初始化 discovery server - -具体请查看: - -https://github.com/apache/shenyu/pull/5174 - -24.Divide 插件适配 shenyu discovery - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5185 - -25.Alert 支持多个 admin 的集群 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5197 - -26.WebSocket 插件适配 shenyu discovery - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5202 - -27.注册服务实例到 shenyu discovery - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5170 - -28.ShenYu Admin 适配 shenyu-discovery 的 local 模式 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5176 - -29.添加 shenyu sdk core 的测试用例 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5267 - -https://github.com/apache/shenyu/pull/5270 - -30.添加 shenyu-discovery 的测试用例 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5285 - -https://github.com/apache/shenyu/pull/5289 - -https://github.com/apache/shenyu/pull/5291 - -https://github.com/apache/shenyu/pull/5297 - -https://github.com/apache/shenyu/pull/5310 - -31.添加 opengauss 的 e2e 测试 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5309 - -32.添加上传插件包大小的限制 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5316 - -33.添加 shenyu-client-websocket 的测试用例 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5322 - -34.升级 shiro 到安全版本(1.18.0) - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5334 - -35.升级 SpringBoot 版本到 2.7.17,更新 license - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5356 - -36.添加网关异常时发送通知到 shenyu-alert - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5382 - -37.添加 EurekaDiscoveryService 单元测试 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5390 - -### 重构 - -1.重构整理 2.6.1 版本(pom.xml) - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4995 - -2.使用 computeIfAbsent 重构 Map 的操作 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4997 - -3.重构 polaris 测试用例 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4986 - -4.迁移 Maven Wrapper 到官方镜像 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5011 - -5.在 WebClientMessageWriter 中编译过的 Pattern - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5026 - -6.重构 HttpUtils 的请求方法 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5027 - -7.升级 github action 版本并重构 ci - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4992 - -https://github.com/apache/shenyu/pull/5039 - -https://github.com/apache/shenyu/pull/5081 - -8.重构数据同步的抽象模板方法 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5001 - -9.重构 MenuProject, MenuModule, MenuDocItem 为 VO 对象 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5062 - -10.统一 dubbo 版本 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5083 - -11.重构 HttpClient 的目录 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5107 - -12.重构 github action ci 缓存 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5096 - -13.重构 motan 插件支持 pojo 对象作为方法参数 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5112 - -14.升级 kafka-client 版本到 3.4.0 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5122 - -15.迁移 admin swagger springfox 到 springdoc - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5113 - -16.升级 dubbo 版本到 3.2.5 并重构过期方法 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5120 - -17.重构 AbstractShenyuSdkClient getOrDefault 方法 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5173 - -18.重构 HttpClient 的参数 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5151 - -19.重构 webclient 插件的实现 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5196 - -20.升级 guava 版本到 32.0.0-jre - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5137 - -21.支持 k8s 作为 e2e 的测试环境 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5217 - -https://github.com/apache/shenyu/pull/5298 - -22.使用@Restapi 作为 rest api 的请求路径映射 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5222 - -23.使用 StringBuilder 作为字符串连接器 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5074 - -24.设置 netty allocator 参数为 unpooled - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5244 - -25.重构启动的 banner - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5329 - -https://github.com/apache/shenyu/pull/5339 - -26.删除重复的代码并且将部分代码作为公用 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5336 - -27.重构 null 的判断方法 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5345 - -28.重构日志插件的选择器处理器 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5357 - -https://github.com/apache/shenyu/pull/5367 - -29.重构自定义插件类加载器 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5368 - -30.重构日志插件支持插件级别的采样比率 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5385 - -31.重构 Context-path 避免重复注册(使用 selector for update) - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5387 - -https://github.com/apache/shenyu/pull/5386 - -### 问题修复 - -1.避免创建 TimeoutException 的永久开销 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4973 - -2.修复示例模块的主类路径 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/497 - -3.修复插件排序问题 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4999 - -4.修复 Makefile Snapshot 版本问题 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4996 - -5.修复 RELEASE-NOTES.md 的拼写错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4991 - -6.修复示例中的错误包名 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5007 - -7.修复密码验证规则,并且添加#和.的支持 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4977 - -8.修复 e2e 中 zookeeper:3.8.0 的健康检查 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5008 - -9.修复不稳定的 ci 检验 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5017 - -10.添加 e2e WaitForHelper 异常日志 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5023 - -11.修复 springcloud 在某些注册中心中间件不能获取 scheme - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5014 - -12.修复 javadoc 编译错误 - -13.修复 HttpUtils 中错误的请求类型 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4975 - -14.修复更新 auth 时未更新用户 id - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/4982 - -15.修复 TCP 插件的 eventloop 线程泄漏 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5048 - -16.格式化 shenyu-integrated-test 中的 quickstart - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5065 - -17.修复 SQL 脚本错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5086 - -https://github.com/apache/shenyu/pull/5037 - -https://github.com/apache/shenyu/pull/5184 - -https://github.com/apache/shenyu/pull/5234 - -https://github.com/apache/shenyu/pull/5368 - -18.修复 uri 插件 path 错误,并且使用 rawpath 替代 path - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5121 - -https://github.com/apache/shenyu/pull/5128 - -19.修复 websocket 插件对 rewrite 插件的支持 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5130 - -20.修复 ElasticSearchLog Plugin 索引名称无效 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5158 - -21.修复 context-path 插件的错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5159 - -22.修复 shenyu-admin 的 cpu 占用过高问题 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5168 - -https://github.com/apache/shenyu/pull/5164 - -23.修复 alert 中 LocalDateTime 的格式化问题 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5180 - -24.修复 shenyu-client 的 apiDoc 的错误重试问题 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5171 - -25.修复 applicationContextAware 初始化顺序过晚 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5195 - -26.修复重复的 response header - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5190 - -27.设置 k8s 的最大等待时间 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5220 - -28.修改 clickhouse 日志插件的 status 字段类型 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5230 - -29.修复 response write plugin 可能造成的内存泄漏 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5228 - -30.修复 dataType 字段选择错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5237 - -31.修复 http 数据同步错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5239 - -32.修复单词拼写错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5251 - -33.修复 shenyu dubbo 代理插件的注册状态 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5243 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5266 - -35.修复 shenyu-registry 的 eureka 注册错误逻辑 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5269 - -36.修复 AbstractLogPluginDataHandler hashcode 错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5280 - -37.修复 ratelimit 插件在集群模式下的 key 错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5286 - -38.修复同一个应用多个 shenyu-client 重复注册 context-path 的错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5320 - -39.修复在插件关闭后不会重新加载插件 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5338 - -40.修复 shenyu admin 上传插件的错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5342 - -41.修复 shenyu 不能加载 resource 目录下的资源 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5372 - -42.修复 Admin 来展示字典值 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5375 - -43.修复 Authorization 在 sign 插件中的冲突 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5381 - -44.修复签名插件的 context-path 路径匹配错误 - -具体 pr 请查看: - -https://github.com/apache/shenyu/pull/5379 - -## 贡献者 - -TeslaCN,GOODBOY008,moremind,devinsong77,runqi-zhao,JooKS-me,kerwin612,li-keguo,yeshadoo,yu199195,DamonXue,liusishan,HaiqiQin,coderDylan,dragon-zhang,haolinkong,mxyyyy,misaya295,kerwin612,ywj1352,dengliming,impactCn,yunlongn,tanpenggood,xcsnx,xuziyang,ShawnJim,cubxxw,tomsun28,wenlongbrother,VampireAchao,wenpanwenpan,Ceilzcx,847850277,realize096,crudboy,tian-pengfei,0xmkzt,whenelse,lahmXu,wang3820,jbampton,eurecalulu,yudayday,YxYL6125,CytFree,GNK48-Ava,lianjunwei,MRgenial,geek-ken,ttfont - -## 成为贡献者 - -我们欢迎每一位贡献者的加入 ShenYu,欢迎贡献者以 Apache Way 的精神参与 ShenYu! - -贡献者指南请参考: - -https://shenyu.apache.org/zh/community/contributor-guide +--- +title: Apache ShenYu 2.6.1 Released +author: 何凤恩 +date: 2024-01-24 +cover: /assets/img/news/Apache-ShenYu-2.6.1-0.png +head: + - - meta + - name: 新闻 +--- + +## 关于 Apache ShenYu + +官网: https://shenyu.apache.org + +GitHub: https://github.com/apache/shenyu + +## 版本预览 + +版本记录:https://github.com/apache/shenyu/compare/v2.6.0...v2.6.1 + +### 新特性 + +1.为 shenyu ingress controller 添加 Dubbo 注解元数据 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5000 + +具体文档请查看: + +https://shenyu.apache.org/zh/docs/user-guide/kubernetes-controller/config + +2.支持插件生命周期 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5031 + +3.添加 shenyu-sdk-openfeign 模块 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5041 + +4.添加 Motan 和 Spring Cloud 添加 ingress controller 支持 + +5.shenyu 支持告警功能 + +![](/assets/img/news/Apache-ShenYu-2.6.1-0.png) + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4782 + +具体文档请查看: + +https://shenyu.apache.org/zh/docs/next/developer/notice-alert + +6.shenyu client 添加 discovery 的注册中心 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5153 + +7.添加 shenyu context-path Ingress controller + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5167 + +8.添加 shenyu grpc Ingress controller + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5240 + +9.添加 shenyu sofa Ingress controller + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5253 + +10.添加 nacos, etcd, eureka 作为 shenyu discovery 服务注册中心 + +![](/assets/img/news/Apache-ShenYu-2.6.1-1.png) + +![](/assets/img/news/Apache-ShenYu-2.6.1-2.png) + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5193 + +11.添加新的插件:basic-plugin + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5258 + +12.添加新插件以及集成测试:shenyu-rabbitmq-logging plugin + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5283 + +https://github.com/apache/shenyu/pull/5312 + +13.通过 shenyu-discovery 绑定 selector + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5261 + +### API 变化 + +1.重构 shenyu 数据同步的数据结构 + +![](/assets/img/news/Apache-ShenYu-2.6.1-3.png) + +2.使用 netty 作为默认的 httpclient + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5200 + +3.重构 shenyu-admin-listener 来支持 shenyu admin 数据同步 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5294 + +https://github.com/apache/shenyu/pull/5347 + +4.删除 shenyu 对 brpc 的支持,包括 brpc 插件,brpc 示例,brpc 集成测试 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5305 + +https://github.com/apache/shenyu/pull/5358 + +5.移除 Apollo 的依赖以便支持 Java 17(自行添加依赖) + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5308 + +具体文档请查看: + +https://shenyu.apache.org/docs/next/user-guide/property-config/use-data-sync/#apollo-synchronization-config + +6.删除 shenyu 的中间件 register center + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5352 + +### 增强 + +1.添加 shenyu model event 的单元测试 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4965 + +2.添加 shenyu admin 测试用例 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4971 + +https://github.com/apache/shenyu/pull/5231 + +https://github.com/apache/shenyu/pull/5263 + +3.添加 motan 的端到端测试用例 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4957 + +4.支持 motan 插件选择协议 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5003 + +5.添加 Grpc 的端到端测试用例 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4966 + +6.升级 apache-rat-plugin 版本到 0.15 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5029 + +7.在匹配时地址 isBlank 条件匹配 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4983 + +8.Clickhouse 支持 ttl 字段 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5032 + +9.支持 HttpUtils 的日志级别判断 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4976 + +10.为 Ingress Reconciler 添加单元测试 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5051 + +https://github.com/apache/shenyu/pull/5169 + +11.当软件包分发时自动 checksum + +具体 pr‘请查看: + +https://github.com/apache/shenyu/pull/5049 + +12.在 tcp 插件中实现零拷贝 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5066 + +13.shenyu-client-springmvc 支持默认的 appname 和 context-path + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5050 + +14.添加 sdk-feign 的示例和集成测试 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5099 + +15.es log 插件支持用户自定义的索引 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5102 + +16.增强 grpc 插件支持 shenyu-loadbalancer 负载均衡算法 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5115 + +17.支持 http2 协议的下游服务 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5125 + +18.重构增强 dubbo 插件支持 shenyu-loadbalancer 负载均衡算法 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5131 + +19.添加 ingress controller 的 springcloud 集成测试 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5139 + +20.添加 WebSocket 插件代理 ping 的功能 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5144 + +21.添加 ingress controller 的 websocket 集成测试 + +22.Rewrite 插件支持按照百分比重写 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5119 + +23.Admin 使用 discovery config 初始化 discovery server + +具体请查看: + +https://github.com/apache/shenyu/pull/5174 + +24.Divide 插件适配 shenyu discovery + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5185 + +25.Alert 支持多个 admin 的集群 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5197 + +26.WebSocket 插件适配 shenyu discovery + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5202 + +27.注册服务实例到 shenyu discovery + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5170 + +28.ShenYu Admin 适配 shenyu-discovery 的 local 模式 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5176 + +29.添加 shenyu sdk core 的测试用例 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5267 + +https://github.com/apache/shenyu/pull/5270 + +30.添加 shenyu-discovery 的测试用例 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5285 + +https://github.com/apache/shenyu/pull/5289 + +https://github.com/apache/shenyu/pull/5291 + +https://github.com/apache/shenyu/pull/5297 + +https://github.com/apache/shenyu/pull/5310 + +31.添加 opengauss 的 e2e 测试 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5309 + +32.添加上传插件包大小的限制 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5316 + +33.添加 shenyu-client-websocket 的测试用例 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5322 + +34.升级 shiro 到安全版本(1.18.0) + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5334 + +35.升级 SpringBoot 版本到 2.7.17,更新 license + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5356 + +36.添加网关异常时发送通知到 shenyu-alert + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5382 + +37.添加 EurekaDiscoveryService 单元测试 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5390 + +### 重构 + +1.重构整理 2.6.1 版本(pom.xml) + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4995 + +2.使用 computeIfAbsent 重构 Map 的操作 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4997 + +3.重构 polaris 测试用例 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4986 + +4.迁移 Maven Wrapper 到官方镜像 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5011 + +5.在 WebClientMessageWriter 中编译过的 Pattern + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5026 + +6.重构 HttpUtils 的请求方法 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5027 + +7.升级 github action 版本并重构 ci + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4992 + +https://github.com/apache/shenyu/pull/5039 + +https://github.com/apache/shenyu/pull/5081 + +8.重构数据同步的抽象模板方法 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5001 + +9.重构 MenuProject, MenuModule, MenuDocItem 为 VO 对象 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5062 + +10.统一 dubbo 版本 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5083 + +11.重构 HttpClient 的目录 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5107 + +12.重构 github action ci 缓存 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5096 + +13.重构 motan 插件支持 pojo 对象作为方法参数 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5112 + +14.升级 kafka-client 版本到 3.4.0 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5122 + +15.迁移 admin swagger springfox 到 springdoc + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5113 + +16.升级 dubbo 版本到 3.2.5 并重构过期方法 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5120 + +17.重构 AbstractShenyuSdkClient getOrDefault 方法 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5173 + +18.重构 HttpClient 的参数 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5151 + +19.重构 webclient 插件的实现 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5196 + +20.升级 guava 版本到 32.0.0-jre + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5137 + +21.支持 k8s 作为 e2e 的测试环境 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5217 + +https://github.com/apache/shenyu/pull/5298 + +22.使用@Restapi 作为 rest api 的请求路径映射 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5222 + +23.使用 StringBuilder 作为字符串连接器 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5074 + +24.设置 netty allocator 参数为 unpooled + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5244 + +25.重构启动的 banner + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5329 + +https://github.com/apache/shenyu/pull/5339 + +26.删除重复的代码并且将部分代码作为公用 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5336 + +27.重构 null 的判断方法 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5345 + +28.重构日志插件的选择器处理器 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5357 + +https://github.com/apache/shenyu/pull/5367 + +29.重构自定义插件类加载器 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5368 + +30.重构日志插件支持插件级别的采样比率 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5385 + +31.重构 Context-path 避免重复注册(使用 selector for update) + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5387 + +https://github.com/apache/shenyu/pull/5386 + +### 问题修复 + +1.避免创建 TimeoutException 的永久开销 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4973 + +2.修复示例模块的主类路径 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/497 + +3.修复插件排序问题 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4999 + +4.修复 Makefile Snapshot 版本问题 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4996 + +5.修复 RELEASE-NOTES.md 的拼写错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4991 + +6.修复示例中的错误包名 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5007 + +7.修复密码验证规则,并且添加#和.的支持 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4977 + +8.修复 e2e 中 zookeeper:3.8.0 的健康检查 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5008 + +9.修复不稳定的 ci 检验 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5017 + +10.添加 e2e WaitForHelper 异常日志 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5023 + +11.修复 springcloud 在某些注册中心中间件不能获取 scheme + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5014 + +12.修复 javadoc 编译错误 + +13.修复 HttpUtils 中错误的请求类型 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4975 + +14.修复更新 auth 时未更新用户 id + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/4982 + +15.修复 TCP 插件的 eventloop 线程泄漏 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5048 + +16.格式化 shenyu-integrated-test 中的 quickstart + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5065 + +17.修复 SQL 脚本错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5086 + +https://github.com/apache/shenyu/pull/5037 + +https://github.com/apache/shenyu/pull/5184 + +https://github.com/apache/shenyu/pull/5234 + +https://github.com/apache/shenyu/pull/5368 + +18.修复 uri 插件 path 错误,并且使用 rawpath 替代 path + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5121 + +https://github.com/apache/shenyu/pull/5128 + +19.修复 websocket 插件对 rewrite 插件的支持 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5130 + +20.修复 ElasticSearchLog Plugin 索引名称无效 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5158 + +21.修复 context-path 插件的错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5159 + +22.修复 shenyu-admin 的 cpu 占用过高问题 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5168 + +https://github.com/apache/shenyu/pull/5164 + +23.修复 alert 中 LocalDateTime 的格式化问题 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5180 + +24.修复 shenyu-client 的 apiDoc 的错误重试问题 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5171 + +25.修复 applicationContextAware 初始化顺序过晚 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5195 + +26.修复重复的 response header + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5190 + +27.设置 k8s 的最大等待时间 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5220 + +28.修改 clickhouse 日志插件的 status 字段类型 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5230 + +29.修复 response write plugin 可能造成的内存泄漏 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5228 + +30.修复 dataType 字段选择错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5237 + +31.修复 http 数据同步错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5239 + +32.修复单词拼写错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5251 + +33.修复 shenyu dubbo 代理插件的注册状态 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5243 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5266 + +35.修复 shenyu-registry 的 eureka 注册错误逻辑 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5269 + +36.修复 AbstractLogPluginDataHandler hashcode 错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5280 + +37.修复 ratelimit 插件在集群模式下的 key 错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5286 + +38.修复同一个应用多个 shenyu-client 重复注册 context-path 的错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5320 + +39.修复在插件关闭后不会重新加载插件 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5338 + +40.修复 shenyu admin 上传插件的错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5342 + +41.修复 shenyu 不能加载 resource 目录下的资源 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5372 + +42.修复 Admin 来展示字典值 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5375 + +43.修复 Authorization 在 sign 插件中的冲突 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5381 + +44.修复签名插件的 context-path 路径匹配错误 + +具体 pr 请查看: + +https://github.com/apache/shenyu/pull/5379 + +## 贡献者 + +TeslaCN,GOODBOY008,moremind,devinsong77,runqi-zhao,JooKS-me,kerwin612,li-keguo,yeshadoo,yu199195,DamonXue,liusishan,HaiqiQin,coderDylan,dragon-zhang,haolinkong,mxyyyy,misaya295,kerwin612,ywj1352,dengliming,impactCn,yunlongn,tanpenggood,xcsnx,xuziyang,ShawnJim,cubxxw,tomsun28,wenlongbrother,VampireAchao,wenpanwenpan,Ceilzcx,847850277,realize096,crudboy,tian-pengfei,0xmkzt,whenelse,lahmXu,wang3820,jbampton,eurecalulu,yudayday,YxYL6125,CytFree,GNK48-Ava,lianjunwei,MRgenial,geek-ken,ttfont + +## 成为贡献者 + +我们欢迎每一位贡献者的加入 ShenYu,欢迎贡献者以 Apache Way 的精神参与 ShenYu! + +贡献者指南请参考: + +https://shenyu.apache.org/zh/community/contributor-guide diff --git a/src/zh/news/Binlog4j-0.md b/src/zh/news/Binlog4j-0.md index d077847dfc..aa85d555c5 100644 --- a/src/zh/news/Binlog4j-0.md +++ b/src/zh/news/Binlog4j-0.md @@ -1,215 +1,215 @@ ---- -title: 轻量级 Mysql Binlog 客户端 Binlog4j 加入 Dromara 社区 -author: 就眠儀式 -date: 2023-08-31 -cover: /assets/img/news/Binlog4j-0.jpg -head: - - - meta - - name: 新闻 ---- - -## 项目介绍 - -Binlog4j 是一款提供宕机续读,高可用集群,数据转换的 Binlog 客户端。 - -## 项目特性 - -- 集群模式, 通过集群部署的方式,保证服务高可用。 -- 宕机续读, 避免宕机期间造成数据丢失。 -- 数据转换, 基于泛型封装 BinlogEvent 的序列化数据。 -- 兼容 传统项目 与 Spring Boot / Cloud 项目。 -- 兼容 Spring Boot 2.x 与 Spring Boot 3.x 版本。 - -## 应用场景 - -包括但不限于(1)数据同步(2)实时计算(3)数据审计(4)数据分析 - -## 下载安装 - -``` - - com.gitee.Jmysy - binlog4j-core - latest.version - -``` - -## 简单使用 - -通过 BinlogClient 创建客户端,IBinlogEventHandler 处理事件通知,该接口支持泛型,数据将遵循驼峰格式封装。 - -``` -public class BootStrap { - - public static void main(String[] args) { - - BinlogClientConfig clientConfig = new BinlogClientConfig(); - clientConfig.setHost("127.0.0.1"); - clientConfig.setPort(3306); - clientConfig.setUsername("root"); - clientConfig.setPassword("taoren@123"); - clientConfig.setServerId(1990); - - IBinlogClient binlogClient = new BinlogClient(clientConfig); - - binlogClient.registerEventHandler("database", "table", new IBinlogEventHandler() { - - @Override - public void onInsert(BinlogEvent event) { - System.out.println("插入数据:{}", event.getData()); - } - - @Override - public void onUpdate(BinlogEvent event) { - System.out.println("修改数据:{}", event.getData()); - } - - @Override - public void onDelete(BinlogEvent event) { - System.out.println("删除数据:{}", event.getData()); - } - }); - - binlogClient.connect(); - } -} -``` - -## 高级特性 - -通过 Persistence 配置为 true 启用宕机续读功能, Binlog4j 会将 binlog 的 filename 与 position 记录到 redis, 同时你需要提供 Redis 配置。 - -``` -public class BootStrap { - - public static void main(String[] args) { - - RedisConfig redisConfig = new RedisConfig(); - redisConfig.setHost("127.0.0.1"); - redisConfig.setPort(6379); - redisConfig.setPassword("taoren@123"); - - BinlogClientConfig clientConfig = new BinlogClientConfig(); - clientConfig.setHost("127.0.0.1"); - clientConfig.setPort(3306); - clientConfig.setUsername("root"); - clientConfig.setPassword("taoren@123"); - clientConfig.setServerId(1990); - clientConfig.setRedisConfig(redisConfig); - clientConfig.setPersistence(true); - clientConfig.setMode(BinlogClientMode.cluster); - - BinlogClient binlogClient = new BinlogClient(clientConfig); - - binlogClient.registerEventHandler("database", "table", new IBinlogEventHandler() { - - @Override - public void onInsert(BinlogEvent event) { - System.out.println("插入数据:{}", event.getData()); - } - - @Override - public void onUpdate(BinlogEvent event) { - System.out.println("修改数据:{}", event.getData()); - } - - @Override - public void onDelete(BinlogEvent event) { - System.out.println("删除数据:{}", event.getData()); - } - }); - - binlogClient.connect(); - } -} -``` - -## 在 Spring Boot 集成 - -``` - - com.gitee.Jmysy - binlog4j-spring-boot-starter - latest.version - -``` - -首先,在 application.yml 或 application.properties 中填写 Binlog4j 配置 - -``` -spring: - binlog4j: - redis-config: - host: 127.0.0.1 - port: 6379 - password: taoren@123 - client-configs: - master: - username: root - password: taoren@123 - host: 127.0.0.1 - port: 3306 - serverId: 1990 - slave: - username: root - password: taoren@123 - host: 127.0.0.1 - port: 3306 - serverId: 1991 -``` - -## 单表监听 - -使用 @BinlogSubscriber 注解, 指定 IBinlogEventHandler 需要注册到哪个客户端, 并且指定监听的 database 与 table。 - -``` -@BinlogSubscriber(clientName = "master", database = "pear-admin", table ="sys_user") -public class UserEventHandler implements IBinlogEventHandler { - - @Override - public void onInsert(BinlogEvent event) { - System.out.println("插入数据:" + event.getData()); - } - - @Override - public void onUpdate(BinlogEvent event) { - System.out.println("修改数据:" + event.getData()); - } - - @Override - public void onDelete(BinlogEvent event) { - System.out.println("删除数据:" + event.getData()); - } - -} -``` - -## 复杂监听 - -@BinlogSubscriber 注解 database 与 table 属性支持 Pattern 匹配, IBinlogEventHandler 在不指定泛型的情况下, event.getData() 为 Map 类型, 用于兼容不同表的数据结构。 - -``` -@BinlogSubscriber(clientName = "master", database = "pear-admin", table ="sys_user") -public class UserEventHandler implements IBinlogEventHandler { - - @Override - public void onInsert(BinlogEvent event) { - System.out.println("插入数据:" + event.getData()); - } - - @Override - public void onUpdate(BinlogEvent event) { - System.out.println("修改数据:" + event.getData()); - } - - @Override - public void onDelete(BinlogEvent event) { - System.out.println("删除数据:" + event.getData()); - } - -} -``` - -## 相关链接 - -Gitee: https://gitee.com/dromara/binlog4j +--- +title: 轻量级 Mysql Binlog 客户端 Binlog4j 加入 Dromara 社区 +author: 就眠儀式 +date: 2023-08-31 +cover: /assets/img/news/Binlog4j-0.jpg +head: + - - meta + - name: 新闻 +--- + +## 项目介绍 + +Binlog4j 是一款提供宕机续读,高可用集群,数据转换的 Binlog 客户端。 + +## 项目特性 + +- 集群模式, 通过集群部署的方式,保证服务高可用。 +- 宕机续读, 避免宕机期间造成数据丢失。 +- 数据转换, 基于泛型封装 BinlogEvent 的序列化数据。 +- 兼容 传统项目 与 Spring Boot / Cloud 项目。 +- 兼容 Spring Boot 2.x 与 Spring Boot 3.x 版本。 + +## 应用场景 + +包括但不限于(1)数据同步(2)实时计算(3)数据审计(4)数据分析 + +## 下载安装 + +``` + + com.gitee.Jmysy + binlog4j-core + latest.version + +``` + +## 简单使用 + +通过 BinlogClient 创建客户端,IBinlogEventHandler 处理事件通知,该接口支持泛型,数据将遵循驼峰格式封装。 + +``` +public class BootStrap { + + public static void main(String[] args) { + + BinlogClientConfig clientConfig = new BinlogClientConfig(); + clientConfig.setHost("127.0.0.1"); + clientConfig.setPort(3306); + clientConfig.setUsername("root"); + clientConfig.setPassword("taoren@123"); + clientConfig.setServerId(1990); + + IBinlogClient binlogClient = new BinlogClient(clientConfig); + + binlogClient.registerEventHandler("database", "table", new IBinlogEventHandler() { + + @Override + public void onInsert(BinlogEvent event) { + System.out.println("插入数据:{}", event.getData()); + } + + @Override + public void onUpdate(BinlogEvent event) { + System.out.println("修改数据:{}", event.getData()); + } + + @Override + public void onDelete(BinlogEvent event) { + System.out.println("删除数据:{}", event.getData()); + } + }); + + binlogClient.connect(); + } +} +``` + +## 高级特性 + +通过 Persistence 配置为 true 启用宕机续读功能, Binlog4j 会将 binlog 的 filename 与 position 记录到 redis, 同时你需要提供 Redis 配置。 + +``` +public class BootStrap { + + public static void main(String[] args) { + + RedisConfig redisConfig = new RedisConfig(); + redisConfig.setHost("127.0.0.1"); + redisConfig.setPort(6379); + redisConfig.setPassword("taoren@123"); + + BinlogClientConfig clientConfig = new BinlogClientConfig(); + clientConfig.setHost("127.0.0.1"); + clientConfig.setPort(3306); + clientConfig.setUsername("root"); + clientConfig.setPassword("taoren@123"); + clientConfig.setServerId(1990); + clientConfig.setRedisConfig(redisConfig); + clientConfig.setPersistence(true); + clientConfig.setMode(BinlogClientMode.cluster); + + BinlogClient binlogClient = new BinlogClient(clientConfig); + + binlogClient.registerEventHandler("database", "table", new IBinlogEventHandler() { + + @Override + public void onInsert(BinlogEvent event) { + System.out.println("插入数据:{}", event.getData()); + } + + @Override + public void onUpdate(BinlogEvent event) { + System.out.println("修改数据:{}", event.getData()); + } + + @Override + public void onDelete(BinlogEvent event) { + System.out.println("删除数据:{}", event.getData()); + } + }); + + binlogClient.connect(); + } +} +``` + +## 在 Spring Boot 集成 + +``` + + com.gitee.Jmysy + binlog4j-spring-boot-starter + latest.version + +``` + +首先,在 application.yml 或 application.properties 中填写 Binlog4j 配置 + +``` +spring: + binlog4j: + redis-config: + host: 127.0.0.1 + port: 6379 + password: taoren@123 + client-configs: + master: + username: root + password: taoren@123 + host: 127.0.0.1 + port: 3306 + serverId: 1990 + slave: + username: root + password: taoren@123 + host: 127.0.0.1 + port: 3306 + serverId: 1991 +``` + +## 单表监听 + +使用 @BinlogSubscriber 注解, 指定 IBinlogEventHandler 需要注册到哪个客户端, 并且指定监听的 database 与 table。 + +``` +@BinlogSubscriber(clientName = "master", database = "pear-admin", table ="sys_user") +public class UserEventHandler implements IBinlogEventHandler { + + @Override + public void onInsert(BinlogEvent event) { + System.out.println("插入数据:" + event.getData()); + } + + @Override + public void onUpdate(BinlogEvent event) { + System.out.println("修改数据:" + event.getData()); + } + + @Override + public void onDelete(BinlogEvent event) { + System.out.println("删除数据:" + event.getData()); + } + +} +``` + +## 复杂监听 + +@BinlogSubscriber 注解 database 与 table 属性支持 Pattern 匹配, IBinlogEventHandler 在不指定泛型的情况下, event.getData() 为 Map 类型, 用于兼容不同表的数据结构。 + +``` +@BinlogSubscriber(clientName = "master", database = "pear-admin", table ="sys_user") +public class UserEventHandler implements IBinlogEventHandler { + + @Override + public void onInsert(BinlogEvent event) { + System.out.println("插入数据:" + event.getData()); + } + + @Override + public void onUpdate(BinlogEvent event) { + System.out.println("修改数据:" + event.getData()); + } + + @Override + public void onDelete(BinlogEvent event) { + System.out.println("删除数据:" + event.getData()); + } + +} +``` + +## 相关链接 + +Gitee: https://gitee.com/dromara/binlog4j diff --git a/src/zh/news/Carbon-0.md b/src/zh/news/Carbon-0.md new file mode 100644 index 0000000000..1c815259b1 --- /dev/null +++ b/src/zh/news/Carbon-0.md @@ -0,0 +1,337 @@ +--- +title: 新晋开源项目 Carbon 加入 Dromara,Gopher 的时间工具来了 +author: carbon +tag: + - Carbon +date: 2024-10-24 +cover: /assets/img/news/Carbon-0-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/Carbon-0-0.png) + +对于 `gopher` 来说,时间处理是一个常见但又复杂的问题,特别是仅依赖内置的 `time.Time` 包时,尤其是我们在格式化时间的时候,需要用固定的 `Layout`,如 + +``` +now := time.Now() + // 这几个数字不能乱写,不然转化过来的时间会很莫名其妙 +strNow := now.Format("2006/01/02 03:04:05") +``` + +这时 `carbon` 的用武之地就展现出来了,在 `Golang` 语言圈中,如果你是做日期和时间相关的工作,还没有听说过 `Carbon`库,我劝你还是把之前编写的日期和时间相关的代码再捋一捋,看看`Carbon`能帮你节省多少时间。 + +`Carbon` 是一个专为 `Golang` 设计的轻量级、语义化、对开发者友好的 时间处理库,被誉为 `Golang` 时间处理的瑞士军刀,它提供了一系列简洁而强大的 `API`,使得时间操作变得异常简单。无论是基本的日期时间计算,还是复杂的时区转换,`Carbon` 都能轻松应对。此外,`Carbon` 已经被 `awesome-go` 收录,还被 `gitee` 评选为年度最有价值项目(`GVP`),证明了其在 `Golang` 社区中的受欢迎程度。 + +#### 安装使用 + +##### Golang 版本大于等于 1.17 (推荐) + +``` +// 使用 github 库 +go get -u github.com/golang-module/carbon/v2 + +import "github.com/golang-module/carbon/v2" + +// 使用 gitee 库 +go get -u gitee.com/golang-module/carbon/v2 + +import "gitee.com/golang-module/carbon/v2" +``` + +##### Golang 版本小于 1.17 (必须) + +``` +// 使用 github 库 +go get -u github.com/golang-module/carbon + +import "github.com/golang-module/carbon" + +// 使用 gitee 库 +go get -u gitee.com/golang-module/carbon + +import "gitee.com/golang-module/carbon" +``` + +#### 用法示例 + +> 假设当前时间为 2020-08-05 13:14:15.999999999 +0800 CST + +##### 设置全局默认值 + +``` +carbon.SetDefault(carbon.Default{ +    Layout: carbon.DateTimeLayout,  +    Timezone: carbon.PRC,  +    WeekStartsAt: carbon.Sunday,  +    Locale: "zh-CN", // 取值范围:lang 目录下翻译文件名,不包含文件后缀 +}) +``` + +##### `Carbon` 和 `time.Time` 互转 + +``` +// 将标准 time.Time 转换成 Carboncarbon.CreateFromStdTime(time.Now()) +// 将 Carbon 转换成标准 time.Timecarbon.Now().StdTime() +``` + +##### 昨天、今天、明天 + +``` +// 今天此刻 +fmt.Printf("%s", carbon.Now()) // 2020-08-05 13:14:15 +carbon.Now().String() // 2020-08-05 13:14:15 +carbon.Now().ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Now().ToDateTimeString() // 2020-08-05 13:14:15 +// 今天日期 +carbon.Now().ToDateString() // 2020-08-05 +// 今天时间 +carbon.Now().ToTimeString() // 13:14:15 +// 指定时区的今天此刻 +carbon.Now(Carbon.NewYork).ToDateTimeString() // 2020-08-05 14:14:15 +// 今天秒级时间戳 +carbon.Now().Timestamp() // 1596604455 +// 今天毫秒级时间戳 +carbon.Now().TimestampMilli() // 1596604455999 +// 今天微秒级时间戳 +carbon.Now().TimestampMicro() // 1596604455999999 +// 今天纳秒级时间戳 +carbon.Now().TimestampNano() // 1596604455999999999 + +// 昨天此刻 +fmt.Printf("%s", carbon.Yesterday()) // 2020-08-04 13:14:15 +carbon.Yesterday().String() // 2020-08-04 13:14:15 +carbon.Yesterday().ToString() // 2020-08-04 13:14:15 +0800 CST +carbon.Yesterday().ToDateTimeString() // 2020-08-04 13:14:15 +// 昨天日期 +carbon.Yesterday().ToDateString() // 2020-08-04 +// 昨天时间 +carbon.Yesterday().ToTimeString() // 13:14:15 +// 指定日期的昨天此刻 +carbon.Parse("2021-01-28 13:14:15").Yesterday().ToDateTimeString() // 2021-01-27 13:14:15 +// 指定时区的昨天此刻 +carbon.Yesterday(Carbon.NewYork).ToDateTimeString() // 2020-08-04 14:14:15 +// 昨天秒级时间戳 +carbon.Yesterday().Timestamp() // 1596518055 +// 昨天毫秒级时间戳 +carbon.Yesterday().TimestampMilli() // 1596518055999 +// 昨天微秒级时间戳 +carbon.Yesterday().TimestampMicro() // 1596518055999999 +// 昨天纳秒级时间戳 +carbon.Yesterday().TimestampNano() // 1596518055999999999 + +// 明天此刻 +fmt.Printf("%s", carbon.Tomorrow()) // 2020-08-06 13:14:15 +carbon.Tomorrow().String() // 2020-08-06 13:14:15 +carbon.Tomorrow().ToString() // 2020-08-06 13:14:15 +0800 CST +carbon.Tomorrow().ToDateTimeString() // 2020-08-06 13:14:15 +// 明天日期 +carbon.Tomorrow().ToDateString() // 2020-08-06 +// 明天时间 +carbon.Tomorrow().ToTimeString() // 13:14:15 +// 指定日期的明天此刻 +carbon.Parse("2021-01-28 13:14:15").Tomorrow().ToDateTimeString() // 2021-01-29 13:14:15 +// 指定时区的明天此刻 +carbon.Tomorrow(Carbon.NewYork).ToDateTimeString() // 2020-08-06 14:14:15 +// 明天秒级时间戳 +carbon.Tomorrow().Timestamp() // 1596690855 +// 明天毫秒级时间戳 +carbon.Tomorrow().TimestampMilli() // 1596690855999 +// 明天微秒级时间戳 +carbon.Tomorrow().TimestampMicro() // 1596690855999999 +// 明天纳秒级时间戳 +carbon.Tomorrow().TimestampNano() // 1596690855999999999 +``` + +##### 创建 `Carbon` 实例 + +``` +// 从秒级时间戳创建 Carbon 实例 +carbon.CreateFromTimestamp(-1).ToString() // 1970-01-01 07:59:59 +0800 CST +carbon.CreateFromTimestamp(0).ToString() // 1970-01-01 08:00:00 +0800 CST +carbon.CreateFromTimestamp(1).ToString() // 1970-01-01 08:00:01 +0800 CST +carbon.CreateFromTimestamp(1649735755).ToString() // 2022-04-12 11:55:55 +0800 CST +// 从毫秒级时间戳创建 Carbon 实例 +carbon.CreateFromTimestampMilli(1649735755981).ToString() // 2022-04-12 11:55:55.981 +0800 CST +// 从微秒级时间戳创建 Carbon 实例 +carbon.CreateFromTimestampMicro(1649735755981566).ToString() // 2022-04-12 11:55:55.981566 +0800 CST +// 从纳秒级时间戳创建 Carbon 实例 +carbon.CreateFromTimestampNano(1649735755981566000).ToString() // 2022-04-12 11:55:55.981566 +0800 CST + +// 从年月日时分秒创建 Carbon 实例 +carbon.CreateFromDateTime(2020, 8, 5, 13, 14, 15).ToString() // 2020-08-05 13:14:15 +0800 CST +// 从年月日时分秒创建 Carbon 实例,包含毫秒 +carbon.CreateFromDateTimeMilli(2020, 8, 5, 13, 14, 15, 999).ToString() // 2020-08-05 13:14:15.999 +0800 CST +// 从年月日时分秒创建 Carbon 实例,包含微秒 +carbon.CreateFromDateTimeMicro(2020, 8, 5, 13, 14, 15, 999999).ToString() // 2020-08-05 13:14:15.999999 +0800 CST +// 从年月日时分秒创建 Carbon 实例,包含纳秒 +carbon.CreateFromDateTimeNano(2020, 8, 5, 13, 14, 15, 999999999).ToString() // 2020-08-05 13:14:15.999999999 +0800 CST + +// 从年月日创建 Carbon 实例 +carbon.CreateFromDate(2020, 8, 5).ToString() // 2020-08-05 00:00:00 +0800 CST +// 从年月日创建 Carbon 实例,包含毫秒 +carbon.CreateFromDateMilli(2020, 8, 5, 999).ToString() // 2020-08-05 00:00:00.999 +0800 CST +// 从年月日创建 Carbon 实例,包含微秒 +carbon.CreateFromDateMicro(2020, 8, 5, 999999).ToString() // 2020-08-05 00:00:00.999999 +0800 CST +// 从年月日创建 Carbon 实例,包含纳秒 +carbon.CreateFromDateNano(2020, 8, 5, 999999999).ToString() // 2020-08-05 00:00:00.999999999 +0800 CST + +// 从时分秒创建 Carbon 实例(年月日默认为当前年月日) +carbon.CreateFromTime(13, 14, 15).ToString() // 2020-08-05 13:14:15 +0800 CST +// 从时分秒创建 Carbon 实例(年月日默认为当前年月日),包含毫秒 +carbon.CreateFromTimeMilli(13, 14, 15, 999).ToString() // 2020-08-05 13:14:15.999 +0800 CST +// 从时分秒创建 Carbon 实例(年月日默认为当前年月日),包含微秒 +carbon.CreateFromTimeMicro(13, 14, 15, 999999).ToString() // 2020-08-05 13:14:15.999999 +0800 CST +// 从时分秒创建 Carbon 实例(年月日默认为当前年月日),包含纳秒 +carbon.CreateFromTimeNano(13, 14, 15, 999999999).ToString() // 2020-08-05 13:14:15.999999999 +0800 CST +``` + +##### 将 `时间字符串` 解析成 `Carbon` 实例 + +``` +carbon.Parse("").ToDateTimeString() // 空字符串 +carbon.Parse("0").ToDateTimeString() // 空字符串 +carbon.Parse("00:00:00").ToDateTimeString() // 空字符串 +carbon.Parse("0000-00-00").ToDateTimeString() // 空字符串 +carbon.Parse("0000-00-00 00:00:00").ToDateTimeString() // 空字符串 + +carbon.Parse("now").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("yesterday").ToString() // 2020-08-04 13:14:15 +0800 CST +carbon.Parse("tomorrow").ToString() // 2020-08-06 13:14:15 +0800 CST + +carbon.Parse("2020").ToString() // 2020-01-01 00:00:00 +0800 CST +carbon.Parse("2020-8").ToString() // 2020-08-01 00:00:00 +0800 CST +carbon.Parse("2020-08").ToString() // 2020-08-01 00:00:00 +0800 CST +carbon.Parse("2020-8-5").ToString() // 2020-08-05 00:00:00 +0800 CST +carbon.Parse("2020-8-05").ToString() // 2020-08-05 00:00:00 +0800 CST +carbon.Parse("2020-08-05").ToString() // 2020-08-05 00:00:00 +0800 CST +carbon.Parse("2020-08-05.999").ToString() // 2020-08-05 00:00:00.999 +0800 CST +carbon.Parse("2020-08-05.999999").ToString() // 2020-08-05 00:00:00.999999 +0800 CST +carbon.Parse("2020-08-05.999999999").ToString() // 2020-08-05 00:00:00.999999999 +0800 CST + +carbon.Parse("2020-8-5 13:14:15").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("2020-8-05 13:14:15").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("2020-08-5 13:14:15").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("2020-08-05 13:14:15").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("2020-08-05 13:14:15.999").ToString() // 2020-08-05 13:14:15.999 +0800 CST +carbon.Parse("2020-08-05 13:14:15.999999").ToString() // 2020-08-05 13:14:15.999999 +0800 CST +carbon.Parse("2020-08-05 13:14:15.999999999").ToString() // 2020-08-05 13:14:15.999999999 +0800 CST + +carbon.Parse("2020-8-5T13:14:15+08:00").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("2020-8-05T13:14:15+08:00").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("2020-08-05T13:14:15+08:00").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("2020-08-05T13:14:15.999+08:00").ToString() // 2020-08-05 13:14:15.999 +0800 CST +carbon.Parse("2020-08-05T13:14:15.999999+08:00").ToString() // 2020-08-05 13:14:15.999999 +0800 CST +carbon.Parse("2020-08-05T13:14:15.999999999+08:00").ToString() // 2020-08-05 13:14:15.999999999 +0800 CST + +carbon.Parse("20200805").ToString() // 2020-08-05 00:00:00 +0800 CST +carbon.Parse("20200805131415").ToString() // 2020-08-05 13:14:15 +0800 CST +carbon.Parse("20200805131415.999").ToString() // 2020-08-05 13:14:15.999 +0800 CST +carbon.Parse("20200805131415.999999").ToString() // 2020-08-05 13:14:15.999999 +0800 CST +carbon.Parse("20200805131415.999999999").ToString() // 2020-08-05 13:14:15.999999999 +0800 CST +carbon.Parse("20200805131415.999+08:00").ToString() // 2020-08-05 13:14:15.999 +0800 CST +carbon.Parse("20200805131415.999999+08:00").ToString() // 2020-08-05 13:14:15.999999 +0800 CST +carbon.Parse("20200805131415.999999999+08:00").ToString() // 2020-08-05 13:14:15.999999999 +0800 CST +``` + +`carbon` 还提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日/简化儒略日、波斯历/伊朗历的支持。 + +##### 将 `公历` 转换成 `农历` + +``` +// 获取农历生肖 +carbon.Parse("2020-08-05 13:14:15").Lunar().Animal() // 鼠 +// 获取农历节日 +carbon.Parse("2021-02-12 13:14:15").Lunar().Festival() // 春节 + +// 获取农历年份 +carbon.Parse("2020-08-05 13:14:15").Lunar().Year() // 2020 +// 获取农历月份 +carbon.Parse("2020-08-05 13:14:15").Lunar().Month() // 6 +// 获取农历闰月月份 +carbon.Parse("2020-08-05 13:14:15").Lunar().LeapMonth() // 4 +// 获取农历日期 +carbon.Parse("2020-08-05 13:14:15").Lunar().Day() // 16 +// 获取农历时辰 +carbon.Parse("2020-08-05 13:14:15").Lunar().Hour() // 13 +// 获取农历分钟 +carbon.Parse("2020-08-05 13:14:15").Lunar().Minute() // 14 +// 获取农历秒数 +carbon.Parse("2020-08-05 13:14:15").Lunar().Second() // 15 + +// 获取农历日期时间字符串 +carbon.Parse("2020-08-05 13:14:15").Lunar().String() // 2020-06-16 13:14:15 +fmt.Printf("%s", carbon.Parse("2020-08-05 13:14:15").Lunar()) // 2020-06-16 13:14:15 +// 获取农历年字符串 +carbon.Parse("2020-08-05 13:14:15").Lunar().ToYearString() // 二零二零 +// 获取农历月字符串 +carbon.Parse("2020-08-05 13:14:15").Lunar().ToMonthString() // 六月 +// 获取农历闰月字符串 +carbon.Parse("2020-04-23 13:14:15").Lunar().ToMonthString() // 闰四月 +// 获取农历周字符串 +carbon.Parse("2020-04-23 13:14:15").Lunar().ToWeekString() // 周四 +// 获取农历天字符串 +carbon.Parse("2020-08-05 13:14:15").Lunar().ToDayString() // 十六 +// 获取农历日期字符串 +carbon.Parse("2020-08-05 13:14:15").Lunar().ToDateString() // 二零二零年六月十六 + +// 是否是零值时间 +carbon.Parse("0000-00-00 00:00:00").Lunar().IsZero() // true +carbon.Parse("2020-08-05 13:14:15").Lunar().IsZero() // false + +// 是否是农历闰年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsLeapYear() // true +// 是否是农历闰月 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsLeapMonth() // false + +// 是否是鼠年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsRatYear() // true +// 是否是牛年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsOxYear() // false +// 是否是虎年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsTigerYear() // false +// 是否是兔年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsRabbitYear() // false +// 是否是龙年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsDragonYear() // false +// 是否是蛇年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsSnakeYear() // false +// 是否是马年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsHorseYear() // false +// 是否是羊年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsGoatYear() // false +// 是否是猴年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsMonkeyYear() // false +// 是否是鸡年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsRoosterYear() // false +// 是否是狗年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsDogYear() // false +// 是否是猪年 +carbon.Parse("2020-08-05 13:14:15").Lunar().IsPigYear() // false +``` + +##### 将 `农历` 转化成 `公历` + +``` +// 将农历 二零二三年腊月十一 转化为 公历 +carbon.CreateFromLunar(2023, 12, 11, 0, 0, 0, false).ToDateTimeString() // 2024-01-21 00:00:00 +// 将农历 二零二三年二月十一 转化为 公历 +carbon.CreateFromLunar(2023, 2, 11, 0, 0, 0, false).ToDateTimeString() // 2023-03-02 00:00:00 +// 将农历 二零二三年闰二月十一 转化为 公历 +carbon.CreateFromLunar(2023, 2, 11, 0, 0, 0, true).ToDateTimeString() // 2023-04-01 00:00:00 +``` + +目前已支持简体中文、繁体中文、英语、日语、德语、西班牙语、法语、阿拉伯语等26国语言的支持。 + +![](/assets/img/news/Carbon-0-1.png) + +Laravel + +##### 结语 + +无论是新手还是经验丰富的 `Golang` 开发者,`Carbon` 都是一个值得尝试的库。通过它,你可以更加优雅地处理时间问题,提高代码的可读性和开发效率。现在就将 `Carbon` 引入你的项目,享受编程带来的乐趣吧! + +GitHub: https://github.com/dromara/carbon + +Gitee: https://gitee.com/dromara/carbon \ No newline at end of file diff --git a/src/zh/news/Carbon-2.6.0.md b/src/zh/news/Carbon-2.6.0.md new file mode 100644 index 0000000000..f8415c7e5d --- /dev/null +++ b/src/zh/news/Carbon-2.6.0.md @@ -0,0 +1,94 @@ +--- +title: 轻量级 Golang 时间处理库 Carbon 2.6.0 发布 +author: carbon +date: 2025-03-25 +cover: /assets/img/news/Carbon-2.6.0-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/Carbon-2.6.0-0.png) + +carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。 + +carbon 目前已捐赠给 dromara\[1\] 开源组织,已被  awesome-go\[2\]  收录,并获得 gitee\[3\] 2024 年最有价值项目(GVP\[4\])和 gitcode\[5\] 2024 年度 G-Star\[6\] 项目,如果您觉得不错,请给个 star 吧 + +github.com/dromara/carbon\[7\] + +gitee.com/dromara/carbon\[8\] + +gitcode.com/dromara/carbon\[9\] + +#### 更新日志 + +* `golang` 最低版本依赖升级到 `1.18` + +* `carbon`, `julian`, `lunar`, `persian` 从值传递改成指针传递 + +* 新增 `ZoneName` 方法获取时区名称 + +* 新增 `HasError` 方法判断是否有错误 + +* 新增 `IsNil` 方法判断是否是 `nil` + +* 新增 `Copy` 方法对 `carbon` 进行深度复制 + +* 新增示例文件 `xxx_example.go` + +* 新增`constant.go` 文件,将常量从 `carbon.go` 文件迁移到此文件 + +* 默认全局时区从 `Local` 更改为 `UTC` + +* `Offset` 方法更名为 `ZoneOffset` + +* `IsSetTestNow` 方法更名为 `IsTestNow` + +* `UnSetTestNow`  方法更名为 `CleanTestNow` + +* 移除 `Location` 方法,由 `Timezone` 方法替代 + +* 更改 `IsValid` 和 `IsInvalid` 方法判断逻辑,`zero time` 不再视为无效时间 + +* 设置全局默认时区时同步更新 `time.Local` + +* 重构 `database.go`,移除 `carbon.DateTime`、`carbon. DateTimeMilli` 、 `carbon.DateTimeMicro`、`carbon.DateTimeNano`、 `carbon. Date`、`carbon.DateMilli`、 `carbon.DateMicro`、 `carbon.DateNano`、 `carbon.Time`、 `carbon.TimeMilli`、 `carbon.TimeMicro`、  `carbon.TimeNano`、`carbon.Timestamp` 、`carbon.TimestampMilli`  、`carbon.TimestampMicro`、`carbon.TimestampNano` 字段类型, 使用泛型字段替代以实现 `MarshalJSON/UnmarshalJSON` 时自定义输出格式 + + +参考资料 + +\[1\]  + +dromara: _https://dromara.org/_ + +\[2\]  + +awesome-go: _https://github.com/avelino/awesome-go#date-and-time_ + +\[3\]  + +gitee: _https://gitee.com_ + +\[4\]  + +GVP: _https://gitee.com/gcp_ + +\[5\]  + +gitcode: _https://gitcode.com_ + +\[6\]  + +G-Star: _https://gitcode.com/g-star_ + +\[7\]  + +github.com/dromara/carbon: _https://github.com/dromara/carbon_ + +\[8\]  + +gitee.com/dromara/carbon: _https://gitee.com/dromara/carbon_ + +\[9\]  + +gitcode.com/dromara/carbon: _https://gitcode.com/dromara/carbon_ \ No newline at end of file diff --git a/src/zh/news/Carbon-2.6.2.md b/src/zh/news/Carbon-2.6.2.md new file mode 100644 index 0000000000..9ba1f7fe00 --- /dev/null +++ b/src/zh/news/Carbon-2.6.2.md @@ -0,0 +1,98 @@ +--- +title: Golang 时间处理库 carbon 2.6.2 发布 +author: carbon +date: 2025-04-11 +cover: /assets/img/news/carbon-2.6.2-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/carbon-2.6.2-0.png) + + + +carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、季节、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。 + +carbon 目前已捐赠给 dromara\[1\] 开源组织,已被  awesome-go\[2\]  收录,并获得 gitee\[3\] 2024 年最有价值项目(GVP\[4\])和 gitcode\[5\] 2024 年度 G-Star\[6\] 项目,如果您觉得不错,请给个 star 吧 + +github.com/dromara/carbon\[7\] + +gitee.com/dromara/carbon\[8\] + +gitcode.com/dromara/carbon\[9\] + +#### 更新日志 + +* `CreateFromLunar`, `CreateFromPersian` 方法去掉 `hour`, `minute`, `second` 参数 + +* 更改部分格式符号定义,涉及到的符号有 `U`, `V`, `X`,`S`,`T` `Z`,`u`,`v`,`x`,`z` + +* 修复农历中 `IsLeapMonth` 判断错误的 bug + +* 修复 `AtomFormat` 和 `AtomLayout` 格式返回值不一致的 bug + +* 修复 `RFC3339Format` 和 `RFC3339Layout` 格式返回值不一致的 bug + +* 设置全局默认时区时不再同步更新 `time.Local` + +* 新增格式符号`o` 来获取时区偏移量 + +* 新增 `TimestampLayout`、`TimestampMilliLayout`、`TimestampMicroLayout` 和 `TimestampNanoLayout` 常量 + +* 新增 `TimestampFormat`、`TimestampMilliFormat`、`TimestampMicroFormat` 和 `TimestampNanoFormat` 常量 + +* 新增 `DateTimeMilli`、`DateTimeMicro`、`DateTimeNano` 字段类型 + +* 新增 `DateMilli`、`DateMicro`、`DateNano` 字段类型 + +* 新增 `TimeMilli`、`TimeMicro`、`TimeNano` 字段类型 + +* 修复 `IsDST` 方法丢失时区的 bug + +* 修复 `StartOfXXX`、`EndOfXXX` 部分方法丢失时区的 bug + +* 修复其他日历转化为公历时缺失时区的 bug + +* 设置默认时区时不再同步更新  `time.Local` + +* 新增 `MaxDuration`、`MinDuration` 方法 + + +参考资料 + +\[1\]  + +dromara: _https://dromara.org/_ + +\[2\]  + +awesome-go: _https://github.com/avelino/awesome-go#date-and-time_ + +\[3\]  + +gitee: _https://gitee.com_ + +\[4\]  + +GVP: _https://gitee.com/gcp_ + +\[5\]  + +gitcode: _https://gitcode.com_ + +\[6\]  + +G-Star: _https://gitcode.com/g-star_ + +\[7\]  + +github.com/dromara/carbon: _https://github.com/dromara/carbon_ + +\[8\]  + +gitee.com/dromara/carbon: _https://gitee.com/dromara/carbon_ + +\[9\]  + +gitcode.com/dromara/carbon: _https://gitcode.com/dromara/carbon_ \ No newline at end of file diff --git a/src/zh/news/Carbon-v2.5.0.md b/src/zh/news/Carbon-v2.5.0.md new file mode 100644 index 0000000000..4917d4650f --- /dev/null +++ b/src/zh/news/Carbon-v2.5.0.md @@ -0,0 +1,94 @@ +--- +title: 轻量级 Golang 时间库Carbon v2.5.0 发布 +author: carbon +date: 2024-11-28 +cover: /assets/img/news/Carbon-v2.5.0-0.png +head: + - - meta + - name: 新闻 +--- + +carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。 + +carbon 目前已捐赠给 dromara\[1\] 开源组织,已被  awesome-go\[2\]  收录,并获得 gitee\[3\] 2024 年最有价值项目(GVP\[4\])和 gitcode\[5\] 2024 年度 G-Star\[6\] 项目,如果您觉得不错,请给个 star 吧 + +github.com/dromara/carbon\[7\] + +gitee.com/dromara/carbon\[8\] + +gitcode.com/dromara/carbon\[9\] + +![](/assets/img/news/Carbon-v2.5.0-0.png) + +#### 更新日志 + +* 仓库地址从 `github.com/golang-module/carbon` 改成 `github.com/dromara/carbon` + +* 增加对 `匈牙利` 的翻译支持,由 @kenlas 翻译 + +* 重新翻译 `日文版 README` 文件,由日本友人 @You-saku 翻译 + +* 修复 `DiffInMonths()` 计算错误的 bug + +* `Lock()`、`Unlock()` 替换成  `RLock()`、`URnlock()` + +* 删除 `ToDateTimeStruct()`、`ToDateTimeMilliStruct()`、`ToDateTimeMicroStruct()` 、`ToDateStruct()`等方法,由新增的 `NewDateTime()`、`NewDateTimeMilli()`、`NewDateTimeMicro()`、NewDate() 等方法替换 + +* `DateTime`、`DateTimeXXX`、`Date`、`DateXXX`、`Time`、`TimeXXX` 等结构体实现  `Scan`、`Value`、`MarshalJSON`、`UnmarshalJSON` 接口 + +* `Scan` 接口实现时支持对 `string`、`[]byte`、`Time.time` 格式的解析支持 + +* `UnmarshalJSON` 解析 `json` 格式时间字符串时,统一使用全局默认时区 `defaultTimezon` + +* `codecov/codecov-action` 从 `v4` 升级到 `v5` + + +参考资料 + +\[1\] + +dromara: _https://dromara.org/_ + +\[2\] + +awesome-go: _https://github.com/avelino/awesome-go#date-and-time_ + +\[3\] + +gitee: _https://gitee.com_ + +\[4\] + +GVP: _https://gitee.com/gcp_ + +\[5\] + +gitcode: _https://gitcode.com_ + +\[6\] + +G-Star: _https://gitcode.com/g-star_ + +\[7\] + +github.com/dromara/carbon: _https://github.com/dromara/carbon_ + +\[8\] + +gitee.com/dromara/carbon: _https://gitee.com/dromara/carbon_ + +\[9\] + +gitcode.com/dromara/carbon: _https://gitcode.com/dromara/carbon_ + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/Carbon-v2.5.0-1.webp) \ No newline at end of file diff --git a/src/zh/news/CloudEon-1.1.0.md b/src/zh/news/CloudEon-1.1.0.md index cfd67e6ecc..5c1147a850 100644 --- a/src/zh/news/CloudEon-1.1.0.md +++ b/src/zh/news/CloudEon-1.1.0.md @@ -1,126 +1,126 @@ ---- -title: 云原生大数据平台CloudEon V1.1.0版本发布! -author: CloudEon开源 -date: 2023-07-31 -cover: /assets/img/news/CloudEon-1.1.0.png -head: - - - meta - - name: 新闻 ---- - -CloudEon 社区的小伙伴们大家好,今天很高兴宣布 CloudEon 于 2023 年 7 月 28 日正式发布 1.1.0   版本。 - -非常感谢 CloudEon 开源社区成员对 1.1.0 版本发布做出的贡献。 - -## 重要更新 - -1.1.0 版本主要有如下重大更新: - -**新增功能:** - -- 支持 Kyuubi 服务,实现 Spark SQL 查询 -- 增加 Kyuubi 监控面板 -- 支持 Iceberg 数据湖功能,实现 Iceberg 在 Flink 和 Spark 上的集成 -- 支持 Elasticsearch 组件 -- 支持数据采集 seatunnel 组件 -- 支持 OLAP 组件 Kylin5 -- 支持实时计算 Dinky 组件 - -**优化改进:** - -- 优化组件 Docker 镜像,精简大小 -- 改进节点 SSH 连接,支持指定私钥 -- 优化监控面板,修复多个面板问题 -- 优化部分组件的容器资源控制问题,包括 hbase/flink/spark/hive/yarn/hdfs/zookeeper - -**修复缺陷:** - -- 修复停止服务时角色 Pod 错乱的问题 -- 修复 Zookeeper 无法修改客户端端口的问题 -- 修复 HBase Shell 无法在 Pod 中使用的问题 -- 修复偶尔出现 ssh 导致的命令执行卡住问题 -- 修复 hive 容器缺失 mysql 驱动问题 - -详情可查看: - -https://github.com/dromara/CloudEon/releases/tag/v1.1.0 - -## 安装部署 - -### **Docker 部署(推荐)** - -如果你本地已经安装了 docker,执行以下命令可以一键安装: - -``` -docker run  -p 7700:7700  --name cloudeon --rm registry.cn-hangzhou.aliyuncs.com/udh/cloudeon:v1.1.0 -``` - -镜像启动成功后,在浏览器中访问 http://docker_ip:7700 进入登录页。镜像中提供初始账户,用户名 admin 密码 admin - -更多配置信息可以查看项目文档。 - -## 社区参与 - -CloudEon 项目依靠社区发展,我们致力为用户提供简单易用的大数据产品,我们强调社区协作,互相帮助,共同成长。 - -首先,如果您在下载和使用 CloudEon 1.1.0 中发现任何问题,欢迎使用 Github Issues 功能,将您遇到的问题和社区分享。 - -https://github.com/dromara/CloudEon/issues - -如果您或者您的公司正在使用 CloudEon,并乐意与社区分享,可以在**Who is using Cloudeon?** 中进行留言。 - -https://github.com/dromara/CloudEon/issues/20 - -我们也接受其他任何形式的帮助,详见: - -https://docs.cloudeon.top/en/dev/%E7%A4%BE%E5%8C%BA%E8%B4%A1%E7%8C%AE/contribute/ - -## 致谢 - -CloudEon 1.1.0 版本的发布离不开所有社区成员的支持和反馈,在社区驱动的模式下,CloudEon 才得以有今天的发展态势,真诚地感谢每一位社区贡献者及用户的信任、支持和帮助。 - -特别感谢对 1.1.0 版本直接贡献的社区成员: - -@Pandas886 - -@limaiwang - -@KangTomwk - -@linshenkx - -@tgluon - -@Mericol - -@linzehao - -@wangkang1 - -@pan3793 - -@tangxiuhong - -另外感谢@wangkang1 贡献组件扩展文档,如果社区小伙伴有想要扩展组件的需求,可以参考文档: - -https://docs.cloudeon.top/en/dev/component\_extension/ - -## 项目简介 - -CloudEon 是一款基于 Kubernetes 的云原生大数据平台,旨在为用户提供一种简单、高效、可扩展的大数据解决方案。如果 CloudEon 项目对您有帮助,请在 Gitee 或 Github 搜索 CloudEon 支持一下,点击 star 加关注。 - -CloudEon 遵循 Apache-2.0 开源协议,代码完全开源,如果您想为开源社区做出贡献,非常欢迎加入 CloudEon 项目,与其他开发者一起共同推动项目的发展。 - -Gitee:https://gitee.com/dromara/CloudEon - -Github:https://github.com/dromara/CloudEon - -官网:https://cloudeon.top/ - -欢迎加入社区技术交流 - -公众号:CloudEon 开源 - -微信社区: - -![](/assets/img/news/CloudEon-1.2.0-4.png) +--- +title: 云原生大数据平台CloudEon V1.1.0版本发布! +author: CloudEon开源 +date: 2023-07-31 +cover: /assets/img/news/CloudEon-1.1.0.png +head: + - - meta + - name: 新闻 +--- + +CloudEon 社区的小伙伴们大家好,今天很高兴宣布 CloudEon 于 2023 年 7 月 28 日正式发布 1.1.0   版本。 + +非常感谢 CloudEon 开源社区成员对 1.1.0 版本发布做出的贡献。 + +## 重要更新 + +1.1.0 版本主要有如下重大更新: + +**新增功能:** + +- 支持 Kyuubi 服务,实现 Spark SQL 查询 +- 增加 Kyuubi 监控面板 +- 支持 Iceberg 数据湖功能,实现 Iceberg 在 Flink 和 Spark 上的集成 +- 支持 Elasticsearch 组件 +- 支持数据采集 seatunnel 组件 +- 支持 OLAP 组件 Kylin5 +- 支持实时计算 Dinky 组件 + +**优化改进:** + +- 优化组件 Docker 镜像,精简大小 +- 改进节点 SSH 连接,支持指定私钥 +- 优化监控面板,修复多个面板问题 +- 优化部分组件的容器资源控制问题,包括 hbase/flink/spark/hive/yarn/hdfs/zookeeper + +**修复缺陷:** + +- 修复停止服务时角色 Pod 错乱的问题 +- 修复 Zookeeper 无法修改客户端端口的问题 +- 修复 HBase Shell 无法在 Pod 中使用的问题 +- 修复偶尔出现 ssh 导致的命令执行卡住问题 +- 修复 hive 容器缺失 mysql 驱动问题 + +详情可查看: + +https://github.com/dromara/CloudEon/releases/tag/v1.1.0 + +## 安装部署 + +### **Docker 部署(推荐)** + +如果你本地已经安装了 docker,执行以下命令可以一键安装: + +``` +docker run  -p 7700:7700  --name cloudeon --rm registry.cn-hangzhou.aliyuncs.com/udh/cloudeon:v1.1.0 +``` + +镜像启动成功后,在浏览器中访问 http://docker_ip:7700 进入登录页。镜像中提供初始账户,用户名 admin 密码 admin + +更多配置信息可以查看项目文档。 + +## 社区参与 + +CloudEon 项目依靠社区发展,我们致力为用户提供简单易用的大数据产品,我们强调社区协作,互相帮助,共同成长。 + +首先,如果您在下载和使用 CloudEon 1.1.0 中发现任何问题,欢迎使用 Github Issues 功能,将您遇到的问题和社区分享。 + +https://github.com/dromara/CloudEon/issues + +如果您或者您的公司正在使用 CloudEon,并乐意与社区分享,可以在**Who is using Cloudeon?** 中进行留言。 + +https://github.com/dromara/CloudEon/issues/20 + +我们也接受其他任何形式的帮助,详见: + +https://docs.cloudeon.top/en/dev/%E7%A4%BE%E5%8C%BA%E8%B4%A1%E7%8C%AE/contribute/ + +## 致谢 + +CloudEon 1.1.0 版本的发布离不开所有社区成员的支持和反馈,在社区驱动的模式下,CloudEon 才得以有今天的发展态势,真诚地感谢每一位社区贡献者及用户的信任、支持和帮助。 + +特别感谢对 1.1.0 版本直接贡献的社区成员: + +@Pandas886 + +@limaiwang + +@KangTomwk + +@linshenkx + +@tgluon + +@Mericol + +@linzehao + +@wangkang1 + +@pan3793 + +@tangxiuhong + +另外感谢@wangkang1 贡献组件扩展文档,如果社区小伙伴有想要扩展组件的需求,可以参考文档: + +https://docs.cloudeon.top/en/dev/component\_extension/ + +## 项目简介 + +CloudEon 是一款基于 Kubernetes 的云原生大数据平台,旨在为用户提供一种简单、高效、可扩展的大数据解决方案。如果 CloudEon 项目对您有帮助,请在 Gitee 或 Github 搜索 CloudEon 支持一下,点击 star 加关注。 + +CloudEon 遵循 Apache-2.0 开源协议,代码完全开源,如果您想为开源社区做出贡献,非常欢迎加入 CloudEon 项目,与其他开发者一起共同推动项目的发展。 + +Gitee:https://gitee.com/dromara/CloudEon + +Github:https://github.com/dromara/CloudEon + +官网:https://cloudeon.top/ + +欢迎加入社区技术交流 + +公众号:CloudEon 开源 + +微信社区: + +![](/assets/img/news/CloudEon-1.2.0-4.png) diff --git a/src/zh/news/CloudEon-1.2.0.md b/src/zh/news/CloudEon-1.2.0.md index e63cb90661..d47675963a 100644 --- a/src/zh/news/CloudEon-1.2.0.md +++ b/src/zh/news/CloudEon-1.2.0.md @@ -1,68 +1,68 @@ ---- -title: 云原生大数据平台CloudEon V1.2.0版本发布! -author: CloudEon开源 -date: 2023-09-07 -cover: /assets/img/news/CloudEon-1.2.0-3.png -head: - - - meta - - name: 新闻 ---- - -### **新增功能(Features):** - -1\. 实现用 k8s job 进行初始化服务任务,替换原来用 ssh 执行命令的方式,提高兼容性和更好监听状态和获取日志 - -2\. 增加 namespace 设置功能,不再写死 default namespace - -![](/assets/img/news/CloudEon-1.2.0-1.png) - -3\. 集成新组件 Trino - -![](/assets/img/news/CloudEon-1.2.0-2.png) - -4\. 集成新组件 Amoro - -![](/assets/img/news/CloudEon-1.2.0-3.png) - -5\. 初步集成 filebeat 用于大数据服务的日志采集 - -6\. 实现下载服务实例配置文件接口 - -### **修复缺陷(Bug Fixes):** - -1. 修复安装 dinky,SQL 脚本找不到问题 -2. 修复 flink standalone 的 rest 地址无法外部访问 -3. 修复 hdfs 组件的环境变量,导致 pod 内无法正常使用 hdfs 命令 -4. 修复 flink 提交任务到 YARN 集群问题 - -### **优化改进(Enhancements):** - -1. 组件 jvm 参数增加 gc 日志文件生成 -2. 优化贡献文档 -3. 优化任务日志输出,精简日志内容 -4. 选择框架会自动显示内置的服务版本 -5. FAQ 文档增加去除节点污点方式 -6. 优化 deployment 启动,删除无用日志打印 -7. 优化 K8S 节点信息提取 hostname 和 ip,兼容 K8S 和 K3S - ---- - -\[项目简介\] - -CloudEon 是一款基于 Kubernetes 的云原生大数据平台,旨在为用户提供一种简单、高效、可扩展的大数据解决方案。如果 CloudEon 项目对您有帮助,请在 Gitee 或 Github 搜索 CloudEon 支持一下,点击 star 加关注。 - -CloudEon 遵循 Apache-2.0 开源协议,代码完全开源,如果您想为开源社区做出贡献,非常欢迎加入 CloudEon 项目,与其他开发者一起共同推动项目的发展。 - -Gitee:https://gitee.com/dromara/CloudEon - -Github:https://github.com/dromara/CloudEon - -官网:https://cloudeon.top/ - -欢迎加入社区技术交流 - -公众号:CloudEon 开源 - -微信社区: - -![](/assets/img/news/CloudEon-1.2.0-4.png) +--- +title: 云原生大数据平台CloudEon V1.2.0版本发布! +author: CloudEon开源 +date: 2023-09-07 +cover: /assets/img/news/CloudEon-1.2.0-3.png +head: + - - meta + - name: 新闻 +--- + +### **新增功能(Features):** + +1\. 实现用 k8s job 进行初始化服务任务,替换原来用 ssh 执行命令的方式,提高兼容性和更好监听状态和获取日志 + +2\. 增加 namespace 设置功能,不再写死 default namespace + +![](/assets/img/news/CloudEon-1.2.0-1.png) + +3\. 集成新组件 Trino + +![](/assets/img/news/CloudEon-1.2.0-2.png) + +4\. 集成新组件 Amoro + +![](/assets/img/news/CloudEon-1.2.0-3.png) + +5\. 初步集成 filebeat 用于大数据服务的日志采集 + +6\. 实现下载服务实例配置文件接口 + +### **修复缺陷(Bug Fixes):** + +1. 修复安装 dinky,SQL 脚本找不到问题 +2. 修复 flink standalone 的 rest 地址无法外部访问 +3. 修复 hdfs 组件的环境变量,导致 pod 内无法正常使用 hdfs 命令 +4. 修复 flink 提交任务到 YARN 集群问题 + +### **优化改进(Enhancements):** + +1. 组件 jvm 参数增加 gc 日志文件生成 +2. 优化贡献文档 +3. 优化任务日志输出,精简日志内容 +4. 选择框架会自动显示内置的服务版本 +5. FAQ 文档增加去除节点污点方式 +6. 优化 deployment 启动,删除无用日志打印 +7. 优化 K8S 节点信息提取 hostname 和 ip,兼容 K8S 和 K3S + +--- + +\[项目简介\] + +CloudEon 是一款基于 Kubernetes 的云原生大数据平台,旨在为用户提供一种简单、高效、可扩展的大数据解决方案。如果 CloudEon 项目对您有帮助,请在 Gitee 或 Github 搜索 CloudEon 支持一下,点击 star 加关注。 + +CloudEon 遵循 Apache-2.0 开源协议,代码完全开源,如果您想为开源社区做出贡献,非常欢迎加入 CloudEon 项目,与其他开发者一起共同推动项目的发展。 + +Gitee:https://gitee.com/dromara/CloudEon + +Github:https://github.com/dromara/CloudEon + +官网:https://cloudeon.top/ + +欢迎加入社区技术交流 + +公众号:CloudEon 开源 + +微信社区: + +![](/assets/img/news/CloudEon-1.2.0-4.png) diff --git a/src/zh/news/Cubic-1.4.5.md b/src/zh/news/Cubic-1.4.5.md index c05f2b6077..9987a42d41 100644 --- a/src/zh/news/Cubic-1.4.5.md +++ b/src/zh/news/Cubic-1.4.5.md @@ -1,55 +1,55 @@ ---- -title: 一站式问题定位解决平台 Cubic v1.4.5 发布 -author: 架构技术专栏 -date: 2023-05-08 -cover: /assets/img/news/Cubic-1.4.5-5.png -head: - - - meta - - name: 新闻 ---- - -##   此次版本更新主要功能: - -- **重点**:新增自定义 ClassLoader,确保 agent 代码和业务代码分别由不同 Classloader 加载,避免互相污染 -- 剥离 agent 核心代码到单独 jar 包,增强隔离性,并为插件化做准备 -- 优化线程抓取功能的逻辑适配 - -**欢迎大家探讨 star** - -**Gitee: https://gitee.com/dromara/cubic** - -**Github: https://github.com/dromara/cubic** - -**官网地址:http://cubic.jiagoujishu.com** - -#### 功能展示 - -##### 1、实例中心(展示当前实例信息) - -![](/assets/img/news/Cubic-1.4.5-1.png) - -##### 2、基础信息(点击实例-》展示当前实例的基础信息) - -![](/assets/img/news/Cubic-1.4.5-2.png) - -##### 3、依赖监控(点击实例-》展示当前实例的依赖包信息) - -![](/assets/img/news/Cubic-1.4.5-3.png) - -##### 4、Arthas 命令操作 - -![](/assets/img/news/Cubic-1.4.5-4.png) - -![](/assets/img/news/Cubic-1.4.5-5.png) - -##### 5、线程池监控 - -![](/assets/img/news/Cubic-1.4.5-6.png) - -##### 6、实时线程栈 - -![](/assets/img/news/Cubic-1.4.5-7.png) - -##### 7、历史线程栈 - -![](/assets/img/news/Cubic-1.4.5-8.png) +--- +title: 一站式问题定位解决平台 Cubic v1.4.5 发布 +author: 架构技术专栏 +date: 2023-05-08 +cover: /assets/img/news/Cubic-1.4.5-5.png +head: + - - meta + - name: 新闻 +--- + +##   此次版本更新主要功能: + +- **重点**:新增自定义 ClassLoader,确保 agent 代码和业务代码分别由不同 Classloader 加载,避免互相污染 +- 剥离 agent 核心代码到单独 jar 包,增强隔离性,并为插件化做准备 +- 优化线程抓取功能的逻辑适配 + +**欢迎大家探讨 star** + +**Gitee: https://gitee.com/dromara/cubic** + +**Github: https://github.com/dromara/cubic** + +**官网地址:http://cubic.jiagoujishu.com** + +#### 功能展示 + +##### 1、实例中心(展示当前实例信息) + +![](/assets/img/news/Cubic-1.4.5-1.png) + +##### 2、基础信息(点击实例-》展示当前实例的基础信息) + +![](/assets/img/news/Cubic-1.4.5-2.png) + +##### 3、依赖监控(点击实例-》展示当前实例的依赖包信息) + +![](/assets/img/news/Cubic-1.4.5-3.png) + +##### 4、Arthas 命令操作 + +![](/assets/img/news/Cubic-1.4.5-4.png) + +![](/assets/img/news/Cubic-1.4.5-5.png) + +##### 5、线程池监控 + +![](/assets/img/news/Cubic-1.4.5-6.png) + +##### 6、实时线程栈 + +![](/assets/img/news/Cubic-1.4.5-7.png) + +##### 7、历史线程栈 + +![](/assets/img/news/Cubic-1.4.5-8.png) diff --git a/src/zh/news/Dante-Cloud-2.7.11.0.md b/src/zh/news/Dante-Cloud-2.7.11.0.md index 98708ec044..a7c9563b4b 100644 --- a/src/zh/news/Dante-Cloud-2.7.11.0.md +++ b/src/zh/news/Dante-Cloud-2.7.11.0.md @@ -1,115 +1,115 @@ ---- -title: Dante Cloud 2.7.11.0,全系更新核心组件及基础设置版本 -author: dante -tag: - - Dante-Cloud -date: 2023-04-27 -cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png -head: - - - meta - - name: 新闻 ---- - -**Dante Cloud** 一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 `Spring Authorization Server` 的、基于 OAuth2.1 协议的微服务架构。基于 `Spring Authorization Server 0.4.2`、`Spring Boot 2.7.11`、`Spring Cloud 2021.0.6`、`Spring Cloud Alibaba 2021.0.5.0`、`Nacos 2.2.2` 等最新版本开发的多租户系统,遵循 `SpringBoot` 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 - -**平台定位** - -- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 -- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 -- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 -- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 - -## \[1\]、为什么更名为  Dante Cloud - -**Dante Cloud** (但丁), 原项目名称 Eurynome Cloud,很多朋友都反映名字太长、读起来拗口、不容易记等问题。因此在加入 Dromara 开源社区之际,将名字进行了变更。 - -**Dante**,即但丁·阿利基耶里(公元 1265 年-公元 1321 年),13 世纪末意大利诗人,现代意大利语的奠基者,欧洲文艺复兴时代的开拓人物之一,以长诗《神曲》(原名《喜剧》)而闻名,后来一位作家叫薄伽丘将其命名为神圣的喜剧。 - -他被认为是中古时期意大利文艺复兴中最伟大的诗人,也是西方最杰出的诗人之一,最伟大的作家之一。恩格斯评价说:“封建的中世纪的终结和现代资本主义纪元的开端,是以一位大人物为标志的,这位人物就是意大利人但丁,他是中世纪的最后一位诗人,同时又是新时代的最初一位诗人” - -更名为 Dante Cloud,寓意本项目会像恩格斯对但丁的评价一样,在行业变革的时期,可以成为一款承上启下,助力企业信息化建设变革的产品。 - -## \[2\]、版本说明 - -自 11 月 24 日,Spring Boot 3.0 以及 Spring Cloud 2022.0.0 等全新版本发布,整个 Java 社区也步入的 Java 17 和 Spring Boot 3 的新时代。紧跟 Java 技术和 Spring 社区的发展,让更多质量更好、性能更优的新特性服务于实际的开发工作,Dante Cloud 也同步进行升级及适配,开发了基于 Spring Authorization Server 1.1.0-RC1、Spring  Boot 3.0.6、Spring Cloud 2022.0.2、Spring Cloud Alibaba 2022.0.0.0-RC1、Spring Cloud Tencent 1.11.1-2022.0.1、Nacos 2.2.2 全新 Dante Cloud 版本。关注请移步 3.0  分支 - -## \[3\]、本次更新内容 - -- 【主要更新】 - -- \[升级\] Spring Boot 版本升级至 2.7.11 -- \[升级\] Spring Authorization Server 版本升级至 0.4.2 -- \[升级\] Nacos 版本升级至 2.2.2 -- \[升级\] Camunda 版本升级至 7.19.0,同步更新数据库脚本 -- \[升级\] Skywalking Agent 版本升级至 8.15.0 -- \[升级\] Debezium 版本及相关基础设施版本升级至 2.2 - -- 【其它更新】 - -- \[漏洞\] 修复 Snakeyaml (CVE-2022-1471) 存在反序列化漏洞 和 (CVE-2022-41854) 存在缓冲区溢出漏洞 -- \[新增\] 新增服务优雅停机支持 -- \[新增\] 新增 MongoDB 基础 Entity、Repository、Service、Controller 和 MybatisPlus 基础 Controller,方便业务接口代码编写。 -- \[优化\] 优化数据自动初始化脚本放置位置,与新版本代码创建数据表需要启动两个服务机制进行统一。 -- \[优化\] Antisamy 版本升级至 1.7.3,同步升级 XSS 攻击防护策略配置文件 -- \[优化\] 优化 Docker Compose 脚本配置 -- \[修复\] 修复自定义社交登录模式中,微信小程序参数获取未补充错误。 - -- 【依赖更新】 - -- \[升级\] snakeyaml 版本升级至 2.0. -- \[升级\] fastjson2 版本升级至 2.0.29 -- \[升级\] wxjava 版本升级至 4.5.0 -- \[升级\] hutool 版本升级至 5.8.18 -- \[升级\] docker-maven-plugin 版本升级至 0.42.1 -- \[升级\] redisson 版本升级至 3.20.1 -- \[升级\] springdoc 版本升级至 1.7.0 -- \[升级\] qiniu-java-sdk 版本升级至 7.13.0 -- \[升级\] aliyun-sdk-oss 版本升级至 3.16.2 -- \[升级\] tencentcloud-sdk-java-sms 版本升级至 3.1.744 -- \[升级\] alipay-sdk-java 版本升级至 4.35.110.ALL -- \[升级\] xnio 版本升级至 3.8.9.Final -- \[升级\] bcprov-jdk15to18 版本升级至 1.73 - -- 【升级说明】 - -因仓库提交文件大小限制,所以从本次发布以后不再上传 Skywalking Agent 相关 Jar 包,有需要请自行下载。 - -Release 详情  https://gitee.com/dromara/dante-cloud/releases/tag/v2.7.11.0 - -## \[4\]、Dante Cloud 2.7.X  特点 - -### 一、前端 - -- 未使用任何流行开源模版,使用全新技术栈,完全纯"手写"全新前端工程。 -- 借鉴参考流行开源版本的使用和设计,新版前端界面风格和操作习惯尽量与当前流行方式统一。 -- 充份使用 Typescript 语言特性,解决大量类型校验问题,尽可能规避 "any" 式的 Typescript 编程语言使用方式。 -- 充份使用 Composition Api 和 Hooks 等 Vue3 框架新版特性进行代码编写。 -- 充份利用 Component、Hooks 以及 Typescript 面向对象等特性,抽取通用组件和代码,尽可能降低工程重复代码。 -- 对较多 Quasar 基础组件和应用功能组件进行封装,以方便代码的统一修改维护和开发使用。 -- 对生产模式下,对基于 Vite3 的工程打包进行深度性能优化。 -- 提供以 docker-compose 方式,对工程生产代码进行容器化打包和部署。 -- 支持密码模式、授权码模式、手机短信模式、第三方社会化等多种登录模式。 - -### 二、后端 - -基于 Spring Authorization Server 深度定制和扩展: - -- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 -- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码)认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录)认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 -- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现 `Client Credentials` 模式支持 Refresh Token 的使用。 -- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 `Scope` 权限对接口进行验证。增加客户端 `Scope` 的权限配置功能,并与已有的用户权限体系解耦 -- 支持 `Spring Authorization Server` `Authorization Code` + `PKCE` 认证模式 -- 支持 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,新增基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 -- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 -- 全面支持 `OpenID Connect` (OIDC) 协议, 系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 -- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 -- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 -- 基于 `JetCache` 的多级缓存支持,实现自定义 `Spring Data JPA` 二级缓存,有效解决 `Spring Cache` 查询缓存更新问题。 -- 全面整合 `@PreAuthorize` 注解权限与 URL 权限,通过后端动态配置,无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 -- 采用分布式服务独立鉴权方案,`Spring Security` `@PreAuthorize` 的权限注解、权限方法以及 URL 权限,通过后端动态配置后,实时动态分发至对应服务。 -- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 -- 基于自定义 Session,混合国密 `SM2`(非对称) 和 `SM4`(对称加密) 算法,实现秘钥动态生成加密传输。利用“一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 - -仓库地址:https://gitee.com/dromara/dante-cloud +--- +title: Dante Cloud 2.7.11.0,全系更新核心组件及基础设置版本 +author: dante +tag: + - Dante-Cloud +date: 2023-04-27 +cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png +head: + - - meta + - name: 新闻 +--- + +**Dante Cloud** 一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 `Spring Authorization Server` 的、基于 OAuth2.1 协议的微服务架构。基于 `Spring Authorization Server 0.4.2`、`Spring Boot 2.7.11`、`Spring Cloud 2021.0.6`、`Spring Cloud Alibaba 2021.0.5.0`、`Nacos 2.2.2` 等最新版本开发的多租户系统,遵循 `SpringBoot` 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 + +**平台定位** + +- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 +- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 +- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 +- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 + +## \[1\]、为什么更名为  Dante Cloud + +**Dante Cloud** (但丁), 原项目名称 Eurynome Cloud,很多朋友都反映名字太长、读起来拗口、不容易记等问题。因此在加入 Dromara 开源社区之际,将名字进行了变更。 + +**Dante**,即但丁·阿利基耶里(公元 1265 年-公元 1321 年),13 世纪末意大利诗人,现代意大利语的奠基者,欧洲文艺复兴时代的开拓人物之一,以长诗《神曲》(原名《喜剧》)而闻名,后来一位作家叫薄伽丘将其命名为神圣的喜剧。 + +他被认为是中古时期意大利文艺复兴中最伟大的诗人,也是西方最杰出的诗人之一,最伟大的作家之一。恩格斯评价说:“封建的中世纪的终结和现代资本主义纪元的开端,是以一位大人物为标志的,这位人物就是意大利人但丁,他是中世纪的最后一位诗人,同时又是新时代的最初一位诗人” + +更名为 Dante Cloud,寓意本项目会像恩格斯对但丁的评价一样,在行业变革的时期,可以成为一款承上启下,助力企业信息化建设变革的产品。 + +## \[2\]、版本说明 + +自 11 月 24 日,Spring Boot 3.0 以及 Spring Cloud 2022.0.0 等全新版本发布,整个 Java 社区也步入的 Java 17 和 Spring Boot 3 的新时代。紧跟 Java 技术和 Spring 社区的发展,让更多质量更好、性能更优的新特性服务于实际的开发工作,Dante Cloud 也同步进行升级及适配,开发了基于 Spring Authorization Server 1.1.0-RC1、Spring  Boot 3.0.6、Spring Cloud 2022.0.2、Spring Cloud Alibaba 2022.0.0.0-RC1、Spring Cloud Tencent 1.11.1-2022.0.1、Nacos 2.2.2 全新 Dante Cloud 版本。关注请移步 3.0  分支 + +## \[3\]、本次更新内容 + +- 【主要更新】 + +- \[升级\] Spring Boot 版本升级至 2.7.11 +- \[升级\] Spring Authorization Server 版本升级至 0.4.2 +- \[升级\] Nacos 版本升级至 2.2.2 +- \[升级\] Camunda 版本升级至 7.19.0,同步更新数据库脚本 +- \[升级\] Skywalking Agent 版本升级至 8.15.0 +- \[升级\] Debezium 版本及相关基础设施版本升级至 2.2 + +- 【其它更新】 + +- \[漏洞\] 修复 Snakeyaml (CVE-2022-1471) 存在反序列化漏洞 和 (CVE-2022-41854) 存在缓冲区溢出漏洞 +- \[新增\] 新增服务优雅停机支持 +- \[新增\] 新增 MongoDB 基础 Entity、Repository、Service、Controller 和 MybatisPlus 基础 Controller,方便业务接口代码编写。 +- \[优化\] 优化数据自动初始化脚本放置位置,与新版本代码创建数据表需要启动两个服务机制进行统一。 +- \[优化\] Antisamy 版本升级至 1.7.3,同步升级 XSS 攻击防护策略配置文件 +- \[优化\] 优化 Docker Compose 脚本配置 +- \[修复\] 修复自定义社交登录模式中,微信小程序参数获取未补充错误。 + +- 【依赖更新】 + +- \[升级\] snakeyaml 版本升级至 2.0. +- \[升级\] fastjson2 版本升级至 2.0.29 +- \[升级\] wxjava 版本升级至 4.5.0 +- \[升级\] hutool 版本升级至 5.8.18 +- \[升级\] docker-maven-plugin 版本升级至 0.42.1 +- \[升级\] redisson 版本升级至 3.20.1 +- \[升级\] springdoc 版本升级至 1.7.0 +- \[升级\] qiniu-java-sdk 版本升级至 7.13.0 +- \[升级\] aliyun-sdk-oss 版本升级至 3.16.2 +- \[升级\] tencentcloud-sdk-java-sms 版本升级至 3.1.744 +- \[升级\] alipay-sdk-java 版本升级至 4.35.110.ALL +- \[升级\] xnio 版本升级至 3.8.9.Final +- \[升级\] bcprov-jdk15to18 版本升级至 1.73 + +- 【升级说明】 + +因仓库提交文件大小限制,所以从本次发布以后不再上传 Skywalking Agent 相关 Jar 包,有需要请自行下载。 + +Release 详情  https://gitee.com/dromara/dante-cloud/releases/tag/v2.7.11.0 + +## \[4\]、Dante Cloud 2.7.X  特点 + +### 一、前端 + +- 未使用任何流行开源模版,使用全新技术栈,完全纯"手写"全新前端工程。 +- 借鉴参考流行开源版本的使用和设计,新版前端界面风格和操作习惯尽量与当前流行方式统一。 +- 充份使用 Typescript 语言特性,解决大量类型校验问题,尽可能规避 "any" 式的 Typescript 编程语言使用方式。 +- 充份使用 Composition Api 和 Hooks 等 Vue3 框架新版特性进行代码编写。 +- 充份利用 Component、Hooks 以及 Typescript 面向对象等特性,抽取通用组件和代码,尽可能降低工程重复代码。 +- 对较多 Quasar 基础组件和应用功能组件进行封装,以方便代码的统一修改维护和开发使用。 +- 对生产模式下,对基于 Vite3 的工程打包进行深度性能优化。 +- 提供以 docker-compose 方式,对工程生产代码进行容器化打包和部署。 +- 支持密码模式、授权码模式、手机短信模式、第三方社会化等多种登录模式。 + +### 二、后端 + +基于 Spring Authorization Server 深度定制和扩展: + +- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 +- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码)认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录)认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 +- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现 `Client Credentials` 模式支持 Refresh Token 的使用。 +- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 `Scope` 权限对接口进行验证。增加客户端 `Scope` 的权限配置功能,并与已有的用户权限体系解耦 +- 支持 `Spring Authorization Server` `Authorization Code` + `PKCE` 认证模式 +- 支持 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,新增基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 +- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 +- 全面支持 `OpenID Connect` (OIDC) 协议, 系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 +- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 +- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 +- 基于 `JetCache` 的多级缓存支持,实现自定义 `Spring Data JPA` 二级缓存,有效解决 `Spring Cache` 查询缓存更新问题。 +- 全面整合 `@PreAuthorize` 注解权限与 URL 权限,通过后端动态配置,无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 +- 采用分布式服务独立鉴权方案,`Spring Security` `@PreAuthorize` 的权限注解、权限方法以及 URL 权限,通过后端动态配置后,实时动态分发至对应服务。 +- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 +- 基于自定义 Session,混合国密 `SM2`(非对称) 和 `SM4`(对称加密) 算法,实现秘钥动态生成加密传输。利用“一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 + +仓库地址:https://gitee.com/dromara/dante-cloud diff --git a/src/zh/news/Dante-Cloud-3.0.6.4.md b/src/zh/news/Dante-Cloud-3.0.6.4.md index 72a41cea7f..b469110c51 100644 --- a/src/zh/news/Dante-Cloud-3.0.6.4.md +++ b/src/zh/news/Dante-Cloud-3.0.6.4.md @@ -1,96 +1,96 @@ ---- -title: Dante Cloud 3.0.6.4 发布,支持IOT设备的Device Flow认证上线 -author: dante -tag: - - Dante-Cloud -date: 2023-05-18 -cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png -head: - - - meta - - name: 新闻 ---- - -**Dante Cloud** 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 Spring Authorization Server 的、基于 OAuth2.1 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 Spring Authorization Server 1.1.0、Spring Boot 3.1.0、Spring Cloud 2022.0.3、Spring Cloud Tencent 1.11.7-2022.0.1、Spring Cloud Alibaba 2022.0.0.0、Nacos 2.2.2 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 - -**平台定位** - -- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 -- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 -- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 -- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 - -**发布背景** - -自 11 月 24 日,Spring Boot 3.0 以及 Spring Cloud 2022.0.0、Spring Cloud Tencent 等全新版本发布,整个 Java 社区也步入的 Java 17 和 Spring Boot 3 的新时代。紧跟 Java 技术和 Spring 社区的发展,让更多质量更好、性能更优的新特性服务于实际的开发工作,Dante Cloud 也同步进行升级及适配,开发了全新的 3.0 版本。 - -## \[1\] 新特性背景 - -OAuth2 **Device Authorization Grant** 认证模式,是在 OIDC 协议支持的模式中,专有的一类 Device Flow 设备模式,允许各类终端或硬件完成认证登录流程。 - -**Device Authorization Grant** (设备授权授予)或 **Device Flow** 对于处理,例如:智能电视、IoT 设备或打印机等,输入受限终端或硬件的身份验证和授权非常有用。由于终端的显示模式可能受限,无法内置登录页面。通过 Device Flow 提供的超链接或者生成二维码,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。用户登录后,设备可以获取所需的访问令牌和刷新令牌。 - -## \[2\] 本次更新内容 - -- 【主要更新】 - -- \[升级\] Spring Authroization Server 版本升级至 1.1.0 -- \[升级\] Spring Security 版本升级至 6.1.0 -- \[新增\] 新增支持智能电视、IOT 设备等输入受限设备的 Device Flow 认证模式 - -- 【其它更新】 - -- \[新增\] 新增 Device Flow 认证系统内置页面 -- \[新增\] 新增 IOT 产品、设备管理 SDK -- \[重构\] 重构 Spring Authorization Server 授权确认页面,与内置认证页面统一风格。 -- \[重构\] 除特殊依赖外,将所有内置页面静态资源引用改为 Webjars 方式。 -- \[重构\] 所有内置页面均改用页面嵌入 Vue 方式重新实现 -- \[优化\] 优化客户端动态自动注册相关功能代码及配置方式 -- \[优化\] 优化数据库初始化脚本,增加 Spring Authorization Server 内置默认 Scope 数据及关联数据 - -- 【依赖更新】 - -- \[升级\] fastjson2 版本升级至 2.0.32 -- \[升级\] tencentcloud-sdk-java-sms 版本升级至 3.1.756 -- \[升级\] aliyun-sdk-oss 版本升级至 3.16.3 - -**新特性界面预览** - -![](/assets/img/news/Dante-Cloud-3.0.6.4-1.png) -![](/assets/img/news/Dante-Cloud-3.0.6.4-2.png) - -## \[3\] Dante Cloud 3.0.0 后端新特性 - -### 1\. 核心基础依赖便捷切换 - -- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 -- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 - -### 2\. 支持 `GraalVM` 原生镜像 - -- 整体调整各类模块 pom build 配置,适当增加冗余重复配置,以支持 `Spring Native` 或 `GraalVM` 编译需要。规避对所有模块进行 Native 编译,而导致错误问题。 - -### 3\. `Spring Authorization Server` 全特性支持及扩展 - -- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 -- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 -- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 -- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 -- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 -- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 -- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 -- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 -- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 -- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 -- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 -- 基于自定义 Session,混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用 “一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 - -## \[4\] Dante Cloud 3.0.0 前端新特性 - -### 采用 `pnpm monorepo` 重构前端 - -- 前端工程包管理器变更为 pnpm。 -- 采用 `monorepo` 模式对前端工程进行重构,抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块 -- 共享模块已进行优化配置,利用 Vite 可编译成独立的组件,单独以组件形式进行发布 -- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用。 +--- +title: Dante Cloud 3.0.6.4 发布,支持IOT设备的Device Flow认证上线 +author: dante +tag: + - Dante-Cloud +date: 2023-05-18 +cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png +head: + - - meta + - name: 新闻 +--- + +**Dante Cloud** 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 Spring Authorization Server 的、基于 OAuth2.1 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 Spring Authorization Server 1.1.0、Spring Boot 3.1.0、Spring Cloud 2022.0.3、Spring Cloud Tencent 1.11.7-2022.0.1、Spring Cloud Alibaba 2022.0.0.0、Nacos 2.2.2 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 + +**平台定位** + +- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 +- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 +- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 +- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 + +**发布背景** + +自 11 月 24 日,Spring Boot 3.0 以及 Spring Cloud 2022.0.0、Spring Cloud Tencent 等全新版本发布,整个 Java 社区也步入的 Java 17 和 Spring Boot 3 的新时代。紧跟 Java 技术和 Spring 社区的发展,让更多质量更好、性能更优的新特性服务于实际的开发工作,Dante Cloud 也同步进行升级及适配,开发了全新的 3.0 版本。 + +## \[1\] 新特性背景 + +OAuth2 **Device Authorization Grant** 认证模式,是在 OIDC 协议支持的模式中,专有的一类 Device Flow 设备模式,允许各类终端或硬件完成认证登录流程。 + +**Device Authorization Grant** (设备授权授予)或 **Device Flow** 对于处理,例如:智能电视、IoT 设备或打印机等,输入受限终端或硬件的身份验证和授权非常有用。由于终端的显示模式可能受限,无法内置登录页面。通过 Device Flow 提供的超链接或者生成二维码,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。用户登录后,设备可以获取所需的访问令牌和刷新令牌。 + +## \[2\] 本次更新内容 + +- 【主要更新】 + +- \[升级\] Spring Authroization Server 版本升级至 1.1.0 +- \[升级\] Spring Security 版本升级至 6.1.0 +- \[新增\] 新增支持智能电视、IOT 设备等输入受限设备的 Device Flow 认证模式 + +- 【其它更新】 + +- \[新增\] 新增 Device Flow 认证系统内置页面 +- \[新增\] 新增 IOT 产品、设备管理 SDK +- \[重构\] 重构 Spring Authorization Server 授权确认页面,与内置认证页面统一风格。 +- \[重构\] 除特殊依赖外,将所有内置页面静态资源引用改为 Webjars 方式。 +- \[重构\] 所有内置页面均改用页面嵌入 Vue 方式重新实现 +- \[优化\] 优化客户端动态自动注册相关功能代码及配置方式 +- \[优化\] 优化数据库初始化脚本,增加 Spring Authorization Server 内置默认 Scope 数据及关联数据 + +- 【依赖更新】 + +- \[升级\] fastjson2 版本升级至 2.0.32 +- \[升级\] tencentcloud-sdk-java-sms 版本升级至 3.1.756 +- \[升级\] aliyun-sdk-oss 版本升级至 3.16.3 + +**新特性界面预览** + +![](/assets/img/news/Dante-Cloud-3.0.6.4-1.png) +![](/assets/img/news/Dante-Cloud-3.0.6.4-2.png) + +## \[3\] Dante Cloud 3.0.0 后端新特性 + +### 1\. 核心基础依赖便捷切换 + +- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 +- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 + +### 2\. 支持 `GraalVM` 原生镜像 + +- 整体调整各类模块 pom build 配置,适当增加冗余重复配置,以支持 `Spring Native` 或 `GraalVM` 编译需要。规避对所有模块进行 Native 编译,而导致错误问题。 + +### 3\. `Spring Authorization Server` 全特性支持及扩展 + +- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 +- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 +- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 +- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 +- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 +- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 +- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 +- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 +- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 +- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 +- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 +- 基于自定义 Session,混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用 “一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 + +## \[4\] Dante Cloud 3.0.0 前端新特性 + +### 采用 `pnpm monorepo` 重构前端 + +- 前端工程包管理器变更为 pnpm。 +- 采用 `monorepo` 模式对前端工程进行重构,抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块 +- 共享模块已进行优化配置,利用 Vite 可编译成独立的组件,单独以组件形式进行发布 +- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用。 diff --git a/src/zh/news/Dante-Cloud-3.1.0.6.md b/src/zh/news/Dante-Cloud-3.1.0.6.md index b1b47174eb..7cb858de74 100644 --- a/src/zh/news/Dante-Cloud-3.1.0.6.md +++ b/src/zh/news/Dante-Cloud-3.1.0.6.md @@ -1,186 +1,186 @@ ---- -title: Dante Cloud 3.1.0.6 发布,拆分核心基础组件,构建产品生态 -author: dante -tag: - - Dante-Cloud -date: 2023-06-14 -cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png -head: - - - meta - - name: 新闻 ---- - -**Dante Cloud** 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 Spring Authorization Server 的、基于 OAuth2.1 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 Spring Authorization Server 1.1.0、Spring Boot 3.1.0、Spring Cloud 2022.0.3、Spring Cloud Tencent 1.11.7-2022.0.1、Spring Cloud Alibaba 2022.0.0.0、Nacos 2.2.2 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 - -**平台定位** - -- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 -- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 -- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 -- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 - -**发布背景** - -为方便用户使用,Dante Cloud 建设初期以“全面”为目标,尽可能的集成、开发和包容更多常用组件,以兼容更多用户的使用需求。Dante Cloud 经过两年的建设、完善与发展、探索,发现这种模式看似全面、面面俱到、可以吸引更多用户的青睐。但对实际的开发、使用的促进效果并不理想。 - -主要存在以下问题: - -- 集成和兼容内容过多,影响代码编译、发布的时效性。 -- 并不是所有内容都对用户有帮助,反而会带来过多的依赖性和耦合性。 -- 原本相对独立性较高的内容,却融入在一个庞大的架构环境中,既不利于理解,也不利于单独使用。 -- 引入过多杂音,分散用户对 Dante Cloud 核心价值内容的关注度,不利于掌握和深入 Dante Cloud 核心。 -- 过多内容的集成,影响各个部分的专注和专业程度,导致多而不精,多而不深的问题。 - -因此,尝试采用构建“产品生态”的方式,解决以上问题,同时给用户带来更好的、更便捷产品使用体验。 - -- 通过构建产品生态,将独立性较强的内容,从系统核心内容中剥离,让 Dante Cloud 本身更加专精和独立,同时降低互相影响和干扰 -- 被剥离的内容以独立的产品形态存在,目标既可以独立使用集成至任意系统中,又可以快速的与 Dante Cloud 融合,形成以 Dante Cloud 为主,生态产品为辅的格局。 -- 生态产品以独立的产品运作,反向促进提升生态产品的完整性、深入性和便捷性。 - -## \[1\] 本次更新内容 - -- 主要更新 - -- \[升级\] Spring Cloud Tencent 1.11.7-2022.0.1 -- \[变更\] Minio 对象存储相关代码,从 Dante Engine 中剥离,成为一个独立的项目产品 -- \[删除\] 删除 pay 和 nosql 相关模块,清理系统中独立性较高的模组,以保持系统内核的专注性 -- \[新增\] 新增 REST 接口动态鉴权是否使用严格模式配置,在严格模式下,所有接口必须配置权限才可使用;在非严格模式下,接口只需要携带 Token 即可使用。 - -- 其它更新 - -- \[新增\] 新增 message-rabbitmq-spring-boot-starter,以方便 RabbitMQ 使用者集成使用 -- \[新增\] 新增对象存储 Minio 服务器不可用错误代码 -- \[新增\] 前端新增对象存储 Bucket 管理界面 -- \[新增\] 前端新增对象存储 Bucket 设置界面 -- \[新增\] 前端新增对象存储 Object 管理界面 -- \[新增\] 前端新增对象存储 Object 设置界面 -- \[新增\] 前端新增基于 vue-simple-uploader 的大文件分片存储支持 -- \[新增\] 新增 OSS 对象存储服务 -- \[新增\] 新增对象存储 Minio 服务器不可用错误代码 -- \[修复\] 修复在未引入 Spring Cloud OpenFeign 环境下,RestTemplate 配置失效导致启动错误的问题 -- \[优化\] 优化基础 Controller 代码,调整判断逻辑,以更好地的支持查询数据成功、未查询到数据、查询失败等三种状态 - -- 依赖更新 - -- \[升级\] common-io 版本升级至 2.13.0 -- \[升级\] redission 版本升级至 3.22.0 -- \[升级\] guava 版本升级至 32.0.1-jre -- \[升级\] skywalking 版本升级至 8.16.0 -- \[升级\] wxjava 版升级至 4.5.1.B -- \[升级\] camunda 版升级至 7.20.0-alpha2 -- \[升级\] Webjars Bootstrap 版升级至 5.3.0 -- \[升级\] tencentcloud-sdk-java-sms 版升级至 3.1.775 -- \[升级\] alipay-sdk-java 版本升级至 4.35.154.ALL - -## \[2\] 生态产品 Dante OSS - -### 优点 | Advantages - -- **零额外学习成本**: 开发者只要会 Spring 和 REST 基本开发,即可无缝集成和使用 Dante OSS -- **降低开发者门槛**: 屏蔽 Minio 标准 Java SDK 使用复杂度,使用 Spring 环境标准方式对原有 API 进行简化封装。Service API 和 REST API 开箱即用 -- **包含的功能丰富**: 改造了 Minio Java SDK 的几乎全部功能,且对大文件分片上传、秒传、直传、断点续传等功能,均采用业内最优解决方案进行实现和融合 -- **规范优雅的代码**: 所有函数参数,并未破坏原有 Minio 代码构造器结构,而是在原有方式的基础上抽象简化,编程体验和代码可读性大幅提升 -- **完善的注释文档**: 对请求参数、方法、REST API、Validation 提供详实的注释、说明和 OpenAPI 标注,用途用法一目了然,无需再翻阅 Minio 文档和源代码,帮助您节省更多时间 -- **完整的前端示例**:前端采用一个完整的项目而非 Demo 的形式,全面的展示了前后端交互涉及、接口调用、参数使用、TS 类型定义等各方面内容,可直接用于实际项目或简单改造后构建自己的产品 - -### 对比 | Compare - -#### 1\. 不只是简单的 Spring Boot Starter 构建 - -1. 构建统一的错误,可以返回更人性化、更易理解的错误信息,同时兼顾更详细错误信息的返回,方便开发人员理解和定位问题。 -2. 采用更易理解和使用的格式对 Minio Java SDK 参数进行重新定义。规避 Minio 默认 XML 方式参数多、不易理解使用、与前端交互不方便等问题。 -3. 隐藏 Minio Java SDK 不易理解和使用的细节,提供详实的注释说明,开发人员在使用时无需再通过翻阅 Minio 在线文档和源代码来了解各个 API 使用细节。 -4. 提供统一标准的 REST API,以及 OpenAPI Swagger3 文档描述和准确的 Spring Validation 校验,可直接集成至系统中使用。 -5. Minio Client 对象池、自定义极简 Minio Server 访问反向代理,提升 - -#### 2\. 标准化业务逻辑和解决方案集合 - -1. 不只是上传、下载等常用方法的封装,涵盖 Minio Java SDK 支持的所有方法和操作。 -2. 选择业内最优的解决方案,实现和集成大文件分片上传、秒传、直传、断点续传等主要业务需求功能。 -3. 结合自身应用经验和需求,将 Minio API 进一步组合成符合实际应用的业务逻辑和功能处理。 -4. 采用一个基于 Vue3、Typescript5、Vite4、Pinia 2 的完整的前端项目作为集成示例,包括详细的 Typescript 类型定义以及 vue-simple-uploader 等主流组件集成和使用方法。 -5. 提供基于 Spring Authorization Server 的单体版、微服务版案例,从 SDK、Spring Boot Starter 到完整项目任你选择。 - -#### 3\. 具体差异说明 - -- \[1\] 基础 API 方法以及方法参数 - -| Minio SDK | Dante OSS | -| ------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| 仅包含基础操作 API | 提供大量重载方法 | -| 必须用构造器创建参数对象 | 重载方法覆盖所有常见参数,按需传参即可 | -| API 全部混在同一个类中 | 根据差异、用途、场景拆分为不同的 Service,例如:getObject 和 downloadObject | -| 源于 XML 对象参数结构复杂 | 自定义实体和转换器简化参数结构 | -| 基础 API 会抛出大量 Exception,具体问题需要自己摸索对应 | 对所有错误进行标准化处理,提供更准确和交互友好的描述信息,可方便地与系统错误体系融合 | - -- \[2\] 前后端交互 - -| Minio SDK | Dante OSS | -| ------------------------------ | -------------------------------------------------------------------- | -| 复杂结构参数不利于 JSON 互转 | 采用最简化参数方便传输并可准确转换成对应 Minio 复杂对象参数 | -| 参数层次结构复杂 | 自定义请求参数实体保持继承结构的同时简化传递参数 | -| 参数多用途不明晰必须查阅源代码 | 使用 OpenAPI 注解详细说明各参数用途可使用 Swagger 查阅 | -| 参数校验规则细节多没有文档说明 | 对照 Minio 源代码,结合自定义实体,增加匹配的 Spring Validation 校验 | -| 不提供 REST API | 提供标准的 REST API 可直接使用 | - -- \[3\] 业务支持 - -| 内容 | Minio SDK | Dante OSS | -| ---------- | --------------------------------------------------- | ------------------------------------------------------------------------------- | -| 常规业务 | 独立方法需要自己按需组合 | 封装常规业务逻辑,可直接调用 REST API 使用 | -| 设置管理 | 对于存储桶、对象的管理只能通过 Minio 服务器管理界面 | 对照 Minio 管理界面方式,将管理功能封装为 Service、REST API 以及 Vue 管理界面 | -| 文件直传 | 提供直传机制,直接暴露 Minio 服务器地址 | 增加超简化反向代理,在满足直传需求的前提下,很好的隐藏 Minio 服务器以提升安全性 | -| 文件直传 | 直传接口无法与现有系统安全体系融合(无法鉴权) | 提供基于 Spring Authorization Server 的、完整的单体版和微服务版案例 | -| 大文件分片 | 内部机制无法直接使用 | 封装主流大文件分片方案,提供前后端使用案例 | -| 文件秒传 | 不支持 | 提供共用化秒传实现,可直接使用,支持多种数据库 | - -- \[4\] 前端开发 - -| Dante OSS | -| ------------------------------------------------------------------------------------------------------- | -| 只要 Minio API 支持,对应的管理功能均会在标准的 Vue3 工程中实现 | -| 提供与后端一致 Typescript 声明文件,可以直接用于基于 Typescript 的前端开发 | -| 完整的、基于 Vue3、Vite4、Typescript5 的前端项目案例,可清晰的了解 Minio 前后端交互和使用,甚至直接使用 | - -## \[3\] Dante Cloud 3.0.0 后端新特性 - -### 1\. 核心基础依赖便捷切换 - -- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 -- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 - -### 2\. 支持 `GraalVM` 原生镜像 - -- 整体调整各类模块 pom build 配置,适当增加冗余重复配置,以支持 `Spring Native` 或 `GraalVM` 编译需要。规避对所有模块进行 Native 编译,而导致错误问题。 - -### 3\. `Spring Authorization Server` 全特性支持及扩展 - -- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 -- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 -- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 -- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 -- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 -- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 -- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 -- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 -- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 -- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 -- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 -- 基于自定义 Session,混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用 “一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 - -### 4\. `Spring Authorization Server` OAuth2 **Device Authorization Grant** 认证模式 - -OAuth2 **Device Authorization Grant** 认证模式,是在 OIDC 协议支持的模式中,专有的一类 Device Flow 设备模式,允许各类终端或硬件完成认证登录流程。 - -**Device Authorization Grant** (设备授权授予)或 **Device Flow** 对于处理,例如:智能电视、IoT 设备或打印机等,输入受限终端或硬件的身份验证和授权非常有用。由于终端的显示模式可能受限,无法内置登录页面。通过 Device Flow 提供的超链接或者生成二维码,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。用户登录后,设备可以获取所需的访问令牌和刷新令牌。 - -## \[4\] Dante Cloud 3.0.0 前端新特性 - -### 采用 `pnpm monorepo` 重构前端 - -- 前端工程包管理器变更为 pnpm。 -- 采用 `monorepo` 模式对前端工程进行重构,抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块 -- 共享模块已进行优化配置,利用 Vite 可编译成独立的组件,单独以组件形式进行发布 -- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用。 +--- +title: Dante Cloud 3.1.0.6 发布,拆分核心基础组件,构建产品生态 +author: dante +tag: + - Dante-Cloud +date: 2023-06-14 +cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png +head: + - - meta + - name: 新闻 +--- + +**Dante Cloud** 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 Spring Authorization Server 的、基于 OAuth2.1 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 Spring Authorization Server 1.1.0、Spring Boot 3.1.0、Spring Cloud 2022.0.3、Spring Cloud Tencent 1.11.7-2022.0.1、Spring Cloud Alibaba 2022.0.0.0、Nacos 2.2.2 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 + +**平台定位** + +- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 +- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 +- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 +- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 + +**发布背景** + +为方便用户使用,Dante Cloud 建设初期以“全面”为目标,尽可能的集成、开发和包容更多常用组件,以兼容更多用户的使用需求。Dante Cloud 经过两年的建设、完善与发展、探索,发现这种模式看似全面、面面俱到、可以吸引更多用户的青睐。但对实际的开发、使用的促进效果并不理想。 + +主要存在以下问题: + +- 集成和兼容内容过多,影响代码编译、发布的时效性。 +- 并不是所有内容都对用户有帮助,反而会带来过多的依赖性和耦合性。 +- 原本相对独立性较高的内容,却融入在一个庞大的架构环境中,既不利于理解,也不利于单独使用。 +- 引入过多杂音,分散用户对 Dante Cloud 核心价值内容的关注度,不利于掌握和深入 Dante Cloud 核心。 +- 过多内容的集成,影响各个部分的专注和专业程度,导致多而不精,多而不深的问题。 + +因此,尝试采用构建“产品生态”的方式,解决以上问题,同时给用户带来更好的、更便捷产品使用体验。 + +- 通过构建产品生态,将独立性较强的内容,从系统核心内容中剥离,让 Dante Cloud 本身更加专精和独立,同时降低互相影响和干扰 +- 被剥离的内容以独立的产品形态存在,目标既可以独立使用集成至任意系统中,又可以快速的与 Dante Cloud 融合,形成以 Dante Cloud 为主,生态产品为辅的格局。 +- 生态产品以独立的产品运作,反向促进提升生态产品的完整性、深入性和便捷性。 + +## \[1\] 本次更新内容 + +- 主要更新 + +- \[升级\] Spring Cloud Tencent 1.11.7-2022.0.1 +- \[变更\] Minio 对象存储相关代码,从 Dante Engine 中剥离,成为一个独立的项目产品 +- \[删除\] 删除 pay 和 nosql 相关模块,清理系统中独立性较高的模组,以保持系统内核的专注性 +- \[新增\] 新增 REST 接口动态鉴权是否使用严格模式配置,在严格模式下,所有接口必须配置权限才可使用;在非严格模式下,接口只需要携带 Token 即可使用。 + +- 其它更新 + +- \[新增\] 新增 message-rabbitmq-spring-boot-starter,以方便 RabbitMQ 使用者集成使用 +- \[新增\] 新增对象存储 Minio 服务器不可用错误代码 +- \[新增\] 前端新增对象存储 Bucket 管理界面 +- \[新增\] 前端新增对象存储 Bucket 设置界面 +- \[新增\] 前端新增对象存储 Object 管理界面 +- \[新增\] 前端新增对象存储 Object 设置界面 +- \[新增\] 前端新增基于 vue-simple-uploader 的大文件分片存储支持 +- \[新增\] 新增 OSS 对象存储服务 +- \[新增\] 新增对象存储 Minio 服务器不可用错误代码 +- \[修复\] 修复在未引入 Spring Cloud OpenFeign 环境下,RestTemplate 配置失效导致启动错误的问题 +- \[优化\] 优化基础 Controller 代码,调整判断逻辑,以更好地的支持查询数据成功、未查询到数据、查询失败等三种状态 + +- 依赖更新 + +- \[升级\] common-io 版本升级至 2.13.0 +- \[升级\] redission 版本升级至 3.22.0 +- \[升级\] guava 版本升级至 32.0.1-jre +- \[升级\] skywalking 版本升级至 8.16.0 +- \[升级\] wxjava 版升级至 4.5.1.B +- \[升级\] camunda 版升级至 7.20.0-alpha2 +- \[升级\] Webjars Bootstrap 版升级至 5.3.0 +- \[升级\] tencentcloud-sdk-java-sms 版升级至 3.1.775 +- \[升级\] alipay-sdk-java 版本升级至 4.35.154.ALL + +## \[2\] 生态产品 Dante OSS + +### 优点 | Advantages + +- **零额外学习成本**: 开发者只要会 Spring 和 REST 基本开发,即可无缝集成和使用 Dante OSS +- **降低开发者门槛**: 屏蔽 Minio 标准 Java SDK 使用复杂度,使用 Spring 环境标准方式对原有 API 进行简化封装。Service API 和 REST API 开箱即用 +- **包含的功能丰富**: 改造了 Minio Java SDK 的几乎全部功能,且对大文件分片上传、秒传、直传、断点续传等功能,均采用业内最优解决方案进行实现和融合 +- **规范优雅的代码**: 所有函数参数,并未破坏原有 Minio 代码构造器结构,而是在原有方式的基础上抽象简化,编程体验和代码可读性大幅提升 +- **完善的注释文档**: 对请求参数、方法、REST API、Validation 提供详实的注释、说明和 OpenAPI 标注,用途用法一目了然,无需再翻阅 Minio 文档和源代码,帮助您节省更多时间 +- **完整的前端示例**:前端采用一个完整的项目而非 Demo 的形式,全面的展示了前后端交互涉及、接口调用、参数使用、TS 类型定义等各方面内容,可直接用于实际项目或简单改造后构建自己的产品 + +### 对比 | Compare + +#### 1\. 不只是简单的 Spring Boot Starter 构建 + +1. 构建统一的错误,可以返回更人性化、更易理解的错误信息,同时兼顾更详细错误信息的返回,方便开发人员理解和定位问题。 +2. 采用更易理解和使用的格式对 Minio Java SDK 参数进行重新定义。规避 Minio 默认 XML 方式参数多、不易理解使用、与前端交互不方便等问题。 +3. 隐藏 Minio Java SDK 不易理解和使用的细节,提供详实的注释说明,开发人员在使用时无需再通过翻阅 Minio 在线文档和源代码来了解各个 API 使用细节。 +4. 提供统一标准的 REST API,以及 OpenAPI Swagger3 文档描述和准确的 Spring Validation 校验,可直接集成至系统中使用。 +5. Minio Client 对象池、自定义极简 Minio Server 访问反向代理,提升 + +#### 2\. 标准化业务逻辑和解决方案集合 + +1. 不只是上传、下载等常用方法的封装,涵盖 Minio Java SDK 支持的所有方法和操作。 +2. 选择业内最优的解决方案,实现和集成大文件分片上传、秒传、直传、断点续传等主要业务需求功能。 +3. 结合自身应用经验和需求,将 Minio API 进一步组合成符合实际应用的业务逻辑和功能处理。 +4. 采用一个基于 Vue3、Typescript5、Vite4、Pinia 2 的完整的前端项目作为集成示例,包括详细的 Typescript 类型定义以及 vue-simple-uploader 等主流组件集成和使用方法。 +5. 提供基于 Spring Authorization Server 的单体版、微服务版案例,从 SDK、Spring Boot Starter 到完整项目任你选择。 + +#### 3\. 具体差异说明 + +- \[1\] 基础 API 方法以及方法参数 + +| Minio SDK | Dante OSS | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| 仅包含基础操作 API | 提供大量重载方法 | +| 必须用构造器创建参数对象 | 重载方法覆盖所有常见参数,按需传参即可 | +| API 全部混在同一个类中 | 根据差异、用途、场景拆分为不同的 Service,例如:getObject 和 downloadObject | +| 源于 XML 对象参数结构复杂 | 自定义实体和转换器简化参数结构 | +| 基础 API 会抛出大量 Exception,具体问题需要自己摸索对应 | 对所有错误进行标准化处理,提供更准确和交互友好的描述信息,可方便地与系统错误体系融合 | + +- \[2\] 前后端交互 + +| Minio SDK | Dante OSS | +| ------------------------------ | -------------------------------------------------------------------- | +| 复杂结构参数不利于 JSON 互转 | 采用最简化参数方便传输并可准确转换成对应 Minio 复杂对象参数 | +| 参数层次结构复杂 | 自定义请求参数实体保持继承结构的同时简化传递参数 | +| 参数多用途不明晰必须查阅源代码 | 使用 OpenAPI 注解详细说明各参数用途可使用 Swagger 查阅 | +| 参数校验规则细节多没有文档说明 | 对照 Minio 源代码,结合自定义实体,增加匹配的 Spring Validation 校验 | +| 不提供 REST API | 提供标准的 REST API 可直接使用 | + +- \[3\] 业务支持 + +| 内容 | Minio SDK | Dante OSS | +| ---------- | --------------------------------------------------- | ------------------------------------------------------------------------------- | +| 常规业务 | 独立方法需要自己按需组合 | 封装常规业务逻辑,可直接调用 REST API 使用 | +| 设置管理 | 对于存储桶、对象的管理只能通过 Minio 服务器管理界面 | 对照 Minio 管理界面方式,将管理功能封装为 Service、REST API 以及 Vue 管理界面 | +| 文件直传 | 提供直传机制,直接暴露 Minio 服务器地址 | 增加超简化反向代理,在满足直传需求的前提下,很好的隐藏 Minio 服务器以提升安全性 | +| 文件直传 | 直传接口无法与现有系统安全体系融合(无法鉴权) | 提供基于 Spring Authorization Server 的、完整的单体版和微服务版案例 | +| 大文件分片 | 内部机制无法直接使用 | 封装主流大文件分片方案,提供前后端使用案例 | +| 文件秒传 | 不支持 | 提供共用化秒传实现,可直接使用,支持多种数据库 | + +- \[4\] 前端开发 + +| Dante OSS | +| ------------------------------------------------------------------------------------------------------- | +| 只要 Minio API 支持,对应的管理功能均会在标准的 Vue3 工程中实现 | +| 提供与后端一致 Typescript 声明文件,可以直接用于基于 Typescript 的前端开发 | +| 完整的、基于 Vue3、Vite4、Typescript5 的前端项目案例,可清晰的了解 Minio 前后端交互和使用,甚至直接使用 | + +## \[3\] Dante Cloud 3.0.0 后端新特性 + +### 1\. 核心基础依赖便捷切换 + +- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 +- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 + +### 2\. 支持 `GraalVM` 原生镜像 + +- 整体调整各类模块 pom build 配置,适当增加冗余重复配置,以支持 `Spring Native` 或 `GraalVM` 编译需要。规避对所有模块进行 Native 编译,而导致错误问题。 + +### 3\. `Spring Authorization Server` 全特性支持及扩展 + +- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 +- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 +- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 +- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 +- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 +- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 +- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 +- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 +- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 +- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 +- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 +- 基于自定义 Session,混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用 “一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 + +### 4\. `Spring Authorization Server` OAuth2 **Device Authorization Grant** 认证模式 + +OAuth2 **Device Authorization Grant** 认证模式,是在 OIDC 协议支持的模式中,专有的一类 Device Flow 设备模式,允许各类终端或硬件完成认证登录流程。 + +**Device Authorization Grant** (设备授权授予)或 **Device Flow** 对于处理,例如:智能电视、IoT 设备或打印机等,输入受限终端或硬件的身份验证和授权非常有用。由于终端的显示模式可能受限,无法内置登录页面。通过 Device Flow 提供的超链接或者生成二维码,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。用户登录后,设备可以获取所需的访问令牌和刷新令牌。 + +## \[4\] Dante Cloud 3.0.0 前端新特性 + +### 采用 `pnpm monorepo` 重构前端 + +- 前端工程包管理器变更为 pnpm。 +- 采用 `monorepo` 模式对前端工程进行重构,抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块 +- 共享模块已进行优化配置,利用 Vite 可编译成独立的组件,单独以组件形式进行发布 +- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用。 diff --git a/src/zh/news/Dante-Cloud-3.1.1.1.md b/src/zh/news/Dante-Cloud-3.1.1.1.md index 277e2a150f..58b1142c16 100644 --- a/src/zh/news/Dante-Cloud-3.1.1.1.md +++ b/src/zh/news/Dante-Cloud-3.1.1.1.md @@ -1,182 +1,182 @@ ---- -title: Dante Cloud 3.1.1.1 发布,采用领域驱动设计(DDD)的微服务架构 -author: dante -tag: - - Dante-Cloud -date: 2023-07-06 -cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png -head: - - - meta - - name: 新闻 ---- - -**Dante Cloud** 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 Spring Authorization Server 的、基于 OAuth2.1 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 Spring Authorization Server 1.1.1、Spring Boot 3.1.1、Spring Cloud 2022.0.3、Spring Cloud Tencent 1.11.7-2022.0.1、Spring Cloud Alibaba 2022.0.0.0、Nacos 2.2.4 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 - -**平台定位** - -- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 -- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 -- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 -- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 - -**发布背景** - -为方便用户使用,Dante Cloud 建设初期以“全面”为目标,尽可能的集成、开发和包容更多常用组件,以兼容更多用户的使用需求。Dante Cloud 经过两年的建设、完善与发展、探索,发现这种模式看似全面、面面俱到、可以吸引更多用户的青睐。但对实际的开发、使用的促进效果并不理想。 - -主要存在以下问题: - -- 集成和兼容内容过多,影响代码编译、发布的时效性。 -- 并不是所有内容都对用户有帮助,反而会带来过多的依赖性和耦合性。 -- 原本相对独立性较高的内容,却融入在一个庞大的架构环境中,既不利于理解,也不利于单独使用。 -- 引入过多杂音,分散用户对 Dante Cloud 核心价值内容的关注度,不利于掌握和深入 Dante Cloud 核心。 -- 过多内容的集成,影响各个部分的专注和专业程度,导致多而不精,多而不深的问题。 - -因此,尝试采用构建“产品生态”的方式,解决以上问题,同时给用户带来更好的、更便捷产品使用体验。 - -- 通过构建产品生态,将独立性较强的内容,从系统核心内容中剥离,让 Dante Cloud 本身更加专精和独立,同时降低互相影响和干扰 -- 被剥离的内容以独立的产品形态存在,目标既可以独立使用集成至任意系统中,又可以快速的与 Dante Cloud 融合,形成以 Dante Cloud 为主,生态产品为辅的格局。 -- 生态产品以独立的产品运作,反向促进提升生态产品的完整性、深入性和便捷性。 - -## \[1\] 本次更新内容 - -- 【主要更新】 - -- \[升级\] Spring Boot Admin 版升级至 3.1.0 -- \[升级\] Debezimu 相关组件及容器版本升级至 2.3 - -- 【其它更新】 - -- \[重构\] 调整 @Inner 注解所在模块,提升代码内聚性。 -- \[优化\] 优化代码编译配置,增加代码编译过程中,自动生成 spring-autoconfigure-metadata.properties 机制,解决在新版 IDE 中部分跨 Module Bean 注入提示找不到,出现标红问题。 -- \[新增\] 前端 OSS 存储桶设置界面,增加版本控制设置功能。 -- \[新增\] 前端 OSS 存储桶设置界面,增加保留设置功能 -- \[新增\] 前端对象列表界面,增加文件夹显示及查看功能 -- \[修复\] Docker Compose 中 Nacos 镜像版本恢复至 v2.2.3。 -- \[修复\] 修复前端在 vite-plugin-dts 3.0.X 环境下,编译模块出错问题。 -- \[修复\] 修复前端封装 HDialog 关闭操作异常问题。 - -- 【依赖更新】 - -- \[升级\] bcprov-jdk15to18 版本升级至 1.75 -- \[升级\] guava 版升级至 32.1.1-jre -- \[升级\] tencentcloud-sdk-java-sms 版本升级至 3.1.789 -- \[升级\] alipay-sdk-java 版本升级至 4.38.4.ALL - -## \[2\] 生态产品 Dante OSS - -### 优点 | Advantages - -- **零额外学习成本**: 开发者只要会 Spring 和 REST 基本开发,即可无缝集成和使用 Dante OSS -- **降低开发者门槛**: 屏蔽 Minio 标准 Java SDK 使用复杂度,使用 Spring 环境标准方式对原有 API 进行简化封装。Service API 和 REST API 开箱即用 -- **包含的功能丰富**: 改造了 Minio Java SDK 的几乎全部功能,且对大文件分片上传、秒传、直传、断点续传等功能,均采用业内最优解决方案进行实现和融合 -- **规范优雅的代码**: 所有函数参数,并未破坏原有 Minio 代码构造器结构,而是在原有方式的基础上抽象简化,编程体验和代码可读性大幅提升 -- **完善的注释文档**: 对请求参数、方法、REST API、Validation 提供详实的注释、说明和 OpenAPI 标注,用途用法一目了然,无需再翻阅 Minio 文档和源代码,帮助您节省更多时间 -- **完整的前端示例**:前端采用一个完整的项目而非 Demo 的形式,全面的展示了前后端交互涉及、接口调用、参数使用、TS 类型定义等各方面内容,可直接用于实际项目或简单改造后构建自己的产品 - -### 对比 | Compare - -#### 1\. 不只是简单的 Spring Boot Starter 构建 - -1. 构建统一的错误,可以返回更人性化、更易理解的错误信息,同时兼顾更详细错误信息的返回,方便开发人员理解和定位问题。 -2. 采用更易理解和使用的格式对 Minio Java SDK 参数进行重新定义。规避 Minio 默认 XML 方式参数多、不易理解使用、与前端交互不方便等问题。 -3. 隐藏 Minio Java SDK 不易理解和使用的细节,提供详实的注释说明,开发人员在使用时无需再通过翻阅 Minio 在线文档和源代码来了解各个 API 使用细节。 -4. 提供统一标准的 REST API,以及 OpenAPI Swagger3 文档描述和准确的 Spring Validation 校验,可直接集成至系统中使用。 -5. Minio Client 对象池、自定义极简 Minio Server 访问反向代理,提升 - -#### 2\. 标准化业务逻辑和解决方案集合 - -1. 不只是上传、下载等常用方法的封装,涵盖 Minio Java SDK 支持的所有方法和操作。 -2. 选择业内最优的解决方案,实现和集成大文件分片上传、秒传、直传、断点续传等主要业务需求功能。 -3. 结合自身应用经验和需求,将 Minio API 进一步组合成符合实际应用的业务逻辑和功能处理。 -4. 采用一个基于 Vue3、Typescript5、Vite4、Pinia 2 的完整的前端项目作为集成示例,包括详细的 Typescript 类型定义以及 vue-simple-uploader 等主流组件集成和使用方法。 -5. 提供基于 Spring Authorization Server 的单体版、微服务版案例,从 SDK、Spring Boot Starter 到完整项目任你选择。 - -#### 3\. 具体差异说明 - -- \[1\] 基础 API 方法以及方法参数 - -| Minio SDK | Dante OSS | -| ------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| 仅包含基础操作 API | 提供大量重载方法 | -| 必须用构造器创建参数对象 | 重载方法覆盖所有常见参数,按需传参即可 | -| API 全部混在同一个类中 | 根据差异、用途、场景拆分为不同的 Service,例如:getObject 和 downloadObject | -| 源于 XML 对象参数结构复杂 | 自定义实体和转换器简化参数结构 | -| 基础 API 会抛出大量 Exception,具体问题需要自己摸索对应 | 对所有错误进行标准化处理,提供更准确和交互友好的描述信息,可方便地与系统错误体系融合 | - -- \[2\] 前后端交互 - -| Minio SDK | Dante OSS | -| ------------------------------ | -------------------------------------------------------------------- | -| 复杂结构参数不利于 JSON 互转 | 采用最简化参数方便传输并可准确转换成对应 Minio 复杂对象参数 | -| 参数层次结构复杂 | 自定义请求参数实体保持继承结构的同时简化传递参数 | -| 参数多用途不明晰必须查阅源代码 | 使用 OpenAPI 注解详细说明各参数用途可使用 Swagger 查阅 | -| 参数校验规则细节多没有文档说明 | 对照 Minio 源代码,结合自定义实体,增加匹配的 Spring Validation 校验 | -| 不提供 REST API | 提供标准的 REST API 可直接使用 | - -- \[3\] 业务支持 - -| 内容 | Minio SDK | Dante OSS | -| ---------- | --------------------------------------------------- | ------------------------------------------------------------------------------- | -| 常规业务 | 独立方法需要自己按需组合 | 封装常规业务逻辑,可直接调用 REST API 使用 | -| 设置管理 | 对于存储桶、对象的管理只能通过 Minio 服务器管理界面 | 对照 Minio 管理界面方式,将管理功能封装为 Service、REST API 以及 Vue 管理界面 | -| 文件直传 | 提供直传机制,直接暴露 Minio 服务器地址 | 增加超简化反向代理,在满足直传需求的前提下,很好的隐藏 Minio 服务器以提升安全性 | -| 文件直传 | 直传接口无法与现有系统安全体系融合(无法鉴权) | 提供基于 Spring Authorization Server 的、完整的单体版和微服务版案例 | -| 大文件分片 | 内部机制无法直接使用 | 封装主流大文件分片方案,提供前后端使用案例 | -| 文件秒传 | 不支持 | 提供共用化秒传实现,可直接使用,支持多种数据库 | - -- \[4\] 前端开发 - -| Dante OSS | -| ------------------------------------------------------------------------------------------------------- | -| 只要 Minio API 支持,对应的管理功能均会在标准的 Vue3 工程中实现 | -| 提供与后端一致 Typescript 声明文件,可以直接用于基于 Typescript 的前端开发 | -| 完整的、基于 Vue3、Vite4、Typescript5 的前端项目案例,可清晰的了解 Minio 前后端交互和使用,甚至直接使用 | - -## \[3\] Dante Cloud 3.0.0 后端新特性 - -### 1\. 核心基础依赖便捷切换 - -- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 -- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 - -### 2\. 支持 `GraalVM` 原生镜像 - -- 整体调整各类模块 pom build 配置,适当增加冗余重复配置,以支持 `Spring Native` 或 `GraalVM` 编译需要。规避对所有模块进行 Native 编译,而导致错误问题。 - -### 3\. `Spring Authorization Server` 全特性支持及扩展 - -- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 -- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 -- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 -- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 -- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 -- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 -- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 -- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 -- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 -- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 -- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 -- 基于自定义 Session,混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用 “一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 - -### 4\. `Spring Authorization Server` OAuth2 **Device Authorization Grant** 认证模式 - -OAuth2 **Device Authorization Grant** 认证模式,是在 OIDC 协议支持的模式中,专有的一类 Device Flow 设备模式,允许各类终端或硬件完成认证登录流程。 - -**Device Authorization Grant** (设备授权授予)或 **Device Flow** 对于处理,例如:智能电视、IoT 设备或打印机等,输入受限终端或硬件的身份验证和授权非常有用。由于终端的显示模式可能受限,无法内置登录页面。通过 Device Flow 提供的超链接或者生成二维码,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。用户登录后,设备可以获取所需的访问令牌和刷新令牌。 - -## \[4\] Dante Cloud 3.0.0 前端新特性 - -- 未使用任何流行开源模版,使用全新技术栈,完全纯"手写"全新前端工程。 -- 借鉴参考流行开源版本的使用和设计,新版前端界面风格和操作习惯尽量与当前流行方式统一。 -- 充份使用 Typescript 语言特性,解决大量类型校验问题,尽可能规避 "any" 式的 Typescript 编程语言使用方式。 -- 充份使用 Composition Api 和 Hooks 等 Vue3 框架新版特性进行代码编写。 -- 充份利用 Component、Hooks 以及 Typescript 面向对象等特性,抽取通用组件和代码,尽可能降低工程重复代码。 -- 对较多 Quasar 基础组件和应用功能组件进行封装,以方便代码的统一修改维护和开发使用。 -- 对生产模式下,对基于 Vite3 的工程打包进行深度性能优化。 -- 提供以 docker-compose 方式,对工程生产代码进行容器化打包和部署。 -- 该版本基于 pnpm,采用 monorepo 模式对前端工程进行重构。构建 monorepo 版本前端,是为扩展更多功能、增加应用级功能做铺垫 -- 抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块。 -- 共享模块已进行优化配置,可编译成独立的组件,单独以组件形式进行发布。 -- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用 +--- +title: Dante Cloud 3.1.1.1 发布,采用领域驱动设计(DDD)的微服务架构 +author: dante +tag: + - Dante-Cloud +date: 2023-07-06 +cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png +head: + - - meta + - name: 新闻 +--- + +**Dante Cloud** 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 Spring Authorization Server 的、基于 OAuth2.1 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 Spring Authorization Server 1.1.1、Spring Boot 3.1.1、Spring Cloud 2022.0.3、Spring Cloud Tencent 1.11.7-2022.0.1、Spring Cloud Alibaba 2022.0.0.0、Nacos 2.2.4 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 + +**平台定位** + +- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 +- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 +- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 +- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 + +**发布背景** + +为方便用户使用,Dante Cloud 建设初期以“全面”为目标,尽可能的集成、开发和包容更多常用组件,以兼容更多用户的使用需求。Dante Cloud 经过两年的建设、完善与发展、探索,发现这种模式看似全面、面面俱到、可以吸引更多用户的青睐。但对实际的开发、使用的促进效果并不理想。 + +主要存在以下问题: + +- 集成和兼容内容过多,影响代码编译、发布的时效性。 +- 并不是所有内容都对用户有帮助,反而会带来过多的依赖性和耦合性。 +- 原本相对独立性较高的内容,却融入在一个庞大的架构环境中,既不利于理解,也不利于单独使用。 +- 引入过多杂音,分散用户对 Dante Cloud 核心价值内容的关注度,不利于掌握和深入 Dante Cloud 核心。 +- 过多内容的集成,影响各个部分的专注和专业程度,导致多而不精,多而不深的问题。 + +因此,尝试采用构建“产品生态”的方式,解决以上问题,同时给用户带来更好的、更便捷产品使用体验。 + +- 通过构建产品生态,将独立性较强的内容,从系统核心内容中剥离,让 Dante Cloud 本身更加专精和独立,同时降低互相影响和干扰 +- 被剥离的内容以独立的产品形态存在,目标既可以独立使用集成至任意系统中,又可以快速的与 Dante Cloud 融合,形成以 Dante Cloud 为主,生态产品为辅的格局。 +- 生态产品以独立的产品运作,反向促进提升生态产品的完整性、深入性和便捷性。 + +## \[1\] 本次更新内容 + +- 【主要更新】 + +- \[升级\] Spring Boot Admin 版升级至 3.1.0 +- \[升级\] Debezimu 相关组件及容器版本升级至 2.3 + +- 【其它更新】 + +- \[重构\] 调整 @Inner 注解所在模块,提升代码内聚性。 +- \[优化\] 优化代码编译配置,增加代码编译过程中,自动生成 spring-autoconfigure-metadata.properties 机制,解决在新版 IDE 中部分跨 Module Bean 注入提示找不到,出现标红问题。 +- \[新增\] 前端 OSS 存储桶设置界面,增加版本控制设置功能。 +- \[新增\] 前端 OSS 存储桶设置界面,增加保留设置功能 +- \[新增\] 前端对象列表界面,增加文件夹显示及查看功能 +- \[修复\] Docker Compose 中 Nacos 镜像版本恢复至 v2.2.3。 +- \[修复\] 修复前端在 vite-plugin-dts 3.0.X 环境下,编译模块出错问题。 +- \[修复\] 修复前端封装 HDialog 关闭操作异常问题。 + +- 【依赖更新】 + +- \[升级\] bcprov-jdk15to18 版本升级至 1.75 +- \[升级\] guava 版升级至 32.1.1-jre +- \[升级\] tencentcloud-sdk-java-sms 版本升级至 3.1.789 +- \[升级\] alipay-sdk-java 版本升级至 4.38.4.ALL + +## \[2\] 生态产品 Dante OSS + +### 优点 | Advantages + +- **零额外学习成本**: 开发者只要会 Spring 和 REST 基本开发,即可无缝集成和使用 Dante OSS +- **降低开发者门槛**: 屏蔽 Minio 标准 Java SDK 使用复杂度,使用 Spring 环境标准方式对原有 API 进行简化封装。Service API 和 REST API 开箱即用 +- **包含的功能丰富**: 改造了 Minio Java SDK 的几乎全部功能,且对大文件分片上传、秒传、直传、断点续传等功能,均采用业内最优解决方案进行实现和融合 +- **规范优雅的代码**: 所有函数参数,并未破坏原有 Minio 代码构造器结构,而是在原有方式的基础上抽象简化,编程体验和代码可读性大幅提升 +- **完善的注释文档**: 对请求参数、方法、REST API、Validation 提供详实的注释、说明和 OpenAPI 标注,用途用法一目了然,无需再翻阅 Minio 文档和源代码,帮助您节省更多时间 +- **完整的前端示例**:前端采用一个完整的项目而非 Demo 的形式,全面的展示了前后端交互涉及、接口调用、参数使用、TS 类型定义等各方面内容,可直接用于实际项目或简单改造后构建自己的产品 + +### 对比 | Compare + +#### 1\. 不只是简单的 Spring Boot Starter 构建 + +1. 构建统一的错误,可以返回更人性化、更易理解的错误信息,同时兼顾更详细错误信息的返回,方便开发人员理解和定位问题。 +2. 采用更易理解和使用的格式对 Minio Java SDK 参数进行重新定义。规避 Minio 默认 XML 方式参数多、不易理解使用、与前端交互不方便等问题。 +3. 隐藏 Minio Java SDK 不易理解和使用的细节,提供详实的注释说明,开发人员在使用时无需再通过翻阅 Minio 在线文档和源代码来了解各个 API 使用细节。 +4. 提供统一标准的 REST API,以及 OpenAPI Swagger3 文档描述和准确的 Spring Validation 校验,可直接集成至系统中使用。 +5. Minio Client 对象池、自定义极简 Minio Server 访问反向代理,提升 + +#### 2\. 标准化业务逻辑和解决方案集合 + +1. 不只是上传、下载等常用方法的封装,涵盖 Minio Java SDK 支持的所有方法和操作。 +2. 选择业内最优的解决方案,实现和集成大文件分片上传、秒传、直传、断点续传等主要业务需求功能。 +3. 结合自身应用经验和需求,将 Minio API 进一步组合成符合实际应用的业务逻辑和功能处理。 +4. 采用一个基于 Vue3、Typescript5、Vite4、Pinia 2 的完整的前端项目作为集成示例,包括详细的 Typescript 类型定义以及 vue-simple-uploader 等主流组件集成和使用方法。 +5. 提供基于 Spring Authorization Server 的单体版、微服务版案例,从 SDK、Spring Boot Starter 到完整项目任你选择。 + +#### 3\. 具体差异说明 + +- \[1\] 基础 API 方法以及方法参数 + +| Minio SDK | Dante OSS | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| 仅包含基础操作 API | 提供大量重载方法 | +| 必须用构造器创建参数对象 | 重载方法覆盖所有常见参数,按需传参即可 | +| API 全部混在同一个类中 | 根据差异、用途、场景拆分为不同的 Service,例如:getObject 和 downloadObject | +| 源于 XML 对象参数结构复杂 | 自定义实体和转换器简化参数结构 | +| 基础 API 会抛出大量 Exception,具体问题需要自己摸索对应 | 对所有错误进行标准化处理,提供更准确和交互友好的描述信息,可方便地与系统错误体系融合 | + +- \[2\] 前后端交互 + +| Minio SDK | Dante OSS | +| ------------------------------ | -------------------------------------------------------------------- | +| 复杂结构参数不利于 JSON 互转 | 采用最简化参数方便传输并可准确转换成对应 Minio 复杂对象参数 | +| 参数层次结构复杂 | 自定义请求参数实体保持继承结构的同时简化传递参数 | +| 参数多用途不明晰必须查阅源代码 | 使用 OpenAPI 注解详细说明各参数用途可使用 Swagger 查阅 | +| 参数校验规则细节多没有文档说明 | 对照 Minio 源代码,结合自定义实体,增加匹配的 Spring Validation 校验 | +| 不提供 REST API | 提供标准的 REST API 可直接使用 | + +- \[3\] 业务支持 + +| 内容 | Minio SDK | Dante OSS | +| ---------- | --------------------------------------------------- | ------------------------------------------------------------------------------- | +| 常规业务 | 独立方法需要自己按需组合 | 封装常规业务逻辑,可直接调用 REST API 使用 | +| 设置管理 | 对于存储桶、对象的管理只能通过 Minio 服务器管理界面 | 对照 Minio 管理界面方式,将管理功能封装为 Service、REST API 以及 Vue 管理界面 | +| 文件直传 | 提供直传机制,直接暴露 Minio 服务器地址 | 增加超简化反向代理,在满足直传需求的前提下,很好的隐藏 Minio 服务器以提升安全性 | +| 文件直传 | 直传接口无法与现有系统安全体系融合(无法鉴权) | 提供基于 Spring Authorization Server 的、完整的单体版和微服务版案例 | +| 大文件分片 | 内部机制无法直接使用 | 封装主流大文件分片方案,提供前后端使用案例 | +| 文件秒传 | 不支持 | 提供共用化秒传实现,可直接使用,支持多种数据库 | + +- \[4\] 前端开发 + +| Dante OSS | +| ------------------------------------------------------------------------------------------------------- | +| 只要 Minio API 支持,对应的管理功能均会在标准的 Vue3 工程中实现 | +| 提供与后端一致 Typescript 声明文件,可以直接用于基于 Typescript 的前端开发 | +| 完整的、基于 Vue3、Vite4、Typescript5 的前端项目案例,可清晰的了解 Minio 前后端交互和使用,甚至直接使用 | + +## \[3\] Dante Cloud 3.0.0 后端新特性 + +### 1\. 核心基础依赖便捷切换 + +- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 +- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 + +### 2\. 支持 `GraalVM` 原生镜像 + +- 整体调整各类模块 pom build 配置,适当增加冗余重复配置,以支持 `Spring Native` 或 `GraalVM` 编译需要。规避对所有模块进行 Native 编译,而导致错误问题。 + +### 3\. `Spring Authorization Server` 全特性支持及扩展 + +- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 +- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 +- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 +- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 +- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 +- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 +- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 +- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 +- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 +- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 +- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 +- 基于自定义 Session,混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用 “一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 + +### 4\. `Spring Authorization Server` OAuth2 **Device Authorization Grant** 认证模式 + +OAuth2 **Device Authorization Grant** 认证模式,是在 OIDC 协议支持的模式中,专有的一类 Device Flow 设备模式,允许各类终端或硬件完成认证登录流程。 + +**Device Authorization Grant** (设备授权授予)或 **Device Flow** 对于处理,例如:智能电视、IoT 设备或打印机等,输入受限终端或硬件的身份验证和授权非常有用。由于终端的显示模式可能受限,无法内置登录页面。通过 Device Flow 提供的超链接或者生成二维码,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。用户登录后,设备可以获取所需的访问令牌和刷新令牌。 + +## \[4\] Dante Cloud 3.0.0 前端新特性 + +- 未使用任何流行开源模版,使用全新技术栈,完全纯"手写"全新前端工程。 +- 借鉴参考流行开源版本的使用和设计,新版前端界面风格和操作习惯尽量与当前流行方式统一。 +- 充份使用 Typescript 语言特性,解决大量类型校验问题,尽可能规避 "any" 式的 Typescript 编程语言使用方式。 +- 充份使用 Composition Api 和 Hooks 等 Vue3 框架新版特性进行代码编写。 +- 充份利用 Component、Hooks 以及 Typescript 面向对象等特性,抽取通用组件和代码,尽可能降低工程重复代码。 +- 对较多 Quasar 基础组件和应用功能组件进行封装,以方便代码的统一修改维护和开发使用。 +- 对生产模式下,对基于 Vite3 的工程打包进行深度性能优化。 +- 提供以 docker-compose 方式,对工程生产代码进行容器化打包和部署。 +- 该版本基于 pnpm,采用 monorepo 模式对前端工程进行重构。构建 monorepo 版本前端,是为扩展更多功能、增加应用级功能做铺垫 +- 抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块。 +- 共享模块已进行优化配置,可编译成独立的组件,单独以组件形式进行发布。 +- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用 diff --git a/src/zh/news/Dante-Cloud-3.1.3.0.md b/src/zh/news/Dante-Cloud-3.1.3.0.md index 18730d5b99..4d51eea347 100644 --- a/src/zh/news/Dante-Cloud-3.1.3.0.md +++ b/src/zh/news/Dante-Cloud-3.1.3.0.md @@ -1,141 +1,141 @@ ---- -title: Dante Cloud 3.1.3.0 发布,采用领域驱动设计(DDD)的微服务架构 -author: dante -tag: - - Dante-Cloud -date: 2023-08-25 -cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png -head: - - - meta - - name: 新闻 ---- - -**Dante Cloud** 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 Spring Authorization Server 的、基于 OAuth2.1 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 Spring Authorization Server 1.1.2、Spring Boot 3.1.3、Spring Cloud 2022.0.4、Spring Cloud Tencent 1.11.9-2022.0.1、Spring Cloud Alibaba 2022.0.0.0、Nacos 2.2.4 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 - -**平台定位** - -- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 -- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 -- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 -- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 - -**初衷** - -作者本人过往工作,主要专注企业信息化项目建设,经手过大大小小、各式各样、规模各异的项目。这些项目经历,足以让我体会 IT 行业的千滋百味。回顾过往,发现其中很多工作,特别是很多会占用技术人员大量时间和精力的工作,大多数情况下并不是像技术攻关、新技术研究、业务架构设计、并发性能调优等有挑战性的工作,而往往都是因细节考虑欠缺、代码质量不高、在用技术老旧、欠缺优化迭代、系统难于维护、项目管理不善等问题产生的需要长期投入的、低效、低意义工作。 - -这也是为什么做 Dante Cloud 的初衷:一方面是以 Dante Cloud 为载体,潜移默化地将过往项目建设的经验教训融入其中,尽可能地帮助使用者规避或者减少无效工作,提升工作效率和质量,有跟多的时间做更有意义的事情;另一方面不断地融合和使用各类新兴技术,帮助使用者尽可能多的了解、学习和运用新技术,让技术不再成为禁锢变为进步和提升的基石。 - -## \[1\] 本次更新内容 - -- 【主要更新】 - -- \[升级\] Spring Boot 版本升级至 3.1.3 -- \[升级\] Spring Authorization Server 版本升级至 1.1.2 -- \[升级\] Spring Cloud Tencent 版本升级至 1.11.9-2022.0.1 -- \[升级\] Spring Boot Admin 版本升级至 3.1.5 - -- 【其它更新】 - -- \[新增\] 新增 idea IDE 中显示 Dante Cloud Logo 配置。 -- \[新增\] DateTimeUtils 增加 Date 互转 ZonedDateTime 方法 -- \[新增\] 前端工程新增 OSS 普通流式上传、下载进度显示。fix: #I7DO83 (ISSUED by jacky) -- \[重构\] 重构部分核心包代码依赖逻辑,解决部分包依赖不合理问题。 -- \[重构\] 参照 Spring Boot 规范,重构部分自定义 Event 所在模块,以及核心 Event 实现定义。解决 Event 代码放置混乱逻辑不易理解问题。 -- \[重构\] 参数 Spring Boot 规范,重构 engine-rest 模块下所有代码模块。同时,调整相关代码,进一步解耦。 -- \[重构\] 参照 Spring Boot 规范,重构短信模块 -- \[重构\] 将原有实体转换代码,重构为 Converter 形式。 -- \[重构\] 将所有 Starter 按照 Spring Boot Starter 规范重构相关代码。 -- \[重构\] 为了保证数据一致性,数据库初始化脚本移动至 dante-cloud-oss-ability 服务中。在线文档数据库初始化内容也同步更新。 -- \[重构\] 重构前端 OSS 流式上传、下载 Typescript 相关定义和接口调用服务代码。流式上传、下载替换为使用后端符合 Dante Java OSS API 规范的统一定义 REST API,并完成前后端联调验证。 -- \[重构\] 重构前端 OSS 大文件分片上传 Typescript 相关定义和接口调用服务代码。流式上传、下载替换为使用后端符合 Dante Java OSS API 规范的统一定义 REST API,并完成前后端联调验证。 -- \[重构\] 因不具备跨业务通用性,调整前端 OSS 相关组件代码放置位置,将其移动到 OSS 页面代码文件夹,以保持业务相关性。 -- \[重构\] 按照 Spring 生态规范,重构 assistant 和 rest 相关模块自动配置代码,让其既符合 Spring 自动配置规范,又可以提升模块代码的内聚性,减少耦合关联和精简依赖包的依赖。 -- \[修复\] 修复 Snakeyaml (CVE-2022-1471) 存在反序列化漏洞 和 (CVE-2022-41854) 存在缓冲区溢出漏洞 -- \[修复\] 修复前端自定义 ListItem 组件 directives 设置不正确导致前端控制台抛错问题。 -- \[修复\] 修复前端创建存储桶界面校验存储桶是否存在错误 -- \[修复\] 修复微服务版本环境下,因自定义代理地址配置错误,导致 OSS 大文件分片上传出错问题。 -- \[修复\] 修复单体版数据库初始化脚本存在重复数据问题 -- \[修复\] 重构 HDialog 自定义封装组件。修复前端上传对话框操作按钮逻辑不合理,导致前端抛错以及上传成功后不会刷新对象列表问题。 -- \[修复\] 修复前端工程页面切换动画不生效问题 -- \[优化\] 默认数据库名称进行变更,修改为与项目名称一致,方便记忆和使用。 -- \[优化\] 优化对象存储相关 Nacos 配置,将原有配置替换为与 Dante OSS 1.3.0 统一的新版配置。 -- \[优化\] 补充 Nacos 2.2.3 Mysql 数据库初始化脚本。 -- \[优化\] 补充可外部化配置的 logback.xml 日志配置文件。包含 Skywalking 日志上报、ELK 日志中心日志收集、Skywalking TraceId 等支持。同时提供常规及 MDC 两种配置 -- \[优化\] 使用 import 方式,优化 springdoc 依赖包的引入方式,减少过多无用的依赖信息。 -- \[优化\] 优化项目 Banner.txt,增加在线文档地址展示 -- \[删除\] 删除 okio 强制版本控制,改为使用统一配置版本。 -- \[删除\] 删除 xnio 强制版本控制,改为使用统一配置版本。 -- \[升级\] Minio Docker 镜像版本升级至 RELEASE.2023-08-23T10-07-06Z - -- 【依赖更新】 - -- \[升级\] snakeyaml 版本升级至 2.1 -- \[升级\] bcprov-jdk18on 版本升级至 1.76 -- \[升级\] tencentcloud-sdk-java-sms 版本升级至 3.1.835 -- \[升级\] alipay-sdk-java 版本升级至 4.38.61.ALL -- \[升级\] redisson 版本升级至 3.23.3 -- \[升级\] minio 版本升级至 8.5.5 -- \[升级\] aws-java-sdk-s3 版本升级至 1.12.533 -- \[升级\] fastjson2 版本升级至 2.0.39 -- \[升级\] mybatis-plus-boot-starter 版本升级至 3.5.3.2 - -- 【文档更新】 - -- Dante Cloud Cookbook 上新基础知识篇《Spring Boot 3 之自动配置与注入顺序控制》,助力快速掌握突破微服务生态的关键“开关”。欢迎扫码阅读! - -![](/assets/img/news/Dante-Cloud-3.1.3.0-1.png) - -## \[2\] Dante Cloud 3.1.X 后端新特性 - -### 1\. 核心基础依赖便捷切换 - -- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 -- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 - -### 2\. `Spring Authorization Server` 全特性支持及扩展 - -- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 -- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 -- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 -- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 -- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 -- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 -- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 -- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 -- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 -- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 -- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 -- 基于自定义 Session,混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用 “一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 - -### 3\. `Spring Authorization Server` OAuth2 **Device Authorization Grant** 认证模式 - -OAuth2 **Device Authorization Grant** 认证模式,是在 OIDC 协议支持的模式中,专有的一类 Device Flow 设备模式,允许各类终端或硬件完成认证登录流程。 - -**Device Authorization Grant** (设备授权授予)或 **Device Flow** 对于处理,例如:智能电视、IoT 设备或打印机等,输入受限终端或硬件的身份验证和授权非常有用。由于终端的显示模式可能受限,无法内置登录页面。通过 Device Flow 提供的超链接或者生成二维码,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。用户登录后,设备可以获取所需的访问令牌和刷新令牌。 - -## \[3\] Dante Cloud 3.1.X 前端新特性 - -- 未使用任何流行开源模版,使用全新技术栈,完全纯"手写"全新前端工程。 -- 借鉴参考流行开源版本的使用和设计,新版前端界面风格和操作习惯尽量与当前流行方式统一。 -- 充份使用 Typescript 语言特性,解决大量类型校验问题,尽可能规避 "any" 式的 Typescript 编程语言使用方式。 -- 充份使用 Composition Api 和 Hooks 等 Vue3 框架新版特性进行代码编写。 -- 充份利用 Component、Hooks 以及 Typescript 面向对象等特性,抽取通用组件和代码,尽可能降低工程重复代码。 -- 对较多 Quasar 基础组件和应用功能组件进行封装,以方便代码的统一修改维护和开发使用。 -- 对生产模式下,对基于 Vite3 的工程打包进行深度性能优化。 -- 提供以 docker-compose 方式,对工程生产代码进行容器化打包和部署。 -- 该版本基于 pnpm,采用 monorepo 模式对前端工程进行重构。构建 monorepo 版本前端,是为扩展更多功能、增加应用级功能做铺垫 -- 抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块。 -- 共享模块已进行优化配置,可编译成独立的组件,单独以组件形式进行发布。 -- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用 - ---- - -**欢迎 Star 一波来支持我们!** - -**Gitee**:https://gitee.com/dromara/dante-cloud**Github**:https://github.com/dromara/dante-cloud - - -
- +--- +title: Dante Cloud 3.1.3.0 发布,采用领域驱动设计(DDD)的微服务架构 +author: dante +tag: + - Dante-Cloud +date: 2023-08-25 +cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png +head: + - - meta + - name: 新闻 +--- + +**Dante Cloud** 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型(DDD)设计思想的、全面拥抱 Spring Authorization Server 的、基于 OAuth2.1 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 Spring Authorization Server 1.1.2、Spring Boot 3.1.3、Spring Cloud 2022.0.4、Spring Cloud Tencent 1.11.9-2022.0.1、Spring Cloud Alibaba 2022.0.0.0、Nacos 2.2.4 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能 + +**平台定位** + +- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 +- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 +- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 +- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 + +**初衷** + +作者本人过往工作,主要专注企业信息化项目建设,经手过大大小小、各式各样、规模各异的项目。这些项目经历,足以让我体会 IT 行业的千滋百味。回顾过往,发现其中很多工作,特别是很多会占用技术人员大量时间和精力的工作,大多数情况下并不是像技术攻关、新技术研究、业务架构设计、并发性能调优等有挑战性的工作,而往往都是因细节考虑欠缺、代码质量不高、在用技术老旧、欠缺优化迭代、系统难于维护、项目管理不善等问题产生的需要长期投入的、低效、低意义工作。 + +这也是为什么做 Dante Cloud 的初衷:一方面是以 Dante Cloud 为载体,潜移默化地将过往项目建设的经验教训融入其中,尽可能地帮助使用者规避或者减少无效工作,提升工作效率和质量,有跟多的时间做更有意义的事情;另一方面不断地融合和使用各类新兴技术,帮助使用者尽可能多的了解、学习和运用新技术,让技术不再成为禁锢变为进步和提升的基石。 + +## \[1\] 本次更新内容 + +- 【主要更新】 + +- \[升级\] Spring Boot 版本升级至 3.1.3 +- \[升级\] Spring Authorization Server 版本升级至 1.1.2 +- \[升级\] Spring Cloud Tencent 版本升级至 1.11.9-2022.0.1 +- \[升级\] Spring Boot Admin 版本升级至 3.1.5 + +- 【其它更新】 + +- \[新增\] 新增 idea IDE 中显示 Dante Cloud Logo 配置。 +- \[新增\] DateTimeUtils 增加 Date 互转 ZonedDateTime 方法 +- \[新增\] 前端工程新增 OSS 普通流式上传、下载进度显示。fix: #I7DO83 (ISSUED by jacky) +- \[重构\] 重构部分核心包代码依赖逻辑,解决部分包依赖不合理问题。 +- \[重构\] 参照 Spring Boot 规范,重构部分自定义 Event 所在模块,以及核心 Event 实现定义。解决 Event 代码放置混乱逻辑不易理解问题。 +- \[重构\] 参数 Spring Boot 规范,重构 engine-rest 模块下所有代码模块。同时,调整相关代码,进一步解耦。 +- \[重构\] 参照 Spring Boot 规范,重构短信模块 +- \[重构\] 将原有实体转换代码,重构为 Converter 形式。 +- \[重构\] 将所有 Starter 按照 Spring Boot Starter 规范重构相关代码。 +- \[重构\] 为了保证数据一致性,数据库初始化脚本移动至 dante-cloud-oss-ability 服务中。在线文档数据库初始化内容也同步更新。 +- \[重构\] 重构前端 OSS 流式上传、下载 Typescript 相关定义和接口调用服务代码。流式上传、下载替换为使用后端符合 Dante Java OSS API 规范的统一定义 REST API,并完成前后端联调验证。 +- \[重构\] 重构前端 OSS 大文件分片上传 Typescript 相关定义和接口调用服务代码。流式上传、下载替换为使用后端符合 Dante Java OSS API 规范的统一定义 REST API,并完成前后端联调验证。 +- \[重构\] 因不具备跨业务通用性,调整前端 OSS 相关组件代码放置位置,将其移动到 OSS 页面代码文件夹,以保持业务相关性。 +- \[重构\] 按照 Spring 生态规范,重构 assistant 和 rest 相关模块自动配置代码,让其既符合 Spring 自动配置规范,又可以提升模块代码的内聚性,减少耦合关联和精简依赖包的依赖。 +- \[修复\] 修复 Snakeyaml (CVE-2022-1471) 存在反序列化漏洞 和 (CVE-2022-41854) 存在缓冲区溢出漏洞 +- \[修复\] 修复前端自定义 ListItem 组件 directives 设置不正确导致前端控制台抛错问题。 +- \[修复\] 修复前端创建存储桶界面校验存储桶是否存在错误 +- \[修复\] 修复微服务版本环境下,因自定义代理地址配置错误,导致 OSS 大文件分片上传出错问题。 +- \[修复\] 修复单体版数据库初始化脚本存在重复数据问题 +- \[修复\] 重构 HDialog 自定义封装组件。修复前端上传对话框操作按钮逻辑不合理,导致前端抛错以及上传成功后不会刷新对象列表问题。 +- \[修复\] 修复前端工程页面切换动画不生效问题 +- \[优化\] 默认数据库名称进行变更,修改为与项目名称一致,方便记忆和使用。 +- \[优化\] 优化对象存储相关 Nacos 配置,将原有配置替换为与 Dante OSS 1.3.0 统一的新版配置。 +- \[优化\] 补充 Nacos 2.2.3 Mysql 数据库初始化脚本。 +- \[优化\] 补充可外部化配置的 logback.xml 日志配置文件。包含 Skywalking 日志上报、ELK 日志中心日志收集、Skywalking TraceId 等支持。同时提供常规及 MDC 两种配置 +- \[优化\] 使用 import 方式,优化 springdoc 依赖包的引入方式,减少过多无用的依赖信息。 +- \[优化\] 优化项目 Banner.txt,增加在线文档地址展示 +- \[删除\] 删除 okio 强制版本控制,改为使用统一配置版本。 +- \[删除\] 删除 xnio 强制版本控制,改为使用统一配置版本。 +- \[升级\] Minio Docker 镜像版本升级至 RELEASE.2023-08-23T10-07-06Z + +- 【依赖更新】 + +- \[升级\] snakeyaml 版本升级至 2.1 +- \[升级\] bcprov-jdk18on 版本升级至 1.76 +- \[升级\] tencentcloud-sdk-java-sms 版本升级至 3.1.835 +- \[升级\] alipay-sdk-java 版本升级至 4.38.61.ALL +- \[升级\] redisson 版本升级至 3.23.3 +- \[升级\] minio 版本升级至 8.5.5 +- \[升级\] aws-java-sdk-s3 版本升级至 1.12.533 +- \[升级\] fastjson2 版本升级至 2.0.39 +- \[升级\] mybatis-plus-boot-starter 版本升级至 3.5.3.2 + +- 【文档更新】 + +- Dante Cloud Cookbook 上新基础知识篇《Spring Boot 3 之自动配置与注入顺序控制》,助力快速掌握突破微服务生态的关键“开关”。欢迎扫码阅读! + +![](/assets/img/news/Dante-Cloud-3.1.3.0-1.png) + +## \[2\] Dante Cloud 3.1.X 后端新特性 + +### 1\. 核心基础依赖便捷切换 + +- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 +- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 + +### 2\. `Spring Authorization Server` 全特性支持及扩展 + +- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 +- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 +- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 +- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 +- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 +- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,将低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 +- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 +- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 +- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 +- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 +- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 +- 基于自定义 Session,混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用 “一人一码机制”,实现密码模式登录数据进行动态加密传输。配合 OAuth2 Client 验证,保护接口调用和前后端数据传输的合理性及安全性。 + +### 3\. `Spring Authorization Server` OAuth2 **Device Authorization Grant** 认证模式 + +OAuth2 **Device Authorization Grant** 认证模式,是在 OIDC 协议支持的模式中,专有的一类 Device Flow 设备模式,允许各类终端或硬件完成认证登录流程。 + +**Device Authorization Grant** (设备授权授予)或 **Device Flow** 对于处理,例如:智能电视、IoT 设备或打印机等,输入受限终端或硬件的身份验证和授权非常有用。由于终端的显示模式可能受限,无法内置登录页面。通过 Device Flow 提供的超链接或者生成二维码,设备会让用户在另一台设备上的浏览器中访问一个网页,以进行登录。用户登录后,设备可以获取所需的访问令牌和刷新令牌。 + +## \[3\] Dante Cloud 3.1.X 前端新特性 + +- 未使用任何流行开源模版,使用全新技术栈,完全纯"手写"全新前端工程。 +- 借鉴参考流行开源版本的使用和设计,新版前端界面风格和操作习惯尽量与当前流行方式统一。 +- 充份使用 Typescript 语言特性,解决大量类型校验问题,尽可能规避 "any" 式的 Typescript 编程语言使用方式。 +- 充份使用 Composition Api 和 Hooks 等 Vue3 框架新版特性进行代码编写。 +- 充份利用 Component、Hooks 以及 Typescript 面向对象等特性,抽取通用组件和代码,尽可能降低工程重复代码。 +- 对较多 Quasar 基础组件和应用功能组件进行封装,以方便代码的统一修改维护和开发使用。 +- 对生产模式下,对基于 Vite3 的工程打包进行深度性能优化。 +- 提供以 docker-compose 方式,对工程生产代码进行容器化打包和部署。 +- 该版本基于 pnpm,采用 monorepo 模式对前端工程进行重构。构建 monorepo 版本前端,是为扩展更多功能、增加应用级功能做铺垫 +- 抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块。 +- 共享模块已进行优化配置,可编译成独立的组件,单独以组件形式进行发布。 +- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用 + +--- + +**欢迎 Star 一波来支持我们!** + +**Gitee**:https://gitee.com/dromara/dante-cloud**Github**:https://github.com/dromara/dante-cloud + + +
+ diff --git a/src/zh/news/Dante-Cloud-3.2.0.0.md b/src/zh/news/Dante-Cloud-3.2.0.0.md index 044a5a0928..018c21a309 100644 --- a/src/zh/news/Dante-Cloud-3.2.0.0.md +++ b/src/zh/news/Dante-Cloud-3.2.0.0.md @@ -1,225 +1,225 @@ ---- -title: Dante Cloud 3.2.0.0 发布,首个适配 Spring Boot 3.2版本及经验分享 -author: dnate cloud -tag: - - Dante-Cloud -date: 2023-12-08 -cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png -head: - - - meta - - name: 新闻 ---- - -**Dante Cloud** (但丁,原 Eurynome Cloud) 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型 (DDD) 设计思想的、全面拥抱 **Spring Authorization Server** 的、基于 **OAuth2.1** 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 **Spring Authorization Server** 1.2.0、**Spring Boot** 3.2.0、**Spring Cloud** 2023.0.0、**Spring Cloud Tencent** 1.12.4-2022.0.4、**Spring Cloud Alibaba** 2022.0.0.0、**Nacos** 2.3.0 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能。 - -**定位** - -- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 -- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 -- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 -- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 - -## \[1\] 软件信息 - -- 软件组成 - -- 核心组件:https://gitee.com/herodotus/dante-engine(已上传中央库) -- 后端工程:https://gitee.com/dromara/dante-cloud -- 前端工程:https://gitee.com/herodotus/dante-cloud-ui - -- 软件生态 - -- Dante Cloud Athena(Dante Cloud 单体版):https://gitee.com/herodotus/dante-cloud-athena -- Dante OSS (像 JPA 一样操作 OSS):https://gitee.com/herodotus/dante-oss - -- 软件文档 - -- 《Dante Cloud 及相关知识学习方法和学习路径的建议》 -- 《OAuth 2 中的 Scope 与 Role 深度解析》 -- 《Spring Boot 3 之自动配置与注入顺序控制》 -- 《Spring Cloud 之 Session 共享及一致性处理》 -- 《OAuth 2 中的鉴权和动态接口鉴权》 -- 更多详情参见:https://www.herodotus.cn/cookbook/ -- 官方文档:https://www.herodotus.cn -- 技术手册: - -## \[2\] 经验分享 - -因 "虚拟线程" 这个新特性,而让 Java 21 备受关注。Spring Boot 3.2.0 作为 2023 年年终的大版本,对虚拟线程已经做到完全支持了,而让人分外期待。Dante Cloud 作为新技术和新特性的拥趸,也在第一时间对 Spring Boot 3.2.0 和 Spring Cloud 2023.0.0 等组件进行适配和尝鲜。 - -相比适配 Spring Boot 3.0 来说,适配 Spring Boot 3.2 会遇到的问题已经少了很多,但也不是一帆风顺。现将适配过程中遇到的问题,分享出来,希望可以帮助感兴趣的朋友尽量避坑。 - -### 组件版本 - -- Spring Boot 3.2.0 -- Spring Cloud 2023.0.0 -- Spring Authrozation Server 1.2.0 -- Spring Cloud Tencent 1.12.4-2022.0.4 -- Spring Cloud Alibaba 2022.0.0.0 -- Nacos 2.3.0 - -### 问题解决 - -1. Spring Boot 和 Spring Cloud - -微服务架构想要适配 Spring Boot 3.2 一定要注意版本对应。Spring Boot 3.2 已经绑定 Spring Cloud 版本为 2023.0.0,低于该版本代码无法运行。 - -2. Spring Cloud Tencent - -适配 Spring Boot 3.2 后,在使用 Spring Cloud Tencent 时,目前发现两个问题: - -- Netty 最新的 4.1.101.Final 版本调用了一个 Grpc 组件不支持的方法,会导致 Spring Cloud Tencent 无法连接到 Server,运行时出错。这个问题本身并不是“适配” 问题,而是 Spring Boot 2.7.18、3.1.6 和 3.2.0 都依赖了最新版本的 Netty,升级就会出错。 -- 因为 Spring Bean 和 Feign 代码存在变化,而 Spring Cloud Tencent 采用 uber.jar 打包方式,导致依赖当前版本会出现代码冲突。 - -解决办法: - -- 将 Netty 版本降至 4.1.100.Final,可解决无法 GRPC 无法连接 Server 问题。 -- 去除对包 `spring-cloud-starter-tencent-all` 和 `spring-cloud-starter-tencent-polaris-circuitbreaker` 的依赖,即可运行但部分功能会不可用 - -> 相关问题已经提交至 Spring Cloud Tencent 社区。https://github.com/Tencent/spring-cloud-tencent/issues/1210。该问题已经修复,grpc-java 也已经发布新版本。需要等待 Spring Cloud Tencent 新版本发布 - -3. Spring Cloud Alibaba - -因为 Feign 底层核心接口存在变化,所以原来可以使用的 Sentinel 全局 Fallback 代码无法使用。而且当前环境,依赖 Sentinel 相关组件,会出现自定义 Feign Contract 配置失效,导致使用 SpringMVC 注解的或者自定义注解的 Feign 代码抛错。 - -目前的临时解决办法就是去除 Sentinel 所有的依赖包。 - -4. Spring Authrozation Server - -Spring Authrozation Server 1.2.0 并没有太多的变化。但其中影响最大的变化,就是 issuer 地址不再允许包含 ip 和 port 以外的路径。如果包含了路径,启动时将会报错。https://github.com/spring-projects/spring-authorization-server/issues/1435 。目前临时的解决办法就手动修改代码,将限制取消。 - -### 额外说明 - -1. Dante Cloud 3.2.0.0 虽然已经适配 Spring Boot 3.2 和 Spring Cloud 2023.0.0,但是因为部分功能并不可用,所以不建议用于生产。 -2. **虽说是“适配”,但是核心目的是对升级 Spring Boot 3.2 和 Spring Cloud 2023.0.0 的工作量和会遇到的问题做评估,有了合理的评估才好决定是否要升级。所以望理解** -3. 虽然前面的内容说的都是“问题”,但这并不是说组件本身有问题,而是放到当前 Dante Cloud 的环境中以及使用未进行版本适配遇到的小插曲,而且和具体自己代码的实现思路和逻辑有很大关系,不同的系统代码遇到问题也未必相同。 - -最后,还是建议等相关组件完成代码适配后再进行尝试。Spring Cloud Tencent 社区已经在进行响应,等 1.13 版本发布后就会开展 Spring Cloud 2023.0.0 版本的适配工作。https://github.com/Tencent/spring-cloud-tencent/issues/1209 - -## \[3\] 本次更新内容 - -- 【主要更新】 - -- \[升级\] Spring Boot 版本升级至 3.2.0 -- \[升级\] Spring Cloud 版本升级至 2023.0.0 -- \[升级\] Spring Authorization Server 版本升级至 1.2.0 -- \[升级\] Spring Boot Admin 版本升级至 3.1.8 -- \[升级\] Nacos 版本升级至 2.3.0 -- \[升级\] Nacos Docker 镜像 版本升级至 2.3.0 -- \[优化\] jetcache 的问题修复及优化。(PR by Kaiser_Li) - -1. 优化计数缓存签章,增加 maxTimes 作为默认值,简化了 counting 方法,一般情况下只需调用 counting(key)即可 -2. 修复 AbstractCountStampManager 中 counting(String identity, int maxTimes)调用报错的问题 -3. 优化 AbstractCountStampManager 中对次数的判断,大于 maxTimes 时都返回错误 - -- 【其它更新】 - -- \[新增\] 新增 Spring Cloud Tencent 读取和使用本地缓存统一化配置。 -- \[新增\] Spring Cloud Tencent Polaris 配置导入包,方便环境搭建和配置 -- \[新增\] 新增 caffeine、jetcache、redis 缓存组件使用详细说明,文档路径:dante-engine/readme/plugins/cache/ 以及 各模块 Readme 说明(PR by Kaiser_Li) -- \[重构\] 重构 RestTemplate 和 OpenFeign 底层 Engine 及负载均衡统一化配置。去除 Spring Boot 3.2 不再支持的 OkHttp3ClientHttpRequestFactory 相关配置,增加基于 JdkClient 的 RestTemplate 和 OpenFeign 统一配置。fix: #I8JNOK -- \[重构\] 重构相关代码,适配 Spring Boot 3.2.0 fix: #I7W5C3 -- \[重构\] 重构相关代码,适配 Spring Cloud 2023.0.0-RC1 fix: #7W5C6 -- \[重构\] 重构 Spring Authorization Server 自定义 Provider 代码,适配最新的 Spring Authorization Server 1.2.0 版本。fix: #I7W5BY -- \[重构\] 重构 Spring Authorization Server 配置代码,去除过时方法,适配最新代码。 -- \[重构\] 重构系统静态权限配置核心代码,统一配置信息出入口,规范调用 API 名称及使用方式。一次性构建解析列表,减少冗余的循环和临时创建 fix: #I8KL29 -- \[重构\] 重构自定义 ApplicationEvent 命名及使用方式。 -- \[修复\] 修复 Emqx 监控数据转 Influxdb2 的 Spring Integration 流程注入配置条件错误。 -- \[修复\] 修复 docker-compose 文件中,polaris 镜像名称不正确问题。 -- \[修复\] 调整 polaris docker-compose 默认端口,适配最新版本 Polarismesh Server。 -- \[修复\] 前端工程适配 Vite 5.0.0,修复 monorepo 模块编译时出现 “The CJS build of Vite's Node API is deprecated” 问题 fix: #I8HLU0 -- \[修复\] 清除 Docker Profile 环境下原有的 Native 配置,解决在 Docker Profile 环境下编译错误问题。fix: #I8ICSZ -- \[优化\] 调整 Polaris 本地配置缓存目录,防止与新增配置导入包冲突和混淆 -- \[优化\] 优化各个服务中,Spring Cloud Tencent 相关配置,去除无用的或者与默认参数相同的配置。 -- \[优化\] 临时解决 Spring Authorization Server 1.2.0 不兼容问题,后续根据实际情况进行完善和修改。https://github.com/spring-projects/spring-authorization-server/issues/1435 -- \[优化\] 删除 dependencies 中重复的或无用的版本控制配置,统一使用 Spring Boot Dependencies 控制依赖版本 -- \[优化\] 前端工程支持 ES 模块代码的编译生成,以及 ES 模块的加载。fix: #I8HLVI -- \[优化\] 去除所有 Native 相关 pom 配置,待 Spring Boot 后续版本统一进行 Native 处理。 -- \[优化\] 优化 message-sdk-mqtt 模块代码,明确入站、出站以及通道相关代码。增加系统统一通道定义类,便于后续其它模块集成使用。fix: #I8IPWG -- \[适配\] 适配 Spring Cloud Alibaba 生态组件。临时去除 Sentinel 相关组件依赖和代码,解决在 Spring Cloud 2023.0.0 环境下,依赖 Sentinel 会引起 Feign 契约配置失效而导致的服务无法启动问题。 -- \[适配\] 代码适配 Hutool 6.0.0-M8 -- \[适配\] Apache Maven 适配至 3.9.6 -- \[升级\] minio docker 镜像版本升级至 RELEASE.2023-12-06T09-09-22Z - -- 【依赖更新】 - -- \[升级\] aws-java-sdk-s3 版本升级至 1.12.606 -- \[升级\] alipay-sdk-java 版本升级至 4.38.149.ALL -- \[升级\] bcprov-jdk15to18 版本升级至 1.77 -- \[升级\] bcprov-jdk18on 版本升级至 1.77 -- \[升级\] wxjava 版本升级至 4.5.7.B -- \[升级\] com.baidu.aip 版本升级至 4.16.17 -- \[升级\] minio 版本升级至 8.5.7 -- \[升级\] hutool 版本升级至 6.0.0-M8 -- \[升级\] mybatis-plus 版本升级至 3.5.4.1 -- \[升级\] mybatis 版本升级至 3.5.14 -- \[升级\] sqlite-jdbc 版本升级至 3.44.1.0 -- \[升级\] springdoc 版本升级至 2.3.0 -- \[升级\] transmittable-thread-local 版本升级至 2.14.4 -- \[升级\] fastjson2 版本升级至 2.0.43 -- \[升级\] commons-io 版本升级至 2.15.1 -- \[升级\] JustAuth 版本升级至 1.16.6 -- \[升级\] quasar webjars 版本升级至 2.14.0 -- \[升级\] vue webjars 版本升级至 3.3.9 -- \[升级\] redisson 版本升级至 3.25.0 -- \[升级\] influxdb-client 版本升级至 6.11.0 - -## \[4\] Dante Cloud 特性 - -### 1\. 核心基础依赖便捷切换 - -- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 -- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 - -### 2\. `Spring Authorization Server` 全特性支持及扩展 - -- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 -- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 -- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 -- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 -- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 -- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 -- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,降低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 -- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 -- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 -- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 -- 新增基于 `Spring Authorization Server` 的、支持智能电视、IoT 等物联网设备认证模式 -- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 -- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 - -### 3\. 全体系化应用和开发特性集成 - -- 微服务架构全体系 Session 共享,实现 Spring Authorization Server、多实例服务、WebSocket、自定义 Session 以及大前端 Session 的统一。`微服务架构下的 Session 可以选择不用,但是不能没有`。 -- 混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用“一人一码机制”,实现前后端数据进行动态加密传输与。Spring Authorization Server OAuth 2.1 授权模式深度融合,构建统一体系的数据传输加密。 -- 全面整合 `@PreAuthorize` 注解权限与 `URL` 权限,通过后端动态配置,无须在代码中配置 `Spring Security` 权限注解以及权限方法,可实现接口鉴权以及权限的统一管理和动态修改 -- 融合 Spring Cloud Stream 和 WebSocket,以优雅的方式实现 WebSocket 服务多实例环境下,点对点、广播消息跨实例推送,在线用户实时统计,完美支持 WebSocket 集群化应用。 -- 借鉴 JPA 标准化设计思想,提取和抽象 OSS 标准化操作,形成统一的 Java OSS API 规范。封装可操作任意厂商的、统一的 REST API,构建定义统一、动态实现的应用模式(类似于 Hibernate 是 JPA 的一种实现,Hibernate 以 Dialect 方式支持不同的数据库一样),在不修改代码的情况下通过修改配置实现 OSS 的无缝切换和迁移 -- 自研基于 `JetCache` 分布式两级缓存,完美实现 JPA Hibernate 二级缓存,支持各类查询数据缓存以及 JPA `@ManyToMany`, `@ManyToOne`等关联查询。完美解决 Spring Cache 仅使用本地缓存、创建 Key 繁琐和分页数据无法更新的问题。支持多实例服务本地缓存和远程缓存数据同步,同时支持 Mybatis Plus 二级缓存 -- 平台统一错误处理,支持自定义错误码体系,有效集成 `OAuth2`、`Spring Validation` 等多方错误体系并有机整合 HTTP 状态码。采用 Customizer 模式,采用错误码自动计算和创建模式,支持代码模块级错误码灵活定义扩展。响应结果更加多样灵活,反馈结果也更加人性化,便于理解和定位问题。 -- 全体系 OkHttp 、HttpClient 统一化集成,实现 OkHttp 、HttpClient 与 RestTemplate 、Openfeign 一体化融合。统一使用 Feign 配置参数,对 OkHttp 、HttpClient 进行参数设定,可策略化选择设置使用 OkHttp 或 HttpClient 作为 RestTemplate 、Openfeign 统一的基础 HttpClient - -### 4\. 采用 `pnpm monorepo` 重构前端 - -- 未使用任何流行开源模版,使用全新技术栈,完全纯"手写"全新前端工程。 -- 借鉴参考流行开源版本的使用和设计,新版前端界面风格和操作习惯尽量与当前流行方式统一。 -- 充份使用 Typescript 语言特性,解决大量类型校验问题,尽可能规避 "any" 式的 Typescript 编程语言使用方式。 -- 充份使用 Composition Api 和 Hooks 等 Vue3 框架新版特性进行代码编写。 -- 充份利用 Component、Hooks 以及 Typescript 面向对象等特性,抽取通用组件和代码,尽可能降低工程重复代码。 -- 对较多 Quasar 基础组件和应用功能组件进行封装,以方便代码的统一修改维护和开发使用。 -- 对生产模式下,对基于 Vite3 的工程打包进行深度性能优化。 -- 提供以 docker-compose 方式,对工程生产代码进行容器化打包和部署。 -- 该版本基于 pnpm,采用 monorepo 模式对前端工程进行重构。构建 monorepo 版本前端,是为扩展更多功能、增加应用级功能做铺垫 -- 抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块。 -- 共享模块已进行优化配置,可编译成独立的组件,单独以组件形式进行发布。 -- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用。 -- **使用极少的 CSS 样式、高度的模块化构建,对后端人员开发前端非常友好。** - ---- - -**欢迎 Star 一波来支持我们!** - -**Gitee**:https://gitee.com/dromara/dante-cloud -**Github**:https://github.com/dromara/dante-cloud - - +--- +title: Dante Cloud 3.2.0.0 发布,首个适配 Spring Boot 3.2版本及经验分享 +author: dnate cloud +tag: + - Dante-Cloud +date: 2023-12-08 +cover: /assets/img/news/Dante-Cloud-3.1.3.0-cover.png +head: + - - meta + - name: 新闻 +--- + +**Dante Cloud** (但丁,原 Eurynome Cloud) 是一款企业级微服务架构和服务能力开发平台,是采用领域驱动模型 (DDD) 设计思想的、全面拥抱 **Spring Authorization Server** 的、基于 **OAuth2.1** 协议的、支持智能电视、IoT 等物联网设备认证的微服务架构。基于 **Spring Authorization Server** 1.2.0、**Spring Boot** 3.2.0、**Spring Cloud** 2023.0.0、**Spring Cloud Tencent** 1.12.4-2022.0.4、**Spring Cloud Alibaba** 2022.0.0.0、**Nacos** 2.3.0 等主流技术栈开发的多租户系统,遵循 SpringBoot 编程思想,高度模块化和可配置化。具备服务发现、配置、熔断、限流、降级、监控、多级缓存、分布式事务、工作流等功能。 + +**定位** + +- 构建成熟的、完善的、全面的,基于 OAuth2.1 的、前后端分离的微服务架构解决方案。 +- 面向企业级应用和互联网应用设计开发,既兼顾传统项目的微服务化,又满足互联网应用开发建设、快速迭代的使用需求。 +- 平台架构使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,是帮助快速跨越架构技术选型、研究探索阶段的利器。 +- 代码简洁规范、结构合理清晰,是新技术开发应用的典型的、综合性案例,助力开发人员对新兴技术的学习和掌握。 + +## \[1\] 软件信息 + +- 软件组成 + +- 核心组件:https://gitee.com/herodotus/dante-engine(已上传中央库) +- 后端工程:https://gitee.com/dromara/dante-cloud +- 前端工程:https://gitee.com/herodotus/dante-cloud-ui + +- 软件生态 + +- Dante Cloud Athena(Dante Cloud 单体版):https://gitee.com/herodotus/dante-cloud-athena +- Dante OSS (像 JPA 一样操作 OSS):https://gitee.com/herodotus/dante-oss + +- 软件文档 + +- 《Dante Cloud 及相关知识学习方法和学习路径的建议》 +- 《OAuth 2 中的 Scope 与 Role 深度解析》 +- 《Spring Boot 3 之自动配置与注入顺序控制》 +- 《Spring Cloud 之 Session 共享及一致性处理》 +- 《OAuth 2 中的鉴权和动态接口鉴权》 +- 更多详情参见:https://www.herodotus.cn/cookbook/ +- 官方文档:https://www.herodotus.cn +- 技术手册: + +## \[2\] 经验分享 + +因 "虚拟线程" 这个新特性,而让 Java 21 备受关注。Spring Boot 3.2.0 作为 2023 年年终的大版本,对虚拟线程已经做到完全支持了,而让人分外期待。Dante Cloud 作为新技术和新特性的拥趸,也在第一时间对 Spring Boot 3.2.0 和 Spring Cloud 2023.0.0 等组件进行适配和尝鲜。 + +相比适配 Spring Boot 3.0 来说,适配 Spring Boot 3.2 会遇到的问题已经少了很多,但也不是一帆风顺。现将适配过程中遇到的问题,分享出来,希望可以帮助感兴趣的朋友尽量避坑。 + +### 组件版本 + +- Spring Boot 3.2.0 +- Spring Cloud 2023.0.0 +- Spring Authrozation Server 1.2.0 +- Spring Cloud Tencent 1.12.4-2022.0.4 +- Spring Cloud Alibaba 2022.0.0.0 +- Nacos 2.3.0 + +### 问题解决 + +1. Spring Boot 和 Spring Cloud + +微服务架构想要适配 Spring Boot 3.2 一定要注意版本对应。Spring Boot 3.2 已经绑定 Spring Cloud 版本为 2023.0.0,低于该版本代码无法运行。 + +2. Spring Cloud Tencent + +适配 Spring Boot 3.2 后,在使用 Spring Cloud Tencent 时,目前发现两个问题: + +- Netty 最新的 4.1.101.Final 版本调用了一个 Grpc 组件不支持的方法,会导致 Spring Cloud Tencent 无法连接到 Server,运行时出错。这个问题本身并不是“适配” 问题,而是 Spring Boot 2.7.18、3.1.6 和 3.2.0 都依赖了最新版本的 Netty,升级就会出错。 +- 因为 Spring Bean 和 Feign 代码存在变化,而 Spring Cloud Tencent 采用 uber.jar 打包方式,导致依赖当前版本会出现代码冲突。 + +解决办法: + +- 将 Netty 版本降至 4.1.100.Final,可解决无法 GRPC 无法连接 Server 问题。 +- 去除对包 `spring-cloud-starter-tencent-all` 和 `spring-cloud-starter-tencent-polaris-circuitbreaker` 的依赖,即可运行但部分功能会不可用 + +> 相关问题已经提交至 Spring Cloud Tencent 社区。https://github.com/Tencent/spring-cloud-tencent/issues/1210。该问题已经修复,grpc-java 也已经发布新版本。需要等待 Spring Cloud Tencent 新版本发布 + +3. Spring Cloud Alibaba + +因为 Feign 底层核心接口存在变化,所以原来可以使用的 Sentinel 全局 Fallback 代码无法使用。而且当前环境,依赖 Sentinel 相关组件,会出现自定义 Feign Contract 配置失效,导致使用 SpringMVC 注解的或者自定义注解的 Feign 代码抛错。 + +目前的临时解决办法就是去除 Sentinel 所有的依赖包。 + +4. Spring Authrozation Server + +Spring Authrozation Server 1.2.0 并没有太多的变化。但其中影响最大的变化,就是 issuer 地址不再允许包含 ip 和 port 以外的路径。如果包含了路径,启动时将会报错。https://github.com/spring-projects/spring-authorization-server/issues/1435 。目前临时的解决办法就手动修改代码,将限制取消。 + +### 额外说明 + +1. Dante Cloud 3.2.0.0 虽然已经适配 Spring Boot 3.2 和 Spring Cloud 2023.0.0,但是因为部分功能并不可用,所以不建议用于生产。 +2. **虽说是“适配”,但是核心目的是对升级 Spring Boot 3.2 和 Spring Cloud 2023.0.0 的工作量和会遇到的问题做评估,有了合理的评估才好决定是否要升级。所以望理解** +3. 虽然前面的内容说的都是“问题”,但这并不是说组件本身有问题,而是放到当前 Dante Cloud 的环境中以及使用未进行版本适配遇到的小插曲,而且和具体自己代码的实现思路和逻辑有很大关系,不同的系统代码遇到问题也未必相同。 + +最后,还是建议等相关组件完成代码适配后再进行尝试。Spring Cloud Tencent 社区已经在进行响应,等 1.13 版本发布后就会开展 Spring Cloud 2023.0.0 版本的适配工作。https://github.com/Tencent/spring-cloud-tencent/issues/1209 + +## \[3\] 本次更新内容 + +- 【主要更新】 + +- \[升级\] Spring Boot 版本升级至 3.2.0 +- \[升级\] Spring Cloud 版本升级至 2023.0.0 +- \[升级\] Spring Authorization Server 版本升级至 1.2.0 +- \[升级\] Spring Boot Admin 版本升级至 3.1.8 +- \[升级\] Nacos 版本升级至 2.3.0 +- \[升级\] Nacos Docker 镜像 版本升级至 2.3.0 +- \[优化\] jetcache 的问题修复及优化。(PR by Kaiser_Li) + +1. 优化计数缓存签章,增加 maxTimes 作为默认值,简化了 counting 方法,一般情况下只需调用 counting(key)即可 +2. 修复 AbstractCountStampManager 中 counting(String identity, int maxTimes)调用报错的问题 +3. 优化 AbstractCountStampManager 中对次数的判断,大于 maxTimes 时都返回错误 + +- 【其它更新】 + +- \[新增\] 新增 Spring Cloud Tencent 读取和使用本地缓存统一化配置。 +- \[新增\] Spring Cloud Tencent Polaris 配置导入包,方便环境搭建和配置 +- \[新增\] 新增 caffeine、jetcache、redis 缓存组件使用详细说明,文档路径:dante-engine/readme/plugins/cache/ 以及 各模块 Readme 说明(PR by Kaiser_Li) +- \[重构\] 重构 RestTemplate 和 OpenFeign 底层 Engine 及负载均衡统一化配置。去除 Spring Boot 3.2 不再支持的 OkHttp3ClientHttpRequestFactory 相关配置,增加基于 JdkClient 的 RestTemplate 和 OpenFeign 统一配置。fix: #I8JNOK +- \[重构\] 重构相关代码,适配 Spring Boot 3.2.0 fix: #I7W5C3 +- \[重构\] 重构相关代码,适配 Spring Cloud 2023.0.0-RC1 fix: #7W5C6 +- \[重构\] 重构 Spring Authorization Server 自定义 Provider 代码,适配最新的 Spring Authorization Server 1.2.0 版本。fix: #I7W5BY +- \[重构\] 重构 Spring Authorization Server 配置代码,去除过时方法,适配最新代码。 +- \[重构\] 重构系统静态权限配置核心代码,统一配置信息出入口,规范调用 API 名称及使用方式。一次性构建解析列表,减少冗余的循环和临时创建 fix: #I8KL29 +- \[重构\] 重构自定义 ApplicationEvent 命名及使用方式。 +- \[修复\] 修复 Emqx 监控数据转 Influxdb2 的 Spring Integration 流程注入配置条件错误。 +- \[修复\] 修复 docker-compose 文件中,polaris 镜像名称不正确问题。 +- \[修复\] 调整 polaris docker-compose 默认端口,适配最新版本 Polarismesh Server。 +- \[修复\] 前端工程适配 Vite 5.0.0,修复 monorepo 模块编译时出现 “The CJS build of Vite's Node API is deprecated” 问题 fix: #I8HLU0 +- \[修复\] 清除 Docker Profile 环境下原有的 Native 配置,解决在 Docker Profile 环境下编译错误问题。fix: #I8ICSZ +- \[优化\] 调整 Polaris 本地配置缓存目录,防止与新增配置导入包冲突和混淆 +- \[优化\] 优化各个服务中,Spring Cloud Tencent 相关配置,去除无用的或者与默认参数相同的配置。 +- \[优化\] 临时解决 Spring Authorization Server 1.2.0 不兼容问题,后续根据实际情况进行完善和修改。https://github.com/spring-projects/spring-authorization-server/issues/1435 +- \[优化\] 删除 dependencies 中重复的或无用的版本控制配置,统一使用 Spring Boot Dependencies 控制依赖版本 +- \[优化\] 前端工程支持 ES 模块代码的编译生成,以及 ES 模块的加载。fix: #I8HLVI +- \[优化\] 去除所有 Native 相关 pom 配置,待 Spring Boot 后续版本统一进行 Native 处理。 +- \[优化\] 优化 message-sdk-mqtt 模块代码,明确入站、出站以及通道相关代码。增加系统统一通道定义类,便于后续其它模块集成使用。fix: #I8IPWG +- \[适配\] 适配 Spring Cloud Alibaba 生态组件。临时去除 Sentinel 相关组件依赖和代码,解决在 Spring Cloud 2023.0.0 环境下,依赖 Sentinel 会引起 Feign 契约配置失效而导致的服务无法启动问题。 +- \[适配\] 代码适配 Hutool 6.0.0-M8 +- \[适配\] Apache Maven 适配至 3.9.6 +- \[升级\] minio docker 镜像版本升级至 RELEASE.2023-12-06T09-09-22Z + +- 【依赖更新】 + +- \[升级\] aws-java-sdk-s3 版本升级至 1.12.606 +- \[升级\] alipay-sdk-java 版本升级至 4.38.149.ALL +- \[升级\] bcprov-jdk15to18 版本升级至 1.77 +- \[升级\] bcprov-jdk18on 版本升级至 1.77 +- \[升级\] wxjava 版本升级至 4.5.7.B +- \[升级\] com.baidu.aip 版本升级至 4.16.17 +- \[升级\] minio 版本升级至 8.5.7 +- \[升级\] hutool 版本升级至 6.0.0-M8 +- \[升级\] mybatis-plus 版本升级至 3.5.4.1 +- \[升级\] mybatis 版本升级至 3.5.14 +- \[升级\] sqlite-jdbc 版本升级至 3.44.1.0 +- \[升级\] springdoc 版本升级至 2.3.0 +- \[升级\] transmittable-thread-local 版本升级至 2.14.4 +- \[升级\] fastjson2 版本升级至 2.0.43 +- \[升级\] commons-io 版本升级至 2.15.1 +- \[升级\] JustAuth 版本升级至 1.16.6 +- \[升级\] quasar webjars 版本升级至 2.14.0 +- \[升级\] vue webjars 版本升级至 3.3.9 +- \[升级\] redisson 版本升级至 3.25.0 +- \[升级\] influxdb-client 版本升级至 6.11.0 + +## \[4\] Dante Cloud 特性 + +### 1\. 核心基础依赖便捷切换 + +- 新增 `Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶等两种基础设施支持。 +- 新增 `Spring Cloud Alibaba`、`Spring Cloud Tencent` 和 `Spring Cloud` 原生微服务全家桶三种基础设值切换能力,可以以相对便捷的方式切换使用 Alibaba、Tencent、Spring 等基础设施环境。可根据自身实际需求选择,不再局限于只能在某一种基础设施环境中运行。 + +### 2\. `Spring Authorization Server` 全特性支持及扩展 + +- 基于 `Spring Authorization Server` 和 `Spring Data JPA` 实现多租户系统架构, 支持 Database 和 Schema 两种模式。 +- 基于 `Spring Data JPA`,重新构建 `Spring Authorization Server` 基础数据存储代码,替代原有 JDBC 数据访问方式,破除 `Spring Authorization Server` 原有数据存储局限,扩展为更符合实际应用的方式和设计。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Resource Ownership Password` (密码) 认证模式,以兼容现有基于 OAuth 2 规范的、前后端分离的应用,支持 `Refresh Token` 的使用。 +- 基于 `Spring Authorization Server`,在 OAuth 2.1 规范基础之上,增加自定义 `Social Credentials` (社会化登录) 认证模式,支持手机短信验证码、微信小程序、基于 `JustAuth` 的第三方应用登录, 支持 `Refresh Token` 的使用。 +- 扩展 `Spring Authorization Server` 默认的 `Client Credentials` 模式,实现真正的使用 Scope 权限对接口进行验证。增加客户端 Scope 的权限配置功能,并与已有的用户权限体系解耦 +- 支持 `Spring Authorization Server` `Authorization Code PKCE` 认证模式 +- 在 `Spring Authorization Server` 的标准的 `JWT Token` 加密校验方式外,支持基于自定义证书的 `JWT Token` 加密校验方式,可通过配置动态修改。 +- 支持 `Opaque Token` (不透明令牌) 格式及校验方式,降低 `JWT Token` 被捕获解析的风险。可通过修改配置参数,设置默认 Token 格式是采用 `Opaque Token` 格式还是 `JWT Token` 格式。 +- 全面支持 `OpenID Connect` (OIDC) 协议,系统使用时可根据使用需求,通过前端开关配置,快速切换 OIDC 模式和传统 OAuth2 模式 +- 深度扩展 `Authorization Code`、`Resource Ownership Password`、`Social Credentials` 几种模式,全面融合 `IdToken`、`Opaque Token`、`JWT Token` 与现有权限体系,同时提供 `IdToken` 和 自定义 Token 扩展两种无须二次请求的用户信息传递方式,减少用户信息的频繁请求。 +- 自定义 `Spring Authorization Server` 授权码模式登录认证页面和授权确认页面,授权码模式登录采用数据加密传输。支持多种验证码类型,暂不支持行为验证码。 +- 新增基于 `Spring Authorization Server` 的、支持智能电视、IoT 等物联网设备认证模式 +- 无须在代码中配置 `Spring Security` 权限注解以及权限方法,即可实现接口鉴权以及权限的动态修改。采用分布式鉴权方案,规避 Gateway 统一鉴权的压力以及重复鉴权问题 +- OAuth2 UserDetails 核心数据支持直连数据库获取和 Feign 远程调用两种模式。OAuth2 直连数据库模式性能更优,Feign 访问远程调用可扩展性更强。可通过配置动态修改采用策略方式。 + +### 3\. 全体系化应用和开发特性集成 + +- 微服务架构全体系 Session 共享,实现 Spring Authorization Server、多实例服务、WebSocket、自定义 Session 以及大前端 Session 的统一。`微服务架构下的 Session 可以选择不用,但是不能没有`。 +- 混合国密 `SM2` (非对称) 和 `SM4` (对称加密) 算法,实现基于数字信封技术的秘钥动态生成加密传输。利用“一人一码机制”,实现前后端数据进行动态加密传输与。Spring Authorization Server OAuth 2.1 授权模式深度融合,构建统一体系的数据传输加密。 +- 全面整合 `@PreAuthorize` 注解权限与 `URL` 权限,通过后端动态配置,无须在代码中配置 `Spring Security` 权限注解以及权限方法,可实现接口鉴权以及权限的统一管理和动态修改 +- 融合 Spring Cloud Stream 和 WebSocket,以优雅的方式实现 WebSocket 服务多实例环境下,点对点、广播消息跨实例推送,在线用户实时统计,完美支持 WebSocket 集群化应用。 +- 借鉴 JPA 标准化设计思想,提取和抽象 OSS 标准化操作,形成统一的 Java OSS API 规范。封装可操作任意厂商的、统一的 REST API,构建定义统一、动态实现的应用模式(类似于 Hibernate 是 JPA 的一种实现,Hibernate 以 Dialect 方式支持不同的数据库一样),在不修改代码的情况下通过修改配置实现 OSS 的无缝切换和迁移 +- 自研基于 `JetCache` 分布式两级缓存,完美实现 JPA Hibernate 二级缓存,支持各类查询数据缓存以及 JPA `@ManyToMany`, `@ManyToOne`等关联查询。完美解决 Spring Cache 仅使用本地缓存、创建 Key 繁琐和分页数据无法更新的问题。支持多实例服务本地缓存和远程缓存数据同步,同时支持 Mybatis Plus 二级缓存 +- 平台统一错误处理,支持自定义错误码体系,有效集成 `OAuth2`、`Spring Validation` 等多方错误体系并有机整合 HTTP 状态码。采用 Customizer 模式,采用错误码自动计算和创建模式,支持代码模块级错误码灵活定义扩展。响应结果更加多样灵活,反馈结果也更加人性化,便于理解和定位问题。 +- 全体系 OkHttp 、HttpClient 统一化集成,实现 OkHttp 、HttpClient 与 RestTemplate 、Openfeign 一体化融合。统一使用 Feign 配置参数,对 OkHttp 、HttpClient 进行参数设定,可策略化选择设置使用 OkHttp 或 HttpClient 作为 RestTemplate 、Openfeign 统一的基础 HttpClient + +### 4\. 采用 `pnpm monorepo` 重构前端 + +- 未使用任何流行开源模版,使用全新技术栈,完全纯"手写"全新前端工程。 +- 借鉴参考流行开源版本的使用和设计,新版前端界面风格和操作习惯尽量与当前流行方式统一。 +- 充份使用 Typescript 语言特性,解决大量类型校验问题,尽可能规避 "any" 式的 Typescript 编程语言使用方式。 +- 充份使用 Composition Api 和 Hooks 等 Vue3 框架新版特性进行代码编写。 +- 充份利用 Component、Hooks 以及 Typescript 面向对象等特性,抽取通用组件和代码,尽可能降低工程重复代码。 +- 对较多 Quasar 基础组件和应用功能组件进行封装,以方便代码的统一修改维护和开发使用。 +- 对生产模式下,对基于 Vite3 的工程打包进行深度性能优化。 +- 提供以 docker-compose 方式,对工程生产代码进行容器化打包和部署。 +- 该版本基于 pnpm,采用 monorepo 模式对前端工程进行重构。构建 monorepo 版本前端,是为扩展更多功能、增加应用级功能做铺垫 +- 抽取 utils、components、apis、bpmn-designer 等相关代码,形成共享模块。 +- 共享模块已进行优化配置,可编译成独立的组件,单独以组件形式进行发布。 +- 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用。 +- **使用极少的 CSS 样式、高度的模块化构建,对后端人员开发前端非常友好。** + +--- + +**欢迎 Star 一波来支持我们!** + +**Gitee**:https://gitee.com/dromara/dante-cloud +**Github**:https://github.com/dromara/dante-cloud + + diff --git a/src/zh/news/Dante-Cloud-3.3.5.0.md b/src/zh/news/Dante-Cloud-3.3.5.0.md new file mode 100644 index 0000000000..f4749495d3 --- /dev/null +++ b/src/zh/news/Dante-Cloud-3.3.5.0.md @@ -0,0 +1,225 @@ +--- +title: Dante Cloud 3.3.5.0发布,国内首个支持阻塞式和响应式融合的微服务 +author: Dante Cloud +date: 2024-10-31 +cover: /assets/img/news/Dante-Cloud-3.3.5.0-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/Dante-Cloud-3.3.5.0-0.png) + +**Dante Cloud** 是国内首个支持阻塞式和响应式融合的微服务。以「**高质量代码、低安全漏洞**」为核心,**采用领域驱动模型(DDD)设计思想,完全基于 Spring 生态全域开源技术和 OAuth2.1 协议,支持智能电视、IoT等物联网设备认证**,满足**国家三级等保要求**、支持接口**国密数字信封加解密**、防刷、高防XSS和SQL注入等一系列安全体系的**多租户微服务解决方案**。 + +## \[一\] 发布背景 + +**Dante Cloud** 一直秉承着“简洁、高效、包容、务实”的理念,使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,不断地深耕细作、去粗取精、用心打造。目标是构建一款`代码质量高、维护投入低、安全防护强`的,可以帮助用户快速跨越架构技术选型、技术研究探索阶段,降低传统项目中因安全漏洞、技术负债、低质代码等潜在隐患所产生的高维护投入,期望像项目名字寓意一样,在行业变革的时期承上启下,助力企业信息化建设和变革的产品。 + +## \[二\] 更新内容 + +* **主要更新** + + +* \[升级\] Spring Boot 版本升级至 3.3.5 + +* \[升级\] Spring Authorization Server 版本升级至 1.3.3 + +* \[升级\] Spring Boot Admin 版本升级至 3.3.4 + +* \[升级\] Debezium 版本升级至 3.0 + +* \[升级\] Camunda 版本升级至 7.22.0 + +* \[升级\] Nacos 版本升级至 2.4.3 + +* \[重构\] 开源版本工程代码包名由 cn.herodotus 修改为 org.dromara,与社区项目保持一致 + +* \[重构\] 单体版系统合并至微服务版本工程中。可以在同一工程启动单体版本或者微服务版。解决原有模式下,需要单独编译微服务版,再在另一个工程中启动单体版。提升开发和使用的便捷性。 + +* \[新增\] 新增服务间文件上传和下载传输机制,支持 OpenFeign 和 Grpc 两种模式,通过热插拔模式切换。 + +* \[新增\] 新增 OSS 文件操作 GRPC 定义模块 + +* \[新增\] 新增服务本地常用文件管理机制 + +* \[新增\] 新增跨模块跨服务认证开启或关闭控制单元 + +* \[新增\] 新增客户端动态注册业务信息同步创建功能 + +* \[新增\] Mqtt 用户账号管理功能 + +* \[新增\] 新增 NoSQL 相关组件自动配置 Starter。 + +* \[新增\] 新增 Influxdb 列式存储和行式存储两种存储设备上报数据支持。 + + +* **其它更新** + + +* \[修复\] 重新构建支持 Postgresql 的 Nacos Server 镜像。修复创建命名空间失败问题。 + +* \[修复\] 修复数据加密策略配置未生效问题 + +* \[修复\] 修复 Kafka Docker Compose 配置错误,导致 Kafka 镜像启动抛错问题 + +* \[修复\] 修复前端 Vite CSS 样式配置不兼容,导致页面启动抛错问题。 + +* \[修复\] 修复微服务环境下,分布式事件使用错误导致字典聚合数据汇总异常问题 + +* \[修复\] 修复使用 AWS SDK V2 创建的预签名地址中,仍旧使用 AWS 默认服务地址不会定位至自定义主机问题 + +* \[修复\] 修复自动配置类引入日志标识 Class 错误问题。 + +* \[修复\] grpc 编译出现 error: emptyList() is not public in LazyStringArrayList; cannot be accessed from outside package com.google.protobuf.LazyStringArrayList.emptyList() 问题。fix: #IAWQ4C + +* \[修复\] 修复 Docker Compose 镜像地址配置错误问题。fix: #IAXUFB + +* \[修复\] 修复 Influxdb2 默认配置与系统提供 Docker Compose 默认配置不一致,导致 Influxdb 测试代码部分通过问题。 + +* \[修复\] 修复前端 `Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0` 告警错误 + +* \[重构\] 重构 AWS SDK V2 高阶 OSS 操作代码,增加高阶操作单元测试。 + +* \[重构\] 服务内文件基本操作变更为使用 NIO 操作 + +* \[重构\] Mybatis Plus 修改为 Bom 引入,同时适配最新版本 Mybatis Plus + +* \[重构\] 重构部分 OAuth2 核心代码,提升代码模块的内聚性降低代码耦合 + +* \[重构\] 重构物联网设备动态开启和关闭认证逻辑,简化和去除原有采用的多重事件跳转方式。 + +* \[重构\] 重构服务本地文件管理定义以及证书生成逻辑代码 + +* \[优化\] 优化 OSS 模型基础操作类命名,以便更容易的区分代码用途。 + +* \[优化\] 优化 OSS 模块代码,池化 S3Presigner 对象管理提升效能。 + +* \[优化\] 优化 OSS 模块代码,提取独立的预签名操作 Service + +* \[优化\] 对照阿里云物联网的使用完善产品和设备管理的接口实现逻辑。 + +* \[优化\] 优化 OIDC 客户端动态注册逻辑,更好的兼容物联网设备管理需求。 + +* \[优化\] 去除重复定义的 ApplicationEvent 消息通道定义,改用统一定义消息通道 + +* \[优化\] 优化 Emqx 客户端状态检测策略化配置方式 + +* \[优化\] 合并部分系统配置参数类定义,增强配置参数划分和归类的合理性 + +* \[优化\] 删除无用重复的常量定义 + +* \[优化\] 提取通用 Spring ParameterizedTypeReference 定义 + +* \[优化\] 自定义函数式接口 ListConverter 代码逻辑,去除 IDE 空值警告 + +* \[升级\] Liberica JDK 基础镜像版本分别升级至 17.0.13-12 和 21.0.5-11 + +* \[升级\] minio docker 镜像版本升级至 RELEASE.2024-10-13T13-34-11Z + +* \[升级\] loki docker 镜像版本升级至 3.2.0 + +* \[升级\] promtail docker 镜像版本升级至 3.2.0 + +* \[升级\] grafana docker 镜像版本升级至 11.2.2 + +* \[升级\] zipkin docker 镜像版本升级至 3.4.2 + +* \[升级\] emqx 镜像版本升级至 5.8.0 + + +* **依赖更新** + + +* \[升级\] aws-java-sdk-s3 版本升级至 1.12.777 + +* \[升级\] software.amazon.awssdk 版本升级至 2.28.29 + +* \[升级\] software.amazon.awssdk.crt 版本升级至 0.31.3 + +* \[升级\] alipay-sdk-java 版本升级至 4.39.234.ALL + +* \[升级\] mysql 版本升级至 9.1.0 + +* \[升级\] mybatis plus 版本升级至 3.5.9 + +* \[升级\] sqlite-jdbc 版本升级至 3.47.0.0 + +* \[升级\] quasar webjars 版本升级至 2.17.1 + +* \[升级\] sweetalert2 webjars 版本升级至 11.14.4 + +* \[升级\] grpc 版本升级至 1.68.0 + +* \[升级\] protobuf 版本升级至 3.25.5 + +* \[升级\] redisson 版本升级至 3.37.0 + +* \[升级\] hutool 版本升级至 6.0.0-M17 + +* \[升级\] checker-qual 版本升级至 3.48.1 + +* \[升级\] nacos-client 版本升级至 2.4.3 + +* \[升级\] opengauss-jdbc 版本升级至 6.0.0-og + +* \[升级\] vue webjars 版本升级至 3.5.12 + + +## \[三\] 设计答疑 + +### 1\. 为什么不做“纯血”响应式 + +响应式固然有其优势所在,但是使用响应式也不得面对一些现实问题: + +* 要做纯血响应式,首先要有生态的保证。目前响应式的接受度还并不是很高,很多组件还都不支持响应式。除非有精力将所有用到的不支持的组件改写一遍,否则很难做到纯血,特别是对于微服务系统来说。 + +* 绝大多数应用都是需要使用数据库的,Java 领域现有的 orm 组件,要么不支持响应式(比如 JPA)、要么支持的不是特别好(比如 Hibernate)、要么需要自己编写的内容太多(比如 R2DBC),所以从投入产出比的角度说目前在数据层面做响应式并不“划算” + + +所以,还是要具体看应用系统的类型,在条件不具备的情况下没有必要做到纯血`响应式`。 + +### 2\. 响应式可以带来哪些好处 + +`响应式`对`阻塞式`的好处,网上有大把的文章介绍,具体就不赘述了。对于实际应用中比较明显的优势: + +* `响应式`资源的利用效能更高,对于高资源消耗的功能,`响应式`的优势更突出 + +* 微服务系统往往会需要集成更多内容,特别在数据层面,可能会存在同时使用多种类型数据存储以及数据的流转和迁移,常规事件驱动与响应式的“流”式思维比传统阻塞式更为契合。 + +* 响应式可以与事件驱动更好的配合。在 Spring 生态中在多个方面都大量使用了事件驱动,而响应式的核心设计思想也与事件驱动殊途同归。 + + +> 正因为 Dante Cloud 用了很多 Spring Integration 的内容,传统阻塞式方式越用越别扭,才越来越觉得有必要做响应式支持。如果有时间可以好好看看 Spring Integration,也许会为你打开一个新的世界。 + +### 3\. 学习响应式编程有哪些难点 + +* 如果基于 Reactor 学习响应式编程,难点和突破点就在于 `Flux` 和 `Mono` 两个类。把这两个类的方法用透、弄明白,基本上就可以消除所有开发阻碍了。 + +* 响应式编程最大的难点就是编程思维的转换,因为习惯了阻塞式编程,一时会很难适应`响应式`的`流`式开发思维 + + +> 世上无难事只怕有心人 + +## \[四\] 系统文档 + +为了更好的帮助大家理解学习 Dante Cloud,新增文档站点 https://www.herodotus.vip 。该站点目前包含矫正和重新梳理后的系统部署相关内容,后续计划根据系统涉及的详细知识点和模块陆续补充对应设计实现和认知理解相关文章。原有站点如无特殊原因,仍旧会保留。 + +* * * + +**欢迎 Star 一波来支持我们!** + +**Gitee**:https://gitee.com/dromara/dante-cloud +**Github**:https://github.com/dromara/dante-cloud + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/Dante-Cloud-3.3.5.0-1.webp) \ No newline at end of file diff --git a/src/zh/news/Dante-Cloud-3.4.3.3.md b/src/zh/news/Dante-Cloud-3.4.3.3.md new file mode 100644 index 0000000000..32cdc01689 --- /dev/null +++ b/src/zh/news/Dante-Cloud-3.4.3.3.md @@ -0,0 +1,143 @@ +--- +title: Dante Cloud 3.4.3.3 发布,演示系统已就绪,现在就来一探究竟! +author: Dante Cloud +date: 2025-03-17 +cover: /assets/img/news/Dante-Cloud-3.4.3.3-0.png +head: + - - meta + - name: 新闻 +--- + +## \[一\] 项目简介  + +**Dante Cloud** 国内首个支持阻塞式和响应式服务并行的微服务平台。是采用**领域驱动模型(DDD)**设计思想,以「**高质量代码、低安全漏洞**」为核心,基于 Spring 生态全域开源技术,高度**模块化和组件化设计**,支持**智能电视、IoT等物联网设备**认证,满足**国家三级等保要求**,支持**接口国密数字信封加解密**等一系列安全体系的多租户微服务解决方案。可以“**一套代码实现微服务和单体两种架构**”的企业级应用系统。 + +![](/assets/img/news/Dante-Cloud-3.4.3.3-0.png) + +## \[二\] 项目理念  + +**Dante Cloud** 一直秉承着“简洁、高效、包容、务实”的理念,使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,不断地深耕细作、去粗取精、用心打造。目标是构建一款`代码质量高、维护投入低、安全防护强`的,可以帮助用户快速跨越架构技术选型、技术研究探索阶段,降低传统项目中因安全漏洞、技术负债、低质代码等潜在隐患所产生的高维护投入,期望像项目名字寓意一样,在行业变革的时期承上启下,助力企业信息化建设和变革的产品。 + +> **Dante Cloud** 核心关注点是:**「高质量的系统代码」**、**「合理的系统架构」**、**「低耦合的模块划分」**、**「高安全性系统实现」**、**「灵活的功能扩展能力」**,**「优质的微服务实现」**,而不是追求 **业务功能** 堆叠的 **丰富** 性。 + +## \[三\] 特性介绍  + +Dante Cloud 演示系统已发布,目前包括在线版、单体离线版(Docker)和微服务镜像版, 欢迎体验使用! + +体验地址:https://www.herodotus.vip/get-started/preview/online.html + +有任何意见和建议,可以【发 ISSUE留言。 + +**效果演示** + +## \[四\] 更新内容  + +* **主要更新** + + +* \[升级\] Spring Boot Admin 版本升级至 3.4.5 + +* \[新增\] SAS 相关的异常和错误反馈不在仅是显示单调的信息,支持浏览器访问场景下以 text/html 类型输出个人人性化界面 + +* \[优化\] 大幅优化单体版和前端在 Context Path 以及 Nginx 反向代理环境下运行的可用性 + + +* **其它更新** + + +* \[重构\] 将授权服务器和资源服务所有异常处理归并至门面配置类中,实现 HttpSecurity 错误配置统一化调用。减少 SAS 核心配置需要注入过多 Bean 问题。 + +* \[重构\] 提取授权服务器和认证服务器门面配置类,减少应用端需要注入的 Bean,方便使用和统一维护。 + +* \[重构\] 使用 Lambda 方式重构 RequestMapping 核心处理逻辑代码。 + +* \[新增\] 新增生成环境浏览器开发工具防护控制环境变量,方便在生产环境关闭防护进行调试。 + +* \[新增\] 修复前端部署至 Nginx 页面缓存策略化配置。 + +* \[新增\] 系统新增 H2 内存数据库支持 + +* \[新增\] 单体版本新增基于 H2 内存数据库的演示模式 Profile。 + +* \[新增\] 前端 Vite 配置增加基础路径配置,修复在反向代理指向子路径的配置方式下,出现静态资源 404 问题 + +* \[新增\] 对原有仅支持 application/json 类型的 SAS 错误响应进行扩展,支持 text/html 类型处理。让通过浏览器访问出现的异常信息展现的更加人性化。 + +* \[新增\] 新增 Thymeleaf 手动渲染页面处理工具,解决手动渲染不支持 '@{}',抛出Link base "/error/css/style.css" cannot be context relative (/...) unless the context used for executing the engine implements the org.thymeleaf.context.IWebContext interface 问题。 + +* \[修复\] 修复 SAS DefaultAuthenticationEventPublisher 如果没有指定默认事件,会抛出跳出系统错误体系不识别异常问题。 + +* \[修复\] 修复部分 SAS 异常,跳出系统自定义错误体系,抛出不携带自定义错误信息异常问题。 + +* \[修复\] 修复 OAuth2 自带异常无法转换为系统错误体系标准异常问题。 + +* \[修复\] 修复调整错误处理类配置方式后,授权码页面被拦截问题。 + +* \[修复\] 修复获取 IP 时,特殊情况会获取到 0:0:0:0:0:0:0:1 而导致异常问题。 + +* \[修复\] 修复前端打包为 Docker 镜像,环境变量设置不生效问题。 + +* \[修复\] 修复前端 Typescript 定义与后端实体不一致,导致前端 OAuth2Appliation 功能显示和操作异常问题。 + +* \[修复\] 优化前端生产编译配置,修复在指定 Base 场景下 css url 方式引用字体出现 404 问题。 + +* \[修复\] 修复 Servlet 环境下 AccessDenied 异常处理逻辑错误,导致抛出信息不够精准问题。 + +* \[修复\] 修复前端编译时输出类型引入错误告警 + +* \[修复\] 修复前端 pinia store ts 重复导出引起编译告警问题。 + +* \[修复\] 修复前端 Vue 页面导出语法错误引起编译告警问题。 + +* \[修复\] 修复自定义登录页面图片在指定上下文路径环境下不显示问题。 + +* \[修复\] 修复系统缺少 oauth2\_authorization\_resource 表初始化脚本问题 + +* \[修复\] 修复在 Context Path 环境下,Cookie Path 设置异常导致导致登录失败问题。 + +* \[修复\] 修复在 Context Path 环境下,包含占位符的权限校验错误问题。 + +* \[修复\] 修复在 Context Path 环境下,接口权限转换没有包含 Context Path,导致权限无法验证通过错误 + +* \[优化\] 去除代码中重复定义的 DefaultOAuth2AuthenticationEventPublisher Bean 配置。 + +* \[优化\] 请求审计日志新增 js、css、image 等静态资源过滤保护,防止记录过多静态资源调用信息。 + +* \[优化\] 删除 Vite 配置文件,无用的编译打包设置 + +* \[升级\] minio docker 镜像版本升级至 RELEASE.2025-03-12T18-04-18Z + +* \[升级\] emqx docker 镜像版本升级至 5.8.5 + +* \[升级\] tdengine 镜像版本升级至 3.3.5.8 + + +* **依赖更新** + + +* \[升级\] hypersistence-utils-hibernate-63 版本升级至 3.9.5 + +* \[升级\] software.amazon.awssdk 版本升级至 2.30.38 + +* \[升级\] software.amazon.awssdk.crt 版本升级至 0.36.2 + +* \[升级\] audience-annotations 版本升级至 0.15.1 + +* \[升级\] grpc 版本升级至 1.71.0 + +* \[升级\] quasar webjars 版本升级至 2.18.1 + +* \[升级\] sms4j-spring-boot-starter 版本升级至 3.3.4 + +* \[升级\] webauthn4j 版本升级至 0.28.6.RELEASE + +* \[升级\] checker-qual 版本升级至 3.49.1 + + +* * * + +**如果本项目对你有所帮助,欢迎 Star 一波来支持我们**! + +**Gitee**:https://gitee.com/dromara/dante-cloud + +**Github**:https://github.com/dromara/dante-cloud \ No newline at end of file diff --git a/src/zh/news/Dante-Cloud-3.5.0.0.md b/src/zh/news/Dante-Cloud-3.5.0.0.md new file mode 100644 index 0000000000..8a7e2de66e --- /dev/null +++ b/src/zh/news/Dante-Cloud-3.5.0.0.md @@ -0,0 +1,174 @@ +--- +title: 重磅升级!Dante Cloud 全面拥抱 Spring Boot 3.5 & Spring Cloud 2025 +author: Dante Cloud +date: 2025-06-03 +cover: /assets/img/news/Dante-Cloud-3.5.0.0-0.png +head: + - - meta + - name: 新闻 +--- + +## \[一\] 项目简介 + +**Dante Cloud** 国内首个支持阻塞式和响应式服务并行的微服务平台。是采用**领域驱动模型(DDD)**设计思想,以「**高质量代码、低安全漏洞**」为核心,基于 Spring 生态全域开源技术,高度**模块化和组件化设计**,支持**智能电视、IoT等物联网设备**认证,满足**国家三级等保要求**,支持**接口国密数字信封加解密**等一系列安全体系的多租户微服务解决方案。可以“**一套代码实现微服务和单体两种架构**”的企业级应用系统。 + +![](/assets/img/news/Dante-Cloud-3.5.0.0-0.png) + + + +## \[二\] 项目理念 + +**Dante Cloud** 一直秉承着“简洁、高效、包容、务实”的理念,使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,不断地深耕细作、去粗取精、用心打造。目标是构建一款`代码质量高、维护投入低、安全防护强`的,可以帮助用户快速跨越架构技术选型、技术研究探索阶段,降低传统项目中因安全漏洞、技术负债、低质代码等潜在隐患所产生的高维护投入,期望像项目名字寓意一样,在行业变革的时期承上启下,助力企业信息化建设和变革的产品。 + +**Dante Cloud** 核心关注点是:**「高质量的系统代码」**、**「合理的系统架构」**、**「低耦合的模块划分」**、**「高安全性系统实现」**、**「灵活的功能扩展能力」**,**「优质的微服务实现」**,而不是追求 **业务功能** 堆叠的 **丰富** 性。 + +## \[三\] 架构设计 + +Dante Cloud 除了提供一套完整的微服务架构以外,还支持以单体架构运行。这里的微服务架构和单体架构并不是分离的两套代码,也不是分离的两个项目。而是完全融合的一整套代码,使用时可以根据需要选择是以微服务模式或者单体模式运行。这是 Dante Cloud 微服务最大的特色之一:**“一套代码、两种架构”**。 + +> 基于 `Spring Boot` 和 `Spring Cloud` 的微服务架构,已经成为企业级应用建设的主流方案。但不可否认的是,搭建一套微服务架构所需的基础设施越来越多,也越来越复杂。仅仅是在开发电脑上搭建一套运行开发调试环境,其复杂度和所需的资源也不容小觑。而很多应用特别是小型应用,在早期开发中或者用户量不大的前期,很多情况下一套单体的,前后端分离的后台就足以满足。完全没有必要上一整套微服务,额外增加复杂度。 + +Dante Cloud **“一套代码、两种架构”** 的特点,可以帮助企业在项目早期以单体架构快速建设项目、方便开发人员在本地进行开发以及新技术研究。在项目后期随着用户规模增大以及并发需求提升时,可以快速无缝迁移至微服务架构。 + +## \[四\] 特性介绍 + +Dante Cloud 演示系统已发布,目前包括在线版、单体离线版(Docker)和微服务镜像版, 欢迎体验使用! + +体验地址:https://www.herodotus.vip/get-started/preview/online.html + +有任何意见和建议,可以【发 ISSUE】留言。 + +## \[五\] Spring 生态新特性总结 + +| 特性领域 | Spring Boot 3.5 特性 | Spring Cloud 2025.0.0 特性 | +|------------|-----------------------------------------------|---------------------------------------------| +| Java 支持 | Java 17+(可能优化 Java 21) | 依赖 Spring Boot 3.5 | +| Web/HTTP | HTTP/3(实验性)、WebSocket 优化 | API 网关(Gateway)优化 | +| 安全 | Spring Security 6.x 增强 | 配置加密/解密优化 | +| 数据访问 | Hibernate 6.x、JDBC 优化 | Config 管理优化 | +| 监控 | Micrometer 1.11.x、Actuator 增强 | Sleuth/OpenTelemetry 优化 | +| 云原生 | Kubernetes/Docker 优化 | 服务发现、熔断、流处理增强 | +| 开发体验 | 构建工具优化、Kotlin 支持 | 动态配置、智能路由 | + + +## \[六\] 更新内容 + +* **主要更新** + + +* \[升级\] Spring Boot 版本升级至 3.5.0 + +* \[升级\] Spring Authorization Server 版本升级至 1.5.0 + +* \[升级\] Spring Cloud 版本升级至 2025.0.0 + +* \[升级\] Spring Cloud Tencent 版本升级至 2.0.1.0-2023.0.3 + +* \[升级\] Spring Boot Admin 版本升级至 3.5.0 + +* \[升级\] Nacos 版本升级至 3.0.1。自封装支持 Postgresql 的 Nacos Docker 镜像已经上传至 Docker Hub 和 Quay.IO + + +* **其它更新** + + +* \[新增\] 增加设置设备属性和调用设备服务参数校验控制 + +* \[新增\] 新增设置设备属性和调用设备服务接口 + +* \[新增\] 新增组合文件管理器定义,以支持不同用途文件的本地及对象存储组合管理。同时解决原有 FileTemplate 和 FileTransformer 定义逻辑不够清晰问题。 + +* \[新增\] 新增 JsonSchema 默认组合文件管理器,并在 core-autoconfigure 模块中默认注入,以保证代码正确运行。 + +* \[新增\] 新增平台级 JsonSchema 组合文件存储管理器定义。 + +* \[修复\] 修复因使用 Import 方式依赖 SCT 导致 Springdoc 版本被干扰导致无法升级问题。 + +* \[修复\] 修复证书工厂测试用例执行错误问题 + +* \[优化\] hikari 和数据库连接相关配置,进一步提升数据库连接和使用效能 + +* \[优化\] 优化 MqttTopic 定义,支持更多 Mqtt 主题应用场景 + +* \[优化\] Spring Boot Test Starter 不再采用全局配置,修改为各模块按需引 + +* \[优化\] 调整对 JustAuth 依赖所在位置及相关代码,提升相关代码内聚性 + +* \[重构\] 重构核心基础模块代码,调整部分代码包路径和结构,减少各模块间的依赖和耦合。 + +* \[重构\] 重构 OAuth2 Client 代码适配最新版本 Spring Security OAuth2 + +* \[重构\] 重构 WebPathUtils 工具类,适配最新版 Spring Security + +* \[重构\] 重构 Spring Authorization Server 认证相关代码,支持 DPoP + +* \[重构\] 重构 Spring Authorization Server 认证相关代码,支持 PAR + +* \[重构\] 重构 FileTemplate 和 FileTransformer 定义,减少不必要的方法定义和交互。 + +* \[升级\] minio docker 镜像版本升级至 RELEASE.2025-05-24T17-08-30Z + +* \[升级\] grafana docker 镜像版本升级至 12.0.1 + +* \[升级\] loki docker 镜像版本升级至 3.5.1 + +* \[升级\] promtail docker 镜像版本升级至 3.5.1 + +* \[升级\] emqx docker 镜像版本升级至 5.9.0 + +* \[升级\] influxdb docker 镜像版本升级至 2.7.12 + +* \[升级\] clickhouse docker 镜像版本升级至 25.5.1 + +* \[升级\] tdengine docker 镜像版本升级至 3.3.6.6 + + +* **依赖更新** + + +* \[升级\] alipay-sdk-java 版本升级至 4.40.237.ALL + +* \[升级\] com.baidu.aip 版本升级至 4.16.20 + +* \[升级\] grpc 版本升级至 1.73.0 + +* \[升级\] json-schema-validator 版本升级至 1.5.7 + +* \[升级\] protobuf 版本升级至 3.25.8 + +* \[升级\] redisson 版本升级至 3.48.0 + +* \[升级\] software.amazon.awssdk 版本升级至 2.31.53 + +* \[升级\] software.amazon.awssdk.crt 版本升级至 0.38.3 + +* \[升级\] sweetalert2 webjars 版本升级至 11.22.0 + +* \[升级\] vue webjars 版本升级至 3.5.16 + +* \[升级\] weixin java 版本升级至 4.7.5-20250529.111829 + +* \[升级\] okio 版本升级至 3.12.0 + +* \[升级\] influxdb-client 版本升级至 7.3.0 + +* \[升级\] json 版本升级至 20250517 + + +## \[七\] 文档说明 + +原文档站点 https://www.herodotus.cn 因服务器到期,已经停止服务。 + +需要查阅 Dante Cloud 项目文档的朋友,可以查看【企业版】文档 https://www.herodotus.vip。(该文档除了功能上的差异外,部署方法和使用逻辑等与开源版本完全一致,而且内容更精细易懂,不会影响开源版的使用)。 + +或者可以访问本项目【社区版】文档 https://dante-cloud.dromara.org,该站点初次访问可能会有点慢,甚至出现部分地区有时打不开的情况。 + +敬请悉知! + +* * * + +**如果本项目对你有所帮助,欢迎 Star 一波来支持我们**! + +**Gitee**:https://gitee.com/dromara/dante-cloud + +**Github**:https://github.com/dromara/dante-cloud \ No newline at end of file diff --git a/src/zh/news/Dante-Cloud-3.5.5.0.md b/src/zh/news/Dante-Cloud-3.5.5.0.md new file mode 100644 index 0000000000..1071305c24 --- /dev/null +++ b/src/zh/news/Dante-Cloud-3.5.5.0.md @@ -0,0 +1,186 @@ +--- +title: Dante Cloud 3.5.5.0 发布, “你们项目是单体还是微服务?” “我都是。” +author: dnate cloud +date: 2025-08-25 +cover: /assets/img/news/Dante-Cloud-3.5.5.0-0.png +head: + - - meta + - name: 新闻 +--- + + + +![](/assets/img/news/Dante-Cloud-3.5.5.0-0.png) + +## \[一\] 项目简介 + +**Dante Cloud** 国内首个支持阻塞式和响应式服务并行的微服务平台。是采用**领域驱动模型(DDD)**设计思想,以「**高质量代码、低安全漏洞**」为核心,基于 Spring 生态全域开源技术,高度**模块化和组件化设计**,支持**智能电视、IoT等物联网设备**认证,满足**国家三级等保要求**,支持**接口国密数字信封加解密**等一系列安全体系的多租户微服务解决方案。独创的可以“**一套代码实现微服务和单体两种架构灵活切换**”的企业级应用系统。 + +## \[二\] 项目理念 + +**Dante Cloud** 一直秉承着“简洁、高效、包容、务实”的理念,使用微服务领域及周边相关的各类新兴技术或主流技术进行建设,不断地深耕细作、去粗取精、用心打造。目标是构建一款`代码质量高、维护投入低、安全防护强`的,可以帮助用户快速跨越架构技术选型、技术研究探索阶段,降低传统项目中因安全漏洞、技术负债、低质代码等潜在隐患所产生的高维护投入,期望像项目名字寓意一样,在行业变革的时期承上启下,助力企业信息化建设和变革的产品。 + +**Dante Cloud** 核心关注点是:**「高质量的系统代码」**、**「合理的系统架构」**、**「低耦合的模块划分」**、**「高安全性系统实现」**、**「灵活的功能扩展能力」**,**「优质的微服务实现」**。不会像其它一些系统一样,追求 **业务功能** 的 **丰富** 性。堆叠大量无法做到真正通用的功能,反倒会成为负担和干扰,不如由用户自己按照需求灵活设计和实现。 + +## \[三\] 架构设计 + +Dante Cloud 除了提供一套完整的微服务架构以外,还支持以单体架构运行。这里的微服务架构和单体架构并不是分离的两套代码,也不是分离的两个项目。而是完全融合的一整套代码,使用时可以根据需要选择是以微服务模式或者单体模式运行。这是 Dante Cloud 微服务最大的特色之一:**“一套代码、两种架构”**。 + +> 基于 `Spring Boot` 和 `Spring Cloud` 的微服务架构,已经成为企业级应用建设的主流方案。但不可否认的是,搭建一套微服务架构所需的基础设施越来越多,也越来越复杂。仅仅是在开发电脑上搭建一套运行开发调试环境,其复杂度和所需的资源也不容小觑。而很多应用特别是小型应用,在早期开发中或者用户量不大的前期,很多情况下一套单体的,前后端分离的后台就足以满足。完全没有必要上一整套微服务,额外增加复杂度。 + +Dante Cloud **“一套代码、两种架构”** 的特点,可以帮助企业在项目早期以单体架构快速建设项目、方便开发人员在本地进行开发以及新技术研究。在项目后期随着用户规模增大以及并发需求提升时,可以快速无缝迁移至微服务架构。 + +## \[四\] 实现组件 + +本项目并未使用任何复杂难懂或难以上手掌握的技术,项目中所涉及核心关键组件中,其中「**近 80% 均为 Spring 生态原生组件**」。技术实现均为各组件标准用法的组合与应用,编码风格和代码设计一直也在极尽努力尽量与 Spring 生态的标准规范用法保持一致,只不过经过大量的版本迭代和重构之后逐渐形成了一定的封装与抽象。 + +学习使用本项目对 **Java 以及 Spring 生态基础知识**,以及对 **微服务思想理念的认知** 要求较高。因此,如果你觉得本项目学习曲线高、掌握难度大,不像其它同类开源项目那样“**简单**”,那么很有可能是你尚未 **真正** 的了解或者掌握 Spring 生态的相关组件。 + +* **听过不等于知道** + +* **知道不等于了解** + +* **了解不等于会用** + +* **会用不等于精通** + + +当然,换个角度,本项目也可以是深入学习掌握 Spring 生态各组件的优秀案例。**建议详细阅读《企业IT架构转型之道:阿里巴巴中台战略思想与架构实战》一书(可以先读前几章)之后再上手本项目,特别是对于仅擅长单体应用的朋友,一定要读**! + +> 本项目提供了《Dante Cloud 及相关知识学习方法和学习路径的建议》,欢迎感兴趣的朋友阅读,【在线阅读】 + +Dante Cloud 所使用的核心组件如下: + + +| 序号 | Spring 生态组件 | 国内开源组件 | +|------|--------------------------|------------------------| +| 1 | Spring Boot | Spring Cloud Alibaba | +| 2 | Spring Security | Spring Cloud Tencent | +| 3 | Spring Security OAuth2 | JetCache | +| 4 | Spring Authorization Server | Mybatis Plus | +| 5 | Spring Data JPA | JustAuth | +| 6 | Spring Data MongoDB | WxJava | +| 7 | Spring Data Redis | Hutool | +| 8 | Spring Data Envers | sms-spring-boot-starter| +| 9 | Spring Cloud | grpc-spring-boot-starter| +| 10 | Spring Cloud Bus | -- | +| 11 | Spring Cloud Stream | -- | +| 12 | Spring Cloud Gateway | -- | +| 13 | Spring Cloud Loadbalancer| -- | +| 14 | Spring Cloud OpenFegin | -- | +| 15 | Spring Cloud Zookeeper | -- | +| 16 | Spring Session | -- | +| 17 | Spring Integration | -- | +| 18 | Spring Kafka | -- | +| 19 | Spring WebSocket | -- | +| 20 | Spring RSocket | -- | +| 21 | Spring Webflux | -- | +| 22 | Micrometer | -- | +| 23 | SpringDoc | -- | +| 24 | Spring Boot Admin | -- | + + + +## \[五\] 交流反馈 + +为了方便 Dante Cloud 开源版及企业版用户交流,深入了解、掌握 Dante Cloud 使用相关技术栈,快速解决实际应用问题。自 2025年8月18日起,Dante Cloud 重新开放 **技术交流群**,欢迎所有 `真心` 交流技术朋友加入。 + +**如何进群**:详见【技术交流群】。 + +## \[六\] 本次更新 + +* **主要更新** + + +* \[升级\] Spring Boot 版本升级至 3.5.5 + +* \[升级\] Spring Boot Admin 版本升级至 3.5.2 + +* \[升级\] Spring Authorization Server 版本升级至 1.5.2 + +* \[升级\] Nacos 版本升级至 3.0.3。自封装支持 Postgresql 的 Nacos Docker 镜像已经上传至 Docker Hub 和 Quay.IO + +* \[优化\] 系统已支持 Redis 8.2.1 版本, + + +* **其它更新** + + +* \[新增\] 前端工程新增主题切换特效 + +* \[修复\] 修复自定义扩展 Client Credentials 模式 Provider 与 SAS 最新配置方式不匹配,导致 Client Credentials 模式使用不稳定问题 + +* \[修复\] 修复物联网设备客户端动态注册,因循环开启认证导致客户端注册生的 Registered Client 信息被覆盖问题 + +* \[修复\] 修复 Spring Session 在退出系统时会抛出 java.lang.IllegalStateException: LettuceConnectionFactory has been STOPPED. Use start() to initialize it 问题。fix:#ICTVGU + +* \[修复\] 修复前端设备码验证轮询 API 返回信息错误 + +* \[修复\] 修复客户端动态注册时 oauth2\_authorization\_resource 表中,出现多条相同 clientId 信息存在,导致查询出错问题 + +* \[修复\] 修复设备码授权默认验证成功后跳转地址错误问题 + +* \[修复\] 修复前端 framework kernel 模块因导入信息错误,导致模块打包过大问题 + +* \[修复\] 修复 @vue/tsconfig 升级至 0.8.0,默认开启 noUncheckedIndexedAccess 和 exactOptionalPropertyTypes 配置,导致打包编译时出现错误提示问题 + +* \[重构\] 重构前端 Axios 组件抽象定义中的类型,让类型验证更准确,减少不必要的类型转换 + +* \[优化\] IP 地址库数据库更新至 2025-08-13 + +* \[优化\] 优化前端客户端动态注册默认参数,避免注册时生成不必要的授权模式 + +* \[优化\] 删除 Baidu OCR OpenAPI 封装模块 + +* \[优化\] 去除 Velocity 组件的依赖以及相关配置 + +* \[优化\] 优化 Maven 配置,去除早期为控制依赖漏洞而引入的 fastjson 统一版本控制 + +* \[优化\] 优化 Gitee ISSUE Template + +* \[优化\] 删除前端 Bpmn 设计器模块打包配置中的无用配置 + +* \[优化\] 优化前端 vite.config.mts 和 tsconfig.json 配置,采用更合理的定义配置,同时去除无用或过时的配置内容 + +* \[优化\] 优化前端应用 Vite 配置,调整自动生成配置文件位置,优化自动导入配置 + +* \[优化\] 优化前端模块 package.json 导出配置,简化模块样式引入路径长途 + +* \[升级\] tempo docker 镜像版本升级至 2.8.2 + +* \[升级\] cassandra docker 镜像版本升级至 5.0.5 + +* \[升级\] clickhouse docker 镜像版本升级至 25.7.4 + +* \[升级\] kestra docker 镜像版本升级至 v0.24.2 + + +* **依赖更新** + + +* \[升级\] alipay-sdk-java 版本升级至 4.40.411.ALL + +* \[升级\] grpc 版本升级至 1.75.0 + +* \[升级\] skywalking agent 版升级至 9.5.0 + +* \[升级\] software.amazon.awssdk 版本升级至 2.32.27 + +* \[升级\] software.amazon.awssdk.crt 版本升级至 0.38.9 + +* \[升级\] springdoc 版本升级至 2.8.10 + +* \[升级\] sweetalert2 webjars 版本升级至 11.22.4 + +* \[升级\] wxjava 版本升级至 4.7.7-20250808.182223 + + +* * * + +**如果本项目对你有所帮助,欢迎 Star 一波来支持我们**! + +**Gitee**:https://gitee.com/dromara/dante-cloud + +**Github**:https://github.com/dromara/dante-cloud + +**Gitcode**:https://gitcode.com/dromara/dante-cloud \ No newline at end of file diff --git a/src/zh/news/Disjob-0.md b/src/zh/news/Disjob-0.md index 99ded4394d..89c1ba3cf4 100644 --- a/src/zh/news/Disjob-0.md +++ b/src/zh/news/Disjob-0.md @@ -1,113 +1,113 @@ ---- -title: 新晋开源项目 DisJob 加入 Dromara 社区,分布式任务调度框架 -author: disjob -date: 2023-09-05 -cover: /assets/img/news/Disjob-0-1.jpg -head: - - - meta - - name: 新闻 ---- - -## 作者简介 - -网名 Ponfee,Dromara 开源组织成员,dromara/disjob 项目作者。在国内多个一线大厂待过,有过后端、全栈、大数据等相关工作经历。 - -## 关于 Disjob - -Disjob 是天然为支持分布式长任务执行而设计的,它除了具备常规的任务调度功能外,还提供:任务拆分及分布式并行执行、暂停及取消运行中的任务、恢复执行被暂停的任务、保存任务的执行快照(Checkpoint)、任务编排(DAG)、广播任务等能力。以下是 Disjob 的整体流程图: - -![](/assets/img/news/Disjob-0-1.jpg) - -## 应用场景举例 - -举个简单的例子:统计在\*\*`(0,1万亿]`\*\*区间内质数的个数。如果单机单线程 CPU 去统计的话不知道要到何年马月,这里我们就可以用`Disjob`框架提供的分布式并行执行的能力来解决该类问题。 - -1. **拆分任务** - -先根据当前的机器资源情况来决定拆分任务的数量,比如按照我们的机器数量及 core CPU 数量(因为质数统计是 CPU 密集型),决定拆分为 10 个任务。 - -2. **分发任务** - -总任务被拆分成 10 个子任务后,框架会使用指定的路由算法把子任务分发给这些机器。 - -3. **接收任务** - -机器接收到子任务后,会提交到框架的自定义线程池中执行。 - -4. **分布式并行执行** - -在执行时我们可以使用分批次方式(通过代码循环)来统计,这里我们指定`task-1`在第一次循环统计`(0, 1亿]`,第二次循环统计`(10亿, 11亿]`,以此类推最后一次循环统计`(9990亿, 9991亿]`。同理其它的 task 也是按同样的方式分布式并行统计。 - -> P.s. 黎曼猜想中可知质数分布是大体均匀的,判断一个数是否质数有很多方法,如埃氏筛法、欧拉筛法、Miller Rabin 素性检验,我们可以使用 Guava 库提供的素性检验。 - -5. **Checkpoint** - -如果在统计过程中机器宕机后怎么办?难道再从头开始统计吗?No No No!我们可以在每循环 10 次(或每隔执行超过 1 分钟)时使用`Checkpoint`保存当前`task-1`的执行快照。宕机异常后的重新启动任务时会读取这份快照数据,从上一次的状态中接着继续统计。 - -> 以下是`task-1`任务保存的快照数据样例 - -``` -{ - "next":4000000001, // 下一次循环时要统计的区间为(40亿, 41亿] - "count":19819734, // 已经统计到了 19819734 个质数 - "finished":false // 当前任务是否已经统计完成:true-是;false-否; -} -``` - -6. **暂停与恢复** - -假如我们的这几台机器资源需要临时做其它的事情,想把当前的统计任务暂停一段时间。No problem!框架是支持`暂停执行中的任务`,只需要在管理后台的`调度实例`页面,找到该任务点击`暂停`按钮即可。在暂停时任务会接收到一个中断信号,收到中断信号时同样可以在代码中使用`Checkpoint`保存当前的执行快照。 - -当其它事情处理完后,我们可以在管理后台的`调度实例`页面,找到被暂停的这个任务,点击`恢复`按钮,此时任务会从上一次保存的状态中恢复继续执行。 - -7. **任务编排** - -现在这个质数统计的总任务已经执行完了,共 10 个子任务,每个子任务都统计出了它的那部分结果。Disjob 能自动帮我汇总结果吗?Yes!框架提供了非常强大且方便的表达式来编排任务,如:`A->B,C,(D->E)->D,F->G`,现在我们可以创建一个汇总任务,然后再把这两个任务编排在一起。以下是本例中质数统计的 job 表数据,其中`job_handler`指定了编排的这两个任务处理器(代码在 Disjob 的开源项目中)。 - -``` -INSERT INTO `sched_job` ( - `job_id`, - `job_group`, - `job_name`, - `job_handler`, - `job_state`, - `job_type`, - `job_param`, - `trigger_type`, - `trigger_value`, - `next_trigger_time` -) VALUES ( - 1003164910267351009, - 'default', - 'prime-count-dag', - 'cn.ponfee.disjob.test.handler.PrimeCountJobHandler -> cn.ponfee.disjob.test.handler.PrimeAccumulateJobHandler', - 1, - 2, - '{\"m\":1,\"n\":10000000000,\"blockSize\":100000000,\"parallel\":10}', - 2, - '2023-09-02 18:00:00', - unix_timestamp()*1000 -); -``` - -> **本例中的质数统计流程图如下** - -![](/assets/img/news/Disjob-0-2.png) - -## 结语 - -Disjob 还有很多的功能需要去完善打磨,正如 Dromara 的口号:一个人或许能走的更快,但一群人会走的更远。期待有更多的人一起参与共建! - -- **项目链接** - -gitee: https://gitee.com/dromara/disjob - -github: https://github.com/dromara/disjob - -个人博客:http://www.ponfee.cn - -- **沟通交流** - -对项目有什么想法或者建议,可以交流 - - +--- +title: 新晋开源项目 DisJob 加入 Dromara 社区,分布式任务调度框架 +author: disjob +date: 2023-09-05 +cover: /assets/img/news/Disjob-0-1.jpg +head: + - - meta + - name: 新闻 +--- + +## 作者简介 + +网名 Ponfee,Dromara 开源组织成员,dromara/disjob 项目作者。在国内多个一线大厂待过,有过后端、全栈、大数据等相关工作经历。 + +## 关于 Disjob + +Disjob 是天然为支持分布式长任务执行而设计的,它除了具备常规的任务调度功能外,还提供:任务拆分及分布式并行执行、暂停及取消运行中的任务、恢复执行被暂停的任务、保存任务的执行快照(Checkpoint)、任务编排(DAG)、广播任务等能力。以下是 Disjob 的整体流程图: + +![](/assets/img/news/Disjob-0-1.jpg) + +## 应用场景举例 + +举个简单的例子:统计在\*\*`(0,1万亿]`\*\*区间内质数的个数。如果单机单线程 CPU 去统计的话不知道要到何年马月,这里我们就可以用`Disjob`框架提供的分布式并行执行的能力来解决该类问题。 + +1. **拆分任务** + +先根据当前的机器资源情况来决定拆分任务的数量,比如按照我们的机器数量及 core CPU 数量(因为质数统计是 CPU 密集型),决定拆分为 10 个任务。 + +2. **分发任务** + +总任务被拆分成 10 个子任务后,框架会使用指定的路由算法把子任务分发给这些机器。 + +3. **接收任务** + +机器接收到子任务后,会提交到框架的自定义线程池中执行。 + +4. **分布式并行执行** + +在执行时我们可以使用分批次方式(通过代码循环)来统计,这里我们指定`task-1`在第一次循环统计`(0, 1亿]`,第二次循环统计`(10亿, 11亿]`,以此类推最后一次循环统计`(9990亿, 9991亿]`。同理其它的 task 也是按同样的方式分布式并行统计。 + +> P.s. 黎曼猜想中可知质数分布是大体均匀的,判断一个数是否质数有很多方法,如埃氏筛法、欧拉筛法、Miller Rabin 素性检验,我们可以使用 Guava 库提供的素性检验。 + +5. **Checkpoint** + +如果在统计过程中机器宕机后怎么办?难道再从头开始统计吗?No No No!我们可以在每循环 10 次(或每隔执行超过 1 分钟)时使用`Checkpoint`保存当前`task-1`的执行快照。宕机异常后的重新启动任务时会读取这份快照数据,从上一次的状态中接着继续统计。 + +> 以下是`task-1`任务保存的快照数据样例 + +``` +{ + "next":4000000001, // 下一次循环时要统计的区间为(40亿, 41亿] + "count":19819734, // 已经统计到了 19819734 个质数 + "finished":false // 当前任务是否已经统计完成:true-是;false-否; +} +``` + +6. **暂停与恢复** + +假如我们的这几台机器资源需要临时做其它的事情,想把当前的统计任务暂停一段时间。No problem!框架是支持`暂停执行中的任务`,只需要在管理后台的`调度实例`页面,找到该任务点击`暂停`按钮即可。在暂停时任务会接收到一个中断信号,收到中断信号时同样可以在代码中使用`Checkpoint`保存当前的执行快照。 + +当其它事情处理完后,我们可以在管理后台的`调度实例`页面,找到被暂停的这个任务,点击`恢复`按钮,此时任务会从上一次保存的状态中恢复继续执行。 + +7. **任务编排** + +现在这个质数统计的总任务已经执行完了,共 10 个子任务,每个子任务都统计出了它的那部分结果。Disjob 能自动帮我汇总结果吗?Yes!框架提供了非常强大且方便的表达式来编排任务,如:`A->B,C,(D->E)->D,F->G`,现在我们可以创建一个汇总任务,然后再把这两个任务编排在一起。以下是本例中质数统计的 job 表数据,其中`job_handler`指定了编排的这两个任务处理器(代码在 Disjob 的开源项目中)。 + +``` +INSERT INTO `sched_job` ( + `job_id`, + `job_group`, + `job_name`, + `job_handler`, + `job_state`, + `job_type`, + `job_param`, + `trigger_type`, + `trigger_value`, + `next_trigger_time` +) VALUES ( + 1003164910267351009, + 'default', + 'prime-count-dag', + 'cn.ponfee.disjob.test.handler.PrimeCountJobHandler -> cn.ponfee.disjob.test.handler.PrimeAccumulateJobHandler', + 1, + 2, + '{\"m\":1,\"n\":10000000000,\"blockSize\":100000000,\"parallel\":10}', + 2, + '2023-09-02 18:00:00', + unix_timestamp()*1000 +); +``` + +> **本例中的质数统计流程图如下** + +![](/assets/img/news/Disjob-0-2.png) + +## 结语 + +Disjob 还有很多的功能需要去完善打磨,正如 Dromara 的口号:一个人或许能走的更快,但一群人会走的更远。期待有更多的人一起参与共建! + +- **项目链接** + +gitee: https://gitee.com/dromara/disjob + +github: https://github.com/dromara/disjob + +个人博客:http://www.ponfee.cn + +- **沟通交流** + +对项目有什么想法或者建议,可以交流 + + diff --git a/src/zh/news/DyJava.md b/src/zh/news/DyJava.md new file mode 100644 index 0000000000..29778bf803 --- /dev/null +++ b/src/zh/news/DyJava.md @@ -0,0 +1,72 @@ +--- +title: 它来了! 抖音开发工具 DyJava 加入 Dromara 开源社区 +author: danmo +date: 2024-04-29 +cover: /assets/img/news/DyJava-0.png +head: + - - meta + - name: 新闻 +--- + +## 作者简介: + +**作者: danmo +Dromara 开源组织成员,Dromara/dy-java 作者 +Java开发工程师,工作经验7年 +https://gitee.com/dromara/dy-java.git** + +**![](/assets/img/news/DyJava-0.png)** + +## 缘起 + +> 随着抖音短视频的火爆,越来越多的开发者希望能够在抖音平台上实现自己的创意和应用。 +> 为了满足这一需求,我们推出了DyJava——一款专为抖音打造的Java开发工具包,助力开发者轻松实现抖音后端开发。 + +## DyJava简介 + +DyJava 是一款功能强大的抖音Java开发工具包,支持多种抖音开发功能模块的后端开发,包括但不限于移动/网站应用、开放平台、抖店和小程序等。 +DyJava致力于简化开发流程,提高开发效率,让开发者能够更专注于创新和业务逻辑的实现。 + +## DyJava特点 + +#### 丰富的功能模块: + +> DyJava支持抖音平台的各种功能模块,满足开发者在不同场景下的需求。 + +#### 简洁的API设计: + +> DyJava的API设计简洁明了,易于理解和使用,让开发者能够快速上手。 + +#### 高效的性能: + +> DyJava采用高性能的Java框架,确保后端服务的稳定性和响应速度。 + +#### 完善的文档和社区支持: + +> DyJava提供详尽的开发文档和活跃的开发者社区,帮助开发者解决开发过程中遇到的问题。 + +## Dyjava 的应用场景详解 + +#### 移动/网站应用开发: + +> 借助DyJava,开发者可以快速构建具有抖音特色的移动应用和网站,为用户提供一致的使用体验。无论是社交互动、内容分享还是电商购物,DyJava都能为开发者提供强大的技术支持。 + +#### 开放平台接入: + +> DyJava让开发者能够轻松接入抖音开放平台,实现与其他抖音应用的互联互通。无论是获取用户信息、发布动态还是分享内容,DyJava都能帮助开发者快速实现所需功能。 + +#### 抖店开发: + +> 对于想要在抖音上开设店铺的商家来说,DyJava提供了一套完整的后端解决方案。从商品管理、订单处理到营销推广,DyJava都能助力商家实现高效运营和用户增长。 + +#### 小程序开发: + +> DyJava支持抖音小程序的后端开发,帮助开发者打造轻量级、跨平台的应用。无论是游戏娱乐、工具助手还是生活服务,DyJava都能让小程序开发者轻松实现创意。 + +## 在最后 + +> 作为一款专为抖音打造的Java开发工具包,DyJava凭借其丰富的功能模块、简洁的API设计、高效的性能和完善的支持体系,无疑将成为抖音后端开发的首选工具。让我们携手DyJava,共同开启抖音后端开发的新篇章! + +**在这个充满无限可能的时代,让我们一起用DyJava创造更多精彩的抖音应用,为用户带来更丰富的娱乐体验!** + +**仓库地址:https://gitee.com/dromara/dy-java** \ No newline at end of file diff --git a/src/zh/news/DynamicTp-1.1.3.md b/src/zh/news/DynamicTp-1.1.3.md index f3a67c4a28..14c3b1d6f2 100644 --- a/src/zh/news/DynamicTp-1.1.3.md +++ b/src/zh/news/DynamicTp-1.1.3.md @@ -1,81 +1,81 @@ ---- -title: 动态线程池框架DynamicTp v1.1.4大版本发布,新增若干实用特性 -author: yanhom -date: 2023-04-28 -cover: /assets/img/news/DynamicTp-1.1.4-cover.png -head: - - - meta - - name: 新闻 ---- - -## DynamicTp 简介 - -DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 - -![](/assets/img/news/DynamicTp-1.1.4-cover.png) -**经过多个版本的迭代,目前最新版本 v1.1.3 具有以下特性** ✅ - -- **代码零侵入**:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入 -- **通知告警**:提供多种报警维度(配置变更通知、活性报警、容量阈值报警、拒绝触发报警、任务执行或等待超时报警),已支持企业微信、钉钉、飞书、邮件报警,同时提供 SPI 接口可自定义扩展实现 -- **运行监控**:定时采集线程池指标数据,支持通过 MicroMeter、JsonLog 日志输出、Endpoint 三种方式,可通过 SPI 接口自定义扩展实现 -- **任务增强**:提供任务包装功能,实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper,可以支持线程池上下文信息传递 -- **多配置中心支持**:基于主流配置中心实现线程池参数动态调整,实时生效,已支持 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris、ServiceComb,同时也提供 SPI 接口可自定义扩展实现 -- **中间件线程池管理**:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警) -- **轻量简单**:基于 SpringBoot 实现,引入 starter,接入只需简单 4 步就可完成,顺利 3 分钟搞定 -- **多模式**:提供了增强线程池 DtpExecutor,IO 密集型场景使用的线程池 EagerDtpExecutor,调度线程池 ScheduledDtpExecutor,有序线程池 OrderedDtpExecutor,可以根据业务场景选择合适的线程池 -- **兼容性**:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架管理,@Bean 定义时加 @DynamicTp 注解即可 -- **可靠性**:框架提供的线程池实现 Spring 生命周期方法,可以在 Spring 容器关闭前尽可能多的处理队列中的任务 -- **高可扩展**:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等) -- **线上大规模应用**:参考美团线程池实践,美团内部已经有该理论成熟的应用经验 - -## v1.1.3 发布记录 - -groupId 及包名改为 org.dromara.dynamictp - -#### Feature - -- 引入时间轮重构任务超时(排队超时、执行超时)功能,@KamToHung -- 增加 ExecutorAdapter,做各种框架线程池的适配器,@dragon-zhang -- WebServer(Tomcat、Undertow、Jetty)支持通知告警,@dragon-zhang -- 阿里云商业版 RocketMQ 线程池管理支持,@Redick01 -- 引入 JsonUtil,根据依赖选择 Jackson/Gson/FastJson 做框架 json 序列化工具,减少外部依赖,@topsuder -- 重构 OrderedDtpExecutor 有序线程池实现,@yanhom, @KamToHung -- 实现优雅关闭线程池功能,@yanhom -- 增加 dependencies 模块,统一管理依赖,@KamToHung -- TaskWrapper 支持 OpenTelemetry,@brendanv - -#### Bugfix - -- 修复飞书告警失败问题,@KamToHung -- 修复配置变更后不通知的问题,@yanhom -- 修复 dtp-alarm 线程 StackOverflowError 异常,@yanhom -- 修复 DtpPostProcessor 初始化晚于线程池实例初始化问题,@KamToHung - -#### Optimize - -- 丰富 Undertow 线程池监控指标,@yanhom -- 优化当引入 Dtp 包,不启用时可以通过手动配置关闭,@ruoan777 -- 优化告警功能,解决实际推送的告警信息看着不准的问题,@ruoan777 -- 线程池内部注册器模块优化,@KamToHung -- Hutool 依赖优化,只引入用到的包,@KamToHung -- 部分代码优化重构,@yanhom,@KamToHung,@dragon-zhang - -## 项目地址 - -**官网**:https://dynamictp.cn[1] - -**gitee 地址**:https://gitee.com/dromara/dynamic-tp[2] - -**github 地址**:https://github.com/dromara/dynamic-tp[3] - -## 加入社群 - -**看到这儿,方便的话给项目一个 star,你的支持是我们前进的动力!** - -使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟群友一起交流讨论。 - -### 参考资料 - -\[1\]https://dynamictp.cn -\[2\]https://gitee.com/dromara/dynamic-tp -\[3\]https://github.com/dromara/dynamic-tp +--- +title: 动态线程池框架DynamicTp v1.1.4大版本发布,新增若干实用特性 +author: yanhom +date: 2023-04-28 +cover: /assets/img/news/DynamicTp-1.1.4-cover.png +head: + - - meta + - name: 新闻 +--- + +## DynamicTp 简介 + +DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 + +![](/assets/img/news/DynamicTp-1.1.4-cover.png) +**经过多个版本的迭代,目前最新版本 v1.1.3 具有以下特性** ✅ + +- **代码零侵入**:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入 +- **通知告警**:提供多种报警维度(配置变更通知、活性报警、容量阈值报警、拒绝触发报警、任务执行或等待超时报警),已支持企业微信、钉钉、飞书、邮件报警,同时提供 SPI 接口可自定义扩展实现 +- **运行监控**:定时采集线程池指标数据,支持通过 MicroMeter、JsonLog 日志输出、Endpoint 三种方式,可通过 SPI 接口自定义扩展实现 +- **任务增强**:提供任务包装功能,实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper,可以支持线程池上下文信息传递 +- **多配置中心支持**:基于主流配置中心实现线程池参数动态调整,实时生效,已支持 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris、ServiceComb,同时也提供 SPI 接口可自定义扩展实现 +- **中间件线程池管理**:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警) +- **轻量简单**:基于 SpringBoot 实现,引入 starter,接入只需简单 4 步就可完成,顺利 3 分钟搞定 +- **多模式**:提供了增强线程池 DtpExecutor,IO 密集型场景使用的线程池 EagerDtpExecutor,调度线程池 ScheduledDtpExecutor,有序线程池 OrderedDtpExecutor,可以根据业务场景选择合适的线程池 +- **兼容性**:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架管理,@Bean 定义时加 @DynamicTp 注解即可 +- **可靠性**:框架提供的线程池实现 Spring 生命周期方法,可以在 Spring 容器关闭前尽可能多的处理队列中的任务 +- **高可扩展**:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等) +- **线上大规模应用**:参考美团线程池实践,美团内部已经有该理论成熟的应用经验 + +## v1.1.3 发布记录 + +groupId 及包名改为 org.dromara.dynamictp + +#### Feature + +- 引入时间轮重构任务超时(排队超时、执行超时)功能,@KamToHung +- 增加 ExecutorAdapter,做各种框架线程池的适配器,@dragon-zhang +- WebServer(Tomcat、Undertow、Jetty)支持通知告警,@dragon-zhang +- 阿里云商业版 RocketMQ 线程池管理支持,@Redick01 +- 引入 JsonUtil,根据依赖选择 Jackson/Gson/FastJson 做框架 json 序列化工具,减少外部依赖,@topsuder +- 重构 OrderedDtpExecutor 有序线程池实现,@yanhom, @KamToHung +- 实现优雅关闭线程池功能,@yanhom +- 增加 dependencies 模块,统一管理依赖,@KamToHung +- TaskWrapper 支持 OpenTelemetry,@brendanv + +#### Bugfix + +- 修复飞书告警失败问题,@KamToHung +- 修复配置变更后不通知的问题,@yanhom +- 修复 dtp-alarm 线程 StackOverflowError 异常,@yanhom +- 修复 DtpPostProcessor 初始化晚于线程池实例初始化问题,@KamToHung + +#### Optimize + +- 丰富 Undertow 线程池监控指标,@yanhom +- 优化当引入 Dtp 包,不启用时可以通过手动配置关闭,@ruoan777 +- 优化告警功能,解决实际推送的告警信息看着不准的问题,@ruoan777 +- 线程池内部注册器模块优化,@KamToHung +- Hutool 依赖优化,只引入用到的包,@KamToHung +- 部分代码优化重构,@yanhom,@KamToHung,@dragon-zhang + +## 项目地址 + +**官网**:https://dynamictp.cn[1] + +**gitee 地址**:https://gitee.com/dromara/dynamic-tp[2] + +**github 地址**:https://github.com/dromara/dynamic-tp[3] + +## 加入社群 + +**看到这儿,方便的话给项目一个 star,你的支持是我们前进的动力!** + +使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟群友一起交流讨论。 + +### 参考资料 + +\[1\]https://dynamictp.cn +\[2\]https://gitee.com/dromara/dynamic-tp +\[3\]https://github.com/dromara/dynamic-tp diff --git a/src/zh/news/DynamicTp-1.1.4.md b/src/zh/news/DynamicTp-1.1.4.md index e5d5a2f376..aa3268ef5f 100644 --- a/src/zh/news/DynamicTp-1.1.4.md +++ b/src/zh/news/DynamicTp-1.1.4.md @@ -1,87 +1,87 @@ ---- -title: 动态线程池框架DynamicTp v1.1.4大版本发布,新增若干实用特性 -author: DynamicTp -date: 2023-09-26 -cover: /assets/img/news/DynamicTp-1.1.4-cover.png -head: - - - meta - - name: 新闻 ---- - -## DynamicTp 简介 - -DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 - -## DynamicTp 特性 - -- **代码零侵入**:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入 -- **通知告警**:提供多种报警维度(配置变更通知、活性报警、容量阈值报警、拒绝触发报警、任务执行或等待超时报警),已支持企业微信、钉钉、飞书、邮件报警,同时提供 SPI 接口可自定义扩展实现 -- **运行监控**:定时采集线程池指标数据,支持通过 MicroMeter、JsonLog 日志输出、Endpoint 三种方式,可通过 SPI 接口自定义扩展实现 -- **任务增强**:提供任务包装功能,实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper,可以支持线程池上下文信息传递 -- **多配置中心支持**:基于主流配置中心实现线程池参数动态调整,实时生效,已支持 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris、ServiceComb,同时也提供 SPI 接口可自定义扩展实现 -- **中间件线程池管理**:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警) -- **轻量简单**:基于 SpringBoot 实现,引入 starter,接入只需简单 4 步就可完成,顺利 3 分钟搞定 -- **多模式**:提供了增强线程池 DtpExecutor,IO 密集型场景使用的线程池 EagerDtpExecutor,调度线程池 ScheduledDtpExecutor,有序线程池 OrderedDtpExecutor,可以根据业务场景选择合适的线程池 -- **兼容性**:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架管理,@Bean 定义时加 @DynamicTp 注解即可 -- **可靠性**:框架提供的线程池实现 Spring 生命周期方法,可以在 Spring 容器关闭前尽可能多的处理队列中的任务 -- **高可扩展**:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等) -- **线上大规模应用**:参考美团线程池实践,美团内部已经有该理论成熟的应用经验 - -## v1.1.4 发版记录 - -- 支持 Spring 项目,SpringBoot 相关特性只在 starter 模块引入,@dragon-zhang -- 添加 jvmti 黑科技模块,方便集成管理各种三方包线程池,@dragon-zhang -- 升级 VariableLinkedBlockingQueue 到 jdk1.8 的 LinkedBlockingQueue 的实现,@yanhom -- 添加插件机制,基于此可以对框架做自定义开发扩展,@WindSearcher -- 细化告警配置,支持不同告警项配置不同接受人,@kyao -- 通知告警平台支持云之家,@chunhui_lu -- 支持 SpringBoot 1.x,@yanhom -- 第三方线程池(tomcat、undertow、dubbo、rocketmq、okhttp3 等等)支持 run_timeout、queue_timeout、reject 告警,@kyao,@yanhom - ![](/assets/img/news/DynamicTp-1.1.4-1.png) - -- 提供 Aware 扩展,可以扩展自定义线程池执行过程,@kyao -- 监控数据新增线程池别名,@zhifei - -#### Bugfix - -- 修复 Tomcat 高低版本兼容性报错问题,@yanhom -- 修复其他 agent 增强线程池后,强转 DtpRunnable 失败的问题,@yanhom -- 修复企微告警无@提醒的问题,@yanhom -- 修复企微告警配置多个接受人不能正确@的问题,@KamToHung -- 修复钉钉告警不能@所有人问题,@chenan -- 修复因 Bean 初始化顺序不确定导致的 ApplicationContextHolder npe 问题,@yanhom -- 修复修复拒绝策略为 CallerRunsPolicy 时,MdcRunnable 会删除主线程 mdc 信息的问题,@kyao - -#### Optimize - -- 优化 dtp 内部 spi 的使用,统一封装管理,@peachyy -- 部分代码优化重构,@yanhom,@KamToHung,@dragon-zhang,@kyao -- 告警信息优化,trace 信息可以自己扩展,集成内部 ELK 等平台,@yanhom - -![](/assets/img/news/DynamicTp-1.1.4-2.png) - -## 项目地址 - -**官网**:https://dynamictp.cn[1] - -**gitee 地址**:https://gitee.com/dromara/dynamic-tp[2] - -**github 地址**:https://github.com/dromara/dynamic-tp[3] - -## 加入社群 - -**看到这儿,方便的话给项目一个 star,你的支持是我们前进的动力!** - -使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟群友一起交流讨论。 - -新增分群 4,可以自行扫码加入,或者加我微信拉入其他群! - - -
- - -https://dynamictp.cn - -https://gitee.com/dromara/dynamic-tp - -https://github.com/dromara/dynamic-tp +--- +title: 动态线程池框架DynamicTp v1.1.4大版本发布,新增若干实用特性 +author: DynamicTp +date: 2023-09-26 +cover: /assets/img/news/DynamicTp-1.1.4-cover.png +head: + - - meta + - name: 新闻 +--- + +## DynamicTp 简介 + +DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 + +## DynamicTp 特性 + +- **代码零侵入**:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入 +- **通知告警**:提供多种报警维度(配置变更通知、活性报警、容量阈值报警、拒绝触发报警、任务执行或等待超时报警),已支持企业微信、钉钉、飞书、邮件报警,同时提供 SPI 接口可自定义扩展实现 +- **运行监控**:定时采集线程池指标数据,支持通过 MicroMeter、JsonLog 日志输出、Endpoint 三种方式,可通过 SPI 接口自定义扩展实现 +- **任务增强**:提供任务包装功能,实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper,可以支持线程池上下文信息传递 +- **多配置中心支持**:基于主流配置中心实现线程池参数动态调整,实时生效,已支持 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris、ServiceComb,同时也提供 SPI 接口可自定义扩展实现 +- **中间件线程池管理**:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警) +- **轻量简单**:基于 SpringBoot 实现,引入 starter,接入只需简单 4 步就可完成,顺利 3 分钟搞定 +- **多模式**:提供了增强线程池 DtpExecutor,IO 密集型场景使用的线程池 EagerDtpExecutor,调度线程池 ScheduledDtpExecutor,有序线程池 OrderedDtpExecutor,可以根据业务场景选择合适的线程池 +- **兼容性**:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架管理,@Bean 定义时加 @DynamicTp 注解即可 +- **可靠性**:框架提供的线程池实现 Spring 生命周期方法,可以在 Spring 容器关闭前尽可能多的处理队列中的任务 +- **高可扩展**:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等) +- **线上大规模应用**:参考美团线程池实践,美团内部已经有该理论成熟的应用经验 + +## v1.1.4 发版记录 + +- 支持 Spring 项目,SpringBoot 相关特性只在 starter 模块引入,@dragon-zhang +- 添加 jvmti 黑科技模块,方便集成管理各种三方包线程池,@dragon-zhang +- 升级 VariableLinkedBlockingQueue 到 jdk1.8 的 LinkedBlockingQueue 的实现,@yanhom +- 添加插件机制,基于此可以对框架做自定义开发扩展,@WindSearcher +- 细化告警配置,支持不同告警项配置不同接受人,@kyao +- 通知告警平台支持云之家,@chunhui_lu +- 支持 SpringBoot 1.x,@yanhom +- 第三方线程池(tomcat、undertow、dubbo、rocketmq、okhttp3 等等)支持 run_timeout、queue_timeout、reject 告警,@kyao,@yanhom + ![](/assets/img/news/DynamicTp-1.1.4-1.png) + +- 提供 Aware 扩展,可以扩展自定义线程池执行过程,@kyao +- 监控数据新增线程池别名,@zhifei + +#### Bugfix + +- 修复 Tomcat 高低版本兼容性报错问题,@yanhom +- 修复其他 agent 增强线程池后,强转 DtpRunnable 失败的问题,@yanhom +- 修复企微告警无@提醒的问题,@yanhom +- 修复企微告警配置多个接受人不能正确@的问题,@KamToHung +- 修复钉钉告警不能@所有人问题,@chenan +- 修复因 Bean 初始化顺序不确定导致的 ApplicationContextHolder npe 问题,@yanhom +- 修复修复拒绝策略为 CallerRunsPolicy 时,MdcRunnable 会删除主线程 mdc 信息的问题,@kyao + +#### Optimize + +- 优化 dtp 内部 spi 的使用,统一封装管理,@peachyy +- 部分代码优化重构,@yanhom,@KamToHung,@dragon-zhang,@kyao +- 告警信息优化,trace 信息可以自己扩展,集成内部 ELK 等平台,@yanhom + +![](/assets/img/news/DynamicTp-1.1.4-2.png) + +## 项目地址 + +**官网**:https://dynamictp.cn[1] + +**gitee 地址**:https://gitee.com/dromara/dynamic-tp[2] + +**github 地址**:https://github.com/dromara/dynamic-tp[3] + +## 加入社群 + +**看到这儿,方便的话给项目一个 star,你的支持是我们前进的动力!** + +使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟群友一起交流讨论。 + +新增分群 4,可以自行扫码加入,或者加我微信拉入其他群! + + +
+ + +https://dynamictp.cn + +https://gitee.com/dromara/dynamic-tp + +https://github.com/dromara/dynamic-tp diff --git a/src/zh/news/DynamicTp-1.1.5.md b/src/zh/news/DynamicTp-1.1.5.md index 7dd2a099a5..257dd0ef6f 100644 --- a/src/zh/news/DynamicTp-1.1.5.md +++ b/src/zh/news/DynamicTp-1.1.5.md @@ -1,82 +1,82 @@ ---- -title: 动态线程池v1.1.5发布,新增TPS、TP99等监控指标 -author: DynamicTp -date: 2023-11-01 -cover: /assets/img/news/DynamicTp-1.1.4-cover.png -head: - - - meta - - name: 新闻 ---- - -## DynamicTp 简介 - -DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 - -## DynamicTp 特性 - -- **代码零侵入**:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入 -- **通知告警**:提供多种报警维度(配置变更通知、活性报警、容量阈值报警、拒绝触发报警、任务执行或等待超时报警),已支持企业微信、钉钉、飞书、邮件报警,同时提供 SPI 接口可自定义扩展实现 -- **运行监控**:定时采集线程池指标数据,支持通过 MicroMeter、JsonLog 日志输出、Endpoint 三种方式,可通过 SPI 接口自定义扩展实现 -- **任务增强**:提供任务包装功能,实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper,可以支持线程池上下文信息传递 -- **多配置中心支持**:基于主流配置中心实现线程池参数动态调整,实时生效,已支持 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris、ServiceComb,同时也提供 SPI 接口可自定义扩展实现 -- **中间件线程池管理**:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警) -- **轻量简单**:基于 SpringBoot 实现,引入 starter,接入只需简单 4 步就可完成,顺利 3 分钟搞定 -- **多模式**:提供了增强线程池 DtpExecutor,IO 密集型场景使用的线程池 EagerDtpExecutor,调度线程池 ScheduledDtpExecutor,有序线程池 OrderedDtpExecutor,可以根据业务场景选择合适的线程池 -- **兼容性**:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架管理,@Bean 定义时加 @DynamicTp 注解即可 -- **可靠性**:框架提供的线程池实现 Spring 生命周期方法,可以在 Spring 容器关闭前尽可能多的处理队列中的任务 -- **高可扩展**:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等) -- **线上大规模应用**:参考美团线程池实践,美团内部已经有该理论成熟的应用经验 - -## v1.1.5 发版记录 - -#### Feature - -- 监控模块新增 TPS、TP99、TP95、TP50 等监控指标,@kyao,@yanhom - -![](/assets/img/news/DynamicTp-1.1.5-1.png) - -- Grafana 监控面板升级,支持更丰富指标查看,@yanhom -- 告警信息里添加系统负载,cpu 使用率,cpu 核数等指标,@yanhom - -![](/assets/img/news/DynamicTp-1.1.5-2.png) - -#### Bugfix - -- 修复 jetty 线程池代理后一直触发任务超时告警的问题,@kyao -- 修复 DtpPostProcessor 增强普通线程池后没返回代理,shutdown 原线程池的问题,@yanhom -- 修复代理三方线程池时直接继承 juc 线程池,没兼容框架内自定义线程池的场景,如 dubbo、motan 的 eager 模式,@yanhom - -#### Optimize - -- 各三方中间件线程池被代理后,原线程池优雅关闭,@yanhom -- 设置 hutool http 工具包的超时时间,@chenkangning -- 调整告警项的默认阈值,@yanhom -- 部分代码优化重构,@yanhom - -## 加入社群 - -**看到这儿,方便的话给项目一个 star,你的支持是我们前进的动力!** - -使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟 1000+群友一起交流讨论。 - -## 项目地址 - -**官网**:https://dynamictp.cn\[1\] - -**gitee 地址**:https://gitee.com/dromara/dynamic-tp\[2\] - -**github 地址**:https://github.com/dromara/dynamic-tp\[3\] - -### 参考资料 - -\[1\] - -https://dynamictp.cn: _https://dynamictp.cn_ - -\[2\] - -https://gitee.com/dromara/dynamic-tp: _https://gitee.com/dromara/dynamic-tp_ - -\[3\] - -https://github.com/dromara/dynamic-tp: _https://github.com/dromara/dynamic-tp_ +--- +title: 动态线程池v1.1.5发布,新增TPS、TP99等监控指标 +author: DynamicTp +date: 2023-11-01 +cover: /assets/img/news/DynamicTp-1.1.4-cover.png +head: + - - meta + - name: 新闻 +--- + +## DynamicTp 简介 + +DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 + +## DynamicTp 特性 + +- **代码零侵入**:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入 +- **通知告警**:提供多种报警维度(配置变更通知、活性报警、容量阈值报警、拒绝触发报警、任务执行或等待超时报警),已支持企业微信、钉钉、飞书、邮件报警,同时提供 SPI 接口可自定义扩展实现 +- **运行监控**:定时采集线程池指标数据,支持通过 MicroMeter、JsonLog 日志输出、Endpoint 三种方式,可通过 SPI 接口自定义扩展实现 +- **任务增强**:提供任务包装功能,实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper,可以支持线程池上下文信息传递 +- **多配置中心支持**:基于主流配置中心实现线程池参数动态调整,实时生效,已支持 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris、ServiceComb,同时也提供 SPI 接口可自定义扩展实现 +- **中间件线程池管理**:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警) +- **轻量简单**:基于 SpringBoot 实现,引入 starter,接入只需简单 4 步就可完成,顺利 3 分钟搞定 +- **多模式**:提供了增强线程池 DtpExecutor,IO 密集型场景使用的线程池 EagerDtpExecutor,调度线程池 ScheduledDtpExecutor,有序线程池 OrderedDtpExecutor,可以根据业务场景选择合适的线程池 +- **兼容性**:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架管理,@Bean 定义时加 @DynamicTp 注解即可 +- **可靠性**:框架提供的线程池实现 Spring 生命周期方法,可以在 Spring 容器关闭前尽可能多的处理队列中的任务 +- **高可扩展**:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等) +- **线上大规模应用**:参考美团线程池实践,美团内部已经有该理论成熟的应用经验 + +## v1.1.5 发版记录 + +#### Feature + +- 监控模块新增 TPS、TP99、TP95、TP50 等监控指标,@kyao,@yanhom + +![](/assets/img/news/DynamicTp-1.1.5-1.png) + +- Grafana 监控面板升级,支持更丰富指标查看,@yanhom +- 告警信息里添加系统负载,cpu 使用率,cpu 核数等指标,@yanhom + +![](/assets/img/news/DynamicTp-1.1.5-2.png) + +#### Bugfix + +- 修复 jetty 线程池代理后一直触发任务超时告警的问题,@kyao +- 修复 DtpPostProcessor 增强普通线程池后没返回代理,shutdown 原线程池的问题,@yanhom +- 修复代理三方线程池时直接继承 juc 线程池,没兼容框架内自定义线程池的场景,如 dubbo、motan 的 eager 模式,@yanhom + +#### Optimize + +- 各三方中间件线程池被代理后,原线程池优雅关闭,@yanhom +- 设置 hutool http 工具包的超时时间,@chenkangning +- 调整告警项的默认阈值,@yanhom +- 部分代码优化重构,@yanhom + +## 加入社群 + +**看到这儿,方便的话给项目一个 star,你的支持是我们前进的动力!** + +使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟 1000+群友一起交流讨论。 + +## 项目地址 + +**官网**:https://dynamictp.cn\[1\] + +**gitee 地址**:https://gitee.com/dromara/dynamic-tp\[2\] + +**github 地址**:https://github.com/dromara/dynamic-tp\[3\] + +### 参考资料 + +\[1\] + +https://dynamictp.cn: _https://dynamictp.cn_ + +\[2\] + +https://gitee.com/dromara/dynamic-tp: _https://gitee.com/dromara/dynamic-tp_ + +\[3\] + +https://github.com/dromara/dynamic-tp: _https://github.com/dromara/dynamic-tp_ diff --git a/src/zh/news/DynamicTp-1.1.6.md b/src/zh/news/DynamicTp-1.1.6.md index dd04ecfaae..456c99abd2 100644 --- a/src/zh/news/DynamicTp-1.1.6.md +++ b/src/zh/news/DynamicTp-1.1.6.md @@ -1,91 +1,91 @@ ---- -title: 动态线程池框架1.1.6发布,支持springboot3 -author: DynamicTp -date: 2023-12-19 -cover: /assets/img/news/DynamicTp-1.1.4-cover.png -head: - - - meta - - name: 新闻 ---- - -## DynamicTp 简介 - -DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 - -![](/assets/img/news/DynamicTp-1.1.4-cover.png) - -## DynamicTp 特性 - -**经过多个版本的迭代,目前最新版本 v1.1.6.1 具有以下特性** ✅ - -- **代码零侵入**:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入 -- **通知告警**:提供多种通知告警维度(配置变更通知、活性报警、队列容量阈值报警、拒绝触发报警、任务执行或等待超时报警),触发配置阈值实时推送告警信息,已支持企微、钉钉、飞书、邮件、云之家报警,同时提供 SPI 接口可自定义扩展实现 -- **运行监控**:定时采集线程池指标数据(20 多种指标,包含线程池维度、队列维度、任务维度、tps、tp99 等),支持通过 MicroMeter、JsonLog 两种方式,也可以通过 SpringBoot Endpoint 端点实时获取最新指标数据,同时提供 SPI 接口可自定义扩展实现 -- **任务增强**:提供任务包装功能(比 Spring 线程池任务包装更强大),实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper、OpenTelemetryWrapper,可以支持线程池上下文信息传递 -- **多配置中心支持**:支持多种主流配置中心,包括 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris、ServiceComb,同时也提供 SPI 接口可自定义扩展实现 -- **中间件线程池管理**:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警) -- **轻量简单**:使用起来极其简单,引入相应依赖,接入只需简单 4 步就可完成,顺利 3 分钟搞定,相当丝滑 -- **多模式**:提供了增强线程池 DtpExecutor,IO 密集型场景使用的线程池 EagerDtpExecutor,调度线程池 ScheduledDtpExecutor,有序线程池 OrderedDtpExecutor,可以根据业务场景选择合适的线程池 -- **兼容性**:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架管理,@Bean 定义时加 @DynamicTp 注解即可 -- **可靠性**:依靠 Spring 生命周期管理,可以做到优雅关闭线程池,在 Spring 容器关闭前尽可能多的处理队列中的任务 -- **高可扩展**:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等) -- **线上大规模应用**:参考美团线程池实践,美团内部已经有该理论成熟的应用经验 - -## v1.1.6.1 发版记录 - -#### Feature - -- 支持 springboot3、jdk17、spring6,@KamToHung,@dragon-zhang,@yanhom -- 支持 springboot 1.x、springboot 2.0.x、spring 5.0.x 等低版本,@yanhom -- 新增初始化器 DtpInitizlizer SPI 接口,可以在框架启动前做一些自定义初始化操作,@yanhom -- 支持兼容 skywalking 9.0 引入的线程池插件,1.1.5 版本在跟 skywalking 线程池插件一起使用有内存泄露问题,@yanhom -- 告警信息里新增堆内存占比相关信息,@yanhom -- 配置文件配置 dynamictp 时,新增 DtpProperties 相关属性字段自动提示功能,@yanhom - -#### Bugfix - -- 修复 allowCoreThreadTimeOut 参数为 true 时,ScheduledDtpExecutor 初始化失败的问题,@kyao -- 修复 ExecutorWrapper#threadPoolStatProvider 成员属性初始化失败问题,@KamToHung -- 修复 ALARM_EXECUTOR 没有移除 traceId,导致告警信息里的 traceId 错乱问题,@yanhom -- 修复线程池别名不一致导致 Prometheus 指标上报失败问题,@androidcj -- 修复使用注解注入 ScheduledThreadPoolExecutor 线程池报错的问题,@kyao -- 修复 ScheduledDtpExecutor 不支持超时告警的问题,@kyao -- 修复 alibaba dubbo 初始化失败问题,@yanhom - -#### Optimize - -- ThreadPoolBuilder、ThreadPoolCreator 方法完善丰富,@yanhom -- 优化 tps、tp99 等指标监控相关代码,@yanhom -- DtpProperties 配置类中一些字段默认值调整,enabledCollect=true,waitForTasksToCompleteOnShutdown=true,awaitTerminationSeconds=3,@yanhom -- 优化 JVMTI 相关模块,@dragon-zhang,@yanhom -- 完善 example,@yanhom -- 部分代码优化重构,@yanhom -- hutool、sc、sca、sb 等依赖版本升级,@yanhom - -#### Refactor - -- 重构 NacosRefresher,去掉在配置中手动指定线程池配置文件 data-id,降低接入成本,@wuhui -- 重构 ApolloRefresher,去掉在配置中手动指定线程池配置文件 namespace,降低接入成本,@BanTanger -- 重构所有 cloud 模块的 refresher,通过监听 EnvironmentChangeEvent 事件,更精准的判断是否线程池配置变动进行刷新,@yanhom - -## maven 依赖 - -**springboot3 以上用 1.1.6.1-3.x,以下用 1.1.6.1** - -## 加入社群 - -**看到这儿,方便的话给项目一个 star,你的支持是我们前进的动力!** - -使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟 1000+ 群友一起交流讨论。 - -加我 vx 拉群,备注 dynamic-tp。 - -![](/assets/img/news/DynamicTp-1.1.4-4.png) - -## 项目地址 - -**官网**:https://dynamictp.cn\[1\] - -**gitee 地址**:https://gitee.com/dromara/dynamic-tp\[2\] - -**github 地址**:https://github.com/dromara/dynamic-tp\[3\] +--- +title: 动态线程池框架1.1.6发布,支持springboot3 +author: DynamicTp +date: 2023-12-19 +cover: /assets/img/news/DynamicTp-1.1.4-cover.png +head: + - - meta + - name: 新闻 +--- + +## DynamicTp 简介 + +DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 + +![](/assets/img/news/DynamicTp-1.1.4-cover.png) + +## DynamicTp 特性 + +**经过多个版本的迭代,目前最新版本 v1.1.6.1 具有以下特性** ✅ + +- **代码零侵入**:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入 +- **通知告警**:提供多种通知告警维度(配置变更通知、活性报警、队列容量阈值报警、拒绝触发报警、任务执行或等待超时报警),触发配置阈值实时推送告警信息,已支持企微、钉钉、飞书、邮件、云之家报警,同时提供 SPI 接口可自定义扩展实现 +- **运行监控**:定时采集线程池指标数据(20 多种指标,包含线程池维度、队列维度、任务维度、tps、tp99 等),支持通过 MicroMeter、JsonLog 两种方式,也可以通过 SpringBoot Endpoint 端点实时获取最新指标数据,同时提供 SPI 接口可自定义扩展实现 +- **任务增强**:提供任务包装功能(比 Spring 线程池任务包装更强大),实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper、OpenTelemetryWrapper,可以支持线程池上下文信息传递 +- **多配置中心支持**:支持多种主流配置中心,包括 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris、ServiceComb,同时也提供 SPI 接口可自定义扩展实现 +- **中间件线程池管理**:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警) +- **轻量简单**:使用起来极其简单,引入相应依赖,接入只需简单 4 步就可完成,顺利 3 分钟搞定,相当丝滑 +- **多模式**:提供了增强线程池 DtpExecutor,IO 密集型场景使用的线程池 EagerDtpExecutor,调度线程池 ScheduledDtpExecutor,有序线程池 OrderedDtpExecutor,可以根据业务场景选择合适的线程池 +- **兼容性**:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架管理,@Bean 定义时加 @DynamicTp 注解即可 +- **可靠性**:依靠 Spring 生命周期管理,可以做到优雅关闭线程池,在 Spring 容器关闭前尽可能多的处理队列中的任务 +- **高可扩展**:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等) +- **线上大规模应用**:参考美团线程池实践,美团内部已经有该理论成熟的应用经验 + +## v1.1.6.1 发版记录 + +#### Feature + +- 支持 springboot3、jdk17、spring6,@KamToHung,@dragon-zhang,@yanhom +- 支持 springboot 1.x、springboot 2.0.x、spring 5.0.x 等低版本,@yanhom +- 新增初始化器 DtpInitizlizer SPI 接口,可以在框架启动前做一些自定义初始化操作,@yanhom +- 支持兼容 skywalking 9.0 引入的线程池插件,1.1.5 版本在跟 skywalking 线程池插件一起使用有内存泄露问题,@yanhom +- 告警信息里新增堆内存占比相关信息,@yanhom +- 配置文件配置 dynamictp 时,新增 DtpProperties 相关属性字段自动提示功能,@yanhom + +#### Bugfix + +- 修复 allowCoreThreadTimeOut 参数为 true 时,ScheduledDtpExecutor 初始化失败的问题,@kyao +- 修复 ExecutorWrapper#threadPoolStatProvider 成员属性初始化失败问题,@KamToHung +- 修复 ALARM_EXECUTOR 没有移除 traceId,导致告警信息里的 traceId 错乱问题,@yanhom +- 修复线程池别名不一致导致 Prometheus 指标上报失败问题,@androidcj +- 修复使用注解注入 ScheduledThreadPoolExecutor 线程池报错的问题,@kyao +- 修复 ScheduledDtpExecutor 不支持超时告警的问题,@kyao +- 修复 alibaba dubbo 初始化失败问题,@yanhom + +#### Optimize + +- ThreadPoolBuilder、ThreadPoolCreator 方法完善丰富,@yanhom +- 优化 tps、tp99 等指标监控相关代码,@yanhom +- DtpProperties 配置类中一些字段默认值调整,enabledCollect=true,waitForTasksToCompleteOnShutdown=true,awaitTerminationSeconds=3,@yanhom +- 优化 JVMTI 相关模块,@dragon-zhang,@yanhom +- 完善 example,@yanhom +- 部分代码优化重构,@yanhom +- hutool、sc、sca、sb 等依赖版本升级,@yanhom + +#### Refactor + +- 重构 NacosRefresher,去掉在配置中手动指定线程池配置文件 data-id,降低接入成本,@wuhui +- 重构 ApolloRefresher,去掉在配置中手动指定线程池配置文件 namespace,降低接入成本,@BanTanger +- 重构所有 cloud 模块的 refresher,通过监听 EnvironmentChangeEvent 事件,更精准的判断是否线程池配置变动进行刷新,@yanhom + +## maven 依赖 + +**springboot3 以上用 1.1.6.1-3.x,以下用 1.1.6.1** + +## 加入社群 + +**看到这儿,方便的话给项目一个 star,你的支持是我们前进的动力!** + +使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟 1000+ 群友一起交流讨论。 + +加我 vx 拉群,备注 dynamic-tp。 + +![](/assets/img/news/DynamicTp-1.1.4-4.png) + +## 项目地址 + +**官网**:https://dynamictp.cn\[1\] + +**gitee 地址**:https://gitee.com/dromara/dynamic-tp\[2\] + +**github 地址**:https://github.com/dromara/dynamic-tp\[3\] diff --git a/src/zh/news/DynamicTp-v1.2.0.md b/src/zh/news/DynamicTp-v1.2.0.md new file mode 100644 index 0000000000..5f1461ad04 --- /dev/null +++ b/src/zh/news/DynamicTp-v1.2.0.md @@ -0,0 +1,140 @@ +--- +title: 动态线程池 v1.2.0 版本发布,核心模块移除对 Spring 的依赖! +author: 2025年02月18日 09:35 +date: 2025-02-18 +cover: /assets/img/news/DynamicTp-v1.2.0-0.png +head: + - - meta + - name: 新闻 +--- + +## DynamicTp 简介 + +DynamicTp 是一款基于配置中心的轻量级动态线程池监控管理工具,主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 + +![](/assets/img/news/DynamicTp-v1.2.0-0.png) + +## DynamicTp 特性 + +**经过多个版本的迭代,目前最新版本 v1.2.0 具有以下特性** ✅ + +![](/assets/img/news/DynamicTp-v1.2.0-1.png) + +## v1.2.0 升级注意事项 + +* Spring 启动类注解移动到 Spring 模块中,包路径有调整,需重新导入 + + +``` +org.dromara.dynamictp.spring.annotation.EnableDynamicTp +``` + +* 配置文件前缀有调整,从 spring.dynamic.tp 调整为 dynamictp + + +``` +dynamictp: +  enabled: true +  enabledBanner: true +  enabledCollect: true +``` + +## v1.2.0 发版记录 + +v1.1.9 及之前版本核心模块强依赖 Spring,代码中用到了不少 Spring 的特性,这样不利于其他非 Spring 项目的接入集成。 + +v1.2.0 是一个大版本,主要功能是对 Spring 做了解耦,Spring 相关特性以独立 Module 的形式提供,其他非 Spring 框架集成 DynamicTp 时通过引入核心模块即可。 + +#### Feature + +* 核心模块中移除对 Spring 的依赖,方便在其他非 Spring 项目中使用。 + + +``` +https://github.com/dromara/dynamic-tp/issues/527 +``` + +#### Bugfix + +* 兼容当 Dubbo 版本在 3.0.9 到 3.1.8 之间时,需要替换的执行器命名为 INTERNAL\_SERVICE\_EXECUTOR,而不是 ExecutorService.class.getName()。 + + +``` +https://github.com/dromara/dynamic-tp/pull/495 +``` + +* 修复 Redis 限流器在 Redis Cluster 模式下报错问题。 + + +``` +https://github.com/dromara/dynamic-tp/pull/502 +``` + +* 修复执行 scheduledFuture cancel 报错问题。 + + +``` +https://github.com/dromara/dynamic-tp/pull/516 +``` + +* 修复 adapter-grpc 模块,grpc client channel executor 被关闭,调用报线程池关闭的错误。 + + +``` +https://github.com/dromara/dynamic-tp/pull/520 +``` + +* 兼容高版本 okhttp3 Dispatcher 中线程池字段为 executorServiceOrNull 的情况。 + + +``` +https://github.com/dromara/dynamic-tp/pull/525 +``` + +#### Optimize + +* 优化线程池配置文件提示功能。 + + +``` +https://github.com/dromara/dynamic-tp/pull/498 +``` + +* etcd kvClient get 添加超时时间控制。 + + +``` +https://github.com/dromara/dynamic-tp/pull/518 +``` + +* zookeeper-starter 客户端初始化支持 zk 认证。 + + +``` +https://gitee.com/dromara/dynamic-tp/pulls/61 +``` + +* 部分代码设计优化重构 + + +## 加入社群 + +以上就是本次发版的全部内容。欢迎大家升级体验! + +使用过程中有任何问题,或者对项目有什么想法或者建议,可以加入社群,跟 1500+ 群友一起交流讨论。 + + + +![](/assets/img/news/DynamicTp-v1.2.0-2.png) + +## 项目地址 + +``` + +官网:https://dynamictp.cn + +gitee:https://gitee.com/dromara/dynamic-tp + +github:https://github.com/dromara/dynamic-tp + +``` \ No newline at end of file diff --git a/src/zh/news/Easy-Es-2.1.0.md b/src/zh/news/Easy-Es-2.1.0.md new file mode 100644 index 0000000000..42aa7abbd1 --- /dev/null +++ b/src/zh/news/Easy-Es-2.1.0.md @@ -0,0 +1,90 @@ +--- +title: 易水寒· 星火燎原 (Easy-Es 2.1.0 焚天破阵) +author: 老汉 +date: 2025-02-06 +cover: /assets/img/news/Easy-Es-2.1.0-0.png +head: + - - meta + - name: 新闻 +--- + +[![](/assets/img/news/Easy-Es-2.1.0-0.png)](https://mp.weixin.qq.com/s?__biz=MzUzNTY2NjAzMg==&mid=2247489578&idx=1&sn=9dd419422155a093397d183257e0e797&scene=21#wechat_redirect) + + +**易水寒· 星火燎原 (Easy-Es 2.1.0 焚天破阵)** + +![](/assets/img/news/Easy-Es-2.1.0-1.png)却说老汉于定军山一役虽险胜,然头顶不毛之地日显荒凉,江湖人称"中分战神"。某夜观星象,忽见一黑子星动,西南方隐现赤芒,掐指一算:"不妙!此乃三刀余孽借塞外邪术卷土重来之兆!" + +![](/assets/img/news/Easy-Es-2.1.0-2.jpg) + +2 + +话音未落,探子急报:三刀残部得西域"Elastic圣教"秘传,以**七剑锁魂阵**困我城池,阵中暗藏七重杀机: + +一曰 **版本桎梏**(RestHighLevelClient 7.14),二曰 **框架孤岛**(Spring旧制),三曰 **索引迷踪**(正则通配缺失),四曰 **校验悖论**(Condition冗余),五曰 **逆向天堑**(实体类难生),六曰 **空箱惊雷**(Boost空指针),七曰 **乱序绝杀**(排序冲突)。七剑齐发,汉军危矣! + +![](/assets/img/news/Easy-Es-2.1.0-3.jpg) + +3 + +正当老汉焦头烂额之际,忽闻云中仙乐缭绕,一青衣道人踏虹而至,其袍绣"Solon"星纹,手持玄铁令牌喝道:"吾乃南华座下首徒'无耳真人',特来解汝七剑之困!" 言罢掷出三卷锦囊: + +**卷一· 混元破阵诀(框架适配)** +"以Solon之轻灵融Spring之浑厚,可破版本桎梏!" 真人指尖轻点,顿见RestHighLevelClient直升**7.17.8**,更以阴阳调和之术兼容**ES7.x/8.x**双生内核。昔日Spring旧部亦得秘法续命,新旧两军合流,剑气直冲云霄! + +> 释义:适配国产Solon框架,适配Spring,并在原来es7.x基础上兼容了es8.x,使得框架具备在更多更广泛系统中使用的能力. + +**卷二· 天工造物篇(功能革新)** +真人挥袖间祭出三大神器: + +* **正则通灵符**(拦截器支持正则通配):索引迷雾尽散,万般字段皆可隔空取物! + +* **条件断舍离**(Condition逻辑优化):非真不验,省去三成内力损耗! + +* **千目观星术**(MapperScan多包扫描):纵有百座代码山,亦能一镜收眼底! + + +> 释义:拦截器插件支持正则通配方法功能;MapperScan支持多包扫描;Condition判断逻辑优化. + +更授**逆向筑城术**:索引蓝图入炉,顷刻炼出实体类傀儡大军,建城速度暴涨十倍! + +> 释义:新增逆向工程,可根据索引结构全自动生成实体类,解放双手. + +**卷三· 补天回春手(缺陷修复)** +真人剑指苍穹:"Boost空箱?且看老夫点石成金!" NPE惊雷瞬息湮灭;又布**时序归一咒**,乱序绝杀之危迎刃而解。汉军旧伤尽愈,士气大振!加之**分身术**,copy\_to嘎嘎乱杀. + +> 释义:修复一处Boost未指定可能导致NPE的缺陷,修复混合查询中设置排序后,searcAfter查询报错问题,新增copy\_to索引功能,可将多个字段值复制到指定字段中,特定场景下提升查询性能. + +三刀残部见阵法被破,急欲遁走。老汉冷笑:"昔日尔等诈降,今日且看真火!" 祭出新悟绝学**【Solon-ES联动技】**,但见: + +* 注解驱动如流星追月,配置简化九重 + +* 依赖注入似江河流转,组件互通无碍 + +* 生态融合若乾坤倒转,插件即插即用 + + +![](/assets/img/news/Easy-Es-2.1.0-4.png) + +一时间天地色变,七剑锁魂阵反噬其主,三刀残部灰飞烟灭! + +战后真人抚须而笑:"Easy-Es今得Solon真传,轻量更胜往昔,性能犹似鬼魅。从今往后,国产框架江湖当以汝为尊!" 言毕化作青烟消散,唯留星图一张,上书文档地址: +**https://easy-es.cn** + +而今老汉头顶中分竟生新芽,众人惊问其故,答曰:"此乃万千侠士以**Watch/Star/Fork**三昧真火滋养所致!" 遂舞剑于易水之畔,剑风所过处,代码如落英缤纷,Bug似朝露遇阳。 +![](/assets/img/news/Easy-Es-2.1.0-5.jpg) + +忽有少年问:"既已无敌,为何仍夜观星象?" +老汉遥望东方:"Elasticsearch 9.0将出...江湖,永远需要下一个版本。" + +(幕落,BGM起:《易水寒》-"代码潮涌千帆竞,开源星火可燎原。") + +![](/assets/img/news/Easy-Es-2.1.0-6.jpg) + +**汉庙香火 为EE点亮Star 禳星续命**: + +Gitee: https://gitee.com/dromara/easy-es + +Gitcode: https://gitcode.com/dromara/easy-es  + +GitHub: https://github.com/dromara/easy-es \ No newline at end of file diff --git a/src/zh/news/EasyAI-0.md b/src/zh/news/EasyAI-0.md index 61a0488c27..9fa0821365 100644 --- a/src/zh/news/EasyAI-0.md +++ b/src/zh/news/EasyAI-0.md @@ -1,101 +1,101 @@ ---- -title: 2024第一弹,JAVA原生AI算法引擎 EasyAI 加入Dromara开源社区 -author: EasyAI -date: 2024-01-05 -cover: /assets/img/news/EasyAI-0-0.png -head: - - - meta - - name: 新闻 ---- - -> **如今 AI 项目无论在工业领域还是生活领域都开始逐渐深入,ChatGPT,文心一言等大模型更是如火如荼,让我们看到了 AI 的强大。不知道多少小伙伴想快速涉猎到 AI 领域呢?因为各种原因 JAVA 在 AI 领域一直是传统弱项,但是 JAVA 程序员却占据了国内程序员就业的半壁江山,所以这次我带来的 java 原生开源 AI 算法引擎-easyAI(不依赖任何第三方库,开箱即用的引擎),介绍给大家。** - ---- - -# EasyAI - JAVA 原生 AI 算法引擎 - -## 作者介绍 - -- dromara 开源组织成员,dromara/easyAI 作者。 -- 2 年 JAVA 开发工程师,2 年游戏开发工程师,5 年图像/自然语言算法研究员与工程师。从业 IT 九年,做算法工作时产出视觉与语言算法发明专利共三件(独立发明),项目若干。工作经历以图像算法为主,语言为辅。做游戏工作时休闲小游戏产出不计其数,做 JAVA 工作时以中间件研发工作为主。 - -## 背景与简介 - -**广大公司,尤其是中小公司是不是遇到过以下痛点?** - -> 1.公司后端突然需要一个 AI 模块来辅助某业务场景,但是因为传统业务关系公司后端大部分都是 java 程序员。去招个资深的算法工程师吧,先不说招聘的时间成本,就因为这么一个需求就去找一个价格这么昂贵的人员也太不经济,最后只能无奈外包。 -> -> 2.突然有了某个 AI 模块需求,用了很长的时间成本从网上买了一套做好的 AI 模块,结果人家是 py 或者 cpp。动不动还要装 cuda 环境(但凡装过 cuda 环境的都知道多少坑),有时候要考虑环境,有时候还要内嵌,要考虑 py/cpp 与 java 互相调用,还有因此而来的效率与安全问题等等。我更希望是完美兼容我的系统,我不想去繁琐的调整运行环境,考虑各种兼容性,我想要直接引入包到我的 maven 里就能开箱即用。 -> -> 3.我去!chatGPT 好牛逼,人用的真爽,可是它不能给我的系统服务。它是大模型很好,但是我的系统不需要大模型!我需要的是一个我可以自定义,去针对我们业务环境的小模型来嵌入我们的系统,为系统服务!大模型是给人用的,但是不能内嵌我们的系统给我们的系统服务。 - -- 如果你有以上痛点,easyAI 就帮到你了!easyAI 是一个纯 java 原生算法引擎。其底层从矩阵运算,微分求导。中层到机器学习 强化学习等各种算法,然后最上层实现了图像视觉,自然语言全部包含在 easyAi 引擎内。 -- 这种从最底层的基础运算到上层 AI 算法上下游的全囊括,使得 java 项目只要引入 easyAi 的 maven 包,就可以做到无缝调用,且可以不依赖任何第三库,让 java 程序员舒服的一批! -- 然而这还没完,easyAi 是引擎,是为 java 开发人员服务的,所以我要封装的更彻底!因此我又封装了完整的依赖于 easyAi 引擎的 AI 业务应用,共大家无脑调用!大家可以直接使用,或者修改后再融入到到自己的业务中。 -- 到了此时 easyAI 才可以既满足了,了解算法知识,可以利用 easyAI 内置中底层算法工具深入开发的算法工作人员。也满足了只想利用现成应用业务代码,去满足自己系统内嵌,服务与自己系统 AI 的普通 java 业务开发人员的需求。 -- 然后再强调一点,easyAI 并不是对主流算法 JAVA 的无差别重新实现,而是根据应用场景对主流算法进行了优化与魔改,让用户即便使用一台普通的电脑,就可开箱直接跑起来。并且保证普通服务器或个人电脑 CPU 下依然达到可用性能的流畅运行。所以我的“easy”并不是只是指的简单,而是对算法进行了廉价,低成本方向的优化。如果我没有办法对某种算法做到廉价优化的算法实现,我也不会放入 easyAI 里面。 -- easyAI 的核心理念,为 java 开发者服务是一方面,另一方面是专注于针对性业务,高性能,低成本优化方向的中小模型的算法引擎 - -### 综上,我们总结出以下的背景 - -- 广泛性:easyAI 可以为占据国内半数程序员以上的 JAVA 业务程序员开发 AI 业务,提供底层算法引擎动力。 -- 深入性:easyAI 无任何依赖,从底层基础算法工具到上层算法模型全囊括,可以支持专业性的深度开发,也可提供简单直接的业务层嵌入 AI,而且这一点在持续扩张中。 -- 廉价性,主要关注针对性可嵌入业务系统中小模型,低训练样本需求,低算力需求,这两个方向去优化算法模型是 easyAI 的方向。 -- 兼容性,因为是原生 JAVA 研发,无任何第三库依赖,所以对 JAVA 项目,引入包后就开箱即用,无任何环境异常会导致的问题。 - -## EasyAI 应用举例 - -- easyAI 下载链接:https://gitee.com/dromara/easyAi -- easyAI 主要 API 详情文档:https://wlhlwl.com/gw/easyAi.html -- easyAI 详细视频教程:https://www.bilibili.com/video/av89134035 -- easyAI 是算法引擎,我们要直观感受它的强大,要从依赖它构建的应用化项目 DEMO 来看,这次我们拿出其中之一的应用案例--强大的自主智能客服工具,支持自动与用户对话,并捕捉用户对话中的需求后自动生成订单!可自主解答用户问题或进行诱导消费!基于 easyAi 算法引擎构建智脑——myJecs - -## myJecs 分类配置及标注后台 - -- 登录页面![](/assets/img/news/EasyAI-0-0.png) -- 配置业务分类及分类订单必要关键信息![](/assets/img/news/EasyAI-0-1.png) -- 对分类业务填写训练样本并标注关键信息![](/assets/img/news/EasyAI-0-2.png) -- 聊天咨询标注直接输入表位置![](/assets/img/news/EasyAI-0-3.png) - -## myJecs 智能客服基本流程演示 - -- 用户第一次进行输入表达自己的想法![](/assets/img/news/EasyAI-0-4.png) -- myJecs 发现用户的描述缺少订单必要信息,则进行反问。用户接收到 myJecs 的反问,进一步补充的自己的想法![](/assets/img/news/EasyAI-0-5.png) -- 用户第二次输入信息依然不满足后台 14 分类法律咨询的订单关键信息要求,继续补充信息,最终完成订单信息补充生成订单。![](/assets/img/news/EasyAI-0-6.png) -- 用户输入想要咨询的问题,myJecs 对用户咨询的问题进行自主解答![](/assets/img/news/EasyAI-0-7.png) - -#### 以上是 myJecs 自定义业务接口的简单案例演示,具体安装部署及细节详情请到其主页下载: https://gitee.com/ldp\_dpsmax/my-jecs - -## 架构设计 - -**常用底层算法模块** - -- 基础矩阵及线代计算模块: - - 1.内置矩阵类,矩阵计算类,可以完成常用矩阵四则运算,奇偶性,多元线性回归,逻辑斯蒂回归,欧式距离,余弦相似性,im2col,逆 im2col,求代数余子式,求逆,求伴随矩阵,内积等,微分等一系列 api。 - - 2.RGB 三通道矩阵,可进行图像转化,剪切,分块,生成图像矩阵等操作方便后续计算。 - -- 机器学习-聚类: - - k 聚类,混合高斯聚类,密度聚类,学习向量量化聚类等 - -- 机器学习-分类及拟合:多层前馈神经网络,多层循环神经网络,残差网络,多层残差循环神经网络,卷积神经网络,决策树,随机森林,k 最近邻等 -- 启发式算法:粒子群,蚁群,模拟退火 -- 强化学习 动态规划,蒙特卡洛分析,马尔可夫,时序差分 - -**常用上层算法模块** - -- 视觉图像:图像识别,图片摘要,目标检测 -- 自然语言:语义理解,拆词分词,推理敏感及关键词,语句补全,语言交流 -- 游戏机器人:自主策略,自主行动 - -## 使用 - -1.将项目下载后打包进本地 maven 库 - -2.将 easyAi pom 文件引入地址引入项目 - -## 关注项目 - -对项目有什么想法或者建议,可以加我 qq 交流群(222475213)或 vx:thenk008,或者创建 issues,一起完善项目。**https://gitee.com/dromara/easyAi** +--- +title: 2024第一弹,JAVA原生AI算法引擎 EasyAI 加入Dromara开源社区 +author: EasyAI +date: 2024-01-05 +cover: /assets/img/news/EasyAI-0-0.png +head: + - - meta + - name: 新闻 +--- + +> **如今 AI 项目无论在工业领域还是生活领域都开始逐渐深入,ChatGPT,文心一言等大模型更是如火如荼,让我们看到了 AI 的强大。不知道多少小伙伴想快速涉猎到 AI 领域呢?因为各种原因 JAVA 在 AI 领域一直是传统弱项,但是 JAVA 程序员却占据了国内程序员就业的半壁江山,所以这次我带来的 java 原生开源 AI 算法引擎-easyAI(不依赖任何第三方库,开箱即用的引擎),介绍给大家。** + +--- + +# EasyAI - JAVA 原生 AI 算法引擎 + +## 作者介绍 + +- dromara 开源组织成员,dromara/easyAI 作者。 +- 2 年 JAVA 开发工程师,2 年游戏开发工程师,5 年图像/自然语言算法研究员与工程师。从业 IT 九年,做算法工作时产出视觉与语言算法发明专利共三件(独立发明),项目若干。工作经历以图像算法为主,语言为辅。做游戏工作时休闲小游戏产出不计其数,做 JAVA 工作时以中间件研发工作为主。 + +## 背景与简介 + +**广大公司,尤其是中小公司是不是遇到过以下痛点?** + +> 1.公司后端突然需要一个 AI 模块来辅助某业务场景,但是因为传统业务关系公司后端大部分都是 java 程序员。去招个资深的算法工程师吧,先不说招聘的时间成本,就因为这么一个需求就去找一个价格这么昂贵的人员也太不经济,最后只能无奈外包。 +> +> 2.突然有了某个 AI 模块需求,用了很长的时间成本从网上买了一套做好的 AI 模块,结果人家是 py 或者 cpp。动不动还要装 cuda 环境(但凡装过 cuda 环境的都知道多少坑),有时候要考虑环境,有时候还要内嵌,要考虑 py/cpp 与 java 互相调用,还有因此而来的效率与安全问题等等。我更希望是完美兼容我的系统,我不想去繁琐的调整运行环境,考虑各种兼容性,我想要直接引入包到我的 maven 里就能开箱即用。 +> +> 3.我去!chatGPT 好牛逼,人用的真爽,可是它不能给我的系统服务。它是大模型很好,但是我的系统不需要大模型!我需要的是一个我可以自定义,去针对我们业务环境的小模型来嵌入我们的系统,为系统服务!大模型是给人用的,但是不能内嵌我们的系统给我们的系统服务。 + +- 如果你有以上痛点,easyAI 就帮到你了!easyAI 是一个纯 java 原生算法引擎。其底层从矩阵运算,微分求导。中层到机器学习 强化学习等各种算法,然后最上层实现了图像视觉,自然语言全部包含在 easyAi 引擎内。 +- 这种从最底层的基础运算到上层 AI 算法上下游的全囊括,使得 java 项目只要引入 easyAi 的 maven 包,就可以做到无缝调用,且可以不依赖任何第三库,让 java 程序员舒服的一批! +- 然而这还没完,easyAi 是引擎,是为 java 开发人员服务的,所以我要封装的更彻底!因此我又封装了完整的依赖于 easyAi 引擎的 AI 业务应用,共大家无脑调用!大家可以直接使用,或者修改后再融入到到自己的业务中。 +- 到了此时 easyAI 才可以既满足了,了解算法知识,可以利用 easyAI 内置中底层算法工具深入开发的算法工作人员。也满足了只想利用现成应用业务代码,去满足自己系统内嵌,服务与自己系统 AI 的普通 java 业务开发人员的需求。 +- 然后再强调一点,easyAI 并不是对主流算法 JAVA 的无差别重新实现,而是根据应用场景对主流算法进行了优化与魔改,让用户即便使用一台普通的电脑,就可开箱直接跑起来。并且保证普通服务器或个人电脑 CPU 下依然达到可用性能的流畅运行。所以我的“easy”并不是只是指的简单,而是对算法进行了廉价,低成本方向的优化。如果我没有办法对某种算法做到廉价优化的算法实现,我也不会放入 easyAI 里面。 +- easyAI 的核心理念,为 java 开发者服务是一方面,另一方面是专注于针对性业务,高性能,低成本优化方向的中小模型的算法引擎 + +### 综上,我们总结出以下的背景 + +- 广泛性:easyAI 可以为占据国内半数程序员以上的 JAVA 业务程序员开发 AI 业务,提供底层算法引擎动力。 +- 深入性:easyAI 无任何依赖,从底层基础算法工具到上层算法模型全囊括,可以支持专业性的深度开发,也可提供简单直接的业务层嵌入 AI,而且这一点在持续扩张中。 +- 廉价性,主要关注针对性可嵌入业务系统中小模型,低训练样本需求,低算力需求,这两个方向去优化算法模型是 easyAI 的方向。 +- 兼容性,因为是原生 JAVA 研发,无任何第三库依赖,所以对 JAVA 项目,引入包后就开箱即用,无任何环境异常会导致的问题。 + +## EasyAI 应用举例 + +- easyAI 下载链接:https://gitee.com/dromara/easyAi +- easyAI 主要 API 详情文档:https://wlhlwl.com/gw/easyAi.html +- easyAI 详细视频教程:https://www.bilibili.com/video/av89134035 +- easyAI 是算法引擎,我们要直观感受它的强大,要从依赖它构建的应用化项目 DEMO 来看,这次我们拿出其中之一的应用案例--强大的自主智能客服工具,支持自动与用户对话,并捕捉用户对话中的需求后自动生成订单!可自主解答用户问题或进行诱导消费!基于 easyAi 算法引擎构建智脑——myJecs + +## myJecs 分类配置及标注后台 + +- 登录页面![](/assets/img/news/EasyAI-0-0.png) +- 配置业务分类及分类订单必要关键信息![](/assets/img/news/EasyAI-0-1.png) +- 对分类业务填写训练样本并标注关键信息![](/assets/img/news/EasyAI-0-2.png) +- 聊天咨询标注直接输入表位置![](/assets/img/news/EasyAI-0-3.png) + +## myJecs 智能客服基本流程演示 + +- 用户第一次进行输入表达自己的想法![](/assets/img/news/EasyAI-0-4.png) +- myJecs 发现用户的描述缺少订单必要信息,则进行反问。用户接收到 myJecs 的反问,进一步补充的自己的想法![](/assets/img/news/EasyAI-0-5.png) +- 用户第二次输入信息依然不满足后台 14 分类法律咨询的订单关键信息要求,继续补充信息,最终完成订单信息补充生成订单。![](/assets/img/news/EasyAI-0-6.png) +- 用户输入想要咨询的问题,myJecs 对用户咨询的问题进行自主解答![](/assets/img/news/EasyAI-0-7.png) + +#### 以上是 myJecs 自定义业务接口的简单案例演示,具体安装部署及细节详情请到其主页下载: https://gitee.com/ldp\_dpsmax/my-jecs + +## 架构设计 + +**常用底层算法模块** + +- 基础矩阵及线代计算模块: + + 1.内置矩阵类,矩阵计算类,可以完成常用矩阵四则运算,奇偶性,多元线性回归,逻辑斯蒂回归,欧式距离,余弦相似性,im2col,逆 im2col,求代数余子式,求逆,求伴随矩阵,内积等,微分等一系列 api。 + + 2.RGB 三通道矩阵,可进行图像转化,剪切,分块,生成图像矩阵等操作方便后续计算。 + +- 机器学习-聚类: + + k 聚类,混合高斯聚类,密度聚类,学习向量量化聚类等 + +- 机器学习-分类及拟合:多层前馈神经网络,多层循环神经网络,残差网络,多层残差循环神经网络,卷积神经网络,决策树,随机森林,k 最近邻等 +- 启发式算法:粒子群,蚁群,模拟退火 +- 强化学习 动态规划,蒙特卡洛分析,马尔可夫,时序差分 + +**常用上层算法模块** + +- 视觉图像:图像识别,图片摘要,目标检测 +- 自然语言:语义理解,拆词分词,推理敏感及关键词,语句补全,语言交流 +- 游戏机器人:自主策略,自主行动 + +## 使用 + +1.将项目下载后打包进本地 maven 库 + +2.将 easyAi pom 文件引入地址引入项目 + +## 关注项目 + +对项目有什么想法或者建议,可以加我 qq 交流群(222475213)或 vx:thenk008,或者创建 issues,一起完善项目。**https://gitee.com/dromara/easyAi** diff --git a/src/zh/news/EasyAI-v1.3.8.md b/src/zh/news/EasyAI-v1.3.8.md new file mode 100644 index 0000000000..c671066456 --- /dev/null +++ b/src/zh/news/EasyAI-v1.3.8.md @@ -0,0 +1,203 @@ +--- +title: 原生Java人工智能算法框架 EasyAI-v1.3.8版本更新 +author: easyAI +date: 2025-03-11 +cover: /assets/img/news/EasyAI-v1.3.8-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/EasyAI-v1.3.8-0.png) + +# 前言 + +EasyAi的出现对于Java的意义,等同于在JavaWeb领域spring出现的意义一样——做一个开箱即用,让每一个开发者都可以使用EasyAi,来开发符合自己人工智能业务需求的小微模型,这就是它的使命! + +# EasyAi介绍 + +EasyAi无任何依赖,它是一个原生Java人工智能算法框架。首先,**它可以Maven一键丝滑引入我们的Java项目,无需任何额外的环境配置与依赖,做到开箱即用**。再者,它既有一些我们已经封装好的图像目标检测及人工智能客服的模块,也提供各种深度学习,机器学习,强化学习,启发式学习,矩阵运算,等底层算法工具。开发者可以通过简单的学习,就能完成根据自身业务,深度开发符合自己业务的小微模型。 + +* EasyAI码云下载链接:https://gitee.com/dromara/easyAi + +* EasyAI技术文档地址:https://www.myeasyai.cn/ + +* EasyAI详细视频教程:https://www.bilibili.com/video/av89134035 + +* EasyAI框架0基础深度开发及人工智能完整体系教程:https://www.bilibili.com/cheese/play/ss17600 + + +# v1.3.8 更新内容 + +## 图像抠图 + +* 对图像语义进行像素级切割,前者输入原图像,后者进行输出抠图后的图像 + + +![](/assets/img/news/EasyAI-v1.3.8-1.png)![](/assets/img/news/EasyAI-v1.3.8-2.png) + +## 如何使用gpu加速 + +### 环境要求(显卡、驱动、cuda) + +* 机器必须有`NVIDIA显卡`,因为EasyAI是基于CUDA进行GPU加速,而非`NVIDIA显卡`(如`AMD显卡`)都不支持CUDA + +* 机器的显卡驱动必须支持`CUDA12.0.0`或更高的版本(通常支持更高版本,则也能支持较低版本) + +* 安装`CUDA12.0.0`,以及该CUDA版本对应的`cuDNN`。安装可参考:https://blog.csdn.net/m0\_45447650/article/details/123704930 + + +### easyai和cuda扩展的版本对应关系。 + + + +| +EasyAI版本 + + | + +CUDA扩展版本 + + | + +平台架构 + + | +| --- | --- | --- | +| + +≥1.3.4 + + | + +1.0.0 + + | + +windows-x86\_64  + inux-x86\_64 + + | + + + +### 引入cuda扩展依赖 + +根据依赖的EasyAI版本选择对应的CUDA扩展版本即可 + +``` + +    org.dromara.easyai.extensions +    easyai-extensions-cuda-12.0.0 +    ${CUDA扩展版本} +    ${平台架构} + +``` + +## 运行&验证 + +前面的步骤都已成功完成后,直接调用EasyAI进行训练,如果`控制台`输出以下内容,则说明CUDA扩展集成成功。 + +``` +EasyAI CUDA-12.0.0 extensions init success. +``` + +### easyAI包已上传至Maven中央库 + +> 以后在pom文件引入以下JAR包地址即可,无需进行下载手动打包。 + +``` +    +    org.dromara.easyai +    easyAi +    1.3.8 +    +``` + + + +## 人脸检测效果演示 + +![](/assets/img/news/EasyAI-v1.3.8-3.png) + +### 图像识别FastYolo效果展示 + +* 使用EasyAi实现图像结算自动贩卖机视觉内核 + + +![](/assets/img/news/EasyAI-v1.3.8-4.jpg) + +### sayOrder人工智能客服 + +* sayOrder是依赖EasyAi进行封装的人工智能客服系统。 + +* 它可以分析用户输入的语义,来识别用户的行为,并通过typeID来区分用户意图ID。并通过捕捉其后台设置的关键词类别,来抓出系统关心的用户在语句中包含的内容,比如语句中的时间,地点等。 + +* 它还可以与用户自主进行问答交互,进行自主解答疑问或者进行其余意图的交流等。 + +* 项目链接地址: https://gitee.com/dromara/sayOrder + + +### sayOrder交互基本业务流程演示 + +* 用户第一次进行输入表达自己的想法![](/assets/img/news/EasyAI-v1.3.8-5.png) + +* SayOrder发现用户的描述缺少订单必要信息,则进行反问。用户接收到SayOrder的反问,进一步补充的自己的想法![](/assets/img/news/EasyAI-v1.3.8-6.png) + +* 用户第二次输入信息依然不满足后台14分类法律咨询的订单关键信息要求,继续补充信息,最终完成订单信息补充生成订单。![](/assets/img/news/EasyAI-v1.3.8-7.png) + +* 用户输入想要咨询的问题,SayOrder对用户咨询的问题进行自主解答![](/assets/img/news/EasyAI-v1.3.8-8.png) + + +### 架构设计 + +**常用底层算法模块** + +* 基础矩阵及线代计算模块: + + 1.内置矩阵类,矩阵计算类,可以完成常用矩阵四则运算,奇偶性,多元线性回归,逻辑斯蒂回归,欧式距离,余弦相似性,im2col,逆im2col,求代数余子式,求逆,求伴随矩阵,内积等,微分等一系列api。 + + 2.RGB三通道矩阵,可进行图像转化,剪切,分块,生成图像矩阵等操作方便后续计算。 + +* 机器学习-聚类: + + k聚类,混合高斯聚类,密度聚类,学习向量量化聚类等 + +* 机器学习-分类及拟合: 多层前馈神经网络,多层循环神经网络,残差网络,多层残差循环神经网络,卷积神经网络,决策树,随机森林,k最近邻等 + +* 启发式算法: 粒子群,蚁群,模拟退火 + +* 强化学习 动态规划,蒙特卡洛分析,马尔可夫,时序差分 + + +**常用上层算法模块** + +* 视觉图像: 图像识别,图片摘要,目标检测 + +* 自然语言: 语义理解,拆词分词,推理敏感及关键词,语句补全,语言交流 + +* 游戏机器人: 自主策略,自主行动 + + +### 使用 + +1.将项目下载后打包进本地maven库 + +2.将easyAi pom文件引入地址引入项目 + +### 关注项目 + +* 对项目有什么想法或者建议与学习,都可以加我工作微信![](/assets/img/news/EasyAI-v1.3.8-9.jpg) + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/EasyAI-v1.3.8-10.webp) \ No newline at end of file diff --git a/src/zh/news/ElectronEgg-v4.md b/src/zh/news/ElectronEgg-v4.md new file mode 100644 index 0000000000..b2d0b173d3 --- /dev/null +++ b/src/zh/news/ElectronEgg-v4.md @@ -0,0 +1,150 @@ +--- +title: ElectronEgg v4 发布,全新的开发体验 +author: ElectronEgg +date: 2025-03-04 +cover: /assets/img/news/ElectronEgg-v4-0.jpg +head: + - - meta + - name: 新闻 +--- + +大家好,`electron-egg v4` 终于发布。这个版本是对过去几年EE框架的全面优化,对于我们来说,应该算的上一个理想的版本了,未来几年应该不会有大的改动,当然也会跟进对 electron 的兼容性升级,推荐更新。 + +v4 重构了框架核心,提供更好的开发体验、加密、ts支持、代码优化、结构调整等等。 + +目前,框架已经广泛应用于记账、政务、企业、医疗、学校、股票交易、ERP、娱乐、视频等领域客户端,请放心使用! + + + +![](/assets/img/news/ElectronEgg-v4-0.jpg) + + + + + +### 为什么使用 + +桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 + +electron 技术是流行趋势,QQ、抖音、B站、百度翻译、阿里网盘、迅雷、有道云笔记 ...... + +### 开源 + +gitee:https://gitee.com/dromara/electron-egg 5300+ + +github:https://github.com/dromara/electron-egg 2000+ + +### 本次更新 + +### 4.0.0 + +1. 【增加】ee-core 增加 ts 支持,添加类型定义 + +2. 【优化】ee-core 重构代码,提供更标准的api + +3. 【优化】ee-core 增加app模块,新的框架启动流程 + +4. 【优化】ee-core config 重写配置加载逻辑 + +5. 【优化】ee-core controller 重写控制器加载逻辑 + +6. 【优化】ee-core core 精简 core 模块,去除冗余的代码和功能 + +7. 【优化】ee-core electron 重写功能,提供 api + +8. 【优化】ee-core jobs 优化 + +9. 【优化】ee-core loader 去除冗余的方法 + +10. 优化】ee-core log 优化 + +11. 【增加】ee-core ps 去除有歧义的 api,新增 appVersion、getDataDir、getBundleDir、getBaseDir、getUserHomeDir、getUserHomeAppDir、getUserHomeHiddenAppDir + +12. 【优化】ee-core socket 优化 + +13. 【优化】ee-core storage 去除 jsondb, sqlitedb修改存储路径和类型支持 + +14. 【优化】ee-core utils 优化 + +15. 【增加】ee-bin 新增 ts 支持,添加 esbuild 构建工具 + +16. 【增加】ee-bin 新增对前端代码加密 + +17. 【优化】ee-bin 优化 热重载功能 + +18. 【优化】ee-bin 修改配置文件 + +19. 【优化】ee-bin 优化 build 功能 + +20. 【优化】ee-bin 修改 move 命令 + +21. 【升级】ee-bin@4.1.4 & ee-core@4.0.1 + +22. 【升级】node@20.16.0 & electron@31.7.6 + + +### 下载 + +``` +# gitee +git clone https://gitee.com/dromara/electron-egg.git + +# github +git clone https://github.com/dromara/electron-egg.git +``` + +### 安装 + +``` +# 根目录,安装 electron 依赖 +npm i + +# 进入【前端目录】安装 frontend 依赖 +cd frontend  +npm i +``` + +### 运行项目 + +``` +npm run start +``` + +## +  + +### 用户案例展示 + +#### 云笔记 + +![](/assets/img/news/ElectronEgg-v4-1.jpg) + +![](/assets/img/news/ElectronEgg-v4-2.png) + +#### 远控 + +![](/assets/img/news/ElectronEgg-v4-3.png) + +![](/assets/img/news/ElectronEgg-v4-4.png) + +#### 音乐 + +![](/assets/img/news/ElectronEgg-v4-5.jpg) + +![](/assets/img/news/ElectronEgg-v4-6.png) + +### 更多 + +访问官网:https://www.kaka996.com/ + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/ElectronEgg-v4-7.webp) \ No newline at end of file diff --git a/src/zh/news/FastRequest-2024.1.9.md b/src/zh/news/FastRequest-2024.1.9.md index 0b4169b816..295eb07dc5 100644 --- a/src/zh/news/FastRequest-2024.1.9.md +++ b/src/zh/news/FastRequest-2024.1.9.md @@ -1,7 +1,7 @@ --- title: OpenAPI 与 Solon 框架支持,Fast Request 2024.1.9 发布 author: 王胜 -date: 2024-12-31 +date: 2024-12-12 cover: https://api-buddy.cn/img/rfr.svg tag: - fastRequest diff --git a/src/zh/news/Forest-1.5.33.md b/src/zh/news/Forest-1.5.33.md index 9d80e8a8eb..d656f1ce97 100644 --- a/src/zh/news/Forest-1.5.33.md +++ b/src/zh/news/Forest-1.5.33.md @@ -1,175 +1,175 @@ ---- -title: Forest v1.5.33 发布,增强了自定义注解组合 -author: 公子骏 -date: 2023-08-30 -cover: /assets/img/news/Forest-1.5.33-cover.png -head: - - - meta - - name: 新闻 ---- - -## Forest 介绍 - -Forest 是一个开源的 Java HTTP 客户端框架,它能够将 HTTP 的所有请求信息(包括 URL、Header 以及 Body 等信息)绑定到您自定义的 Interface 方法上,能够通过调用本地接口方法的方式发送 HTTP 请求 - -### 获得奖项 - -- 2021 年度 OSC 中国开源项目评选「最受欢迎项目」 -- 2022 年度 OSC 中国开源项目评选「最火热中国开源项目社区」 - -### 简单的栗子 - -- **声明式接口** - -创建一个 interface,并用`@Get`注解修饰接口方法。 - -``` -public interface MyClient { - @Get("http://localhost:8080/hello") - String hello(); -} -``` - -通过`@Get`注解,将上面的 MyClient 接口中的`simpleRequest()`方法绑定了一个 HTTP 请求, 其 URL 为`http://localhost:8080/hello`,并默认使用 GET 方式,且将请求响应的数据以 String 的方式返回给调用者 - -- **编程式接口** - -``` -Forest.get("http://localhost:8080/hello").execute(); -``` - -编程式接口则更为简单直接 - -## v1.5.33 新增特性 - -- 增强自定义注解组合 -- 支持 Socks 代理 - -### 增强自定义注解组合 - -组合注解是 Forest 提供的自定义注解的一种方式,这种方式只需定义注解自身,已经绑定上需要组合的注解即可,相比通过需要自定义注解声明周期的方式,要方便快捷不少 - -``` -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) -// 组合Header注解 -@Headers("Content-Type: application/json") -// 组合Address注解 -@Address(host = "127.0.0.1", port = 80) -public @interface MySite { - // 自定义的 @MySite 注解 -} -``` - -此时使用自定义的`@MySite`的注解,就相当于加上了`Content-Type: application/json`头和`host = "127.0.0.1", port = 80`的根地址 - -``` -// @MySite 等价于 -// @Header("Content-Type: application/json") + @Address(host = "127.0.0.1", port = 80) -@MySite -public interface MyClient { - // ... ... -} -``` - -如果你想更灵活一点,想为`@MySite`注解添加`host`和`port`属性,并覆盖`@Address`注解的`host`和`port`属性,这在以前版本是做不到的,而 v1.5.33 版本可以 - -``` -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) -// 加上 @RequestAttributes 注解才能解析注解中定义的属性 -@RequestAttributes -// 组合Header注解 -@Headers("Content-Type: application/json") -// 组合Address注解 -@Address -public @interface MySite { - - // 重写 @Address 注解的 host 属性 - @OverrideAttribute - String host(); - - // 重写 @Address 注解的 port 属性 - @OverrideAttribute - int port(); -} -``` - -此时就可以通过`@MySite`注解的`host`和`port`属性从外部传入根地址信息了 - -``` -@MySite(host = "127.0.0.1", port = 80) -public interface MyClient { - // ... ... -} -``` - -### Socks 协议代理 - -在以前版本也可以实现 Socks 代理功能,但需要自定义后端的 OkHttpClient 对象或 Apache 的 HttpClient 对象,然后还要后端 HTTP 框架的 Client 对象绑定自定义的 Socket Connection 部分代码,非常的麻烦。如果再加上 SSL 和用户验证的需求,那更是烦上加烦 - -而此次 Forest 直接支持了 Socks 协议代理,分别为声明式接口和编程式接口提供了友好的 Api,让事情变成原本就该有的简单 - -#### 声明式 Socks 代理 - -只要加上`@SocksProxy`注解,并填上`host`和`port`就能轻松实现 Socks 代理 - -``` -@Post("http://localhost:8080/hello") -@SocksProxy(host = "127.0.0.1", port = "1089") -String simplePostWithSocksProxy(); -``` - -加上用户密码验证也十分简单 - -``` -@Post("http://localhost:8080/hello") -@SocksProxy(host = "127.0.0.1", port = "1089", username = "root", password = "xxxxxx") -String simplePostWithSocksProxy(); -``` - -#### 编程式 Socks 代理 - -通过静态方法`ForestProxy.socks`即可快速构建 Socks 协议代理 - -``` -ForestProxy proxy = ForestProxy.socks("127.0.0.1", 3128); -``` - -加上用户密码验证 - -``` -ForestProxy proxy = ForestProxy.socks("127.0.0.1", 3128) - .username("foo") // 验证用户名 - .password("bar"); // 验证密码 -``` - -## 官网和仓库地址 - -> #### 官网地址: -> -> http://forest.dtflyx.com -> -> #### Gitee 仓库地址: -> -> https://gitee.com/dromara/forest -> -> #### Github 仓库地址: -> -> https://github.com/dromara/forest - -## 本次更新内容 - -> - feat: #I6MLMD 支持 socks 代理 -> - feat: 组合注解支持属性重写 -> - fix: #I7UPBR @Body 注解的数组参数无法正常解析为 JSON 数组 -> - fix: #I7F3F0 Content-Type 为 application/xml 的情况下,发送 byte 数组数据错误 -> - fix: #I7QLTS @JSONBody Collectioncodes 报错 -> - add: SocksProxy 注解 -> - add: OverrideAttribute 注解 -> - opt: 优化 URL 更新方式 -> - update: forest-solon-plugin 升级 solon 为:2.4.0 - -![](/assets/img/news/Forest-1.5.33-1.png) +--- +title: Forest v1.5.33 发布,增强了自定义注解组合 +author: 公子骏 +date: 2023-08-30 +cover: /assets/img/news/Forest-1.5.33-cover.png +head: + - - meta + - name: 新闻 +--- + +## Forest 介绍 + +Forest 是一个开源的 Java HTTP 客户端框架,它能够将 HTTP 的所有请求信息(包括 URL、Header 以及 Body 等信息)绑定到您自定义的 Interface 方法上,能够通过调用本地接口方法的方式发送 HTTP 请求 + +### 获得奖项 + +- 2021 年度 OSC 中国开源项目评选「最受欢迎项目」 +- 2022 年度 OSC 中国开源项目评选「最火热中国开源项目社区」 + +### 简单的栗子 + +- **声明式接口** + +创建一个 interface,并用`@Get`注解修饰接口方法。 + +``` +public interface MyClient { + @Get("http://localhost:8080/hello") + String hello(); +} +``` + +通过`@Get`注解,将上面的 MyClient 接口中的`simpleRequest()`方法绑定了一个 HTTP 请求, 其 URL 为`http://localhost:8080/hello`,并默认使用 GET 方式,且将请求响应的数据以 String 的方式返回给调用者 + +- **编程式接口** + +``` +Forest.get("http://localhost:8080/hello").execute(); +``` + +编程式接口则更为简单直接 + +## v1.5.33 新增特性 + +- 增强自定义注解组合 +- 支持 Socks 代理 + +### 增强自定义注解组合 + +组合注解是 Forest 提供的自定义注解的一种方式,这种方式只需定义注解自身,已经绑定上需要组合的注解即可,相比通过需要自定义注解声明周期的方式,要方便快捷不少 + +``` +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +// 组合Header注解 +@Headers("Content-Type: application/json") +// 组合Address注解 +@Address(host = "127.0.0.1", port = 80) +public @interface MySite { + // 自定义的 @MySite 注解 +} +``` + +此时使用自定义的`@MySite`的注解,就相当于加上了`Content-Type: application/json`头和`host = "127.0.0.1", port = 80`的根地址 + +``` +// @MySite 等价于 +// @Header("Content-Type: application/json") + @Address(host = "127.0.0.1", port = 80) +@MySite +public interface MyClient { + // ... ... +} +``` + +如果你想更灵活一点,想为`@MySite`注解添加`host`和`port`属性,并覆盖`@Address`注解的`host`和`port`属性,这在以前版本是做不到的,而 v1.5.33 版本可以 + +``` +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +// 加上 @RequestAttributes 注解才能解析注解中定义的属性 +@RequestAttributes +// 组合Header注解 +@Headers("Content-Type: application/json") +// 组合Address注解 +@Address +public @interface MySite { + + // 重写 @Address 注解的 host 属性 + @OverrideAttribute + String host(); + + // 重写 @Address 注解的 port 属性 + @OverrideAttribute + int port(); +} +``` + +此时就可以通过`@MySite`注解的`host`和`port`属性从外部传入根地址信息了 + +``` +@MySite(host = "127.0.0.1", port = 80) +public interface MyClient { + // ... ... +} +``` + +### Socks 协议代理 + +在以前版本也可以实现 Socks 代理功能,但需要自定义后端的 OkHttpClient 对象或 Apache 的 HttpClient 对象,然后还要后端 HTTP 框架的 Client 对象绑定自定义的 Socket Connection 部分代码,非常的麻烦。如果再加上 SSL 和用户验证的需求,那更是烦上加烦 + +而此次 Forest 直接支持了 Socks 协议代理,分别为声明式接口和编程式接口提供了友好的 Api,让事情变成原本就该有的简单 + +#### 声明式 Socks 代理 + +只要加上`@SocksProxy`注解,并填上`host`和`port`就能轻松实现 Socks 代理 + +``` +@Post("http://localhost:8080/hello") +@SocksProxy(host = "127.0.0.1", port = "1089") +String simplePostWithSocksProxy(); +``` + +加上用户密码验证也十分简单 + +``` +@Post("http://localhost:8080/hello") +@SocksProxy(host = "127.0.0.1", port = "1089", username = "root", password = "xxxxxx") +String simplePostWithSocksProxy(); +``` + +#### 编程式 Socks 代理 + +通过静态方法`ForestProxy.socks`即可快速构建 Socks 协议代理 + +``` +ForestProxy proxy = ForestProxy.socks("127.0.0.1", 3128); +``` + +加上用户密码验证 + +``` +ForestProxy proxy = ForestProxy.socks("127.0.0.1", 3128) + .username("foo") // 验证用户名 + .password("bar"); // 验证密码 +``` + +## 官网和仓库地址 + +> #### 官网地址: +> +> http://forest.dtflyx.com +> +> #### Gitee 仓库地址: +> +> https://gitee.com/dromara/forest +> +> #### Github 仓库地址: +> +> https://github.com/dromara/forest + +## 本次更新内容 + +> - feat: #I6MLMD 支持 socks 代理 +> - feat: 组合注解支持属性重写 +> - fix: #I7UPBR @Body 注解的数组参数无法正常解析为 JSON 数组 +> - fix: #I7F3F0 Content-Type 为 application/xml 的情况下,发送 byte 数组数据错误 +> - fix: #I7QLTS @JSONBody Collectioncodes 报错 +> - add: SocksProxy 注解 +> - add: OverrideAttribute 注解 +> - opt: 优化 URL 更新方式 +> - update: forest-solon-plugin 升级 solon 为:2.4.0 + +![](/assets/img/news/Forest-1.5.33-1.png) diff --git a/src/zh/news/Forest-v1.7.md b/src/zh/news/Forest-v1.7.md new file mode 100644 index 0000000000..33ca4c3681 --- /dev/null +++ b/src/zh/news/Forest-v1.7.md @@ -0,0 +1,257 @@ +--- +title: Forest v1.7 发布!前方高能,一大波新特性来袭! +author: Forest +date: 2025-07-01 +cover: /assets/img/news/Forest-v1.7-0.png +head: + - - meta + - name: 新闻 +--- + +## + +![](/assets/img/news/Forest-v1.7-0.png) + +## Forest介绍 + +Forest 是一个开源的 Java HTTP 客户端框架,它能够将 HTTP 的所有请求信息(包括 URL、Header 以及 Body 等信息)绑定到您自定义的 Interface 方法上,能够通过调用本地接口方法的方式发送 HTTP 请求 + +### 简单的栗子 + +* **声明式接口** + + +创建一个 interface,并用`@Get`注解修饰接口方法。 + +``` +public interface MyClient { +    @Get("http://localhost:8080/hello") +    String hello(); +} +``` + +通过`@Get`注解,将上面的 MyClient 接口中的`simpleRequest()`方法绑定了一个 HTTP 请求, 其 URL 为`http://localhost:8080/hello`,并默认使用 GET 方式,且将请求响应的数据以 String 的方式返回给调用者 + +* **编程式接口** + + +``` +Forest.get("http://localhost:8080/hello").execute(); +``` + +编程式接口则更为简单直接 + +## v1.7 版本升级 + +本次发布包含了很多重大更新内容,我们对字符串模板的语法进行了增强,支持了空安全语法和深度变量引用,并提供了更友好的报错信息;在Cookie方面也进行了增强,提供了Cookie自动保存和读取的机制,并添加了更完善的API接口;我们也对拦截器进行了优化改进,不再建议直接使用`Interceptor`接口和它的`onSuccess`方法,取而代之的是`ForestInterceptor`和`onResponse`方法,它们要比前者更安全,性能也更好;除此之外,此次更新对请求的性能进行了全面的优化,在默认使用编程式接口、不打印日志的情况下,可以达到和 hutool 的 HttpUtil 差不多的耗时。 + +## v1.7 新增的一大波特性 + +* 空安全语法 + +* Elvis 表达式 + +* 深度引用 + +* 嵌套字符串模板 + +* 更安全的拦截器 + +* 请求级别日志开关 + + +#### 空安全语法 + +在原来版本,如果在字符串模板中引用一个没定义过的变量会支持报错。现在用空安全语法可以让它不再报错,而是直接返回 null 值。 + +``` +// 没定义过 testVar 变量,通过 ? 一样可以正常引用 +Forest.get("/test/{testVar?}"); +``` + +或者,引用了一个不存在或值为 null 的变量后,再用点`.`访问它的属性,这在老版本中自然是直接报错的。现在,可以通过`?.`符号自动判断是否为空。 + +``` +// 会先判断 testVar 是否为空,然后 testVar.a 是否为空,其中一个为空就会直接返回 null +// 不会报错 +Forest.get("/test/{testVar?.a?.name}"); +``` + +#### Elvis 表达式 + +新版本支持使用 Elvis 操作符`??`,也称为是空值合并操作符。简单来说就是在一个变量或一个表达式后面追加两个问号(`??`),并在它的右边再跟上一个表达式作为左边变量或表达式为空的情况下所返回的默认值。 + +``` +// 如果变量 a 为空或未定义,则返回字符串 ok +// 最后 URL 为 http://localhost:8080/ok +// 若变量 a 不为空,则返回它自己的值 +Forest.get("http://localhost:8080/{a ?? 'ok'}") +``` + +Elvis 表达式也可以和空安全属性访问操作符`?.`相结合 + +``` +// 如果变量 a 为空或未定义,则返回字符串 ok,不会再继续读取 a.b  +// 最后 URL 为 http://localhost:8080/ok +Forest.get("http://localhost:8080/{a?.b ?? 'ok'}") +``` + +#### 深度引用 + +在原来版本中,字符串模板中的变量只能引用到第一层,如果该变量的值是引用其他的变量,字符串模板并不会进行解析。而现在,不管引用了多少层变量,都可以解析到底。 + +``` +forest: +  variables: +    var1: "{user.name}" +    var2: "{user.password}" +    user: +      name: foo +      password: bar +``` + +在Java代码中直接引用 + +``` +// 最终 URL 为: /test/foo/bar +Forest.get("/test/{var1}/{var2}"); +``` + +如果不想进行深度引用,可以使用深度引用停止语法,即在变量后加上`!`符号,那么就只会引用一层该变量的值,至于该变量的值是否会包含其他字符串模板的内容,就不会再进行解析了。 + +``` +// 最终 URL 为: /test/{user.name}/{user.password} +// 变量 var1 和 var2 的值直接返回字符串,而不会进行任何解析 +Forest.get("/test/{var1!}/{var2!}"); +``` + +#### 嵌套字符串模板 + +用两个反引号`` ` ``将要拼接的字符串内容包裹起来(如 `` `字符串模板内容` ``),并且当中可以使用`` `#{配置属性}` ``、`` `${表达式}` ``、`` `{表达式}` ``等嵌套表达式语法 + +``` +// 如果 a 为空,则返回 b 和 c 用斜杠拼接后的字符串 +// 若 a 为空, b 为 foo,c 为 bar,则 URL 为 /foo/bar +Forest.get("/{a ?? `{b}/{c}`}") + +// 任何一种形态内容都可以,可以理解为就是一种字符串,一种在表达式内部可动态拼接的字符串 +Forest.get("/{a ?? `?b={b}&c={c}`}") + +``` + +#### 更安全的拦截器 + +本次版本更新后,不再建议使用`Interceptor`(当然要用也可以用,不影响以前的老代码),同时引入了更安全的`ForestInterceptor`接口 + +``` +public class MyInterceptor implements ForestInterceptor { + + +    @Override +    public ResponseResult onResponse(ForestRequest request, ForestResponse response) { +        if (response.isError()) { +            // 返回错误标志 +            return error(response.getException()); +        } +        // 通过 response.getResult() 或 response.get(数据类型.class) 来获取响应数据 + +        // 返回继续执行标志 +        return proceed(); +    } +} + +``` + +#### 请求级别日志开关 + +不再需要 new 一个 LogConfiguration 对象,直接在 ForestRequest 的链式调用中即可设置日志开关 + +``` +Forest.post("/test") +        .logEnabled(true) // 请求日志总开关 +        .logRequest(true) // 请求内容日志开关 +        .logRequestHeaders(true) // 请求头日志开关 +        .logRequestBody(true)  // 请求体日志开源 +        .logResponseStatus(true) // 响应状态日志开关 +        .logResponseHeaders(true) // 响应头日志开关 +        .logResponseContent(false) // 响应体内容日志开关 +        .execute(); +``` + +## 官网和仓库地址 + +> #### 官网地址: +> +> http://forest.dtflyx.com +> +> #### Gitee 仓库地址: +> +> https://gitee.com/dromara/forest +> +> #### Github 仓库地址: +> +> https://github.com/dromara/forest + +## 本次更新内容 + +* feat: 字符串模板支持空安全语法 + +* feat: 更友好的字符串模板错误消息 + +* feat: 字符串模板`{`、`${`等符号支持转义`\\{`、`\\${` + +* feat: 字符串模板支持深度变量引用 + +* feat: 字符串模板支持停止深度引用的语法 + +* feat: 支持嵌套字符串模板 + +* feat: 新增更安全的 Forest 拦截器接口 ForestInterceptor + +* feat: 通过配置自定义异步线程池拒绝策略 + +* feat: 支持Bear 认证器 + +* feat: 支持`@Var`作为方法和类的变量绑定注解 + +* feat: 新增 ForestRequest 级别的日志开关接口 + +* feat: 增强 Cookie 相关 API 接口 + +* feat: 后台自动清理过期 Cookie + +* feat: 支持 Cookie 自动化存取机制 + +* fix: 和老版本forest冲突时,新版本Forest类缺乏get(url)、post(url)等方法签名,造成错误 (#IC7LIH) + +* fix: body log在部分环境中文乱码 + +* fix: 修改接口中常量的命名,避免用户在其与只有大小写区别的方法之间产生混淆 + +* fix: 调用声名式接口的 hashCode() 方法会死循环 + +* fix: 以 ForestResponse 为返回类型时,T 的子类匹配问题 + +* fix: 重复读取响应流时报错 + +* fix: 嵌套json字符串无法正常解析 + +* refactor: 重构URL解析过程 + +* refactor: 重构变量作用域 + +* refactor: 重构 Forest 变量体系 + +* refactor: ForestCookie 不再依赖 OkHttp + +* refactor: ForestCookie.parse() 接口 + +* refactor: 添加键值对类型请求体删除接口 ForestBody.removeNameValueBody + +* optimize: 优化请求性能 + +* optimize: 根据 Response 类型动态判断响应是否自动关闭 + +* optimize: 默认后端改为 httpclient + +* optimize: 拦截器优化 \ No newline at end of file diff --git a/src/zh/news/Gopher-0.md b/src/zh/news/Gopher-0.md new file mode 100644 index 0000000000..d727e000eb --- /dev/null +++ b/src/zh/news/Gopher-0.md @@ -0,0 +1,908 @@ +--- +title: 新晋 Gopher 的开源加密库 Dongle 加入 Dromara 社区 +author: Dongle +date: 2024-11-13 +cover: /assets/img/news/Gopher-0-0.png +head: + - - meta + - name: 新闻 +--- + +在现代计算领域,信息安全逐渐成为焦点话题。密码学,作为信息保护的关键技术之一,允许我们加密(保密)和解密(解密)数据。Golang 中有许多库可以帮助我们轻松实现这些功能,其中dongle(加密狗)是一个强大且广泛使用的库。本篇文章旨在深入探讨dongle库的使用,以及如何利用它执行常见的加密和解密任务。 + +![](/assets/img/news/Gopher-0-0.png) + +**Dongle** https://github.com/dromara/dongle 是一个广泛使用的轻量级、语义化、对开发者友好的 golang 开源加密库,专为密码学和数据安全提供一系列强大的工具。这个项目旨在简化加密算法的实现,使得开发者能够专注于他们的应用逻辑,而不是底层的加密细节。以下将从多个方面介绍这个库: + + + +#### 主要功能: + +dongle 包含许多经典和现代的加密算法,如AES(高级加密标准)、RSA、DH(Diffie-Hellman)密钥交换,以及各种哈希函数(如MD5和SHA)。同时,它还提供了数字签名、随机数生成和其他一些有用的密码学操作。 + +#### 安装使用 + +``` +// 使用 github 库 +go get -u github.com/dromara/dongle + +import ( +    "github.com/dromara/dongle" +) + +// 使用 gitee 库 +go get -u gitee.com/dromara/dongle + +import ( +    "gitee.com/dromara/dongle" +) + +// 使用 gitcode 库 +go get -u gitcode.com/dromara/dongle + +import ( +    "gitcode.com/dromara/dongle" +) +``` + +#### 用法示例 + +#### 编码&解码 + +##### Hex 编码、解码 + +``` +// 对字符串进行 hex 编码,输出字符串 +dongle.Encode.FromString("hello world").ByHex().ToString() // 68656c6c6f20776f726c64 +// 对字符串进行 hex 解码,输出字符串 +dongle.Decode.FromString("68656c6c6f20776f726c64").ByHex().ToString() // hello world + +// 对字节切片进行 hex 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByHex().ToBytes() // []byte("68656c6c6f20776f726c64") +// 对字节切片进行 hex 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("68656c6c6f20776f726c64")).ByHex().ToBytes() // []byte("hello world") +``` + +##### Base16 编码、解码 + +``` +// 对字符串进行 base16 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase16().ToString() // 68656c6c6f20776f726c64 +// 对字符串进行 base16 解码,输出字符串 +dongle.Decode.FromString("68656c6c6f20776f726c64").ByBase16().ToString() // hello world + +// 对字节切片进行 base16 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase16().ToBytes() // []byte("68656c6c6f20776f726c64") +// 对字节切片进行 base16 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("68656c6c6f20776f726c64")).ByBase16().ToBytes() // []byte("hello world") +``` + +##### Base32 编码、解码 + +``` +// 对字符进行 base32 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase32().ToString() // NBSWY3DPEB3W64TMMQ====== +// 对字符串进行 base32 解码,输出字符串 +dongle.Decode.FromString("NBSWY3DPEB3W64TMMQ======").ByBase32().ToString() // hello world + +// 对字节切片进行 base32 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase32().ToBytes() // []byte("NBSWY3DPEB3W64TMMQ======") +// 对字节切片进行 base32 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("NBSWY3DPEB3W64TMMQ======")).ByBase32().ToBytes() // []byte("hello world") +``` + +##### Base45 编码、解码 + +``` +// 对字符进行 base45 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase45().ToString() // +8D VD82EK4F.KEA2 +// 对字符串进行 base45 解码,输出字符串 +dongle.Decode.FromString("+8D VD82EK4F.KEA2").ByBase45().ToString() // hello world + +// 对字节切片进行 base45 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase45().ToBytes() // []byte("+8D VD82EK4F.KEA2") +// 对字节切片进行 base45 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("+8D VD82EK4F.KEA2")).ByBase45().ToBytes() // []byte("hello world") +``` + +##### Base58 编码、解码 + +``` +// 对字符进行 base58 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase58().ToString() // StV1DL6CwTryKyV +// 对字符串进行 base58 解码,输出字符串 +dongle.Decode.FromString("StV1DL6CwTryKyV").ByBase58().ToString() // hello world + +// 对字节切片进行 base58 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase58().ToBytes() // []byte("StV1DL6CwTryKyV") +// 对字节切片进行 base58 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("StV1DL6CwTryKyV")).ByBase58().ToBytes() // []byte("hello world") +``` + +##### Base62 编码、解码 + +``` +// 对字符进行 base62 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase62().ToString() // AAwf93rvy4aWQVw +// 对字符串进行 base62 解码,输出字符串 +dongle.Decode.FromString("AAwf93rvy4aWQVw").ByBase62().ToString() // hello world + +// 对字节切片进行 base62 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase62().ToBytes() // []byte("AAwf93rvy4aWQVw") +// 对字节切片进行 base62 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("AAwf93rvy4aWQVw")).ByBase62().ToBytes() // []byte("hello world") +``` + +##### Base64 编码、解码 + +``` +// 对字符串进行 base64 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase64().ToString() // aGVsbG8gd29ybGQ= +// 对字符串进行 base64 解码,输出字符串 +dongle.Decode.FromString("aGVsbG8gd29ybGQ=").ByBase64().ToString() // hello world + +// 对字节切片进行 base64 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase64().ToBytes() // []byte("aGVsbG8gd29ybGQ=") +// 对字节切片进行 base64 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("aGVsbG8gd29ybGQ=")).ByBase64().ToBytes() // []byte("hello world") +``` + +##### Base64URL 编码、解码 + +``` +// 对 url 字符进行 base64 编码,输出字符串 +dongle.Encode.FromString("www.gouguoyin.cn").ByBase64URL().ToString() // d3d3LmdvdWd1b3lpbi5jbg== +// 对 url 字符进行 base64 解码,输出字符串 +dongle.Decode.FromString("d3d3LmdvdWd1b3lpbi5jbg==").ByBase64URL().ToString() // www.gouguoyin.cn + +// 对 url 字节切片进行 base64 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("www.gouguoyin.cn")).ByBase64URL().ToBytes() // []byte("d3d3LmdvdWd1b3lpbi5jbg==") +// 对 url 字符进行 base64 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("d3d3LmdvdWd1b3lpbi5jbg==")).ByBase64URL().ToBytes() // []byte("www.gouguoyin.cn") +``` + +##### Base85 编码、解码 + +``` +// 对字符串进行 base85 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase85().ToString() // BOu!rD]j7BEbo7 +// 对字符串进行 base85 解码,输出字符串 +dongle.Decode.FromString("BOu!rD]j7BEbo7").ByBase85().ToString() // hello world + +// 对字节切片进行 base85 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase85().ToBytes() // []byte("BOu!rD]j7BEbo7") +// 对字节切片进行 base85 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("BOu!rD]j7BEbo7")).ByBase85().ToBytes() // []byte("hello world") +``` + +##### Base91 编码、解码 + +``` +// 对字符串进行 base91 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase91().ToString() // TPwJh>Io2Tv!lE +// 对字符串进行 base91 解码,输出字符串 +dongle.Decode.FromString("TPwJh>Io2Tv!lE").ByBase91().ToString() // hello world + +// 对字节切片进行 base91 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase91().ToBytes() // []byte("TPwJh>Io2Tv!lE") +// 对字节切片进行 base91 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("BOu!rD]j7BEbo7")).ByBase91().ToBytes() // []byte("hello world") +``` + +##### Base100 编码、解码 + +``` +// 对字符串进行 base100 编码,输出字符串 +dongle.Encode.FromString("hello world").ByBase100().ToString() // 👟👜👣👣👦🐗👮👦👩👣👛 +// 对字符串进行 base100 解码,输出字符串 +dongle.Decode.FromString("👟👜👣👣👦🐗👮👦👩👣👛").ByBase100().ToString() // hello world + +// 对字节切片进行 base100 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("hello world")).ByBase100().ToBytes() // []byte("👟👜👣👣👦🐗👮👦👩👣👛") +// 对字节切片进行 base100 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("👟👜👣👣👦🐗👮👦👩👣👛")).ByBase100().ToBytes() // []byte("hello world") +``` + +##### SafeURL 编码、解码 + +``` +// 对 url 字符进行转义编码,输出字符串 +dongle.Encode.FromString("www.gouguoyin.cn?sex=男&age=18").BySafeURL().ToString() // www.gouguoyin.cn%3Fsex%3D%E7%94%B7%26age%3D18 +// 对 url 字符进行转义解码,输出字符串 +dongle.Decode.FromString("www.gouguoyin.cn%3Fsex%3D%E7%94%B7%26age%3D18").BySafeURL().ToString() // www.gouguoyin.cn?sex=男&age=18 + +// 对 url 字节切片进行转义编码,输出字节切片 +dongle.Encode.FromBytes([]byte("www.gouguoyin.cn?sex=男&age=18")).BySafeURL().ToBytes() // []byte("www.gouguoyin.cn%3Fsex%3D%E7%94%B7%26age%3D18") +// 对 url 字符进行转义解码,输出字节切片 +dongle.Decode.FromBytes([]byte("www.gouguoyin.cn%3Fsex%3D%E7%94%B7%26age%3D18")).BySafeURL().ToBytes() // []byte("www.gouguoyin.cn?sex=男&age=18") +``` + +##### Morse 编码、解码 + +> 默认分隔符是 `/` + +``` +// 对字符串进行 morse 编码,输出字符串 +dongle.Encode.FromString("dongle").ByMorse().ToString() // -../---/-./--./.-../. +// 对字符串进行 morse 解码,输出字符串 +dongle.Decode.FromString("-../---/-./--./.-../.").ByMorse().ToString() // dongle + +// 对字节切片进行 morse 编码,输出字节切片 +dongle.Encode.FromBytes([]byte("dongle")).ByMorse("|").ToBytes() // []byte("-..|---|-.|--.|.-..|.") +// 对字节切片进行 morse 解码,输出字节切片 +dongle.Decode.FromBytes([]byte("-..|---|-.|--.|.-..|.")).ByMorse("|").ToBytes() // []byte("dongle") +``` + +#### 加密&解密 + +##### Md2 加密 + +``` +// 对字符串进行 md2 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByMd2().ToHexString() // d9cce882ee690a5c1ce70beff3a78c77 +// 对字符串进行 md2 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByMd2().ToBase64String() // 2czogu5pClwc5wvv86eMdw== + +// 对字节切片进行 md2 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd2().ToHexBytes() // []byte("d9cce882ee690a5c1ce70beff3a78c77") +// 对字节切片进行 md2 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd2().ToBase64Bytes() // []byte("2czogu5pClwc5wvv86eMdw==") + +``` + +##### Md4 加密 + +``` +// 对字符串进行 md4 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByMd4().ToHexString() // aa010fbc1d14c795d86ef98c95479d17 +// 对字符串进行 md4 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByMd4().ToBase64String() // qgEPvB0Ux5XYbvmMlUedFw== + +// 对字节切片进行 md4 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd4().ToHexBytes() // []byte("aa010fbc1d14c795d86ef98c95479d17") +// 对字节切片进行 md4 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd4().ToBase64Bytes() // []byte("qgEPvB0Ux5XYbvmMlUedFw==") + +``` + +##### Md5 加密 + +``` +// 对字符串进行 md5 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByMd5().ToHexString() // 5eb63bbbe01eeed093cb22bb8f5acdc3 +// 对字符串进行 md5 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByMd5().ToBase64String() // XrY7u+Ae7tCTyyK7j1rNww== + +// 对字节切片进行 md5 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd5().ToHexBytes() // []byte("5eb63bbbe01eeed093cb22bb8f5acdc3") +// 对字节切片进行 md5 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByMd5().ToBase64Bytes() // []byte("XrY7u+Ae7tCTyyK7j1rNww==") +``` + +##### Sha1 加密 + +``` +// 对字符串进行 sha1 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha1().ToHexString() // 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed +// 对字符串进行 sha1 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha1().ToBase64String() // Kq5sNclPz7QV2+lfQIuc6R7oRu0= + +// 对字节切片进行 sha1 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha1().ToHexBytes() // []byte("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed") +// 对字节切片进行 sha1 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha1().ToBase64Bytes() // []byte("Kq5sNclPz7QV2+lfQIuc6R7oRu0=") +``` + +##### Sha3 加密 + +> 包含 sha3-224, sha3-256, sha3-384, sha3-512 + +``` +// 对字符串进行 sha3-224 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").Sha3(224).ToHexString() // dfb7f18c77e928bb56faeb2da27291bd790bc1045cde45f3210bb6c5 +// 对字符串进行 sha3-224 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").Sha3(224).ToBase64String() // 37fxjHfpKLtW+ustonKRvXkLwQRc3kXzIQu2xQ== +// 对字节切片进行 sha3-224 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(224).ToHexBytes() // []byte("dfb7f18c77e928bb56faeb2da27291bd790bc1045cde45f3210bb6c5") +// 对字节切片进行 sha3-224 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(224).ToBase64Bytes() // []byte("37fxjHfpKLtW+ustonKRvXkLwQRc3kXzIQu2xQ==") + +// 对字符串进行 sha3-256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").Sha3(256).ToHexString() // 644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938 +// 对字符串进行 sha3-256 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").Sha3(256).ToBase64String() // ZEvMflZDcwQJmarInnYi88px+6HZcv2Uoxw7+/JOOTg= +// 对字节切片进行 sha3-256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(256).ToHexBytes() // []byte("644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938") +// 对字节切片进行 sha3-256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(256).ToBase64Bytes() // []byte("ZEvMflZDcwQJmarInnYi88px+6HZcv2Uoxw7+/JOOTg=") + +// 对字符串进行 sha3-384 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").Sha3(384).ToHexString() // 83bff28dde1b1bf5810071c6643c08e5b05bdb836effd70b403ea8ea0a634dc4997eb1053aa3593f590f9c63630dd90b +// 对字符串进行 sha3-384 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").Sha3(384).ToBase64String() // g7/yjd4bG/WBAHHGZDwI5bBb24Nu/9cLQD6o6gpjTcSZfrEFOqNZP1kPnGNjDdkL +// 对字节切片进行 sha3-384 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(384).ToHexBytes() // []byte("83bff28dde1b1bf5810071c6643c08e5b05bdb836effd70b403ea8ea0a634dc4997eb1053aa3593f590f9c63630dd90b") +// 对字节切片进行 sha3-384 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(384).ToBase64Bytes() // []byte("g7/yjd4bG/WBAHHGZDwI5bBb24Nu/9cLQD6o6gpjTcSZfrEFOqNZP1kPnGNjDdkL") + +// 对字符串进行 sha3-512 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").Sha3(512).ToHexString() // 840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a +// 对字符串进行 sha3-512 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").Sha3(512).ToBase64String() // hAAGZT6ayelRF6FckVyquBZikY6SXengBPd0/4LXB5pA1NJ7GzcmV8YdRtRwMEyIx4izpFJ60HTR3MvuXbqpmg== +// 对字节切片进行 sha3-512 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(512).ToHexBytes() // []byte("840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a") +// 对字节切片进行 sha3-512 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).Sha3(512).ToBase64Bytes() // []byte("hAAGZT6ayelRF6FckVyquBZikY6SXengBPd0/4LXB5pA1NJ7GzcmV8YdRtRwMEyIx4izpFJ60HTR3MvuXbqpmg==") + +``` + +##### Sha224 加密 + +``` +// 对字符串进行 sha224 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha224().ToHexString() // 2f05477fc24bb4faefd86517156dafdecec45b8ad3cf2522a563582b +// 对字符串进行 sha224 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha224().ToBase64String() // LwVHf8JLtPrv2GUXFW2v3s7EW4rTzyUipWNYKw== + +// 对字节切片进行 sha224 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha224().ToHexBytes() // []byte("2f05477fc24bb4faefd86517156dafdecec45b8ad3cf2522a563582b") +// 对字节切片进行 sha224 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha224().ToBase64Bytes() // []byte("LwVHf8JLtPrv2GUXFW2v3s7EW4rTzyUipWNYKw==") +``` + +##### Sha256 加密 + +``` +// 对字符串进行 sha256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha256().ToHexString() // b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +// 对字符串进行 sha256 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha256().ToBase64String() // uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek= + +// 对字节切片进行 sha256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha256().ToHexBytes() // []byte("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9") +// 对字节切片进行 sha256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha256().ToBase64Bytes() // []byte("uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=") +``` + +##### Sha384 加密 + +``` +// 对字符串进行 sha384 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha384().ToHexString() // fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcbb83578b3e417cb71ce646efd0819dd8c088de1bd +// 对字符串进行 sha384 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha384().ToBase64String() // /b2OdaZ/KfcBpOBAOF4uI5hjA+oQI5IRr5B/y7g1eLPkF8txzmRu/QgZ3YwIjeG9 + +// 对字节切片进行 sha384 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha384().ToHexBytes() // []byte("fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcbb83578b3e417cb71ce646efd0819dd8c088de1bd") +// 对字节切片进行 sha384 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha384().ToBase64Bytes() // []byte("/b2OdaZ/KfcBpOBAOF4uI5hjA+oQI5IRr5B/y7g1eLPkF8txzmRu/QgZ3YwIjeG9") +``` + +##### Sha512 加密 + +> 包含 sha512, sha512-224, sha512-256 + +``` +// 对字符串进行 sha512 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha512().ToHexBytes() // 309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f +// 对字符串进行 sha512 加密,输出经过 base6 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha512().ToBase64String() // MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw== +// 对字节切片进行 sha512 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512().ToHexBytes() // []byte("309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f") +// 对字节切片进行 sha512 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512().ToBase64Bytes() // []byte("MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw==") + +// 对字符串进行 sha512-224 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha512(224).ToHexBytes() // 22e0d52336f64a998085078b05a6e37b26f8120f43bf4db4c43a64ee +// 对字符串进行 sha512-224 加密,输出经过 base6 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha512(224).ToBase64String() // IuDVIzb2SpmAhQeLBabjeyb4Eg9Dv020xDpk7g== +// 对字节切片进行 sha512-224 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512(224).ToHexBytes() // []byte("22e0d52336f64a998085078b05a6e37b26f8120f43bf4db4c43a64ee") +// 对字节切片进行 sha512-224 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512(224).ToBase64Bytes() // []byte("IuDVIzb2SpmAhQeLBabjeyb4Eg9Dv020xDpk7g==") + +// 对字符串进行 sha512-256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha512(256).ToHexBytes() // 0ac561fac838104e3f2e4ad107b4bee3e938bf15f2b15f009ccccd61a913f017 +// 对字符串进行 sha512-256 加密,输出经过 base6 编码的字符串 +dongle.Encrypt.FromString("hello world").BySha512(256).ToBase64String() // CsVh+sg4EE4/LkrRB7S+4+k4vxXysV8AnMzNYakT8Bc= +// 对字节切片进行 sha512-256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512(256).ToHexBytes() // []byte("0ac561fac838104e3f2e4ad107b4bee3e938bf15f2b15f009ccccd61a913f017") +// 对字节切片进行 sha512-256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).BySha512(256).ToBase64Bytes() // []byte("CsVh+sg4EE4/LkrRB7S+4+k4vxXysV8AnMzNYakT8Bc=") +``` + +##### Shake128 加密 + +``` +// 对字符串进行 shake128-256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByShake128(256).ToHexString() // 3a9159f071e4dd1c8c4f968607c30942e120d8156b8b1e72e0d376e8871cb8b8 +// 对字符串进行 shake128-256 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByShake128(256).ToBase64String() // OpFZ8HHk3RyMT5aGB8MJQuEg2BVrix5y4NN26IccuLg= +// 对字节切片进行 shake128-256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake128(256).ToHexBytes() // []byte("3a9159f071e4dd1c8c4f968607c30942e120d8156b8b1e72e0d376e8871cb8b8") +// 对字节切片进行 shake128-256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake128(256).ToBase64Bytes() // []byte("OpFZ8HHk3RyMT5aGB8MJQuEg2BVrix5y4NN26IccuLg=") + +// 对字符串进行 shake128-512 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByShake128(512).ToHexString() // 3a9159f071e4dd1c8c4f968607c30942e120d8156b8b1e72e0d376e8871cb8b899072665674f26cc494a4bcf027c58267e8ee2da60e942759de86d2670bba1aa +// 对字符串进行 shake128-512 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByShake128(512).ToBase64String() // OpFZ8HHk3RyMT5aGB8MJQuEg2BVrix5y4NN26IccuLiZByZlZ08mzElKS88CfFgmfo7i2mDpQnWd6G0mcLuhqg== +// 对字节切片进行 shake128-512 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake128(512).ToHexBytes() // []byte("3a9159f071e4dd1c8c4f968607c30942e120d8156b8b1e72e0d376e8871cb8b899072665674f26cc494a4bcf027c58267e8ee2da60e942759de86d2670bba1aa") +// 对字节切片进行 shake128-512 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake128(512).ToBase64Bytes() // []byte("OpFZ8HHk3RyMT5aGB8MJQuEg2BVrix5y4NN26IccuLiZByZlZ08mzElKS88CfFgmfo7i2mDpQnWd6G0mcLuhqg==") +``` + +##### Shake256 加密 + +``` +// 对字符串进行 shake256-384 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByShake256(384).ToHexString() // 369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df2 +// 对字符串进行 shake256-384 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByShake256(384).ToBase64String() // Npdxuyy50rBMHVTMpIfjctnxh/c/e6P2W5XI7neYxSf088LVXC1Gop8ulF1GnD3y +// 对字节切片进行 shake256-384 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake256(384).ToHexBytes() // []byte("369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df2") +// 对字节切片进行 shake256-384 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake256(384).ToBase64Bytes() // []byte("Npdxuyy50rBMHVTMpIfjctnxh/c/e6P2W5XI7neYxSf088LVXC1Gop8ulF1GnD3y") + +// 对字符串进行 shake256-512 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByShake256(512).ToHexString() // 369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df27853a8735271f5cc2d9e889544357116 +// 对字符串进行 shake256-512 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByShake256(512).ToBase64String() // Npdxuyy50rBMHVTMpIfjctnxh/c/e6P2W5XI7neYxSf088LVXC1Gop8ulF1GnD3yeFOoc1Jx9cwtnoiVRDVxFg== +// 对字节切片进行 shake256-512 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake256(512).ToHexBytes() // []byte("369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df27853a8735271f5cc2d9e889544357116") +// 对字节切片进行 shake256-512 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByShake256(512).ToBase64Bytes() // []byte("Npdxuyy50rBMHVTMpIfjctnxh/c/e6P2W5XI7neYxSf088LVXC1Gop8ulF1GnD3yeFOoc1Jx9cwtnoiVRDVxFg==") +``` + +##### Ripemd160 加密 + +``` +// 对字符串进行 ripemd160 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByRipemd160().ToHexString() // 98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f +// 对字符串进行 ripemd160 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByRipemd160().ToBase64String() // mMYVeEzLX+WTb7wMvp39tAjZLw8= + +// 对字节切片进行 ripemd160 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByRipemd160().ToHexBytes() // []byte("98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f") +// 对字节切片进行 ripemd160 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByRipemd160().ToBase64Bytes() // []byte("mMYVeEzLX+WTb7wMvp39tAjZLw8=") +``` + +##### Blake2b 加密 + +> 包含 blake2b-256, blake2b-384, blake2b-512 + +``` +// 对字符串进行 blake2b-256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByBlake2b(256).ToHexBytes() // 256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610 +// 对字符串进行 blake2b-256 加密,输出经过 base6 编码的字符串 +dongle.Encrypt.FromString("hello world").ByBlake2b(256).ToBase64String() // JWyDspcRTSAbMBefPw7wys6Xg2ItpZdDJrQ2F4ru9hA= +// 对字节切片进行 blake2b-256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(256).ToHexBytes() // []byte("256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610") +// 对字节切片进行 blake2b-256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(256).ToBase64Bytes() // []byte("JWyDspcRTSAbMBefPw7wys6Xg2ItpZdDJrQ2F4ru9hA=") + +// 对字符串进行 blake2b-384 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByBlake2b(384).ToHexBytes() // 8c653f8c9c9aa2177fb6f8cf5bb914828faa032d7b486c8150663d3f6524b086784f8e62693171ac51fc80b7d2cbb12b +// 对字符串进行 blake2b-384 加密,输出经过 base6 编码的字符串 +dongle.Encrypt.FromString("hello world").ByBlake2b(384).ToBase64String() // jGU/jJyaohd/tvjPW7kUgo+qAy17SGyBUGY9P2UksIZ4T45iaTFxrFH8gLfSy7Er +// 对字节切片进行 blake2b-384 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(384).ToHexBytes() // []byte("8c653f8c9c9aa2177fb6f8cf5bb914828faa032d7b486c8150663d3f6524b086784f8e62693171ac51fc80b7d2cbb12b") +// 对字节切片进行 blake2b-384 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(384).ToBase64Bytes() // []byte("jGU/jJyaohd/tvjPW7kUgo+qAy17SGyBUGY9P2UksIZ4T45iaTFxrFH8gLfSy7Er") + +// 对字符串进行 blake2b-512 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByBlake2b(512).ToHexBytes() // 021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0 +// 对字符串进行 blake2b-512 加密,输出经过 base6 编码的字符串 +dongle.Encrypt.FromString("hello world").ByBlake2b(512).ToBase64String() // Ahzth5kpbOylV4MquUGlC0oR+DR4zxQfUfkz9lOrn7zAWgN83b7QbjCb8zSULE5YzfGkbiN5EczX/Pl4fLx/0A== +// 对字节切片进行 blake2b-512 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(512).ToHexBytes() // []byte("021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0") +// 对字节切片进行 blake2b-512 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2b(512).ToBase64Bytes() // []byte("Ahzth5kpbOylV4MquUGlC0oR+DR4zxQfUfkz9lOrn7zAWgN83b7QbjCb8zSULE5YzfGkbiN5EczX/Pl4fLx/0A==") +``` + +##### Blake2s 加密 + +> 目前仅支持 blake2s-256 + +``` +// 对字符串进行 blake2s-256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByBlake2s(256).ToHexString() // 9aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b +// 对字符串进行 blake2s-256 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByBlake2s(256).ToBase64String() // muxoBnlFYRB+WUsfaoprDJKgy6ms9eXpPMoG94GBOws= + +// 对字节切片进行 blake2s-256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2s(256).ToHexBytes() // []byte("9aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b") +// 对字节切片进行 blake2s-256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByBlake2s(256).ToBase64Bytes() // []byte("muxoBnlFYRB+WUsfaoprDJKgy6ms9eXpPMoG94GBOws=") +``` + +##### Hmac-md2 加密 + +``` +// 对字符串进行 hmac-md2 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacMd2("dongle").ToHexString() // 88ed6ef9ab699d03a702f2a6fb1c0673 +// 对字符串进行 hmac-md2 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacMd2("dongle").ToBase64String() // iO1u+atpnQOnAvKm+xwGcw== + +// 对字节切片进行 hmac-md2 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd2([]byte("dongle")).ToHexBytes() // []byte("88ed6ef9ab699d03a702f2a6fb1c0673") +// 对字节切片进行 hmac-md2 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd2([]byte("dongle")).ToBase64Bytes() // []byte("iO1u+atpnQOnAvKm+xwGcw==") +``` + +##### Hmac-md4 加密 + +``` +// 对字符串进行 hmac-md4 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacMd4("dongle").ToHexString() // 7a9df5247cbf76a8bc17c9c4f5a75b6b +// 对字符串进行 hmac-md4 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacMd4("dongle").ToBase64String() // ep31JHy/dqi8F8nE9adbaw== + +// 对字节切片进行 hmac-md4 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd4([]byte("dongle")).ToHexBytes() // []byte("7a9df5247cbf76a8bc17c9c4f5a75b6b") +// 对字节切片进行 hmac-md4 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd4([]byte("dongle")).ToBase64Bytes() // []byte("ep31JHy/dqi8F8nE9adbaw==") +``` + +##### Hmac-md5 加密 + +``` +// 对字符串进行 hmac-md5 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacMd5("dongle").ToHexString() // 4790626a275f776956386e5a3ea7b726 +// 对字符串进行 hmac-md5 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacMd5("dongle").ToBase64String() // R5Biaidfd2lWOG5aPqe3Jg== + +// 对字节切片进行 hmac-md5 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd5([]byte("dongle")).ToHexBytes() // []byte("4790626a275f776956386e5a3ea7b726") +// 对字节切片进行 hmac-md5 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacMd5([]byte("dongle")).ToBase64Bytes() // []byte("R5Biaidfd2lWOG5aPqe3Jg==") +``` + +##### Hmac-sha1 加密 + +``` +// 对字符串进行 hmac-sha1 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha1("dongle").ToHexString() // 91c103ef93ba7420902b0d1bf0903251c94b4a62 +// 对字符串进行 hmac-sha1 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha1("dongle").ToBase64String() // kcED75O6dCCQKw0b8JAyUclLSmI= + +// 对字节切片进行 hmac-sha1 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha1([]byte("dongle")).ToHexBytes() // []byte("91c103ef93ba7420902b0d1bf0903251c94b4a62") +// 对字节切片进行 hmac-sha1 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha1([]byte("dongle")).ToBase64Bytes() // []byte("kcED75O6dCCQKw0b8JAyUclLSmI=") +``` + +##### Hmac-sha3 加密 + +> 包含 hmac-sha3-224, hmac-sha3-256, hmac-sha3-384, hmac-sha3-512 + +``` +// 对字符串进行 hmac-sha3-224 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 224).ToHexString() // fb8f061d9d1dddd2f5d3b9064a5e98e3e4b6df27ea93ce67627583ce +// 对字符串进行 hmac-sha3-224 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 224).ToBase64String() // +48GHZ0d3dL107kGSl6Y4+S23yfqk85nYnWDzg== +// 对字节切片进行 hmac-sha3-224 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 224).ToHexBytes() // []byte("fb8f061d9d1dddd2f5d3b9064a5e98e3e4b6df27ea93ce67627583ce") +// 对字节切片进行 hmac-sha3-224 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 224).ToBase64Bytes() // []byte("+48GHZ0d3dL107kGSl6Y4+S23yfqk85nYnWDzg==") + +// 对字符串进行 hmac-sha3-256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 256).ToHexString() // 8193367fde28cf5c460adb449a04b3dd9c184f488bdccbabf0526c54f90c4460 +// 对字符串进行 hmac-sha3-256 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 256).ToBase64String() // gZM2f94oz1xGCttEmgSz3ZwYT0iL3Mur8FJsVPkMRGA= +// 对字节切片进行 hmac-sha3-256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 256).ToHexBytes() // []byte("8193367fde28cf5c460adb449a04b3dd9c184f488bdccbabf0526c54f90c4460") +// 对字节切片进行 hmac-sha3-256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 256).ToBase64Bytes() // []byte("gZM2f94oz1xGCttEmgSz3ZwYT0iL3Mur8FJsVPkMRGA=") + +// 对字符串进行 hmac-sha3-384 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 384).ToHexString() // 3f76f5cda69cada3ee6b33f8458cd498b063075db263dd8b33f2a3992a8804f9569a7c86ffa2b8f0748babeb7a6fc0e7 +// 对字符串进行 hmac-sha3-384 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 384).ToBase64String() // P3b1zaacraPuazP4RYzUmLBjB12yY92LM/KjmSqIBPlWmnyG/6K48HSLq+t6b8Dn +// 对字节切片进行 hmac-sha3-384 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 384).ToHexBytes() // []byte("3f76f5cda69cada3ee6b33f8458cd498b063075db263dd8b33f2a3992a8804f9569a7c86ffa2b8f0748babeb7a6fc0e7") +// 对字节切片进行 hmac-sha3-384 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 384).ToBase64Bytes() // []byte("P3b1zaacraPuazP4RYzUmLBjB12yY92LM/KjmSqIBPlWmnyG/6K48HSLq+t6b8Dn") + +// 对字符串进行 hmac-sha3-512 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 512).ToHexString() // a99653d0407d659eccdeed43bb7cccd2e2b05a2c34fd3467c4198cf2ad26a466738513e88839fb55e64eb49df65bc52ed0fec2775bd9e086edd4fb4024add4a2 +// 对字符串进行 hmac-sha3-512 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha3("dongle", 512).ToBase64String() // qZZT0EB9ZZ7M3u1Du3zM0uKwWiw0/TRnxBmM8q0mpGZzhRPoiDn7VeZOtJ32W8Uu0P7Cd1vZ4Ibt1PtAJK3Uog== +// 对字节切片进行 hmac-sha3-512 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 512).ToHexBytes() // []byte("a99653d0407d659eccdeed43bb7cccd2e2b05a2c34fd3467c4198cf2ad26a466738513e88839fb55e64eb49df65bc52ed0fec2775bd9e086edd4fb4024add4a2") +// 对字节切片进行 hmac-sha3-512 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha3([]byte("dongle"), 512).ToBase64Bytes() // []byte("qZZT0EB9ZZ7M3u1Du3zM0uKwWiw0/TRnxBmM8q0mpGZzhRPoiDn7VeZOtJ32W8Uu0P7Cd1vZ4Ibt1PtAJK3Uog==") +``` + +##### Hmac-sha224 加密 + +``` +// 对字符串进行 hmac-sha224 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha224("dongle").ToHexString() // e15b9e5a7eccb1f17dc81dc07c909a891936dc3429dc0d940accbcec +// 对字符串进行 hmac-sha224 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha224("dongle").ToBase64String() // 4VueWn7MsfF9yB3AfJCaiRk23DQp3A2UCsy87A==== + +// 对字节切片进行 hmac-sha224 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha224([]byte("dongle")).ToHexBytes() // []byte("e15b9e5a7eccb1f17dc81dc07c909a891936dc3429dc0d940accbcec") +// 对字节切片进行 hmac-sha224 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha224([]byte("dongle")).ToBase64Bytes() // []byte("4VueWn7MsfF9yB3AfJCaiRk23DQp3A2UCsy87A==") +``` + +##### Hmac-sha256 加密 + +``` +// 对字符串进行 hmac-sha256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha256("dongle").ToHexString() // b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +// 对字符串进行 hmac-sha256 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha256("dongle").ToBase64String() // uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek= + +// 对字节切片进行 hmac-sha256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha256([]byte("dongle")).ToHexBytes() // []byte("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9") +// 对字节切片进行 hmac-sha256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha256([]byte("dongle")).ToBase64Bytes() // []byte("uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=") +``` + +##### Hmac-sha384 加密 + +``` +// 对字符串进行 hmac-sha384 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha384("dongle").ToHexString() // 421fcaa740216a31bbcd1f86f2212e0c68aa4b156a8ebc2ae55b3e75c4ee0509ea0325a0570ae739006b61d91d817fe8 +// 对字符串进行 hmac-sha384 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha384("dongle").ToBase64String() // Qh/Kp0AhajG7zR+G8iEuDGiqSxVqjrwq5Vs+dcTuBQnqAyWgVwrnOQBrYdkdgX/o +// 对字节切片进行 hmac-sha384 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha384([]byte("dongle")).ToHexBytes() // []byte("421fcaa740216a31bbcd1f86f2212e0c68aa4b156a8ebc2ae55b3e75c4ee0509ea0325a0570ae739006b61d91d817fe8") +// 对字节切片进行 hmac-sha384 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha384([]byte("dongle")).ToBase64Bytes() // []byte("Qh/Kp0AhajG7zR+G8iEuDGiqSxVqjrwq5Vs+dcTuBQnqAyWgVwrnOQBrYdkdgX/o") +``` + +##### Hmac-sha512 加密 + +> 包含 hmac-sha512, hmac-sha512-224, hmac-sha512-256 + +``` +// 对字符串进行 hmac-sha512 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle").ToHexString() // d971b790bbc2a4ac81062bbffac693c9c234bae176c8faf5e304dbdb153032a826f12353964b4a4fb87abecd2dc237638a630cbad54a6b94b1f6ef5d5e2835d1 +// 对字符串进行 hmac-sha512 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle").ToBase64String() // 2XG3kLvCpKyBBiu/+saTycI0uuF2yPr14wTb2xUwMqgm8SNTlktKT7h6vs0twjdjimMMutVKa5Sx9u9dXig10Q== +// 对字节切片进行 hmac-sha512 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle")).ToHexBytes() // []byte("d971b790bbc2a4ac81062bbffac693c9c234bae176c8faf5e304dbdb153032a826f12353964b4a4fb87abecd2dc237638a630cbad54a6b94b1f6ef5d5e2835d1") +// 对字节切片进行 hmac-sha512 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle")).ToBase64Bytes() // []byte("2XG3kLvCpKyBBiu/+saTycI0uuF2yPr14wTb2xUwMqgm8SNTlktKT7h6vs0twjdjimMMutVKa5Sx9u9dXig10Q==") + +// 对字符串进行 hmac-sha512-224 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle", 224).ToHexString() // c4145bcc385c29f0e5683cd5450be9deb522d556de3b046a7ffa1eb3 +// 对字符串进行 hmac-sha512-224 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle", 224).ToBase64String() // xBRbzDhcKfDlaDzVRQvp3rUi1VbeOwRqf/oesw== +// 对字节切片进行 hmac-sha512-224 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle"), 224).ToHexBytes() // []byte("c4145bcc385c29f0e5683cd5450be9deb522d556de3b046a7ffa1eb3") +// 对字节切片进行 hmac-sha512-224 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle"), 224).ToBase64Bytes() // []byte("xBRbzDhcKfDlaDzVRQvp3rUi1VbeOwRqf/oesw==") + +// 对字符串进行 hmac-sha512-256 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle", 256).ToHexString() // e99fae71bcb43651ae10e952989eadf897faccb43966ee5122bb1b1d82f7a7c2 +// 对字符串进行 hmac-sha512-256 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSha512("dongle", 256).ToBase64String() // 6Z+ucby0NlGuEOlSmJ6t+Jf6zLQ5Zu5RIrsbHYL3p8I= +// 对字节切片进行 hmac-sha512-256 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle"), 256).ToHexBytes() // []byte("e99fae71bcb43651ae10e952989eadf897faccb43966ee5122bb1b1d82f7a7c2") +// 对字节切片进行 hmac-sha512-256 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSha512([]byte("dongle"), 256).ToBase64Bytes() // []byte("6Z+ucby0NlGuEOlSmJ6t+Jf6zLQ5Zu5RIrsbHYL3p8I=") +``` + +##### Hmac-ripemd160 加密 + +``` +// 对字符串进行 hmac-ripemd160 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacRipemd160("dongle").ToHexString() // 3691ad040e80c43dc6e8ffe9bc6ef3d5bd8786b8 +// 对字符串进行 hmac-ripemd160 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacRipemd160("dongle").ToBase64String() // NpGtBA6AxD3G6P/pvG7z1b2Hhrg= + +// 对字节切片进行 hmac-ripemd160 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacRipemd160([]byte("dongle")).ToHexBytes() // []byte("3691ad040e80c43dc6e8ffe9bc6ef3d5bd8786b8") +// 对字节切片进行 hmac-ripemd160 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacRipemd160([]byte("dongle")).ToBase64Bytes() // []byte("NpGtBA6AxD3G6P/pvG7z1b2Hhrg=") +``` + +##### Hmac-sm3 加密 + +``` +// 对字符串进行 hmac-sm3 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSm3("dongle").ToHexString() // 8c733aae1d553c466a08c3e9e5daac3e99ae220181c7c1bc8c2564961de751b3 +// 对字符串进行 hmac-sm3 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByHmacSm3("dongle").ToBase64String() // jHM6rh1VPEZqCMPp5dqsPpmuIgGBx8G8jCVklh3nUbM= + +// 对字节切片进行 hmac-sm3 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSm3([]byte("dongle")).ToHexBytes() // []byte("8c733aae1d553c466a08c3e9e5daac3e99ae220181c7c1bc8c2564961de751b3") +// 对字节切片进行 hmac-sm3 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByHmacSm3([]byte("dongle")).ToBase64Bytes() // []byte("jHM6rh1VPEZqCMPp5dqsPpmuIgGBx8G8jCVklh3nUbM=") +``` + +##### Aes 加密、解密 + +``` +cipher := dongle.NewCipher() +cipher.SetMode(dongle.CBC) // CBC、CFB、OFB、CTR、ECB +cipher.SetPadding(dongle.PKCS7) // No、Empty、Zero、PKCS5、PKCS7、AnsiX923、ISO97971 +cipher.SetKey("0123456789abcdef") // key 长度必须是 16、24 或 32 字节 +cipher.SetIV("0123456789abcdef") // iv 长度必须是 16 字节,ECB 模式不需要设置 iv + +// 对字符串进行 aes 加密,输出未经编码的原始字符串 +rawString := dongle.Encrypt.FromString("hello world").ByAes(cipher).ToRawString() +// 对未经编码的原始字符串进行 aes 解密,输出字符串 +dongle.Decrypt.FromRawString(rawString).ByAes(cipher).ToString() // hello world + +// 对字符串进行 aes 加密,输出经过 hex 编码的字符串 +dongle.Encrypt.FromString("hello world").ByAes(cipher).ToHexString() // c1e9b4529aac9793010f4677f6358efe +// 对经过 hex 编码的字符串进行 aes 解密,输出字符串 +dongle.Decrypt.FromHexString("c1e9b4529aac9793010f4677f6358efe").ByAes(cipher).ToString() // hello world + +// 对字符串进行 aes 加密,输出经过 base64 编码的字符串 +dongle.Encrypt.FromString("hello world").ByAes(cipher).ToBase64String() // wem0Upqsl5MBD0Z39jWO/g== +// 对经过 base64 编码的字符串进行 aes 解密,输出字符串 +dongle.Decrypt.FromBase64String("wem0Upqsl5MBD0Z39jWO/g==").ByAes(cipher).ToString() // hello world + +// 对字节切片进行 aes 加密,输出未经编码的原始字节切片 +rawBytes := dongle.Encrypt.FromBytes([]byte("hello world")).ByAes(cipher).ToRawBytes() +// 对未经编码的原始字节切片进行 aes 解密,输出字节切片 +dongle.Decrypt.FromRawBytes(rawBytes).ByAes(cipher).ToBytes() // []byte("hello world") + +// 对字节切片进行 aes 加密,输出经过 hex 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByAes(cipher).ToHexBytes() // []byte("c1e9b4529aac9793010f4677f6358efe") +// 对经过 hex 编码的字节切片进行 aes 解密,输出字节切片 +dongle.Decrypt.FromHexBytes([]byte("c1e9b4529aac9793010f4677f6358efe")).ByAes(cipher).ToBytes() // []byte("hello world") + +// 对字节切片进行 aes 加密,输出经过 base64 编码的字节切片 +dongle.Encrypt.FromBytes([]byte("hello world")).ByAes(cipher).ToBase64Bytes() // []byte("ZdgjvfHFgaHe0cukLgPKUg==") +// 对经过 base64 编码的字节切片进行 aes 解密,输出字节切片 +dongle.Decrypt.FromBase64Bytes(()byte("wem0Upqsl5MBD0Z39jWO/g==")).ByAes(cipher).ToBytes() // []byte("hello world") +``` + +目前已支持的对称、非对称加密、哈希和消息认证码如下 + +* \[x\] Hex 编码、解码 + +* \[x\] Base16 编码、解码 + +* \[x\] Base32 编码、解码 + +* \[x\] Base45 编码、解码 + +* \[x\] Base58 编码、解码 + +* \[x\] Base62 编码、解码 + +* \[x\] Base64 编码、解码 + +* \[x\] Base64URL 编码、解码 + +* \[x\] SafeURL 编码、解码 + +* \[x\] Base85 编码、解码 + +* \[x\] Base91 编码、解码 + +* \[x\] Base100 编码、解码 + +* \[x\] Morse 编码、解码 + +* \[x\] Md2 加密 + +* \[x\] Md4 加密 + +* \[x\] Md5 加密 + +* \[x\] Sha1 加密 + +* \[x\] Sha3-224 加密 + +* \[x\] Sha3-256 加密 + +* \[x\] Sha3-384 加密 + +* \[x\] Sha3-512 加密 + +* \[x\] Sha224 加密 + +* \[x\] Sha256 加密 + +* \[x\] Sha384 加密 + +* \[x\] Sha512 加密 + +* \[x\] Sha512-224 加密 + +* \[x\] Sha512-256 加密 + +* \[x\] Shake128 加密 + +* \[x\] Shake256 加密 + +* \[x\] Ripemd160 加密 + +* \[x\] Blake2b-256 加密 + +* \[x\] Blake2b-384 加密 + +* \[x\] Blake2b-512 加密 + +* \[x\] Blake2s-256 加密 + +* \[x\] Hmac-md2 加密 + +* \[x\] Hmac-md4 加密 + +* \[x\] Hmac-md5 加密 + +* \[x\] Hmac-sha1 加密 + +* \[x\] Hmac-sha3-224 加密 + +* \[x\] Hmac-sha3-256 加密 + +* \[x\] Hmac-sha3-384 加密 + +* \[x\] Hmac-sha3-512 加密 + +* \[x\] Hmac-sha224 加密 + +* \[x\] Hmac-sha256 加密 + +* \[x\] Hmac-sha384 加密 + +* \[x\] Hmac-sha512 加密 + +* \[x\] Hmac-sha512-224 加密 + +* \[x\] Hmac-sha512-256 加密 + +* \[x\] Hmac-ripemd160 加密 + +* \[x\] Hmac-sm3 加密 + +* \[ \] Rc2 加密、解密 + +* \[x\] Rc4 加密、解密 + +* \[ \] Rc5 加密、解密 + +* \[ \] Rc6 加密、解密 + +* \[x\] Tea 加密、解密 + +* \[ \] Xtea 加密、解密 + +* \[x\] Aes 加密、解密 + +* \[x\] Blowfish 加密、解密 + +* \[x\] Des 加密、解密 + +* \[x\] 3Des 加密、解密 + +* \[x\] Rsa 加密、解密 + +* \[ \] Ecc 加密、解密 + +* \[ \] Sm2 加密、解密 + +* \[x\] Sm3 加密 + +* \[ \] Sm4 加密、解密 + +* \[ \] Sm7 加密、解密 + +* \[ \] Sm9 加密、解密 + +* \[x\] Bcrypt 签名、验签 + +* \[x\] Ed25519 签名、验签 + +* \[x\] Rsa 签名、验签 + +* \[ \] Dsa 签名、验签 + + +![](/assets/img/news/Gopher-0-1.png) + +**Github: https://github.com/dromara/dongle** \ No newline at end of file diff --git a/src/zh/news/HertzBeat-1.3.1.md b/src/zh/news/HertzBeat-1.3.1.md index 74903737a5..8fac5e482d 100644 --- a/src/zh/news/HertzBeat-1.3.1.md +++ b/src/zh/news/HertzBeat-1.3.1.md @@ -1,90 +1,90 @@ ---- -title: 50 天 36 位小伙伴的贡献,开源实时监控工具 HertzBeat v1.3.1 发布 -author: tom -tag: - - HertzBeat -date: 2023-05-08 -cover: /assets/img/news/HertzBeat-1.4.0-1.png -head: - - - meta - - name: 新闻 ---- - -## 50 天 36 位小伙伴的贡献,开源实时监控工具 HertzBeat v1.3.1 发布 - -官网: hertzbeat.dromara.org | hertzbeat.com  | tancloud.cn - -### What is HertzBeat? - -> HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,无需 Agent 的开源实时监控告警工具。 -> 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等指标监控,阈值告警通知一步到位。 -> 支持更自由化的阈值规则(计算表达式),`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式及时送达。 - -> 我们将`Http,Jmx,Ssh,Snmp,Jdbc`等协议规范可配置化,您只需配置`YML`监控模版就能使用这些协议去自定义采集任何您想要的指标。 - -> 您相信只需定义 YML 就能立刻适配一款 K8s 或 Docker 等新的监控类型吗? - -![](/assets/img/news/HertzBeat-1.3.2-1.png) - -**Github: https://github.com/dromara/hertzbeat** - -**Gitee: https://gitee.com/dromara/hertzbeat** - -### v1.3.1 版本来了 - -经过 51 天总 36 位小伙伴们的贡献迭代更新,HertzBeat v1.3.1 正式发布啦, **推荐升级食用**! - -- **支持时序数据库 GreptimeDB (aviatorscript, sofastack 的开源大佬的新项目,点赞) 存储指标数据**,用户可以自由选择喜欢的时序数据库来存储。 -- **新增很多朋友期待的监控导入导出功能**,对于导入导出格式 Excel, Json, Yaml,小孩才做选择题,这些我们全都要。 -- **支持告警静默功能**,监控服务维护期间,业务升级期间再也不怕无必要的频繁告警了。新增告警静默规则即可,支持一次性时间段和周期性时间静默。 -- **支持 openguass influxdb 存储指标数据**,与 HuaweiCloud 的开源活动合作需求,用户可以使用 HuaweiCloud 云上的 openguass influxdb 一键开通接入存储指标数据。 -- **新的监控模板概念,我们把所有监控的指标采集定义都视为一个个监控模版** 用户只需要定义一个新的监控模版,就能适配一款新的监控类型,目前官方已支持的监控模版可以访问官网 **https://hertzbeat.com/zh-cn/docs/template** 查看,欢迎大家分享贡献更多自己的监控模版。 -- **支持 tdengine3.0, EulerOS 指标和 SpringBoot3 指标, HuaweiCloud sms 发送消息等等** -- **还有更多新功能就不一一详述了,等待你的探索哦** - -**感谢 hertzbeat 贡献者们的贡献**!👍👍 - -**非常欢迎文档,编码,找 BUG,I18n 等各方面的贡献者加入** - ---- - -**只需要一条 docker 命令即可安装体验 hertzbeat** - -**`docker run -d -p 1157:1157 --name hertzbeat tancloud/hertzbeat`** - -#### Release Doc - -**https://github.com/dromara/hertzbeat/releases/tag/v1.3.1** - -#### 升级注意 ⚠️. - -- 若之前使用的 TDengine 时序数据库,需升级至 TDengine3.0+ -- 需要执行 SQL 升级脚本 - -``` -ALTER table hzb_alert_define modify field varchar(255) default null; -COMMIT; -``` - ---- - -## ⛄ 已支持 - -> 我们将监控采集类型(mysql,jvm,k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控! -> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。 - -- 网站监控,  端口可用性, Http Api, Ping 连通性, Jvm, SiteMap 全站, Ssl 证书, SpringBoot, FTP 服务器 -- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB,  达梦, OpenGauss, ClickHouse, IoTDB, Redis 集群 -- Linux, Ubuntu, CentOS, Windows -- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ -- Kubernetes, Docker -- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch -- 和更多自定义监控模版。 -- 通知支持  `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook`。 - ---- - -**欢迎在仓库给颗 Star 小星星来支持我们。** - -**Github: https://github.com/dromara/hertzbeat** -**Gitee: https://gitee.com/dromara/hertzbeat** +--- +title: 50 天 36 位小伙伴的贡献,开源实时监控工具 HertzBeat v1.3.1 发布 +author: tom +tag: + - HertzBeat +date: 2023-05-08 +cover: /assets/img/news/HertzBeat-1.4.0-1.png +head: + - - meta + - name: 新闻 +--- + +## 50 天 36 位小伙伴的贡献,开源实时监控工具 HertzBeat v1.3.1 发布 + +官网: hertzbeat.dromara.org | hertzbeat.com  | tancloud.cn + +### What is HertzBeat? + +> HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,无需 Agent 的开源实时监控告警工具。 +> 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等指标监控,阈值告警通知一步到位。 +> 支持更自由化的阈值规则(计算表达式),`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式及时送达。 + +> 我们将`Http,Jmx,Ssh,Snmp,Jdbc`等协议规范可配置化,您只需配置`YML`监控模版就能使用这些协议去自定义采集任何您想要的指标。 + +> 您相信只需定义 YML 就能立刻适配一款 K8s 或 Docker 等新的监控类型吗? + +![](/assets/img/news/HertzBeat-1.3.2-1.png) + +**Github: https://github.com/dromara/hertzbeat** + +**Gitee: https://gitee.com/dromara/hertzbeat** + +### v1.3.1 版本来了 + +经过 51 天总 36 位小伙伴们的贡献迭代更新,HertzBeat v1.3.1 正式发布啦, **推荐升级食用**! + +- **支持时序数据库 GreptimeDB (aviatorscript, sofastack 的开源大佬的新项目,点赞) 存储指标数据**,用户可以自由选择喜欢的时序数据库来存储。 +- **新增很多朋友期待的监控导入导出功能**,对于导入导出格式 Excel, Json, Yaml,小孩才做选择题,这些我们全都要。 +- **支持告警静默功能**,监控服务维护期间,业务升级期间再也不怕无必要的频繁告警了。新增告警静默规则即可,支持一次性时间段和周期性时间静默。 +- **支持 openguass influxdb 存储指标数据**,与 HuaweiCloud 的开源活动合作需求,用户可以使用 HuaweiCloud 云上的 openguass influxdb 一键开通接入存储指标数据。 +- **新的监控模板概念,我们把所有监控的指标采集定义都视为一个个监控模版** 用户只需要定义一个新的监控模版,就能适配一款新的监控类型,目前官方已支持的监控模版可以访问官网 **https://hertzbeat.com/zh-cn/docs/template** 查看,欢迎大家分享贡献更多自己的监控模版。 +- **支持 tdengine3.0, EulerOS 指标和 SpringBoot3 指标, HuaweiCloud sms 发送消息等等** +- **还有更多新功能就不一一详述了,等待你的探索哦** + +**感谢 hertzbeat 贡献者们的贡献**!👍👍 + +**非常欢迎文档,编码,找 BUG,I18n 等各方面的贡献者加入** + +--- + +**只需要一条 docker 命令即可安装体验 hertzbeat** + +**`docker run -d -p 1157:1157 --name hertzbeat tancloud/hertzbeat`** + +#### Release Doc + +**https://github.com/dromara/hertzbeat/releases/tag/v1.3.1** + +#### 升级注意 ⚠️. + +- 若之前使用的 TDengine 时序数据库,需升级至 TDengine3.0+ +- 需要执行 SQL 升级脚本 + +``` +ALTER table hzb_alert_define modify field varchar(255) default null; +COMMIT; +``` + +--- + +## ⛄ 已支持 + +> 我们将监控采集类型(mysql,jvm,k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控! +> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。 + +- 网站监控,  端口可用性, Http Api, Ping 连通性, Jvm, SiteMap 全站, Ssl 证书, SpringBoot, FTP 服务器 +- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB,  达梦, OpenGauss, ClickHouse, IoTDB, Redis 集群 +- Linux, Ubuntu, CentOS, Windows +- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ +- Kubernetes, Docker +- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch +- 和更多自定义监控模版。 +- 通知支持  `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook`。 + +--- + +**欢迎在仓库给颗 Star 小星星来支持我们。** + +**Github: https://github.com/dromara/hertzbeat** +**Gitee: https://gitee.com/dromara/hertzbeat** diff --git a/src/zh/news/HertzBeat-1.3.2.md b/src/zh/news/HertzBeat-1.3.2.md index 039aa406c7..9004a4f8c7 100644 --- a/src/zh/news/HertzBeat-1.3.2.md +++ b/src/zh/news/HertzBeat-1.3.2.md @@ -1,88 +1,88 @@ ---- -title: 开源实时监控 HertzBeat v1.3.2 发布, 更稳定更易用 -author: tom -tag: - - HertzBeat -date: 2023-07-05 -cover: /assets/img/news/HertzBeat-1.4.0-1.png -head: - - - meta - - name: 新闻 ---- - -### HertzBeat 介绍 - -> HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,无需 Agent 的开源实时监控告警工具。 -> 致力于**易用友好**,全 WEB 页面操作,鼠标点一点就能监控告警,零上手学习成本。 -> 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等指标监控,阈值告警通知一步到位。 -> 支持更自由化的阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式及时送达。 - -> 我们将`Http,Jmx,Ssh,Snmp,Jdbc`等协议规范可配置化,您只需配置`YML`就能使用这些协议去自定义采集任何您想要的指标。 - -官网: hertzbeat.com | hertzbeat.dromara.org | tancloud.cn - -![](/assets/img/news/HertzBeat-1.3.2-1.png) - -**Github: https://github.com/dromara/hertzbeat** - -**Gitee: https://gitee.com/dromara/hertzbeat** - -### v1.3.2 来了 - -**这次累计 27 位小伙伴们的辛苦贡献才出来了这个令人欣喜的版本。\*\***感谢他们!爱心 💗\*\* - -这个版本我们支持对**freebsd, debian, opensuse, redhat, apache doris**等新的监控类型和指标。 - -- 支持 WEB 页面配置邮件服务器,**取代之前的文件配置** -- 支持告警收敛,**是否遇到了重复告警频繁发送,有了告警收敛马上解决** -- 公共消息队列支持 Kafka,**除了我们默认的内置内存消息队列,也支持了外置 Kafka 消息队列,提升系统性能。** -- 新的监控中心页面,**聚合所有监控类型,不用像之前那样切来切去了。** -- 支持标签分组展示,**把同一业务类别的监控们分组标记,统一管理。** -- 阈值配置不仅仅有表达式,**还支持更人性化的操作 UI,之前的表达式对新人不太友好很容易出错,现在可以直接 UI 操作啦,它可以和表达式互相切换。** -- 还有 HTTP ipv6 等更多功能。 - -修复了大量 BUG,完善文档代码,提高了整体的稳定可用性。更多新功能欢迎探索! - -只需要一条 docker 命令即可安装体验 hertzbeat: -`docker run -d -p 1157:1157 --name hertzbeat tancloud/hertzbeat` - -若 dockerhub 网络超时,可以使用下面命令: -`docker run -d -p 1157:1157 --name hertzbeat quay.io/tancloud/hertzbeat` - ---- - -**监控告警全页面操作,0 学习成本,无需修改配置文件加监控,上效果:** - -![](/assets/img/news/HertzBeat-1.3.2-2.png) -![](/assets/img/news/HertzBeat-1.3.2-3.png) -![](/assets/img/news/HertzBeat-1.3.2-4.png) -![](/assets/img/news/HertzBeat-1.3.2-5.png) -![](/assets/img/news/HertzBeat-1.3.2-6.png) -![](/assets/img/news/HertzBeat-1.3.2-7.png) - ---- - -## ⛄ 已支持 - -> 我们将监控采集类型(mysql,jvm,k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控! -> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。 - -- Website, Port Telnet, Http Api, Ping Connect, Jvm, SiteMap, Ssl Certificate, SpringBoot2, FTP Server, SpringBoot3 -- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, DM, OpenGauss, ClickHouse, IoTDB, Redis Cluster, Redis Sentinel -- Linux, Ubuntu, CentOS, Windows, EulerOS, Fedora CoreOS, OpenSUSE, Rocky Linux, Red Hat, FreeBSD, AlmaLinux, Debian Linux -- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ -- Kubernetes, Docker -- CiscoSwitch, HpeSwitch, HuaweiSwitch, TpLinkSwitch, H3cSwitch -- 和更多自定义监控模版。 -- 通知支持 Discord Slack Telegram 邮件 钉钉 微信 飞书 短信 Webhook。 - ---- - -**欢迎 star 三连来支持我们** - -**Github: https://github.com/dromara/hertzbeat** -**Gitee: https://gitee.com/dromara/hertzbeat** - -加微信进交流群哇: - -![](/assets/img/news/HertzBeat-1.4.1-8.jpg) +--- +title: 开源实时监控 HertzBeat v1.3.2 发布, 更稳定更易用 +author: tom +tag: + - HertzBeat +date: 2023-07-05 +cover: /assets/img/news/HertzBeat-1.4.0-1.png +head: + - - meta + - name: 新闻 +--- + +### HertzBeat 介绍 + +> HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,无需 Agent 的开源实时监控告警工具。 +> 致力于**易用友好**,全 WEB 页面操作,鼠标点一点就能监控告警,零上手学习成本。 +> 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等指标监控,阈值告警通知一步到位。 +> 支持更自由化的阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式及时送达。 + +> 我们将`Http,Jmx,Ssh,Snmp,Jdbc`等协议规范可配置化,您只需配置`YML`就能使用这些协议去自定义采集任何您想要的指标。 + +官网: hertzbeat.com | hertzbeat.dromara.org | tancloud.cn + +![](/assets/img/news/HertzBeat-1.3.2-1.png) + +**Github: https://github.com/dromara/hertzbeat** + +**Gitee: https://gitee.com/dromara/hertzbeat** + +### v1.3.2 来了 + +**这次累计 27 位小伙伴们的辛苦贡献才出来了这个令人欣喜的版本。\*\***感谢他们!爱心 💗\*\* + +这个版本我们支持对**freebsd, debian, opensuse, redhat, apache doris**等新的监控类型和指标。 + +- 支持 WEB 页面配置邮件服务器,**取代之前的文件配置** +- 支持告警收敛,**是否遇到了重复告警频繁发送,有了告警收敛马上解决** +- 公共消息队列支持 Kafka,**除了我们默认的内置内存消息队列,也支持了外置 Kafka 消息队列,提升系统性能。** +- 新的监控中心页面,**聚合所有监控类型,不用像之前那样切来切去了。** +- 支持标签分组展示,**把同一业务类别的监控们分组标记,统一管理。** +- 阈值配置不仅仅有表达式,**还支持更人性化的操作 UI,之前的表达式对新人不太友好很容易出错,现在可以直接 UI 操作啦,它可以和表达式互相切换。** +- 还有 HTTP ipv6 等更多功能。 + +修复了大量 BUG,完善文档代码,提高了整体的稳定可用性。更多新功能欢迎探索! + +只需要一条 docker 命令即可安装体验 hertzbeat: +`docker run -d -p 1157:1157 --name hertzbeat tancloud/hertzbeat` + +若 dockerhub 网络超时,可以使用下面命令: +`docker run -d -p 1157:1157 --name hertzbeat quay.io/tancloud/hertzbeat` + +--- + +**监控告警全页面操作,0 学习成本,无需修改配置文件加监控,上效果:** + +![](/assets/img/news/HertzBeat-1.3.2-2.png) +![](/assets/img/news/HertzBeat-1.3.2-3.png) +![](/assets/img/news/HertzBeat-1.3.2-4.png) +![](/assets/img/news/HertzBeat-1.3.2-5.png) +![](/assets/img/news/HertzBeat-1.3.2-6.png) +![](/assets/img/news/HertzBeat-1.3.2-7.png) + +--- + +## ⛄ 已支持 + +> 我们将监控采集类型(mysql,jvm,k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控! +> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。 + +- Website, Port Telnet, Http Api, Ping Connect, Jvm, SiteMap, Ssl Certificate, SpringBoot2, FTP Server, SpringBoot3 +- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, DM, OpenGauss, ClickHouse, IoTDB, Redis Cluster, Redis Sentinel +- Linux, Ubuntu, CentOS, Windows, EulerOS, Fedora CoreOS, OpenSUSE, Rocky Linux, Red Hat, FreeBSD, AlmaLinux, Debian Linux +- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ +- Kubernetes, Docker +- CiscoSwitch, HpeSwitch, HuaweiSwitch, TpLinkSwitch, H3cSwitch +- 和更多自定义监控模版。 +- 通知支持 Discord Slack Telegram 邮件 钉钉 微信 飞书 短信 Webhook。 + +--- + +**欢迎 star 三连来支持我们** + +**Github: https://github.com/dromara/hertzbeat** +**Gitee: https://gitee.com/dromara/hertzbeat** + +加微信进交流群哇: + +![](/assets/img/news/HertzBeat-1.4.1-8.jpg) diff --git a/src/zh/news/HertzBeat-1.4.0.md b/src/zh/news/HertzBeat-1.4.0.md index 4be4ca9495..bab12d80b6 100644 --- a/src/zh/news/HertzBeat-1.4.0.md +++ b/src/zh/news/HertzBeat-1.4.0.md @@ -1,178 +1,178 @@ ---- -title: 重磅,集群版 HertzBeat 发布,易用友好的开源实时监控系统! -author: tom -tag: - - HertzBeat -date: 2023-08-15 -cover: /assets/img/news/HertzBeat-1.4.0-1.png -head: - - - meta - - name: 新闻 ---- - -### 什么是 HertzBeat? - -HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,无需 Agent 的开源实时监控告警系统。 - -### 特点 - -- 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控阈值告警通知一步到位。 -- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 -- 将 `Http,Jmx,Ssh,Snmp,Jdbc` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? -- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 -- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式消息及时送达。 - -> `HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助开发者和团队快速搭建自有监控系统。 -> 当然我们也提供了对应的 SAAS 版本监控云服务,中小团队和个人无需再为监控自有资源而去部署一套监控系统,登录即可免费开始。 - -![](/assets/img/news/HertzBeat-1.4.1-7.png) - -**Github: https://github.com/dromara/hertzbeat** - -**Gitee: https://gitee.com/dromara/hertzbeat** - -### 集群版来啦 - -我们之前的 hertzbeat 一直是单机版本,组件代码模块化但不支持采集器独立部署,所支持的监控数量上限受到了单节点的天然限制,且无法应对多个隔离网络的资源的统一纳管。 -经过一个多月的迭代,我们重写了采集任务调度,采集器独立部署,设计单机版和集群版用同一套代码方便后续的维护升级,单机集群两种模式可相互切换无感知。最终很高兴,集群版如期与大家见面了。 - -集群版不仅仅给我们带来了更强大的监控性能,更有云边协同等功能让人充满想象。 - -#### 高性能集群 - -- 支持部署采集器集群,多采集器集群横向扩展,指数级提高可监控数量与采集性能。 -- 监控任务在采集器集群中自调度,单采集器挂掉无感知故障迁移采集任务,新加入采集器节点自动调度分担采集压力。 -- 单机模式与集群模式相互切换部署非常方便,无需额外组件部署。 - -![](/assets/img/news/HertzBeat-1.4.0-2.png) - -#### 云边协同 - -> 支持部署边缘采集器集群,与主 HertzBeat 服务云边协同提升采集能力。 - -在多个网络不相通的隔离网络中,在以往的方案中我们需要在每个网络都部署一套监控系统,这导致数据不互通,管理部署维护都不方便。 -HertzBeat 提供云边协同能力,可以在多个隔离网络部署边缘采集器,添加监控时指定采集器,采集器在隔离网络内部进行监控任务采集,采集数据上报,由主 HertzBeat 服务统一调度管理展示。 -这多用于多个隔离数据中心或不同厂商云资源和云下资源的统一监控场景。 - -![](/assets/img/news/HertzBeat-1.4.0-2.png) - -### 为什么要开源集群版? - -往往一些做需要商业化的开源产品的策略会是单机版作为玩具给小玩家们的入门使用,然后集群版作为闭源产品给有需求的氪金玩家付费使用。这样的模式是可以说非常不错的,即保证开源也得到了收益,也适用于很多开源项目的发展策略,可能会在商业路径上走得更通顺点。 -网络上有些人会对这样的分单机和集群版的开源项目嗤之以鼻,觉得它们是伪开源,开源是噱头,他们觉得开源应该什么都开源免费出来,开源团队什么都应该无私奉献出来。。。。很无语这类人,有投入才有回报,当你免费使用着开源软件并得到价值的时候,是否应该想一想你付出给开源软件了什么而不是一味的索取。 -那回到正题,我们又为什么要开源集群版?仅因为热爱开源?如果说我们还在少年可能这话你信,但一个快奔 30 还有家庭责任的人说出这话你信吗,我自己都不信 😂。 -首先我们来看看开源能带来什么,或者为什么要做开源。最开始全职开源的想法很简单,做自己喜欢的开源产品(已实现),程序员的梦想能部署在成千上万的服务器上(看下载量已实现),然后基于此开源产品挣钱(暂未哭)。 - -- 用户流量。开源项目免费提供给用户和开发者,吸引用户使用,宣传等方面都有优势。 -- 用户信任。开源的产品天生容易获取用户的信任和使用耐心,或者说降低用户的信任门槛。 -- 社区协作。开源的产品可以吸引到顶级贡献者一起贡献,接收用户的反馈 issue,pr 贡献等,在社区的驱动下使开源项目越来越好,正向反馈后也会有更多人参与和使用。社区协作我觉得这是开源的意义,而且这样不仅仅只是程序员之间的贡献代码协作,用户都是协作对象(比如我们这个项目有大量的运维朋友贡献代码和文档),如果是仅仅代码开源而不社区协作,那还不如放个安装包给别人免费使用下载就好。 -- 产品生态。这对一些需要生态的产品是需要的,比如 hertzbeat,需要支持对接各种类型协议的监控类型,大量的监控模版。一个好的开源项目生态才能吸引到其它贡献者贡献和分享,在生态中互通有无,最终大家在生态中都受益。这在闭源程序中是很难做到的。 - -上面几点,重在社区协作和产品生态,这也是开源集群版的原因,只有卷开源产品卷自己到更强的产品力,比如集群这一技术特性天生会吸引到开发者(而且集群本身就是我们社区协作的产物),会吸引到更多的用户和贡献者使用反馈大家一起迭代,社区驱动进而正向促进开源项目和满足用户功能体验。 -而对于开源商业化,开源商业化的前提是得有个真正好的,受欢迎,被广泛使用的开源产品,然后在此基础上做商业化挣钱。 - -对了这里再说下开源不等同于免费,如果基于 HertzBeat 二次开发修改了 logo,名称,版权等,请先找我们授权否则会有法律风险,我们有权利追诉破坏开源并因此获利的团队个人的全部违法所得。免费使用不是白嫖,这种破坏开源协议的才是,目前发现大量白嫖怪,小心点哈你们。我每年正月初七都会祝你们用这些钱吃的安心,住的放心,玩的开心哈。(仅个人言论不代表社区) - -### 尝试部署集群版 - -1. `docker` 环境仅需一条命令即可开始 - -`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` - -`或者使用 quay.io (若 dockerhub 网络链接超时)` - -`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat quay.io/tancloud/hertzbeat` - -2. 浏览器访问 `http://localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat` -3. 部署采集器集群 - -``` -docker run -d -e IDENTITY=custom-collector-name -e MANAGER_IP=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector tancloud/hertzbeat-collector -``` - -- `-e IDENTITY=custom-collector-name` : 配置此采集器的唯一性标识符名称,多个采集器名称不能相同,建议自定义英文名称。 -- `-e MANAGER_IP=127.0.0.1` : 配置连接主 HertaBeat 服务的对外 IP。 -- `-e MANAGER_PORT=1158` : 配置连接主 HertzBeat 服务的对外端口,默认 1158。 - -更多配置详细步骤参考官网 通过 Docker 方式安装 HertzBeat - -![](/assets/img/news/HertzBeat-1.4.0-3.png) - -### 更多的 v1.4.0 版本更新 - -> 更多版本新功能更新欢迎探索,感谢社区小伙伴们的辛苦贡献,爱心 💗! - -- \[doc\] add v1.3.2 publish doc by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1075 -- remove elasticsearch unused param index by @Ceilzcx in https://github.com/dromara/hertzbeat/pull/1080 -- feature support monitoring apache airflow by @luoxuanzao in https://github.com/dromara/hertzbeat/pull/1081 -- add luoxuanzao as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1083 -- \[collector\] bugfix sshd cannot use private key to connect by @gcdd1993 in https://github.com/dromara/hertzbeat/pull/1084 -- bugfix update dashboard alerts cards height not consist by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1087 -- Feature#serverchan by @zqr10159 in https://github.com/dromara/hertzbeat/pull/1092 -- bugfix dm database monitoring connect error  by @lisongning in https://github.com/dromara/hertzbeat/pull/1094 -- add lisongning as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1096 -- update alert rule operator display "<=" to ">=" by @Ceilzcx in https://github.com/dromara/hertzbeat/pull/1097 -- \[doc\]  add custom monitoring relate document by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1098 -- add YutingNie as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1103 -- Remove unreachable status by @YutingNie in https://github.com/dromara/hertzbeat/pull/1102 -- 139 auto update alert status by @l646505418 in https://github.com/dromara/hertzbeat/pull/1104 -- feat: aviator fn for str contains, exists & matches by @mikezzb in https://github.com/dromara/hertzbeat/pull/1106 -- add mikezzb as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1107 -- bugfix common alarm do not need monitorId tag existed by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1108 -- bugfix extern alert do not have labels mapping inner monitor by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1111 -- feature: support apache spark metrics monitoring by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1114 -- add a-little-fool as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1116 -- \[Feature\]Add third report of TenCloud by @zqr10159 in https://github.com/dromara/hertzbeat/pull/1113 -- \[Feature\]Add third report of TenCloud (#1113) by @zqr10159 in https://github.com/dromara/hertzbeat/pull/1119 -- \[manager\] fix: can query by tags when tagValue is null by @l646505418 in https://github.com/dromara/hertzbeat/pull/1118 -- bugfix the notification template environment variable display error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1120 -- add littlezhongzer as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1127 -- feature:monitor brearer token api, ignore letter case to comparison by @littlezhongzer in https://github.com/dromara/hertzbeat/pull/1122 -- docs: enhance README by @mikezzb in https://github.com/dromara/hertzbeat/pull/1128 -- Update app-oracle.yml by @ChenXiangxxxxx in https://github.com/dromara/hertzbeat/pull/1129 -- add ChenXiangxxxxx as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1130 -- fix alarm silence strategy setting failed by @Ceilzcx in https://github.com/dromara/hertzbeat/pull/1131 -- support run sql script file in jdbc protocol config by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1117 -- bugfix return old cache json file when upgrade version by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1137 -- support ssh protocol config choose if reuse connection by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1136 -- feat(web): alert threshold UI support matches & contains by @mikezzb in https://github.com/dromara/hertzbeat/pull/1138 -- support hertzbeat metrics collector cluster by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1101 -- add collector card in dashboard by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1147 -- bugfix: linux collect warning: bad syntax, perhaps a bogus '-' by @Mr-zhou315 in https://github.com/dromara/hertzbeat/pull/1151 -- add Mr-zhou315 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1157 -- support config timezone locale language region on web ui by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1154 -- bugfix monitoring template app name already exists by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1152 -- bugfix can not startup when error monitoring template yml file by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1153 -- tags also deleted when the monitor is deleted by @Ceilzcx in https://github.com/dromara/hertzbeat/pull/1159 -- monitoring param host with http head will not be error reported by @littlezhongzer in https://github.com/dromara/hertzbeat/pull/1155 -- \[script\] feature update build.sh and Dockerfile: detect app version a… by @XimfengYao in https://github.com/dromara/hertzbeat/pull/1162 -- add XimfengYao as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1163 -- \[doc\] add collector clusters document by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1161 -- \[hertzbeat\] release hertzbeat version v1.4.0 by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1168 - ---- - -## ⛄ 已支持 - -> 我们将监控采集类型(mysql,jvm,k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控! -> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。 - -- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server -- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster -- Linux, Ubuntu, CentOS, Windows -- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ -- Kubernetes, Docker -- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch -- and more for your custom monitoring. -- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`. -- 和更多自定义监控模版。 -- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook`。 - ---- - -欢迎 star 一波来支持我们哦。 - -**Github: https://github.com/dromara/hertzbeat** -**Gitee: https://gitee.com/dromara/hertzbeat** - -![](/assets/img/news/HertzBeat-1.4.1-8.jpg) +--- +title: 重磅,集群版 HertzBeat 发布,易用友好的开源实时监控系统! +author: tom +tag: + - HertzBeat +date: 2023-08-15 +cover: /assets/img/news/HertzBeat-1.4.0-1.png +head: + - - meta + - name: 新闻 +--- + +### 什么是 HertzBeat? + +HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,无需 Agent 的开源实时监控告警系统。 + +### 特点 + +- 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控阈值告警通知一步到位。 +- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 +- 将 `Http,Jmx,Ssh,Snmp,Jdbc` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? +- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 +- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式消息及时送达。 + +> `HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助开发者和团队快速搭建自有监控系统。 +> 当然我们也提供了对应的 SAAS 版本监控云服务,中小团队和个人无需再为监控自有资源而去部署一套监控系统,登录即可免费开始。 + +![](/assets/img/news/HertzBeat-1.4.1-7.png) + +**Github: https://github.com/dromara/hertzbeat** + +**Gitee: https://gitee.com/dromara/hertzbeat** + +### 集群版来啦 + +我们之前的 hertzbeat 一直是单机版本,组件代码模块化但不支持采集器独立部署,所支持的监控数量上限受到了单节点的天然限制,且无法应对多个隔离网络的资源的统一纳管。 +经过一个多月的迭代,我们重写了采集任务调度,采集器独立部署,设计单机版和集群版用同一套代码方便后续的维护升级,单机集群两种模式可相互切换无感知。最终很高兴,集群版如期与大家见面了。 + +集群版不仅仅给我们带来了更强大的监控性能,更有云边协同等功能让人充满想象。 + +#### 高性能集群 + +- 支持部署采集器集群,多采集器集群横向扩展,指数级提高可监控数量与采集性能。 +- 监控任务在采集器集群中自调度,单采集器挂掉无感知故障迁移采集任务,新加入采集器节点自动调度分担采集压力。 +- 单机模式与集群模式相互切换部署非常方便,无需额外组件部署。 + +![](/assets/img/news/HertzBeat-1.4.0-2.png) + +#### 云边协同 + +> 支持部署边缘采集器集群,与主 HertzBeat 服务云边协同提升采集能力。 + +在多个网络不相通的隔离网络中,在以往的方案中我们需要在每个网络都部署一套监控系统,这导致数据不互通,管理部署维护都不方便。 +HertzBeat 提供云边协同能力,可以在多个隔离网络部署边缘采集器,添加监控时指定采集器,采集器在隔离网络内部进行监控任务采集,采集数据上报,由主 HertzBeat 服务统一调度管理展示。 +这多用于多个隔离数据中心或不同厂商云资源和云下资源的统一监控场景。 + +![](/assets/img/news/HertzBeat-1.4.0-2.png) + +### 为什么要开源集群版? + +往往一些做需要商业化的开源产品的策略会是单机版作为玩具给小玩家们的入门使用,然后集群版作为闭源产品给有需求的氪金玩家付费使用。这样的模式是可以说非常不错的,即保证开源也得到了收益,也适用于很多开源项目的发展策略,可能会在商业路径上走得更通顺点。 +网络上有些人会对这样的分单机和集群版的开源项目嗤之以鼻,觉得它们是伪开源,开源是噱头,他们觉得开源应该什么都开源免费出来,开源团队什么都应该无私奉献出来。。。。很无语这类人,有投入才有回报,当你免费使用着开源软件并得到价值的时候,是否应该想一想你付出给开源软件了什么而不是一味的索取。 +那回到正题,我们又为什么要开源集群版?仅因为热爱开源?如果说我们还在少年可能这话你信,但一个快奔 30 还有家庭责任的人说出这话你信吗,我自己都不信 😂。 +首先我们来看看开源能带来什么,或者为什么要做开源。最开始全职开源的想法很简单,做自己喜欢的开源产品(已实现),程序员的梦想能部署在成千上万的服务器上(看下载量已实现),然后基于此开源产品挣钱(暂未哭)。 + +- 用户流量。开源项目免费提供给用户和开发者,吸引用户使用,宣传等方面都有优势。 +- 用户信任。开源的产品天生容易获取用户的信任和使用耐心,或者说降低用户的信任门槛。 +- 社区协作。开源的产品可以吸引到顶级贡献者一起贡献,接收用户的反馈 issue,pr 贡献等,在社区的驱动下使开源项目越来越好,正向反馈后也会有更多人参与和使用。社区协作我觉得这是开源的意义,而且这样不仅仅只是程序员之间的贡献代码协作,用户都是协作对象(比如我们这个项目有大量的运维朋友贡献代码和文档),如果是仅仅代码开源而不社区协作,那还不如放个安装包给别人免费使用下载就好。 +- 产品生态。这对一些需要生态的产品是需要的,比如 hertzbeat,需要支持对接各种类型协议的监控类型,大量的监控模版。一个好的开源项目生态才能吸引到其它贡献者贡献和分享,在生态中互通有无,最终大家在生态中都受益。这在闭源程序中是很难做到的。 + +上面几点,重在社区协作和产品生态,这也是开源集群版的原因,只有卷开源产品卷自己到更强的产品力,比如集群这一技术特性天生会吸引到开发者(而且集群本身就是我们社区协作的产物),会吸引到更多的用户和贡献者使用反馈大家一起迭代,社区驱动进而正向促进开源项目和满足用户功能体验。 +而对于开源商业化,开源商业化的前提是得有个真正好的,受欢迎,被广泛使用的开源产品,然后在此基础上做商业化挣钱。 + +对了这里再说下开源不等同于免费,如果基于 HertzBeat 二次开发修改了 logo,名称,版权等,请先找我们授权否则会有法律风险,我们有权利追诉破坏开源并因此获利的团队个人的全部违法所得。免费使用不是白嫖,这种破坏开源协议的才是,目前发现大量白嫖怪,小心点哈你们。我每年正月初七都会祝你们用这些钱吃的安心,住的放心,玩的开心哈。(仅个人言论不代表社区) + +### 尝试部署集群版 + +1. `docker` 环境仅需一条命令即可开始 + +`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` + +`或者使用 quay.io (若 dockerhub 网络链接超时)` + +`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat quay.io/tancloud/hertzbeat` + +2. 浏览器访问 `http://localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat` +3. 部署采集器集群 + +``` +docker run -d -e IDENTITY=custom-collector-name -e MANAGER_IP=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector tancloud/hertzbeat-collector +``` + +- `-e IDENTITY=custom-collector-name` : 配置此采集器的唯一性标识符名称,多个采集器名称不能相同,建议自定义英文名称。 +- `-e MANAGER_IP=127.0.0.1` : 配置连接主 HertaBeat 服务的对外 IP。 +- `-e MANAGER_PORT=1158` : 配置连接主 HertzBeat 服务的对外端口,默认 1158。 + +更多配置详细步骤参考官网 通过 Docker 方式安装 HertzBeat + +![](/assets/img/news/HertzBeat-1.4.0-3.png) + +### 更多的 v1.4.0 版本更新 + +> 更多版本新功能更新欢迎探索,感谢社区小伙伴们的辛苦贡献,爱心 💗! + +- \[doc\] add v1.3.2 publish doc by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1075 +- remove elasticsearch unused param index by @Ceilzcx in https://github.com/dromara/hertzbeat/pull/1080 +- feature support monitoring apache airflow by @luoxuanzao in https://github.com/dromara/hertzbeat/pull/1081 +- add luoxuanzao as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1083 +- \[collector\] bugfix sshd cannot use private key to connect by @gcdd1993 in https://github.com/dromara/hertzbeat/pull/1084 +- bugfix update dashboard alerts cards height not consist by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1087 +- Feature#serverchan by @zqr10159 in https://github.com/dromara/hertzbeat/pull/1092 +- bugfix dm database monitoring connect error  by @lisongning in https://github.com/dromara/hertzbeat/pull/1094 +- add lisongning as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1096 +- update alert rule operator display "<=" to ">=" by @Ceilzcx in https://github.com/dromara/hertzbeat/pull/1097 +- \[doc\]  add custom monitoring relate document by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1098 +- add YutingNie as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1103 +- Remove unreachable status by @YutingNie in https://github.com/dromara/hertzbeat/pull/1102 +- 139 auto update alert status by @l646505418 in https://github.com/dromara/hertzbeat/pull/1104 +- feat: aviator fn for str contains, exists & matches by @mikezzb in https://github.com/dromara/hertzbeat/pull/1106 +- add mikezzb as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1107 +- bugfix common alarm do not need monitorId tag existed by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1108 +- bugfix extern alert do not have labels mapping inner monitor by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1111 +- feature: support apache spark metrics monitoring by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1114 +- add a-little-fool as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1116 +- \[Feature\]Add third report of TenCloud by @zqr10159 in https://github.com/dromara/hertzbeat/pull/1113 +- \[Feature\]Add third report of TenCloud (#1113) by @zqr10159 in https://github.com/dromara/hertzbeat/pull/1119 +- \[manager\] fix: can query by tags when tagValue is null by @l646505418 in https://github.com/dromara/hertzbeat/pull/1118 +- bugfix the notification template environment variable display error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1120 +- add littlezhongzer as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1127 +- feature:monitor brearer token api, ignore letter case to comparison by @littlezhongzer in https://github.com/dromara/hertzbeat/pull/1122 +- docs: enhance README by @mikezzb in https://github.com/dromara/hertzbeat/pull/1128 +- Update app-oracle.yml by @ChenXiangxxxxx in https://github.com/dromara/hertzbeat/pull/1129 +- add ChenXiangxxxxx as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1130 +- fix alarm silence strategy setting failed by @Ceilzcx in https://github.com/dromara/hertzbeat/pull/1131 +- support run sql script file in jdbc protocol config by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1117 +- bugfix return old cache json file when upgrade version by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1137 +- support ssh protocol config choose if reuse connection by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1136 +- feat(web): alert threshold UI support matches & contains by @mikezzb in https://github.com/dromara/hertzbeat/pull/1138 +- support hertzbeat metrics collector cluster by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1101 +- add collector card in dashboard by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1147 +- bugfix: linux collect warning: bad syntax, perhaps a bogus '-' by @Mr-zhou315 in https://github.com/dromara/hertzbeat/pull/1151 +- add Mr-zhou315 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1157 +- support config timezone locale language region on web ui by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1154 +- bugfix monitoring template app name already exists by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1152 +- bugfix can not startup when error monitoring template yml file by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1153 +- tags also deleted when the monitor is deleted by @Ceilzcx in https://github.com/dromara/hertzbeat/pull/1159 +- monitoring param host with http head will not be error reported by @littlezhongzer in https://github.com/dromara/hertzbeat/pull/1155 +- \[script\] feature update build.sh and Dockerfile: detect app version a… by @XimfengYao in https://github.com/dromara/hertzbeat/pull/1162 +- add XimfengYao as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1163 +- \[doc\] add collector clusters document by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1161 +- \[hertzbeat\] release hertzbeat version v1.4.0 by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1168 + +--- + +## ⛄ 已支持 + +> 我们将监控采集类型(mysql,jvm,k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控! +> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。 + +- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server +- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster +- Linux, Ubuntu, CentOS, Windows +- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ +- Kubernetes, Docker +- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch +- and more for your custom monitoring. +- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`. +- 和更多自定义监控模版。 +- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook`。 + +--- + +欢迎 star 一波来支持我们哦。 + +**Github: https://github.com/dromara/hertzbeat** +**Gitee: https://gitee.com/dromara/hertzbeat** + +![](/assets/img/news/HertzBeat-1.4.1-8.jpg) diff --git a/src/zh/news/HertzBeat-1.4.1.md b/src/zh/news/HertzBeat-1.4.1.md index fcf9df6a9a..8f9452b33e 100644 --- a/src/zh/news/HertzBeat-1.4.1.md +++ b/src/zh/news/HertzBeat-1.4.1.md @@ -1,94 +1,94 @@ ---- -title: 更好的用户体验, 开源实时监控 HertzBeat 新版发布 -author: tom -tag: - - HertzBeat -date: 2023-09-27 -cover: /assets/img/news/HertzBeat-1.4.1-1.png -head: - - - meta - - name: 新闻 ---- - -> 哈喽大家好,时间很快 1 个多月又过去了,HertzBeat 经过近两个月的迭代终于发布了 v1.4.1 版本。为什么是终于,因为有点难哈哈。我们参考 rocketmq 重构了 netty 的 server client 端模块,重构了采集器集群调度。比起上一版本有了更优雅的通讯代码,更完善全面的集群。采集任务一致性 hash 调度,集群心跳保活,断开主动重连,主动上线下线,主动停机等等这些都有。设计了新的控制台登陆界面和欢迎页面,支持了采集器集群的 UI 管理,合并了开源之夏两位同学的帮助提示头特性和阈值表达式特性,很多用户需要的监控指标名称国际化等等,最重要的当然是修复若干 BUG,增强了用户体验(自我感觉良好)。 - -### 总结起来如下: - -- **重构 netty client server, 采集器集群调度** 感谢 @Ceilzcx @tomsun28 -- **采集器集群的 UI 界面管理** 感谢 @Ceilzcx @tomsun28 -- **功能页面帮助信息模块和阈值表达式增强** 开源之夏和 GLCC 课题 感谢 @YutingNie @mikezzb -- **新的控制台登陆界面和欢迎页面** -- **监控指标名称国际化** 用户可以看指标的中英文名称啦,欢迎一起完善监控模版里面的 i18n 国际化资源 -- **支持 kubernetes helm charts 一键部署** 见 https://artifacthub.io/packages/search?repo=hertzbeat - -**更多的特性和 BUG 修复,稳定性提示** 感谢 @zqr10159 @Carpe-Wang @luxx-lq @l646505418 @LINGLUOJUN @luelueking @qyaaaa @novohit @gcdd1993 - -### 上效果图: - -- 新的登陆页面 UI - -![](/assets/img/news/HertzBeat-1.4.1-1.png) - -![](/assets/img/news/HertzBeat-1.4.1-2.png) - -- 支持采集器集群管理 - -![](/assets/img/news/HertzBeat-1.4.1-3.png) - -![](/assets/img/news/HertzBeat-1.4.1-4.png) - -- 友好的帮助文档头 - -![](/assets/img/news/HertzBeat-1.4.1-5.png) - -- 监控指标名称国际化 - -![](/assets/img/news/HertzBeat-1.4.1-6.png) - -### 什么是 HertzBeat? - -HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,无需 Agent 的开源实时监控告警系统。 - -### 特点 - -- 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控阈值告警通知一步到位。 -- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 -- 将 `Http,Jmx,Ssh,Snmp,Jdbc` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? -- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 -- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式消息及时送达。 - -![](/assets/img/news/HertzBeat-1.4.1-7.png) - -**Github: https://github.com/dromara/hertzbeat** - -**Gitee: https://gitee.com/dromara/hertzbeat** - -### 尝试部署 - -1. `docker` 环境仅需一条命令即可开始 - -`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` - -`或者使用 quay.io (若 dockerhub 网络链接超时)` - -`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat quay.io/tancloud/hertzbeat` - -2. 浏览器访问 `http://localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat` -3. 部署采集器集群 - -``` -docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector tancloud/hertzbeat-collector -``` - -- `-e IDENTITY=custom-collector-name` : 配置此采集器的唯一性标识符名称,多个采集器名称不能相同,建议自定义英文名称。 -- `-e MANAGER_HOST=127.0.0.1` : 配置连接主 HertaBeat 服务的对外 IP。 -- `-e MANAGER_PORT=1158` : 配置连接主 HertzBeat 服务的对外端口,默认 1158。 - ---- - -**Github: https://github.com/dromara/hertzbeat** -**Gitee: https://gitee.com/dromara/hertzbeat** - -感兴趣的话给个 star 吧:smile:,QQ 交流群:236915833  微信交流群请加好友: - - +--- +title: 更好的用户体验, 开源实时监控 HertzBeat 新版发布 +author: tom +tag: + - HertzBeat +date: 2023-09-27 +cover: /assets/img/news/HertzBeat-1.4.1-1.png +head: + - - meta + - name: 新闻 +--- + +> 哈喽大家好,时间很快 1 个多月又过去了,HertzBeat 经过近两个月的迭代终于发布了 v1.4.1 版本。为什么是终于,因为有点难哈哈。我们参考 rocketmq 重构了 netty 的 server client 端模块,重构了采集器集群调度。比起上一版本有了更优雅的通讯代码,更完善全面的集群。采集任务一致性 hash 调度,集群心跳保活,断开主动重连,主动上线下线,主动停机等等这些都有。设计了新的控制台登陆界面和欢迎页面,支持了采集器集群的 UI 管理,合并了开源之夏两位同学的帮助提示头特性和阈值表达式特性,很多用户需要的监控指标名称国际化等等,最重要的当然是修复若干 BUG,增强了用户体验(自我感觉良好)。 + +### 总结起来如下: + +- **重构 netty client server, 采集器集群调度** 感谢 @Ceilzcx @tomsun28 +- **采集器集群的 UI 界面管理** 感谢 @Ceilzcx @tomsun28 +- **功能页面帮助信息模块和阈值表达式增强** 开源之夏和 GLCC 课题 感谢 @YutingNie @mikezzb +- **新的控制台登陆界面和欢迎页面** +- **监控指标名称国际化** 用户可以看指标的中英文名称啦,欢迎一起完善监控模版里面的 i18n 国际化资源 +- **支持 kubernetes helm charts 一键部署** 见 https://artifacthub.io/packages/search?repo=hertzbeat + +**更多的特性和 BUG 修复,稳定性提示** 感谢 @zqr10159 @Carpe-Wang @luxx-lq @l646505418 @LINGLUOJUN @luelueking @qyaaaa @novohit @gcdd1993 + +### 上效果图: + +- 新的登陆页面 UI + +![](/assets/img/news/HertzBeat-1.4.1-1.png) + +![](/assets/img/news/HertzBeat-1.4.1-2.png) + +- 支持采集器集群管理 + +![](/assets/img/news/HertzBeat-1.4.1-3.png) + +![](/assets/img/news/HertzBeat-1.4.1-4.png) + +- 友好的帮助文档头 + +![](/assets/img/news/HertzBeat-1.4.1-5.png) + +- 监控指标名称国际化 + +![](/assets/img/news/HertzBeat-1.4.1-6.png) + +### 什么是 HertzBeat? + +HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,无需 Agent 的开源实时监控告警系统。 + +### 特点 + +- 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控阈值告警通知一步到位。 +- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 +- 将 `Http,Jmx,Ssh,Snmp,Jdbc` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? +- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 +- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式消息及时送达。 + +![](/assets/img/news/HertzBeat-1.4.1-7.png) + +**Github: https://github.com/dromara/hertzbeat** + +**Gitee: https://gitee.com/dromara/hertzbeat** + +### 尝试部署 + +1. `docker` 环境仅需一条命令即可开始 + +`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` + +`或者使用 quay.io (若 dockerhub 网络链接超时)` + +`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat quay.io/tancloud/hertzbeat` + +2. 浏览器访问 `http://localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat` +3. 部署采集器集群 + +``` +docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector tancloud/hertzbeat-collector +``` + +- `-e IDENTITY=custom-collector-name` : 配置此采集器的唯一性标识符名称,多个采集器名称不能相同,建议自定义英文名称。 +- `-e MANAGER_HOST=127.0.0.1` : 配置连接主 HertaBeat 服务的对外 IP。 +- `-e MANAGER_PORT=1158` : 配置连接主 HertzBeat 服务的对外端口,默认 1158。 + +--- + +**Github: https://github.com/dromara/hertzbeat** +**Gitee: https://gitee.com/dromara/hertzbeat** + +感兴趣的话给个 star 吧:smile:,QQ 交流群:236915833  微信交流群请加好友: + + diff --git a/src/zh/news/HertzBeat-1.4.2.md b/src/zh/news/HertzBeat-1.4.2.md index 79132f6088..cac00eb167 100644 --- a/src/zh/news/HertzBeat-1.4.2.md +++ b/src/zh/news/HertzBeat-1.4.2.md @@ -1,99 +1,99 @@ ---- -title: 开源实时监控 HertzBeat 1.4.2 版本发布,自定义消息模版 -author: hertzbeat -tag: - - HertzBeat -date: 2023-11-13 -cover: /assets/img/news/HertzBeat-1.4.1-1.png -head: - - - meta - - name: 新闻 ---- - -> 哈喽大家早上好,开源实时监控 HertzBeat 新版本 v1.4.2 来啦,欢迎了解使用。 - -**新版本特性如下** - -- **自定义消息通知模版特性,开源之夏课题** -- **支持华为云 OBS 存储监控模版文件** -- **支持 MQTT 消息服务器 emqx 监控** -- **支持对 udp 端口可用性监控** -- **更多的特性功能支持和 BUG 修复** -- **安装包内置 JDK 一键启动** - -**更多的特性和 BUG 修复欢迎使用探索,1.4.2 版本共有 13 位社区小伙伴们参与,感谢他们的贡献 ❤️** - -> 也欢迎更多朋友加入我们参与开源项目建设,众多 issue 待你认领。 - -监控模版指标国际化任务 - https://github.com/dromara/hertzbeat/issues/1212 - -### 什么是 HertzBeat? - -HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,无需 Agent 的开源实时监控告警系统。 - -### 特点 - -- 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控阈值告警通知一步到位。 -- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 -- 将 `Http,Jmx,Ssh,Snmp,Jdbc` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? -- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 -- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式消息及时送达。 - -> `HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助开发者和团队快速搭建自有监控系统。 -> 当然我们也提供了对应的 \*\*SAAS 版本监控云服务**,中小团队和个人无需再为监控自有资源而去部署一套监控系统,**登录即可免费开始\*\*。 - -![](/assets/img/news/HertzBeat-1.4.2-1.png) - -**Github: https://github.com/dromara/hertzbeat** - -**Gitee: https://gitee.com/dromara/hertzbeat** - -### 尝试部署 - -1. `docker` 环境仅需一条命令即可开始 - -`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` - -`或者使用 quay.io (若 dockerhub 网络链接超时)` - -`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat quay.io/tancloud/hertzbeat` - -2. 浏览器访问 `http://localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat` -3. 部署采集器集群 - -``` -docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector tancloud/hertzbeat-collector -``` - -- `-e IDENTITY=custom-collector-name` : 配置此采集器的唯一性标识符名称,多个采集器名称不能相同,建议自定义英文名称。 -- `-e MANAGER_HOST=127.0.0.1` : 配置连接主 HertzBeat 服务的对外 IP。 -- `-e MANAGER_PORT=1158` : 配置连接主 HertzBeat 服务的对外端口,默认 1158。 - -更多配置详细步骤参考 通过 Docker 方式安装 HertzBeat - ---- - -## ⛄ 已支持 - -> 我们将监控采集类型(mysql,jvm,k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控! -> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。 - -- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server -- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster -- Linux, Ubuntu, CentOS, Windows -- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ -- Kubernetes, Docker -- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch -- and more for your custom monitoring. -- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`. -- 和更多自定义监控模版。 -- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。 - ---- - -**Github: https://github.com/dromara/hertzbeat** -**Gitee: https://gitee.com/dromara/hertzbeat** - -**感兴趣的话给个 star 吧,QQ 交流群:236915833 微信交流群加好友:** - - +--- +title: 开源实时监控 HertzBeat 1.4.2 版本发布,自定义消息模版 +author: hertzbeat +tag: + - HertzBeat +date: 2023-11-13 +cover: /assets/img/news/HertzBeat-1.4.1-1.png +head: + - - meta + - name: 新闻 +--- + +> 哈喽大家早上好,开源实时监控 HertzBeat 新版本 v1.4.2 来啦,欢迎了解使用。 + +**新版本特性如下** + +- **自定义消息通知模版特性,开源之夏课题** +- **支持华为云 OBS 存储监控模版文件** +- **支持 MQTT 消息服务器 emqx 监控** +- **支持对 udp 端口可用性监控** +- **更多的特性功能支持和 BUG 修复** +- **安装包内置 JDK 一键启动** + +**更多的特性和 BUG 修复欢迎使用探索,1.4.2 版本共有 13 位社区小伙伴们参与,感谢他们的贡献 ❤️** + +> 也欢迎更多朋友加入我们参与开源项目建设,众多 issue 待你认领。 + +监控模版指标国际化任务 - https://github.com/dromara/hertzbeat/issues/1212 + +### 什么是 HertzBeat? + +HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,无需 Agent 的开源实时监控告警系统。 + +### 特点 + +- 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控阈值告警通知一步到位。 +- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 +- 将 `Http,Jmx,Ssh,Snmp,Jdbc` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? +- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 +- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式消息及时送达。 + +> `HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助开发者和团队快速搭建自有监控系统。 +> 当然我们也提供了对应的 \*\*SAAS 版本监控云服务**,中小团队和个人无需再为监控自有资源而去部署一套监控系统,**登录即可免费开始\*\*。 + +![](/assets/img/news/HertzBeat-1.4.2-1.png) + +**Github: https://github.com/dromara/hertzbeat** + +**Gitee: https://gitee.com/dromara/hertzbeat** + +### 尝试部署 + +1. `docker` 环境仅需一条命令即可开始 + +`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` + +`或者使用 quay.io (若 dockerhub 网络链接超时)` + +`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat quay.io/tancloud/hertzbeat` + +2. 浏览器访问 `http://localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat` +3. 部署采集器集群 + +``` +docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector tancloud/hertzbeat-collector +``` + +- `-e IDENTITY=custom-collector-name` : 配置此采集器的唯一性标识符名称,多个采集器名称不能相同,建议自定义英文名称。 +- `-e MANAGER_HOST=127.0.0.1` : 配置连接主 HertzBeat 服务的对外 IP。 +- `-e MANAGER_PORT=1158` : 配置连接主 HertzBeat 服务的对外端口,默认 1158。 + +更多配置详细步骤参考 通过 Docker 方式安装 HertzBeat + +--- + +## ⛄ 已支持 + +> 我们将监控采集类型(mysql,jvm,k8s)都定义为 yml 监控模版,用户可以导入这些模版来支持对应类型的监控! +> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。 + +- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server +- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster +- Linux, Ubuntu, CentOS, Windows +- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ +- Kubernetes, Docker +- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch +- and more for your custom monitoring. +- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`. +- 和更多自定义监控模版。 +- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。 + +--- + +**Github: https://github.com/dromara/hertzbeat** +**Gitee: https://gitee.com/dromara/hertzbeat** + +**感兴趣的话给个 star 吧,QQ 交流群:236915833 微信交流群加好友:** + + diff --git a/src/zh/news/HertzBeat-1.4.3.md b/src/zh/news/HertzBeat-1.4.3.md index 20c29a0304..425e63fe04 100644 --- a/src/zh/news/HertzBeat-1.4.3.md +++ b/src/zh/news/HertzBeat-1.4.3.md @@ -1,149 +1,149 @@ ---- -title: HertzBeat 1.4.3新版本发布,Prometheus兼容。CNCF全景图 -author: tom -tag: - - HertzBeat -date: 2023-12-12 -cover: /assets/img/news/HertzBeat-1.4.3-0.png -head: - - - meta - - name: 新闻 ---- - -### 大家早上好,又到了一月一版的时间点,很高兴 HertzBeat 的最新版本 1.4.3 发布啦! - -### 还有一个好消息,很开心上周 HertzBeat 被 CNCF 云原生全景图收录,这也算是对这个开源项目的一个认可,继续冲还有就是后续在云原生能力的更加增强。 - -![](/assets/img/news/HertzBeat-1.4.3-0.png) - -**1.4.3 版本更新简述如下:** - -- 三方外部告警上报增强 -- 支持 mysql api port website mongodb jvm redis 等监控指标的 i18n 国际化 -- Prometheus 生态兼容,支持添加 Prometheus 任务 -- 支持使用 VictoriaMetrics 时序数据库作为系统指标数据存储 -- 支持监控 Spring Gateway 指标 -- 新增更多 Windows 监控指标 -- 添加 e2e 测试模块,由 api-testing 支持 -- 更多的特性,文档优化和 BUG 修复 - -> 上面这些特性其中想拿 支持 Prometheus 任务出来说一说。 - -对 Prometheus 兼容目前 HertzBeat 的能力可以分为三种。 - -- 在页面添加支持 Prometheus 协议的对端应用或 exporter 即可,无需适配。 - -> 例如我们监控有暴露 prometheus metrics 接口的 iotdb 数据库,在页面添加任务,即可看到采集数据并阈值告警等,无需提前适配模版。 - -![](/assets/img/news/HertzBeat-1.4.3-1.png) - -![](/assets/img/news/HertzBeat-1.4.3-2.png) - -![](/assets/img/news/HertzBeat-1.4.3-3.png) - -- 提前根据应用暴露的 Prometheus metrics 接口数据编写监控模版,然后在页面该监控类型即可。需要适配,但指标名称等内容自定义更友好。 - -> 还是例如这个 iotdb 数据库,我们根据其暴露的 prometheus metrics 接口编写监控模版,然后将其作为一款新的监控类型,然后在页面添加监控资源。 - -![](/assets/img/news/HertzBeat-1.4.3-4.png) - -![](/assets/img/news/HertzBeat-1.4.3-5.png) - -![](/assets/img/news/HertzBeat-1.4.3-6.png) - -- 第三种能力是当这个资源已经被 prometheus 监控时,我们通过查询 PromQL 从 prometheus server 直接拿这个监控资源的数据来作为采集指标数据。需要编写监控模版配置 PromQL. - -> 例如我们自定义监控模版从 prometheus server 拿 kafka 的数据。 - -![](/assets/img/news/HertzBeat-1.4.3-7.png) - -![](/assets/img/news/HertzBeat-1.4.3-8.png) - -还有些不足后面还需加强,一个是 prometheus 的资源自动发现,还有就是指标类型 Histogram 还没支持等,后面继续完善优化。 -这一次我们也适配了 VictoriaMetrics 时序数据库作为指标存储层,参考了 VictoriaMetrics 的优雅数据结构设计,我们自己的数据结构也更新了一波。因为 VictoriaMetrics 兼容 PromQL,我们后续可能会考虑深入结合 VictoriaMetrics 的能力做一些高级特性。 - -### 什么是 HertzBeat? - -HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,兼容 Prometheus,无需 Agent 的开源实时监控告警系统。 - -### 特点 - -- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。 -- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 -- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? -- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。 -- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 -- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。 - -**Github: https://github.com/dromara/hertzbeat** - -**Gitee: https://gitee.com/dromara/hertzbeat** - -### 尝试部署 - -1. `docker` 环境仅需一条命令即可开始 - -`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` - ---- - -### 更新日志 - -> 欢迎探索更多,感谢社区小伙伴们的辛苦贡献, 笔芯 💗! - -- update package deploy doc by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1330 -- bugfix duplicate collect job when update monitor templates by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1332 -- bugfix number variable in freemarker template display error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1334 -- \[alerter\] Enhanced reporting of external general alert API by @SurryChen in https://github.com/dromara/hertzbeat/pull/1326 -- \[doc\] update hertzbeat-mysql-tdengine readme by @jiashu1024 in https://github.com/dromara/hertzbeat/pull/1335 -- add jiashu1024 as a contributor for doc by @allcontributors in https://github.com/dromara/hertzbeat/pull/1336 -- app-mysql.yml: Adjust slow query translation by @1036664317 in https://github.com/dromara/hertzbeat/pull/1337 -- add 1036664317 as a contributor for doc by @allcontributors in https://github.com/dromara/hertzbeat/pull/1338 -- Bump com.google.guava:guava from 31.0.1-jre to 32.0.0-jre by @dependabot in https://github.com/dromara/hertzbeat/pull/1339 -- \[feature\] support auto collect metrics by prometheus task by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1342 -- \[doc\] add vinci as new committer by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1341 -- \[feature\] add tag word cloud in dashboard by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1345 -- support custom prometheus endpoint path by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1346 -- bugfix tdengine query interval history metrics data with instance error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1348 -- unlimit Alert.java content field length by @xiaoguolong in https://github.com/dromara/hertzbeat/pull/1351 -- add xiaoguolong as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1353 -- update monitor detail table ui layout by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1352 -- \[doc\]add star history by @zqr10159 in https://github.com/dromara/hertzbeat/pull/1356 -- feature: app-mongodb.yml by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1359 -- alarm threshold support prometheus task metrics by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1354 -- support victoriametrics as metrics data storage by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1361 -- Add time type to support query_time of mysql and mariadb by @Clownsw in https://github.com/dromara/hertzbeat/pull/1364 -- add Clownsw as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1365 -- Error occured when I followed running steps to start Front-web by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1366 -- add Calvin979 as a contributor for doc by @allcontributors in https://github.com/dromara/hertzbeat/pull/1367 -- enriches the cncf landscape by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1368 -- Fix flaky test in CollectUtilTest by @bbelide2 in https://github.com/dromara/hertzbeat/pull/1371 -- add bbelide2 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1372 -- Fix flaky test replaceSmilingPlaceholder by @bbelide2 in https://github.com/dromara/hertzbeat/pull/1373 -- add docker-compose script hertzbeat+mysql+victoria-metrics all in one by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1370 -- Feature: app-jvm.yml support for international name aliases by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1376 -- add Calvin979 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1377 -- feature: support monitoring spring gateway metrics by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1374 -- update code comment and doc, bugfix concurrent exception by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1378 -- update windows define and accept snmp leaf by @jinyaoMa in https://github.com/dromara/hertzbeat/pull/1379 -- add jinyaoMa as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1380 -- fix exception when sending email has special chars by @Carpe-Wang in https://github.com/dromara/hertzbeat/pull/1383 -- test: add e2e testing for some basic APIs by @LinuxSuRen in https://github.com/dromara/hertzbeat/pull/1387 -- add LinuxSuRen as a contributor for code, and test by @allcontributors in https://github.com/dromara/hertzbeat/pull/1389 -- bugfix auto generate monitor name error when add monitor by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1384 -- bugfix CalculateAlarm execAlertExpression NPE by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1388 -- Feature: app-redis.yml support for international name aliases by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1390 -- test: add more monitor related e2e testing case by @LinuxSuRen in https://github.com/dromara/hertzbeat/pull/1391 -- chore: update the pr template about the e2e testing by @LinuxSuRen in https://github.com/dromara/hertzbeat/pull/1392 -- add help header ui when update or add monitors by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1399 -- \[hertzbeat\] release hertzbeat version v1.4.3 by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1400 - -**Full Changelog**: https://github.com/dromara/hertzbeat/compare/v1.4.2...v1.4.3 - -**Github: https://github.com/dromara/hertzbeat** -**Gitee: https://gitee.com/dromara/hertzbeat** - -\***\*看到不妨 star 下吧**,QQ 交流群:236915833 微信交流群加好友: -\*\* - - +--- +title: HertzBeat 1.4.3新版本发布,Prometheus兼容。CNCF全景图 +author: tom +tag: + - HertzBeat +date: 2023-12-12 +cover: /assets/img/news/HertzBeat-1.4.3-0.png +head: + - - meta + - name: 新闻 +--- + +### 大家早上好,又到了一月一版的时间点,很高兴 HertzBeat 的最新版本 1.4.3 发布啦! + +### 还有一个好消息,很开心上周 HertzBeat 被 CNCF 云原生全景图收录,这也算是对这个开源项目的一个认可,继续冲还有就是后续在云原生能力的更加增强。 + +![](/assets/img/news/HertzBeat-1.4.3-0.png) + +**1.4.3 版本更新简述如下:** + +- 三方外部告警上报增强 +- 支持 mysql api port website mongodb jvm redis 等监控指标的 i18n 国际化 +- Prometheus 生态兼容,支持添加 Prometheus 任务 +- 支持使用 VictoriaMetrics 时序数据库作为系统指标数据存储 +- 支持监控 Spring Gateway 指标 +- 新增更多 Windows 监控指标 +- 添加 e2e 测试模块,由 api-testing 支持 +- 更多的特性,文档优化和 BUG 修复 + +> 上面这些特性其中想拿 支持 Prometheus 任务出来说一说。 + +对 Prometheus 兼容目前 HertzBeat 的能力可以分为三种。 + +- 在页面添加支持 Prometheus 协议的对端应用或 exporter 即可,无需适配。 + +> 例如我们监控有暴露 prometheus metrics 接口的 iotdb 数据库,在页面添加任务,即可看到采集数据并阈值告警等,无需提前适配模版。 + +![](/assets/img/news/HertzBeat-1.4.3-1.png) + +![](/assets/img/news/HertzBeat-1.4.3-2.png) + +![](/assets/img/news/HertzBeat-1.4.3-3.png) + +- 提前根据应用暴露的 Prometheus metrics 接口数据编写监控模版,然后在页面该监控类型即可。需要适配,但指标名称等内容自定义更友好。 + +> 还是例如这个 iotdb 数据库,我们根据其暴露的 prometheus metrics 接口编写监控模版,然后将其作为一款新的监控类型,然后在页面添加监控资源。 + +![](/assets/img/news/HertzBeat-1.4.3-4.png) + +![](/assets/img/news/HertzBeat-1.4.3-5.png) + +![](/assets/img/news/HertzBeat-1.4.3-6.png) + +- 第三种能力是当这个资源已经被 prometheus 监控时,我们通过查询 PromQL 从 prometheus server 直接拿这个监控资源的数据来作为采集指标数据。需要编写监控模版配置 PromQL. + +> 例如我们自定义监控模版从 prometheus server 拿 kafka 的数据。 + +![](/assets/img/news/HertzBeat-1.4.3-7.png) + +![](/assets/img/news/HertzBeat-1.4.3-8.png) + +还有些不足后面还需加强,一个是 prometheus 的资源自动发现,还有就是指标类型 Histogram 还没支持等,后面继续完善优化。 +这一次我们也适配了 VictoriaMetrics 时序数据库作为指标存储层,参考了 VictoriaMetrics 的优雅数据结构设计,我们自己的数据结构也更新了一波。因为 VictoriaMetrics 兼容 PromQL,我们后续可能会考虑深入结合 VictoriaMetrics 的能力做一些高级特性。 + +### 什么是 HertzBeat? + +HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,兼容 Prometheus,无需 Agent 的开源实时监控告警系统。 + +### 特点 + +- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。 +- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 +- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? +- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。 +- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 +- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。 + +**Github: https://github.com/dromara/hertzbeat** + +**Gitee: https://gitee.com/dromara/hertzbeat** + +### 尝试部署 + +1. `docker` 环境仅需一条命令即可开始 + +`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` + +--- + +### 更新日志 + +> 欢迎探索更多,感谢社区小伙伴们的辛苦贡献, 笔芯 💗! + +- update package deploy doc by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1330 +- bugfix duplicate collect job when update monitor templates by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1332 +- bugfix number variable in freemarker template display error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1334 +- \[alerter\] Enhanced reporting of external general alert API by @SurryChen in https://github.com/dromara/hertzbeat/pull/1326 +- \[doc\] update hertzbeat-mysql-tdengine readme by @jiashu1024 in https://github.com/dromara/hertzbeat/pull/1335 +- add jiashu1024 as a contributor for doc by @allcontributors in https://github.com/dromara/hertzbeat/pull/1336 +- app-mysql.yml: Adjust slow query translation by @1036664317 in https://github.com/dromara/hertzbeat/pull/1337 +- add 1036664317 as a contributor for doc by @allcontributors in https://github.com/dromara/hertzbeat/pull/1338 +- Bump com.google.guava:guava from 31.0.1-jre to 32.0.0-jre by @dependabot in https://github.com/dromara/hertzbeat/pull/1339 +- \[feature\] support auto collect metrics by prometheus task by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1342 +- \[doc\] add vinci as new committer by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1341 +- \[feature\] add tag word cloud in dashboard by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1345 +- support custom prometheus endpoint path by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1346 +- bugfix tdengine query interval history metrics data with instance error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1348 +- unlimit Alert.java content field length by @xiaoguolong in https://github.com/dromara/hertzbeat/pull/1351 +- add xiaoguolong as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1353 +- update monitor detail table ui layout by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1352 +- \[doc\]add star history by @zqr10159 in https://github.com/dromara/hertzbeat/pull/1356 +- feature: app-mongodb.yml by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1359 +- alarm threshold support prometheus task metrics by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1354 +- support victoriametrics as metrics data storage by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1361 +- Add time type to support query_time of mysql and mariadb by @Clownsw in https://github.com/dromara/hertzbeat/pull/1364 +- add Clownsw as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1365 +- Error occured when I followed running steps to start Front-web by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1366 +- add Calvin979 as a contributor for doc by @allcontributors in https://github.com/dromara/hertzbeat/pull/1367 +- enriches the cncf landscape by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1368 +- Fix flaky test in CollectUtilTest by @bbelide2 in https://github.com/dromara/hertzbeat/pull/1371 +- add bbelide2 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1372 +- Fix flaky test replaceSmilingPlaceholder by @bbelide2 in https://github.com/dromara/hertzbeat/pull/1373 +- add docker-compose script hertzbeat+mysql+victoria-metrics all in one by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1370 +- Feature: app-jvm.yml support for international name aliases by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1376 +- add Calvin979 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1377 +- feature: support monitoring spring gateway metrics by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1374 +- update code comment and doc, bugfix concurrent exception by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1378 +- update windows define and accept snmp leaf by @jinyaoMa in https://github.com/dromara/hertzbeat/pull/1379 +- add jinyaoMa as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1380 +- fix exception when sending email has special chars by @Carpe-Wang in https://github.com/dromara/hertzbeat/pull/1383 +- test: add e2e testing for some basic APIs by @LinuxSuRen in https://github.com/dromara/hertzbeat/pull/1387 +- add LinuxSuRen as a contributor for code, and test by @allcontributors in https://github.com/dromara/hertzbeat/pull/1389 +- bugfix auto generate monitor name error when add monitor by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1384 +- bugfix CalculateAlarm execAlertExpression NPE by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1388 +- Feature: app-redis.yml support for international name aliases by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1390 +- test: add more monitor related e2e testing case by @LinuxSuRen in https://github.com/dromara/hertzbeat/pull/1391 +- chore: update the pr template about the e2e testing by @LinuxSuRen in https://github.com/dromara/hertzbeat/pull/1392 +- add help header ui when update or add monitors by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1399 +- \[hertzbeat\] release hertzbeat version v1.4.3 by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1400 + +**Full Changelog**: https://github.com/dromara/hertzbeat/compare/v1.4.2...v1.4.3 + +**Github: https://github.com/dromara/hertzbeat** +**Gitee: https://gitee.com/dromara/hertzbeat** + +\***\*看到不妨 star 下吧**,QQ 交流群:236915833 微信交流群加好友: +\*\* + + diff --git a/src/zh/news/HertzBeat-1.4.4.md b/src/zh/news/HertzBeat-1.4.4.md index d0b784fbe5..a592324859 100644 --- a/src/zh/news/HertzBeat-1.4.4.md +++ b/src/zh/news/HertzBeat-1.4.4.md @@ -1,138 +1,138 @@ ---- -title: HertzBeat 新版本1.4.4 发布,支持监控Nginx,Websocket等 -author: tom -tag: - - HertzBeat -date: 2024-01-23 -cover: /assets/img/news/HertzBeat-1.4.0-1.png -head: - - - meta - - name: 新闻 ---- - -大家早上好,又到了一月一版的时间点,这应该是春节前的最后一个版本,很高兴 HertzBeat 的最新版本 1.4.4 发布啦! - -### 1.4.4 版本更新简述如下: - -**新增了对 SNMP V3 版本协议的支持,支持对 Nginx, NebulaGraph, Pop3, Memcached, Hive, DNS, WebSockets, NTP, SMTP 等等一系列新的监控类型。修复了大量 BUG 和文档完善,更多特性欢迎使用探索!** - -- support snmp v3 monitoring protocol @TJxiaobao -- support monitoring NebulaGraph metrics @ZY945 -- support monitoring pop3 metrics @a-little-fool -- support monitoring memcached metrics @ZY945 -- support monitoring nginx metrics @a-little-fool -- support monitoring hive metrics @a-little-fool -- feature: support for dns monitoring by @Calvin979 -- monitoring the availability of websockets through handshake. by @ZY945 -- add ntp protocol and support ntp monitoring by @ZY945 -- add smtp protocol and support smtp monitoring by @ZY945 -- more feature, document and bugfix - -### 什么是 HertzBeat? - -HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,兼容 Prometheus,无需 Agent 的开源实时监控告警系统。 - -![](/assets/img/news/HertzBeat-1.4.4-1.png) - -### 特点 - -- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。 -- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 -- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? -- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。 -- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 -- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。 - -**Github: https://github.com/dromara/hertzbeat** - -**Gitee: https://gitee.com/dromara/hertzbeat** - -### 尝试部署 - -1. `docker` 环境仅需一条命令即可开始 - -`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` - ---- - -### 更新日志 - -> 欢迎探索更多,感谢社区小伙伴们的辛苦贡献, 笔芯 💗! - -- bugfix metrics tags value store jpa data-storage error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1403 -- add smtp protocol and support smtp monitoring by @ZY945 in https://github.com/dromara/hertzbeat/pull/1407 -- add ZY945 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1409 -- support new parse type 'log' in ssh collect protocol by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1410 -- add ntp protocol and support ntp monitoring by @ZY945 in https://github.com/dromara/hertzbeat/pull/1411 -- monitoring the availability of websockets through handshake. by @ZY945 in https://github.com/dromara/hertzbeat/pull/1413 -- \[Task-1386\] When adding tags in tag management, random colors are given by default. by @prolevel1 in https://github.com/dromara/hertzbeat/pull/1412 -- add prolevel1 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1415 -- #1397 feature: support for dns monitoring by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1416 -- Support monitoring hive metrics by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1417 -- support legend pageable in history data charts by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1414 -- update component tip and help tip doc by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1418 -- feature: support monitoring nginx metrics and add a help doc by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1420 -- update parser to parse from prometheus txt metrics data by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1421 -- support monitoring memcached metrics and add a help doc by @ZY945 in https://github.com/dromara/hertzbeat/pull/1423 -- support all ssh connect key exchange by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1424 -- \[doc\] add code of conduct by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1425 -- update label structure store in victoria metrics, make it prometheus like by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1426 -- feature: support monitoring pop3 metrics and add help doc by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1427 -- Update sidebars.json by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1428 -- Add zh-cn help doc by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1429 -- update monitoring state un-manage to unmonitored, update pic by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1430 -- Add jpa to date type storage by @Clownsw in https://github.com/dromara/hertzbeat/pull/1431 -- bugfix ^o^ token error, protect metrics api auth by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1434 -- Add relevant documents for SMTP and NTP by @ZY945 in https://github.com/dromara/hertzbeat/pull/1437 -- bugfix threshold init error in mysql env by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1435 -- app-rabbitmq.yml support for international name aliases by @ZY945 in https://github.com/dromara/hertzbeat/pull/1439 -- fix(\*): error create lru-cache-timeout-cleaner thread by @Clownsw in https://github.com/dromara/hertzbeat/pull/1438 -- app-rabbitmq.yml Modifying Error Fields. by @ZY945 in https://github.com/dromara/hertzbeat/pull/1440 -- support monitoring NebulaGraph metrics and add help doc by @ZY945 in https://github.com/dromara/hertzbeat/pull/1441 -- Fix Nginx Collect validateParams function NPE by @Clownsw in https://github.com/dromara/hertzbeat/pull/1442 -- feature: add metrics i18n for app-springboot3.yml by @liyin in https://github.com/dromara/hertzbeat/pull/1445 -- feat: add metrics i18n for app-docker.yml by @liyin in https://github.com/dromara/hertzbeat/pull/1446 -- update docker-compose script and fix version by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1447 -- bugfix java.lang.IllegalArgumentException: Illegal character in query… by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1443 -- bugfix delete monitor error after monitor canceled by @ZhangZixuan1994 in https://github.com/dromara/hertzbeat/pull/1451 -- add ZhangZixuan1994 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1454 -- remove sleep, probably busy-waiting by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1456 -- \[doc\] add new committer ZY945 by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1453 -- Update app-zookeeper.yml by @hurenjie1 in https://github.com/dromara/hertzbeat/pull/1458 -- add hurenjie1 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1459 -- update dashboard ui, remove ssh custom SignatureFactories, update app name by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1460 -- \[Task\] Monitoring Template Yml Metrics I18n | 监控模版指标国际化任务认领 #1212 by @tslj1024 in https://github.com/dromara/hertzbeat/pull/1461 -- add tslj1024 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1462 -- Add alarm trigger time for alarm restore by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1464 -- bugfix history range query not work when victoria-metrics store by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1463 -- bugfix springboot3 translation by @liyin in https://github.com/dromara/hertzbeat/pull/1467 -- bugfix telegram-notice can not input bot-token by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1465 -- feat: support hostname target by @ldysdu in https://github.com/dromara/hertzbeat/pull/1455 -- add ldysdu as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1471 -- \[feature\] support snmp v3 monitoring protocol by @TJxiaobao in https://github.com/dromara/hertzbeat/pull/1469 -- bugfix alarm trigger-times not work when alarm and recovered trigger cyclically by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1468 -- update switch monitoring metrics i18n by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1472 -- fixed: snmpv3 contextName bug by @TJxiaobao in https://github.com/dromara/hertzbeat/pull/1473 -- Fix npt of webhook notify by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1474 -- \[hertzbeat\] release hertzbeat version v1.4.4 by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1475 -- bugfix nginx collect http deadlock error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1476 -- alarm calculate ignore metrics collect code - TIMEOUT by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1478 - -## New Contributors - -- @ZY945 made their first contribution in https://github.com/dromara/hertzbeat/pull/1407 -- @prolevel1 made their first contribution in https://github.com/dromara/hertzbeat/pull/1412 -- @ZhangZixuan1994 made their first contribution in https://github.com/dromara/hertzbeat/pull/1451 -- @hurenjie1 made their first contribution in https://github.com/dromara/hertzbeat/pull/1458 -- @tslj1024 made their first contribution in https://github.com/dromara/hertzbeat/pull/1461 -- @ldysdu made their first contribution in https://github.com/dromara/hertzbeat/pull/1455 - -**Full Changelog**: https://github.com/dromara/hertzbeat/compare/v1.4.3...v1.4.4 - -> 欢迎来 **Star** 支持一波哦! - -**Github: https://github.com/dromara/hertzbeat** - -**Gitee: https://gitee.com/dromara/hertzbeat** - -**Discord: https://discord.gg/eaDqCXgy** +--- +title: HertzBeat 新版本1.4.4 发布,支持监控Nginx,Websocket等 +author: tom +tag: + - HertzBeat +date: 2024-01-23 +cover: /assets/img/news/HertzBeat-1.4.0-1.png +head: + - - meta + - name: 新闻 +--- + +大家早上好,又到了一月一版的时间点,这应该是春节前的最后一个版本,很高兴 HertzBeat 的最新版本 1.4.4 发布啦! + +### 1.4.4 版本更新简述如下: + +**新增了对 SNMP V3 版本协议的支持,支持对 Nginx, NebulaGraph, Pop3, Memcached, Hive, DNS, WebSockets, NTP, SMTP 等等一系列新的监控类型。修复了大量 BUG 和文档完善,更多特性欢迎使用探索!** + +- support snmp v3 monitoring protocol @TJxiaobao +- support monitoring NebulaGraph metrics @ZY945 +- support monitoring pop3 metrics @a-little-fool +- support monitoring memcached metrics @ZY945 +- support monitoring nginx metrics @a-little-fool +- support monitoring hive metrics @a-little-fool +- feature: support for dns monitoring by @Calvin979 +- monitoring the availability of websockets through handshake. by @ZY945 +- add ntp protocol and support ntp monitoring by @ZY945 +- add smtp protocol and support smtp monitoring by @ZY945 +- more feature, document and bugfix + +### 什么是 HertzBeat? + +HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,兼容 Prometheus,无需 Agent 的开源实时监控告警系统。 + +![](/assets/img/news/HertzBeat-1.4.4-1.png) + +### 特点 + +- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。 +- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 +- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? +- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。 +- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 +- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。 + +**Github: https://github.com/dromara/hertzbeat** + +**Gitee: https://gitee.com/dromara/hertzbeat** + +### 尝试部署 + +1. `docker` 环境仅需一条命令即可开始 + +`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat tancloud/hertzbeat` + +--- + +### 更新日志 + +> 欢迎探索更多,感谢社区小伙伴们的辛苦贡献, 笔芯 💗! + +- bugfix metrics tags value store jpa data-storage error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1403 +- add smtp protocol and support smtp monitoring by @ZY945 in https://github.com/dromara/hertzbeat/pull/1407 +- add ZY945 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1409 +- support new parse type 'log' in ssh collect protocol by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1410 +- add ntp protocol and support ntp monitoring by @ZY945 in https://github.com/dromara/hertzbeat/pull/1411 +- monitoring the availability of websockets through handshake. by @ZY945 in https://github.com/dromara/hertzbeat/pull/1413 +- \[Task-1386\] When adding tags in tag management, random colors are given by default. by @prolevel1 in https://github.com/dromara/hertzbeat/pull/1412 +- add prolevel1 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1415 +- #1397 feature: support for dns monitoring by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1416 +- Support monitoring hive metrics by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1417 +- support legend pageable in history data charts by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1414 +- update component tip and help tip doc by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1418 +- feature: support monitoring nginx metrics and add a help doc by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1420 +- update parser to parse from prometheus txt metrics data by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1421 +- support monitoring memcached metrics and add a help doc by @ZY945 in https://github.com/dromara/hertzbeat/pull/1423 +- support all ssh connect key exchange by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1424 +- \[doc\] add code of conduct by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1425 +- update label structure store in victoria metrics, make it prometheus like by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1426 +- feature: support monitoring pop3 metrics and add help doc by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1427 +- Update sidebars.json by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1428 +- Add zh-cn help doc by @a-little-fool in https://github.com/dromara/hertzbeat/pull/1429 +- update monitoring state un-manage to unmonitored, update pic by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1430 +- Add jpa to date type storage by @Clownsw in https://github.com/dromara/hertzbeat/pull/1431 +- bugfix ^o^ token error, protect metrics api auth by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1434 +- Add relevant documents for SMTP and NTP by @ZY945 in https://github.com/dromara/hertzbeat/pull/1437 +- bugfix threshold init error in mysql env by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1435 +- app-rabbitmq.yml support for international name aliases by @ZY945 in https://github.com/dromara/hertzbeat/pull/1439 +- fix(\*): error create lru-cache-timeout-cleaner thread by @Clownsw in https://github.com/dromara/hertzbeat/pull/1438 +- app-rabbitmq.yml Modifying Error Fields. by @ZY945 in https://github.com/dromara/hertzbeat/pull/1440 +- support monitoring NebulaGraph metrics and add help doc by @ZY945 in https://github.com/dromara/hertzbeat/pull/1441 +- Fix Nginx Collect validateParams function NPE by @Clownsw in https://github.com/dromara/hertzbeat/pull/1442 +- feature: add metrics i18n for app-springboot3.yml by @liyin in https://github.com/dromara/hertzbeat/pull/1445 +- feat: add metrics i18n for app-docker.yml by @liyin in https://github.com/dromara/hertzbeat/pull/1446 +- update docker-compose script and fix version by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1447 +- bugfix java.lang.IllegalArgumentException: Illegal character in query… by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1443 +- bugfix delete monitor error after monitor canceled by @ZhangZixuan1994 in https://github.com/dromara/hertzbeat/pull/1451 +- add ZhangZixuan1994 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1454 +- remove sleep, probably busy-waiting by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1456 +- \[doc\] add new committer ZY945 by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1453 +- Update app-zookeeper.yml by @hurenjie1 in https://github.com/dromara/hertzbeat/pull/1458 +- add hurenjie1 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1459 +- update dashboard ui, remove ssh custom SignatureFactories, update app name by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1460 +- \[Task\] Monitoring Template Yml Metrics I18n | 监控模版指标国际化任务认领 #1212 by @tslj1024 in https://github.com/dromara/hertzbeat/pull/1461 +- add tslj1024 as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1462 +- Add alarm trigger time for alarm restore by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1464 +- bugfix history range query not work when victoria-metrics store by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1463 +- bugfix springboot3 translation by @liyin in https://github.com/dromara/hertzbeat/pull/1467 +- bugfix telegram-notice can not input bot-token by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1465 +- feat: support hostname target by @ldysdu in https://github.com/dromara/hertzbeat/pull/1455 +- add ldysdu as a contributor for code by @allcontributors in https://github.com/dromara/hertzbeat/pull/1471 +- \[feature\] support snmp v3 monitoring protocol by @TJxiaobao in https://github.com/dromara/hertzbeat/pull/1469 +- bugfix alarm trigger-times not work when alarm and recovered trigger cyclically by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1468 +- update switch monitoring metrics i18n by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1472 +- fixed: snmpv3 contextName bug by @TJxiaobao in https://github.com/dromara/hertzbeat/pull/1473 +- Fix npt of webhook notify by @Calvin979 in https://github.com/dromara/hertzbeat/pull/1474 +- \[hertzbeat\] release hertzbeat version v1.4.4 by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1475 +- bugfix nginx collect http deadlock error by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1476 +- alarm calculate ignore metrics collect code - TIMEOUT by @tomsun28 in https://github.com/dromara/hertzbeat/pull/1478 + +## New Contributors + +- @ZY945 made their first contribution in https://github.com/dromara/hertzbeat/pull/1407 +- @prolevel1 made their first contribution in https://github.com/dromara/hertzbeat/pull/1412 +- @ZhangZixuan1994 made their first contribution in https://github.com/dromara/hertzbeat/pull/1451 +- @hurenjie1 made their first contribution in https://github.com/dromara/hertzbeat/pull/1458 +- @tslj1024 made their first contribution in https://github.com/dromara/hertzbeat/pull/1461 +- @ldysdu made their first contribution in https://github.com/dromara/hertzbeat/pull/1455 + +**Full Changelog**: https://github.com/dromara/hertzbeat/compare/v1.4.3...v1.4.4 + +> 欢迎来 **Star** 支持一波哦! + +**Github: https://github.com/dromara/hertzbeat** + +**Gitee: https://gitee.com/dromara/hertzbeat** + +**Discord: https://discord.gg/eaDqCXgy** diff --git a/src/zh/news/HertzBeat-1.5.0.md b/src/zh/news/HertzBeat-1.5.0.md index 2e23245da2..a6fd622730 100644 --- a/src/zh/news/HertzBeat-1.5.0.md +++ b/src/zh/news/HertzBeat-1.5.0.md @@ -1,70 +1,70 @@ ---- -title: 赫兹跳动 v1.5.0 发布,新增强大 Status Page 构建能力 -author: tom -tag: - - HertzBeat -date: 2024-02-08 -cover: /assets/img/news/HertzBeat-1.5.0-0.png -head: - - - meta - - name: 新闻 ---- - -## 新年好呀 - -没想到在除夕之前还能出一个版本,HertzBeat v1.5.0 发布啦,刚好一月一版。 - -这个版本我们主要是新提供了 Status Page 状态页面构建功能,用户可以通过赫兹跳动构建自己团队服务的对外状态页面,支持状态页状态与监控实例状态联动,支持发布维护异常事件。 - -**通过 Status Page 轻松向用户传达您产品服务的实时状态** - -**如果你们团队提供用户在线服务,那 Status Page 状态页的重要性我们就不要再一一强调了吧。参考 Github 状态页 https://www.githubstatus.com/ , 阿里云状态页 https://status.aliyun.com/。** - -**提升团队服务的透明度和专业度,增强用户信任,降低沟通成本。还不快来 10 分钟构建一个专属的 Status Page** - -![](/assets/img/news/HertzBeat-1.5.0-0.png) - -![](/assets/img/news/HertzBeat-1.5.0-1.png) - -![](/assets/img/news/HertzBeat-1.5.0-2.png) - -![](/assets/img/news/HertzBeat-1.5.0-3.png) - -![](/assets/img/news/HertzBeat-1.5.0-4.png) - -**后面在 status page 还会加消息订阅功能等,欢迎在 github issue 上你理想的 status page 功能需求** - -**https://github.com/dromara/hertzbeat/issues** - -## 更新日志 - -- support status page function @tomsun28 -- add new field description in hertzbeat tag detail by @GEM0816g -- bugfix: search monitors error when pageable @avvCode -- Alert define add export and import function @a-little-fool @yqxxgh -- refactor tdengine store code, auto close resource by @Carpe-Wang -- alert notice add monitor host by @yqxxgh -- snowflake id by @liyin -- interface info displays in Mb by @Calvin979 -- Alert yml config by @zqr10159 -- more feature, document and bugfix - -## 🎡 HertzBeat 介绍 - -HertzBeat 赫兹跳动 是一个易用友好的开源实时监控告警系统,无需 Agent,高性能集群,兼容 Prometheus,提供强大的自定义监控和状态页构建能力。 - -### 特点 - -- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。 -- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 -- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? -- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。 -- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 -- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。 -- 提供强大的状态页构建能力,轻松向用户传达您产品服务的实时状态。 - -Github: https://github.com/dromara/hertzbeat - -Gitee: https://gitee.com/dromara/hertzbeat - -今年就这样了,大家新年快乐!准备去干饭,来个 star 哇! +--- +title: 赫兹跳动 v1.5.0 发布,新增强大 Status Page 构建能力 +author: tom +tag: + - HertzBeat +date: 2024-02-08 +cover: /assets/img/news/HertzBeat-1.5.0-0.png +head: + - - meta + - name: 新闻 +--- + +## 新年好呀 + +没想到在除夕之前还能出一个版本,HertzBeat v1.5.0 发布啦,刚好一月一版。 + +这个版本我们主要是新提供了 Status Page 状态页面构建功能,用户可以通过赫兹跳动构建自己团队服务的对外状态页面,支持状态页状态与监控实例状态联动,支持发布维护异常事件。 + +**通过 Status Page 轻松向用户传达您产品服务的实时状态** + +**如果你们团队提供用户在线服务,那 Status Page 状态页的重要性我们就不要再一一强调了吧。参考 Github 状态页 https://www.githubstatus.com/ , 阿里云状态页 https://status.aliyun.com/。** + +**提升团队服务的透明度和专业度,增强用户信任,降低沟通成本。还不快来 10 分钟构建一个专属的 Status Page** + +![](/assets/img/news/HertzBeat-1.5.0-0.png) + +![](/assets/img/news/HertzBeat-1.5.0-1.png) + +![](/assets/img/news/HertzBeat-1.5.0-2.png) + +![](/assets/img/news/HertzBeat-1.5.0-3.png) + +![](/assets/img/news/HertzBeat-1.5.0-4.png) + +**后面在 status page 还会加消息订阅功能等,欢迎在 github issue 上你理想的 status page 功能需求** + +**https://github.com/dromara/hertzbeat/issues** + +## 更新日志 + +- support status page function @tomsun28 +- add new field description in hertzbeat tag detail by @GEM0816g +- bugfix: search monitors error when pageable @avvCode +- Alert define add export and import function @a-little-fool @yqxxgh +- refactor tdengine store code, auto close resource by @Carpe-Wang +- alert notice add monitor host by @yqxxgh +- snowflake id by @liyin +- interface info displays in Mb by @Calvin979 +- Alert yml config by @zqr10159 +- more feature, document and bugfix + +## 🎡 HertzBeat 介绍 + +HertzBeat 赫兹跳动 是一个易用友好的开源实时监控告警系统,无需 Agent,高性能集群,兼容 Prometheus,提供强大的自定义监控和状态页构建能力。 + +### 特点 + +- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。 +- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。 +- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗? +- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。 +- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。 +- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。 +- 提供强大的状态页构建能力,轻松向用户传达您产品服务的实时状态。 + +Github: https://github.com/dromara/hertzbeat + +Gitee: https://gitee.com/dromara/hertzbeat + +今年就这样了,大家新年快乐!准备去干饭,来个 star 哇! diff --git a/src/zh/news/LiteFlow-2.10.4.md b/src/zh/news/LiteFlow-2.10.4.md index 2de9358e95..842c9af2fe 100644 --- a/src/zh/news/LiteFlow-2.10.4.md +++ b/src/zh/news/LiteFlow-2.10.4.md @@ -1,115 +1,115 @@ ---- -title: LiteFlow v2.10.4发布,规则引擎之路进行到底 -author: 铂赛东 -tag: - - LiteFlow -date: 2023-06-13 -cover: /assets/img/news/LiteFlow-2.10.4.png -head: - - - meta - - name: 新闻 ---- - -## LiteFlow 介绍 - -**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** - -LiteFlow 也是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 - -同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3000 人。 - -如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: - -> 项目官网: -> -> https://liteflow.yomahub.com -> -> gitee 托管仓库: -> -> https://gitee.com/dromara/liteFlow -> -> github 托管仓库: -> -> https://github.com/dromara/liteflow - -## LiteFlow v2.10.4 - -本次新版本主要为增强版本。修复了一些使用中碰到的 Bug。部分代码作了一些增强。 - -本次更新总共为 4 个 issue。 - -感谢社区里那么多的使用者给我提出很多宝贵的意见。 - -本次更新列表: - -``` -增强 #I6XPN4 组件上添加 @RefreshScope 初始化就失败了 - -https://gitee.com/dromara/liteFlow/issues/I6XPN4 - -修复 #I7C6VR ELIF表达式中目前无法加入AND OR NOT表达式 - -https://gitee.com/dromara/liteFlow/issues/I7C6VR - -修复 #I7CCAB 2.10.0中CatchCondition会把ChainEndException也捕获 - -https://gitee.com/dromara/liteFlow/issues/I7CCAB - -修复 #I7COX4 SWITCH跳转到设置tag的chain上,存在问题 - -https://gitee.com/dromara/liteFlow/issues/I7COX4 -``` - -## 低头砍树,抬头看路 - -距离上次发版已经有一个多月了。 - -很明显,我放慢了发版的速度。以前一个礼拜一个版本的速度让我觉得过于快了,有时候抬起头去看看方向也是非常重要的。 - -在之前的迭代版本中,LiteFlow 大体上经历了 4 个阶段: - -1.从标签式流程全面转向 EL 表达式流程,这个阶段奠定了 LiteFlow 的核心语法结构。 - -2.语法构造阶段,重写了所有的语法底层解析。使得底层更加坚固。 - -3.插件化扩展阶段,设计了除了核心之外所有一切均可为插件的架构,并在插件架构上逐渐推出了 6 种脚本插件和 5 种存储插件。 - -4.语义扩展阶段,增加了非常多的新的语法,使得在语义方面更加完善。 - -而在以后,LiteFlow 在之前打下的基础上,会往以下几个方向进行深度拓展: - -1.推出更加高级的语法装配 API - -2.开放自定义语法的扩展 API - -3.丰富元数据,开放元数据的分析 API - -4.重做监控,推出基于 Prometheus 的 Grafana 大盘,能监控到每一个节点的各项指标数据 - -5.提供规则和脚本版本概念,加入回滚,覆盖,多版本本并行等特性。 - -6.基于语法装配 API,提供后台管理动态生成规则。并可对规则进行全方位的界面式管理 - -所以,LiteFlow 会一直是一个长期维护,并不断进化的国产开源规则引擎。正如我在官网中写的那样:我希望可以把 LiteFlow 这个项目带入中国顶尖开源软件的行列。 - -LiteFlow 这个项目并不是属于作者一个人的,它同样属于团队成员,属于那么多的贡献者,属于社区。 - -我希望有更多的人来参与这个项目,参与开源。 - -LiteFlow 在今年夏天也参加了中国科学院软件研究所发起的开源软件供应链点亮计划-《开源之夏 2023》,比较意外的是,有非常多的同学报名了 LiteFlow 的相关课题的研究。除了中选课题的同学之外,我也邀请了一些优秀的同学来参与其他课题的研究。 - -希望你们都能顺利通过课题,成为团队的成员,为 LiteFlow 项目添砖加瓦。 - -## 支持和赞助 LiteFlow - -开源一个项目并坚持 2 年并不容易,如果各位对 LiteFlow 这个项目有信心并且愿意支持的话,可以在官网首先点击\*`给LiteFlow发电`\*按钮。 - -但不管你是否选择赞助,我仍然会在社区里尽可能的解决你们的问题。 - -社区里的问题太多,如果没回答上,请多艾特我几遍。 - -## 如何加群 - -LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 - -关于加群的方式,请参考:https://liteflow.yomahub.com/pages/73c2c3/ +--- +title: LiteFlow v2.10.4发布,规则引擎之路进行到底 +author: 铂赛东 +tag: + - LiteFlow +date: 2023-06-13 +cover: /assets/img/news/LiteFlow-2.10.4.png +head: + - - meta + - name: 新闻 +--- + +## LiteFlow 介绍 + +**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** + +LiteFlow 也是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 + +同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3000 人。 + +如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: + +> 项目官网: +> +> https://liteflow.yomahub.com +> +> gitee 托管仓库: +> +> https://gitee.com/dromara/liteFlow +> +> github 托管仓库: +> +> https://github.com/dromara/liteflow + +## LiteFlow v2.10.4 + +本次新版本主要为增强版本。修复了一些使用中碰到的 Bug。部分代码作了一些增强。 + +本次更新总共为 4 个 issue。 + +感谢社区里那么多的使用者给我提出很多宝贵的意见。 + +本次更新列表: + +``` +增强 #I6XPN4 组件上添加 @RefreshScope 初始化就失败了 + +https://gitee.com/dromara/liteFlow/issues/I6XPN4 + +修复 #I7C6VR ELIF表达式中目前无法加入AND OR NOT表达式 + +https://gitee.com/dromara/liteFlow/issues/I7C6VR + +修复 #I7CCAB 2.10.0中CatchCondition会把ChainEndException也捕获 + +https://gitee.com/dromara/liteFlow/issues/I7CCAB + +修复 #I7COX4 SWITCH跳转到设置tag的chain上,存在问题 + +https://gitee.com/dromara/liteFlow/issues/I7COX4 +``` + +## 低头砍树,抬头看路 + +距离上次发版已经有一个多月了。 + +很明显,我放慢了发版的速度。以前一个礼拜一个版本的速度让我觉得过于快了,有时候抬起头去看看方向也是非常重要的。 + +在之前的迭代版本中,LiteFlow 大体上经历了 4 个阶段: + +1.从标签式流程全面转向 EL 表达式流程,这个阶段奠定了 LiteFlow 的核心语法结构。 + +2.语法构造阶段,重写了所有的语法底层解析。使得底层更加坚固。 + +3.插件化扩展阶段,设计了除了核心之外所有一切均可为插件的架构,并在插件架构上逐渐推出了 6 种脚本插件和 5 种存储插件。 + +4.语义扩展阶段,增加了非常多的新的语法,使得在语义方面更加完善。 + +而在以后,LiteFlow 在之前打下的基础上,会往以下几个方向进行深度拓展: + +1.推出更加高级的语法装配 API + +2.开放自定义语法的扩展 API + +3.丰富元数据,开放元数据的分析 API + +4.重做监控,推出基于 Prometheus 的 Grafana 大盘,能监控到每一个节点的各项指标数据 + +5.提供规则和脚本版本概念,加入回滚,覆盖,多版本本并行等特性。 + +6.基于语法装配 API,提供后台管理动态生成规则。并可对规则进行全方位的界面式管理 + +所以,LiteFlow 会一直是一个长期维护,并不断进化的国产开源规则引擎。正如我在官网中写的那样:我希望可以把 LiteFlow 这个项目带入中国顶尖开源软件的行列。 + +LiteFlow 这个项目并不是属于作者一个人的,它同样属于团队成员,属于那么多的贡献者,属于社区。 + +我希望有更多的人来参与这个项目,参与开源。 + +LiteFlow 在今年夏天也参加了中国科学院软件研究所发起的开源软件供应链点亮计划-《开源之夏 2023》,比较意外的是,有非常多的同学报名了 LiteFlow 的相关课题的研究。除了中选课题的同学之外,我也邀请了一些优秀的同学来参与其他课题的研究。 + +希望你们都能顺利通过课题,成为团队的成员,为 LiteFlow 项目添砖加瓦。 + +## 支持和赞助 LiteFlow + +开源一个项目并坚持 2 年并不容易,如果各位对 LiteFlow 这个项目有信心并且愿意支持的话,可以在官网首先点击\*`给LiteFlow发电`\*按钮。 + +但不管你是否选择赞助,我仍然会在社区里尽可能的解决你们的问题。 + +社区里的问题太多,如果没回答上,请多艾特我几遍。 + +## 如何加群 + +LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 + +关于加群的方式,请参考:https://liteflow.yomahub.com/pages/73c2c3/ diff --git a/src/zh/news/LiteFlow-2.10.5.md b/src/zh/news/LiteFlow-2.10.5.md index 5e6c261379..f409b5c101 100644 --- a/src/zh/news/LiteFlow-2.10.5.md +++ b/src/zh/news/LiteFlow-2.10.5.md @@ -1,103 +1,103 @@ ---- -title: LiteFlow v2.10.5发布,这个强大的规则引擎值得一用 -author: 铂赛东 -tag: - - LiteFlow -date: 2023-06-26 -cover: /assets/img/news/LiteFlow-2.10.5-1.png -head: - - - meta - - name: 新闻 ---- - -## LiteFlow 介绍 - -**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** - -LiteFlow 也是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 - -同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3000 人。 - -如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: - -> 项目官网: -> -> https://liteflow.yomahub.com -> -> gitee 托管仓库: -> -> https://gitee.com/dromara/liteFlow -> -> github 托管仓库: -> -> https://github.com/dromara/liteflow - -## LiteFlow v2.10.5 - -v2.10.5 版本主要就是对日志从底层进行架构改造。增加了支持传入自定义的请求 ID。 - -以及修复了一些循环表达式中的 Bug。 - -我们对日志进行了大幅度的改造,支持了自定义的 RequestId 传入,这个特性可以很方便的和你自己系统的 traceId 进行集成。 - -你可以调用如下方法来传入一个已有的 requestId: - -``` -LiteflowResponse response = flowExecutor.execute2RespWithRid("chain1", arg, "T001234", YourContext.class); -``` - -那么,这个链路中所有的框架日志都会带有\[T001234\]这个传入的 ID 前缀了。 - -另外新版本的 LiteFlow 还提供了一个日志包装类。只要你在组件中把 slf4j 的日志声明换成如下形式,那么你在组件中自己打出的日志也会带有请求 ID 前缀。 - -``` -private final LFLog logger = LFLoggerManager.getLogger(FlowExecutor.class); -``` - -其中`LFLog`这个类是继承自 slf4j 的`Logger`类的,所以它的使用方式和`Logger`是完全一致的。 - -你只需要把定义换一下就 ok 了。 - -如果在一个链路中相同请求的日志都拥有同一个请求 ID,那么对于定位问题来说,会很方便。推荐大家使用此特性。 - -## 循环场景中的一些 bug 的修复 - -看来大家对循环特性使用的还是比较多的。在使用的过程中,社区内也给出了很多反馈意见。 - -可能是之前对循环定义的测试用例有些少了,所以对于一些场景没覆盖到。这次修复了社区内提供的 2 个 Bug。 - -同时也补全了测试用例。 - -## 完整更新列表 - -``` -增强 #I7F567 增加对业务自定义requestId传入的支持 - -https://gitee.com/dromara/liteFlow/issues/I7F567 - -修复 #I7EKS8 在isAccess中进行setIsEnd(true)流程没有结束的问题 - -https://gitee.com/dromara/liteFlow/issues/I7EKS8 - -修复 #I7EKP3 同一个线程里分别调用2个Chain,currObj没有隔离的情况 - -https://gitee.com/dromara/liteFlow/issues/I7EKP3 - -修复 #I7E8RN 次数循环组件 下标获取问题 - -https://gitee.com/dromara/liteFlow/issues/I7E8RN -``` - -## 支持和赞助 LiteFlow - -开源一个项目并坚持 2 年并不容易,如果各位对 LiteFlow 这个项目有信心并且愿意支持的话,可以在官网首先点击\*`给LiteFlow发电`\*按钮。 - -但不管你是否选择赞助,我仍然会在社区里尽可能的解决你们的问题。 - -社区里的问题太多,如果没回答上,请多艾特我几遍。 - -## 如何加群 - -LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 - -关于加群的方式,请参考:https://liteflow.yomahub.com/pages/73c2c3/ +--- +title: LiteFlow v2.10.5发布,这个强大的规则引擎值得一用 +author: 铂赛东 +tag: + - LiteFlow +date: 2023-06-26 +cover: /assets/img/news/LiteFlow-2.10.5-1.png +head: + - - meta + - name: 新闻 +--- + +## LiteFlow 介绍 + +**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** + +LiteFlow 也是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 + +同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3000 人。 + +如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: + +> 项目官网: +> +> https://liteflow.yomahub.com +> +> gitee 托管仓库: +> +> https://gitee.com/dromara/liteFlow +> +> github 托管仓库: +> +> https://github.com/dromara/liteflow + +## LiteFlow v2.10.5 + +v2.10.5 版本主要就是对日志从底层进行架构改造。增加了支持传入自定义的请求 ID。 + +以及修复了一些循环表达式中的 Bug。 + +我们对日志进行了大幅度的改造,支持了自定义的 RequestId 传入,这个特性可以很方便的和你自己系统的 traceId 进行集成。 + +你可以调用如下方法来传入一个已有的 requestId: + +``` +LiteflowResponse response = flowExecutor.execute2RespWithRid("chain1", arg, "T001234", YourContext.class); +``` + +那么,这个链路中所有的框架日志都会带有\[T001234\]这个传入的 ID 前缀了。 + +另外新版本的 LiteFlow 还提供了一个日志包装类。只要你在组件中把 slf4j 的日志声明换成如下形式,那么你在组件中自己打出的日志也会带有请求 ID 前缀。 + +``` +private final LFLog logger = LFLoggerManager.getLogger(FlowExecutor.class); +``` + +其中`LFLog`这个类是继承自 slf4j 的`Logger`类的,所以它的使用方式和`Logger`是完全一致的。 + +你只需要把定义换一下就 ok 了。 + +如果在一个链路中相同请求的日志都拥有同一个请求 ID,那么对于定位问题来说,会很方便。推荐大家使用此特性。 + +## 循环场景中的一些 bug 的修复 + +看来大家对循环特性使用的还是比较多的。在使用的过程中,社区内也给出了很多反馈意见。 + +可能是之前对循环定义的测试用例有些少了,所以对于一些场景没覆盖到。这次修复了社区内提供的 2 个 Bug。 + +同时也补全了测试用例。 + +## 完整更新列表 + +``` +增强 #I7F567 增加对业务自定义requestId传入的支持 + +https://gitee.com/dromara/liteFlow/issues/I7F567 + +修复 #I7EKS8 在isAccess中进行setIsEnd(true)流程没有结束的问题 + +https://gitee.com/dromara/liteFlow/issues/I7EKS8 + +修复 #I7EKP3 同一个线程里分别调用2个Chain,currObj没有隔离的情况 + +https://gitee.com/dromara/liteFlow/issues/I7EKP3 + +修复 #I7E8RN 次数循环组件 下标获取问题 + +https://gitee.com/dromara/liteFlow/issues/I7E8RN +``` + +## 支持和赞助 LiteFlow + +开源一个项目并坚持 2 年并不容易,如果各位对 LiteFlow 这个项目有信心并且愿意支持的话,可以在官网首先点击\*`给LiteFlow发电`\*按钮。 + +但不管你是否选择赞助,我仍然会在社区里尽可能的解决你们的问题。 + +社区里的问题太多,如果没回答上,请多艾特我几遍。 + +## 如何加群 + +LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 + +关于加群的方式,请参考:https://liteflow.yomahub.com/pages/73c2c3/ diff --git a/src/zh/news/LiteFlow-2.10.6.md b/src/zh/news/LiteFlow-2.10.6.md index 61815b06e6..efc7dab796 100644 --- a/src/zh/news/LiteFlow-2.10.6.md +++ b/src/zh/news/LiteFlow-2.10.6.md @@ -1,231 +1,231 @@ ---- -title: LiteFlow v2.10.6发布!一款社区驱动型优秀的规则引擎框架 -author: 铂赛东 -tag: - - LiteFlow -date: 2023-07-24 -cover: /assets/img/news/LiteFlow-2.10.6-cover.jpg -head: - - - meta - - name: 新闻 ---- - -## LiteFlow 介绍 - -**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** - -LiteFlow 也是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 - -同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3000 人。 - -如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: - -> 项目官网: -> -> https://liteflow.yomahub.com -> -> gitee 托管仓库: -> -> https://gitee.com/dromara/liteFlow -> -> github 托管仓库: -> -> https://github.com/dromara/liteflow - -## 前言 - -v2.10.6 版本是一个进行诸多增强的版本。总计有 11 个 issue 的增强,3 个 issue 的修复。 - -## 全面支持 JDK17 - -其实 LiteFlow 在 2.10.0 的时候,已经支持了 JDK17。但是项目并未对 JDK17 进行完整的测试。导致很多小伙伴实际在 JDK17 环境中跑起来,出现了一些问题。 - -这是由于 LiteFlow 的 900 多个测试用例全部都是针对于 JDK8 来跑的。并未在 JDK17 下进行完整的测试。 - -而 2.10.6 这个版本,我们系统的针对 JDK17 进行了完整的测试。通过了全部的测试用例。所以大家可以放心的使用。 - -当然,LiteFlow 也是支持 Springboot3 的。 - -有的同学可能会问,JDK20 支持不支持?其实连作者自己也不知道是否支持 JDK20,因为没有进行系统的测试过。这版本太新了。 - -## 支持 SQL 插件使用自己定义的数据源 - -LiteFlow 支持将 EL 表达式,脚本存放在所有的关系型数据库中。但是之前的版本,LiteFlow 有自己的一套数据库连接配置,这就导致了往往使用者同一套数据库配置要配置 2 遍,还有一些使用者在实际的环境中,可能数据源是加密的,或者数据源是从外部获取的。这就没法使用 LiteFlow 提供的数据源配置了。 - -所以,这次我们支持了让 SQL 插件能够使用项目内的数据源来获取数据。更加优雅和方便。 - -甚至于我们还考虑到了多数据源的场景,让 LiteFlow 能够智能的挑选正确的数据源。 - -## LiteFlow 的测试用例全面转向 Junit5 - -这也许和使用者没啥太大关系。 - -LiteFlow 这个框架功能点非常多,使用人数也非常庞大。所以每一次发版,我都需要补很多测试用例。测试用例是 LiteFlow 整个框架质量的命脉。甚至于测试用例成为了和核心代码一样重要的存在。所以 LiteFlow 目前有将近 1000 个测试用例。 - -在 2.10.6 版本的源代码中,所有的测试用例从原先的 Junit4 全部转向了 Junit5。更加稳定。更加有保障。 - -## 对脚本和 Java 的联动进行加强 - -主要加强点在`@ScriptBean`这个注解,这个是脚本和 Java 联动的关键注解,在社区群里,有同学报出这个注解有时无法正常的工作,导致脚本拿不到 Java 的对象。对这个问题,我进行了深入研究。在 2.10.6 版本中,对这个注解进行了很多的优化,应该能彻底解决之前的问题。 - -## 对声明式组件增加了组件名称的设定 - -在 2.10.6 版本中,声明式组件支持了声明`nodeName` 这个属性了。 - -## 错误事件通知的改造 - -原先错误事件回调通知是这样定义的: - -``` -public void onError() throws Exception; -``` - -这样定义导致有些同学不知道如何取`Exception`,为了使 api 更加友好,这次这个回调方法改成了如下形式: - -``` -public void onError(Exception e) throws Exception; -``` - -## 全局拦截器的改造 - -原先全局拦截器的定义如下: - -``` -@Component -public class CmpAspect implements ICmpAroundAspect { - @Override - public void beforeProcess(String nodeId, Slot slot) { - YourContextBean context = slot.getContextBean(YourContextBean.class); - //before business - } - - @Override - public void afterProcess(String nodeId, Slot slot) { - YourContextBean context = slot.getContextBean(YourContextBean.class); - //after business - } -} -``` - -这种方式无法直观的拿到很多信息,为了 api 更加友好,这次我们改造了这个接口的实现参数: - -``` -@Component -public class CmpAspect implements ICmpAroundAspect { - @Override - public void beforeProcess(NodeComponent cmp) { - YourContextBean context = cmp.getContextBean(YourContextBean.class); - //before business - } - - @Override - public void afterProcess(NodeComponent cmp) { - YourContextBean context = cmp.getContextBean(YourContextBean.class); - //after business - } -} -``` - -## 布尔表达式中`OR`的短路判断 - -在社区中有同学反应,如果有以下 EL 语句: - -``` -IF(OR(a,b,c), x); -``` - -如果 a 返回 true,那其实 b 和 c 应该不用执行,而事实情况是 LiteFlow 把 a,b,c 都执行了。 - -所以这次我们也收到了贡献者的 PR,把这个问题给优化了。 - -## 同一个组件复用时 Step 的记录问题 - -`LiteflowResponse`的 step 可以用来回溯整个链路的实际执行情况。但是在相同组件复用的情况下,这个 step 的记录在之前版本中有些问题。此次我们也修复了这个问题。 - -## 日志方面的增强 - -在 2.10.6 版本中,系统默认会以 info 级别打出所有的节点的耗时信息。 - -如果觉得日志太多的话,可以通过设置`liteflow.print-execution-log=false`来进行关闭整个 LiteFlow 框架的系统日志。 - -## 完整更新列表 - -``` -增强 #I7KR2F 测试用例全面更新为junit5 - -https://gitee.com/dromara/liteFlow/issues/I7KR2F - -增强 #I7J59V java17下进行完整的测试用例测试 - -https://gitee.com/dromara/liteFlow/issues/I7J59V - -增强 #I7KZCZ 希望可以使用配置文件中已经配置的数据源 - -https://gitee.com/dromara/liteFlow/issues/I7KZCZ - -增强 #I7KY2N 非操作符的短路控制优化 - -https://gitee.com/dromara/liteFlow/issues/I7KY2N - -增强 #I7HPAN onError方法增加Exception入参 - -https://gitee.com/dromara/liteFlow/issues/I7HPAN - -增强 #I7KOPV 声明组件增加nodeName的定义 - -https://gitee.com/dromara/liteFlow/issues/I7KOPV - -增强 #I7KHE5 关于注解声明式使用场景LiteFlowMethodEnum增加getDisplayName - -https://gitee.com/dromara/liteFlow/issues/I7KHE5 - -增强 #I7K3T1 自带AOP拦截需要增强获取tag等信息 - -https://gitee.com/dromara/liteFlow/issues/I7K3T1 - -增强 #I7JZ4D 希望框架有与或非表达式的相关日志 - -https://gitee.com/dromara/liteFlow/issues/I7JZ4D - -增强 #I7J1VJ 希望针对节点执行耗时的打印日志支持控制 - -https://gitee.com/dromara/liteFlow/issues/I7J1VJ - -增强 #I7LGZR 忘记填写 chainName 的错误提示 - -https://gitee.com/dromara/liteFlow/issues/I7LGZR - -修复 #I7L5DX 2.10.5版本中ScriptBean注解注入bean失败 - -https://gitee.com/dromara/liteFlow/issues/I7L5DX - -修复 #I7HTR4 同一组件不同tag,取step时候存在问题 - -https://gitee.com/dromara/liteFlow/issues/I7HTR4 - -修复 #I7GMTS 本地文件监听异常会导致监听线程停止 - -https://gitee.com/dromara/liteFlow/issues/I7GMTS -``` - -## LiteFlow 发展路线 - -有可能 2.10.6 版本是 2.10.X 系列的最后一个版本了。 - -接下去就是 v2.11.0 了,v2.11.0 会带来超多的新增特性。v2.11.X 系列的主旋律将围绕着元数据增强进行展开。也为后续的管理平台系列打下一个基础。 - -回答个大家最想问的问题,问题省略,答案如下:2.11.0 版本没有 UI,UI 没有那么快。计划今年推出,需要开发的。作者也有工作,需要投入大量业余时间的。但是既然我上次已经调查过了,自然会用心去做这个规划。 - -## 支持和赞助 LiteFlow - -开源一个项目并坚持 2 年并不容易,如果各位对 LiteFlow 这个项目有信心并且愿意支持的话,可以在官网首先点 `给LiteFlow发电` 按钮。 - -但不管你是否选择赞助,我仍然会在社区里尽可能的解决你们的问题。 - -社区里的问题太多,如果没回答上,请多艾特我几遍。 - -## 如何加群 - -LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 - -关于加群的方式,请参考:https://liteflow.yomahub.com/pages/73c2c3/ +--- +title: LiteFlow v2.10.6发布!一款社区驱动型优秀的规则引擎框架 +author: 铂赛东 +tag: + - LiteFlow +date: 2023-07-24 +cover: /assets/img/news/LiteFlow-2.10.6-cover.jpg +head: + - - meta + - name: 新闻 +--- + +## LiteFlow 介绍 + +**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** + +LiteFlow 也是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 + +同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3000 人。 + +如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: + +> 项目官网: +> +> https://liteflow.yomahub.com +> +> gitee 托管仓库: +> +> https://gitee.com/dromara/liteFlow +> +> github 托管仓库: +> +> https://github.com/dromara/liteflow + +## 前言 + +v2.10.6 版本是一个进行诸多增强的版本。总计有 11 个 issue 的增强,3 个 issue 的修复。 + +## 全面支持 JDK17 + +其实 LiteFlow 在 2.10.0 的时候,已经支持了 JDK17。但是项目并未对 JDK17 进行完整的测试。导致很多小伙伴实际在 JDK17 环境中跑起来,出现了一些问题。 + +这是由于 LiteFlow 的 900 多个测试用例全部都是针对于 JDK8 来跑的。并未在 JDK17 下进行完整的测试。 + +而 2.10.6 这个版本,我们系统的针对 JDK17 进行了完整的测试。通过了全部的测试用例。所以大家可以放心的使用。 + +当然,LiteFlow 也是支持 Springboot3 的。 + +有的同学可能会问,JDK20 支持不支持?其实连作者自己也不知道是否支持 JDK20,因为没有进行系统的测试过。这版本太新了。 + +## 支持 SQL 插件使用自己定义的数据源 + +LiteFlow 支持将 EL 表达式,脚本存放在所有的关系型数据库中。但是之前的版本,LiteFlow 有自己的一套数据库连接配置,这就导致了往往使用者同一套数据库配置要配置 2 遍,还有一些使用者在实际的环境中,可能数据源是加密的,或者数据源是从外部获取的。这就没法使用 LiteFlow 提供的数据源配置了。 + +所以,这次我们支持了让 SQL 插件能够使用项目内的数据源来获取数据。更加优雅和方便。 + +甚至于我们还考虑到了多数据源的场景,让 LiteFlow 能够智能的挑选正确的数据源。 + +## LiteFlow 的测试用例全面转向 Junit5 + +这也许和使用者没啥太大关系。 + +LiteFlow 这个框架功能点非常多,使用人数也非常庞大。所以每一次发版,我都需要补很多测试用例。测试用例是 LiteFlow 整个框架质量的命脉。甚至于测试用例成为了和核心代码一样重要的存在。所以 LiteFlow 目前有将近 1000 个测试用例。 + +在 2.10.6 版本的源代码中,所有的测试用例从原先的 Junit4 全部转向了 Junit5。更加稳定。更加有保障。 + +## 对脚本和 Java 的联动进行加强 + +主要加强点在`@ScriptBean`这个注解,这个是脚本和 Java 联动的关键注解,在社区群里,有同学报出这个注解有时无法正常的工作,导致脚本拿不到 Java 的对象。对这个问题,我进行了深入研究。在 2.10.6 版本中,对这个注解进行了很多的优化,应该能彻底解决之前的问题。 + +## 对声明式组件增加了组件名称的设定 + +在 2.10.6 版本中,声明式组件支持了声明`nodeName` 这个属性了。 + +## 错误事件通知的改造 + +原先错误事件回调通知是这样定义的: + +``` +public void onError() throws Exception; +``` + +这样定义导致有些同学不知道如何取`Exception`,为了使 api 更加友好,这次这个回调方法改成了如下形式: + +``` +public void onError(Exception e) throws Exception; +``` + +## 全局拦截器的改造 + +原先全局拦截器的定义如下: + +``` +@Component +public class CmpAspect implements ICmpAroundAspect { + @Override + public void beforeProcess(String nodeId, Slot slot) { + YourContextBean context = slot.getContextBean(YourContextBean.class); + //before business + } + + @Override + public void afterProcess(String nodeId, Slot slot) { + YourContextBean context = slot.getContextBean(YourContextBean.class); + //after business + } +} +``` + +这种方式无法直观的拿到很多信息,为了 api 更加友好,这次我们改造了这个接口的实现参数: + +``` +@Component +public class CmpAspect implements ICmpAroundAspect { + @Override + public void beforeProcess(NodeComponent cmp) { + YourContextBean context = cmp.getContextBean(YourContextBean.class); + //before business + } + + @Override + public void afterProcess(NodeComponent cmp) { + YourContextBean context = cmp.getContextBean(YourContextBean.class); + //after business + } +} +``` + +## 布尔表达式中`OR`的短路判断 + +在社区中有同学反应,如果有以下 EL 语句: + +``` +IF(OR(a,b,c), x); +``` + +如果 a 返回 true,那其实 b 和 c 应该不用执行,而事实情况是 LiteFlow 把 a,b,c 都执行了。 + +所以这次我们也收到了贡献者的 PR,把这个问题给优化了。 + +## 同一个组件复用时 Step 的记录问题 + +`LiteflowResponse`的 step 可以用来回溯整个链路的实际执行情况。但是在相同组件复用的情况下,这个 step 的记录在之前版本中有些问题。此次我们也修复了这个问题。 + +## 日志方面的增强 + +在 2.10.6 版本中,系统默认会以 info 级别打出所有的节点的耗时信息。 + +如果觉得日志太多的话,可以通过设置`liteflow.print-execution-log=false`来进行关闭整个 LiteFlow 框架的系统日志。 + +## 完整更新列表 + +``` +增强 #I7KR2F 测试用例全面更新为junit5 + +https://gitee.com/dromara/liteFlow/issues/I7KR2F + +增强 #I7J59V java17下进行完整的测试用例测试 + +https://gitee.com/dromara/liteFlow/issues/I7J59V + +增强 #I7KZCZ 希望可以使用配置文件中已经配置的数据源 + +https://gitee.com/dromara/liteFlow/issues/I7KZCZ + +增强 #I7KY2N 非操作符的短路控制优化 + +https://gitee.com/dromara/liteFlow/issues/I7KY2N + +增强 #I7HPAN onError方法增加Exception入参 + +https://gitee.com/dromara/liteFlow/issues/I7HPAN + +增强 #I7KOPV 声明组件增加nodeName的定义 + +https://gitee.com/dromara/liteFlow/issues/I7KOPV + +增强 #I7KHE5 关于注解声明式使用场景LiteFlowMethodEnum增加getDisplayName + +https://gitee.com/dromara/liteFlow/issues/I7KHE5 + +增强 #I7K3T1 自带AOP拦截需要增强获取tag等信息 + +https://gitee.com/dromara/liteFlow/issues/I7K3T1 + +增强 #I7JZ4D 希望框架有与或非表达式的相关日志 + +https://gitee.com/dromara/liteFlow/issues/I7JZ4D + +增强 #I7J1VJ 希望针对节点执行耗时的打印日志支持控制 + +https://gitee.com/dromara/liteFlow/issues/I7J1VJ + +增强 #I7LGZR 忘记填写 chainName 的错误提示 + +https://gitee.com/dromara/liteFlow/issues/I7LGZR + +修复 #I7L5DX 2.10.5版本中ScriptBean注解注入bean失败 + +https://gitee.com/dromara/liteFlow/issues/I7L5DX + +修复 #I7HTR4 同一组件不同tag,取step时候存在问题 + +https://gitee.com/dromara/liteFlow/issues/I7HTR4 + +修复 #I7GMTS 本地文件监听异常会导致监听线程停止 + +https://gitee.com/dromara/liteFlow/issues/I7GMTS +``` + +## LiteFlow 发展路线 + +有可能 2.10.6 版本是 2.10.X 系列的最后一个版本了。 + +接下去就是 v2.11.0 了,v2.11.0 会带来超多的新增特性。v2.11.X 系列的主旋律将围绕着元数据增强进行展开。也为后续的管理平台系列打下一个基础。 + +回答个大家最想问的问题,问题省略,答案如下:2.11.0 版本没有 UI,UI 没有那么快。计划今年推出,需要开发的。作者也有工作,需要投入大量业余时间的。但是既然我上次已经调查过了,自然会用心去做这个规划。 + +## 支持和赞助 LiteFlow + +开源一个项目并坚持 2 年并不容易,如果各位对 LiteFlow 这个项目有信心并且愿意支持的话,可以在官网首先点 `给LiteFlow发电` 按钮。 + +但不管你是否选择赞助,我仍然会在社区里尽可能的解决你们的问题。 + +社区里的问题太多,如果没回答上,请多艾特我几遍。 + +## 如何加群 + +LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 + +关于加群的方式,请参考:https://liteflow.yomahub.com/pages/73c2c3/ diff --git a/src/zh/news/LiteFlow-2.11.0.md b/src/zh/news/LiteFlow-2.11.0.md index fd35d56ed3..6d9d995726 100644 --- a/src/zh/news/LiteFlow-2.11.0.md +++ b/src/zh/news/LiteFlow-2.11.0.md @@ -1,269 +1,269 @@ ---- -title: 新的面貌!LiteFlow发布大版本v2.11.0,五大新特性,国内新一代的规则引擎! -author: LiteFlow -tag: - - LiteFlow -date: 2023-09-01 -cover: /assets/img/news/LiteFlow-2.11.0-cover.png -head: - - - meta - - name: 新闻 ---- - -## LiteFlow 介绍 - -**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** - -LiteFlow 是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 - -同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3500 人。测试用例 1500 个,质量有保障。 - -如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: - -``` -项目官网: -https://liteflow.cc - -gitee 托管仓库: -https://gitee.com/dromara/liteFlow - -github 托管仓库: -https://github.com/dromara/liteflow -``` - -## v2.11.0 介绍 - -v2.11.0 是一个大版本,LiteFlow 提供了 5 个大的新特性,以及 3 个加强,2 个 bug 修复。 - -LiteFlow 同时更换了新的项目 Logo 和启动了新的域名。 - -LF 在这个版本改头换面,以全新的姿态继续向前冲~ - -## 新的项目 LOGO - -![](/assets/img/news/LiteFlow-2.11.0-1.png) - -感谢设计师`Evan Lou`的帮助,为 LiteFlow 带来了全新的 Logo。🤙 - -新 Logo 寓意: - -1.整体是一片树叶形状,树叶既代表了轻量,也象征着 LiteFlow 会一直像树叶那样进行光合作用,释放氧气。 - -2.叶子里的叶脉是一个 L 和 F 的交错变体,象征着 LiteFlow 的缩写。 - -3.叶脉简单且延展开,象征流程的分叉。 - -4.整个叶子被叶脉分隔成 4 部分,象征着编排,4 个不同的部分组成了一片完美的叶子。 - -## 启用全新域名 - -LiteFlow 在 v2.11.0 启用了全新的域名:https://liteflow.cc。简单且好记。 - -同时老的域名也是可以访问的,会自动重定向到新的域名上。 - -## 新的 Slogan - -LiteFlow 会从 2.11.0 这个版本开始,为每一个版本都设计一句 Slogan。 - -这个版本的 Slogan 为:`Keep on the light side`。 - -中文寓意为:`逐光而行`。 - -这句 Slogan 一语双关,其中`light`谐音`lite`,表示我们会一直坚持 LiteFlow 的开源迭代。 - -逐光而行,我希望我自己可以践行,人总要需要一道光来照亮生命,我们疲惫且努力的追逐那道光,远离黑暗。 - -## 特性 1:能够用 Java 原生语言来写脚本 - -丰富的脚本语言支持是 LiteFlow 的一大特色,脚本语言轻巧,能否被即时刷新的特点受到了很多开发者的喜爱。 - -LiteFlow 之前支持了挺多的脚本语言,分别是:Groovy,Javascript,Python,QLExpress,Lua,Aviator。 - -但是不同的语言有不同的语法结构,很多不熟悉的这些语言写法的同学可能还要花点时间去学习该如何写。 - -这次版本 LiteFlow 支持了原生 Java 脚本语言的写法,这意味着你可以无学习成本的直接在脚本里用 Java 来书写逻辑。 - -![](/assets/img/news/LiteFlow-2.11.0-2.png) - -关于 Java 脚本语言的详细文档请参照官网中`脚本组件->脚本语言种类->Java脚本引擎`。 - -## 特性 2:支持了 Redis 中存储规则和脚本 - -LiteFlow 支持丰富的存储插件,所有的关系型数据库以及各种注册中心。 - -这次我们新增了 Redis 插件,用 Redis 来存储规则以及脚本。Redis 在各大企业中用的比较多。为了保证使用的多样性。我们甚至开发了 2 种模式:`轮询拉取模式`和`订阅模式`,各有特点。 - -其中轮询拉取模式,我们做了非常多的优化,我们对每一个脚本和规则生成了指纹,只比对指纹,而非拉取整个数据,消耗极小。 - -订阅模式非常实时,但是需要你用`Redission`框架来操作 Redis,算有一定的使用限制。具体选用哪种模式,交由开发者来决定。 - -关于 redis 插件的详细文档请参照官网中`规则文件->Redis配置源`。 - -## 特性 3:全方位的超时控制 - -在以前的 LiteFlow 版本中,超时控制只能针对`WHEN`表达式,且是全局配置。 - -在新版本中,我们支持了全方位的超时控制体系,一个`maxWaitSeconds`关键字可对任意的组件、表达式、流程进行超时控制。 - -对任意表达式的控制: - -``` - - THEN(a,b).maxWaitSeconds(5); - - - FOR(2).DO(a).maxWaitSeconds(3); - -``` - -对组件的超时控制 - -``` - - WHEN( - a.maxWaitSeconds(2), - b.maxWaitSeconds(3) - ); - -``` - -对子流程的超时控制 - -``` - - THEN(b) - - - testChain.maxWaitSeconds(3); - -``` - -关于这块的详细文档请参考官网文档中的`高级特性->超时控制`。 - -## 特性 4:异步循环模式 - -经常群里的小伙伴会反映:LiteFLow 的循环体系只支持同步,如果能支持异步就好了。 - -这个版本我们支持了!对三种循环模式都予以了异步支持。 - -``` - - FOR(2).parallel(true).DO(THEN(a,b,c)); - - - WHILE(x).parallel(true).DO(THEN(a,b,c)); - - - ITERATOR(x).parallel(true).DO(THEN(a,b,c)); - -``` - -一个`parallel`关键字搞定。是不是很简单? - -关于异步循环的详细文档请参考官网文档中的`高级特性->异步循环模式`。 - -## 特性 5:组件回滚 - -这也就是所谓的逆操作。LiteFlow 从这个版本开始有逆向操作了!LiteFlow 会自动帮你记录执行的顺序,当某个组件抛出异常时,会自动按实际执行的顺序进行逆操作。 - -这一切的前提只需要你在组件中实现`rollback`方法。 - -在自动执行完回滚操作后,LiteFlow 的日志还会把回滚的步骤都打出来,让你一目了然。 - -``` -@LiteflowComponent("a") -public class ACmp extends NodeComponent { - - @Override - public void process() { - //do your biz - } - - @Override - public void rollback() throws Exception { - //do your biz - } -} -``` - -关于组件回滚的详细文档请参考官网文档中的`高级特性->组件回滚`。 - -## 测试用例增加到 1200 个左右 - -我相信,一个开源框架的上限是看这个框架所解决的问题和这个框架提供的特性和设计。而下限则是这个开源框架的测试用例的完备性。 - -我们不仅要开疆拓土,提供新特性和新的探索,也要守疆土,确保整体稳定性和质量。 - -我们这次将测试用例数量提高到近 1200 个,几乎覆盖到了所有的地方。 - -大家可以放心用。我们也有完备的社区和组织,社区目前拥有 3500 人左右。 - -## PPT 更新 - -官网提供的 PPT 同步更新到 v2.11.0 这个版本。 - -获取 PPT 的方式请参考:https://liteflow.cc/pages/8d6666/ - -## 完整更新列表 - -``` -更新列表 - -特性 #I7V6VB 建议增加Janino插件,支持java脚本的书写 - -https://gitee.com/dromara/liteFlow/issues/I7V6VB - -特性 #I7T53A 增加Redis存储规则的支持 - -https://gitee.com/dromara/liteFlow/issues/I7T53A - -特性 #I7I3LL 允许对EL中的每⼀个组件设置超时时间控制 - -https://gitee.com/dromara/liteFlow/issues/I7I3LL - -特性 #I7HJFX 循环表达式中支持异步模式 - -https://gitee.com/dromara/liteFlow/issues/I7HJFX - -特性 #I590JT 对于回滚流程的支持 - -https://gitee.com/dromara/liteFlow/issues/I590JT - -增强 #I7QC8V SQL插件格式化加入CDATA - -https://gitee.com/dromara/liteFlow/issues/I7QC8V - -增强 #I7Q8BX 部分代码有NPE风险 - -https://gitee.com/dromara/liteFlow/issues/I7Q8BX - -增强 #I7Q4BN 默认主线程池的名称拼写 - -https://gitee.com/dromara/liteFlow/issues/I7Q4BN - -修复 #I7WNDA SQLException: 使用druid+oracle会报出关闭的语句错误 - -https://gitee.com/dromara/liteFlow/issues/I7WNDA - -修复 #I7TYS3 当组件出现Exception的时候,afterProcess获取不到 - -https://gitee.com/dromara/liteFlow/issues/I7TYS3 -``` - -## 支持和赞助 LiteFlow - -开源一个项目并坚持 2 年并不容易,如果各位对 LiteFlow 这个项目有信心并且愿意支持的话,可以在官网首先点击 `给LiteFlow发电` 按钮。 - -但不管你是否选择赞助,我仍然会在社区里尽可能的解决你们的问题。 - -社区里的问题太多,如果没回答上,请多艾特我几遍。 - -## 如何加群 - -LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 - -关于加群的方式,请参考:https://liteflow.cc/pages/73c2c3/ - -你也可以直接以下这个码来加入新群: - - +--- +title: 新的面貌!LiteFlow发布大版本v2.11.0,五大新特性,国内新一代的规则引擎! +author: LiteFlow +tag: + - LiteFlow +date: 2023-09-01 +cover: /assets/img/news/LiteFlow-2.11.0-cover.png +head: + - - meta + - name: 新闻 +--- + +## LiteFlow 介绍 + +**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** + +LiteFlow 是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 + +同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3500 人。测试用例 1500 个,质量有保障。 + +如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: + +``` +项目官网: +https://liteflow.cc + +gitee 托管仓库: +https://gitee.com/dromara/liteFlow + +github 托管仓库: +https://github.com/dromara/liteflow +``` + +## v2.11.0 介绍 + +v2.11.0 是一个大版本,LiteFlow 提供了 5 个大的新特性,以及 3 个加强,2 个 bug 修复。 + +LiteFlow 同时更换了新的项目 Logo 和启动了新的域名。 + +LF 在这个版本改头换面,以全新的姿态继续向前冲~ + +## 新的项目 LOGO + +![](/assets/img/news/LiteFlow-2.11.0-1.png) + +感谢设计师`Evan Lou`的帮助,为 LiteFlow 带来了全新的 Logo。🤙 + +新 Logo 寓意: + +1.整体是一片树叶形状,树叶既代表了轻量,也象征着 LiteFlow 会一直像树叶那样进行光合作用,释放氧气。 + +2.叶子里的叶脉是一个 L 和 F 的交错变体,象征着 LiteFlow 的缩写。 + +3.叶脉简单且延展开,象征流程的分叉。 + +4.整个叶子被叶脉分隔成 4 部分,象征着编排,4 个不同的部分组成了一片完美的叶子。 + +## 启用全新域名 + +LiteFlow 在 v2.11.0 启用了全新的域名:https://liteflow.cc。简单且好记。 + +同时老的域名也是可以访问的,会自动重定向到新的域名上。 + +## 新的 Slogan + +LiteFlow 会从 2.11.0 这个版本开始,为每一个版本都设计一句 Slogan。 + +这个版本的 Slogan 为:`Keep on the light side`。 + +中文寓意为:`逐光而行`。 + +这句 Slogan 一语双关,其中`light`谐音`lite`,表示我们会一直坚持 LiteFlow 的开源迭代。 + +逐光而行,我希望我自己可以践行,人总要需要一道光来照亮生命,我们疲惫且努力的追逐那道光,远离黑暗。 + +## 特性 1:能够用 Java 原生语言来写脚本 + +丰富的脚本语言支持是 LiteFlow 的一大特色,脚本语言轻巧,能否被即时刷新的特点受到了很多开发者的喜爱。 + +LiteFlow 之前支持了挺多的脚本语言,分别是:Groovy,Javascript,Python,QLExpress,Lua,Aviator。 + +但是不同的语言有不同的语法结构,很多不熟悉的这些语言写法的同学可能还要花点时间去学习该如何写。 + +这次版本 LiteFlow 支持了原生 Java 脚本语言的写法,这意味着你可以无学习成本的直接在脚本里用 Java 来书写逻辑。 + +![](/assets/img/news/LiteFlow-2.11.0-2.png) + +关于 Java 脚本语言的详细文档请参照官网中`脚本组件->脚本语言种类->Java脚本引擎`。 + +## 特性 2:支持了 Redis 中存储规则和脚本 + +LiteFlow 支持丰富的存储插件,所有的关系型数据库以及各种注册中心。 + +这次我们新增了 Redis 插件,用 Redis 来存储规则以及脚本。Redis 在各大企业中用的比较多。为了保证使用的多样性。我们甚至开发了 2 种模式:`轮询拉取模式`和`订阅模式`,各有特点。 + +其中轮询拉取模式,我们做了非常多的优化,我们对每一个脚本和规则生成了指纹,只比对指纹,而非拉取整个数据,消耗极小。 + +订阅模式非常实时,但是需要你用`Redission`框架来操作 Redis,算有一定的使用限制。具体选用哪种模式,交由开发者来决定。 + +关于 redis 插件的详细文档请参照官网中`规则文件->Redis配置源`。 + +## 特性 3:全方位的超时控制 + +在以前的 LiteFlow 版本中,超时控制只能针对`WHEN`表达式,且是全局配置。 + +在新版本中,我们支持了全方位的超时控制体系,一个`maxWaitSeconds`关键字可对任意的组件、表达式、流程进行超时控制。 + +对任意表达式的控制: + +``` + + THEN(a,b).maxWaitSeconds(5); + + + FOR(2).DO(a).maxWaitSeconds(3); + +``` + +对组件的超时控制 + +``` + + WHEN( + a.maxWaitSeconds(2), + b.maxWaitSeconds(3) + ); + +``` + +对子流程的超时控制 + +``` + + THEN(b) + + + testChain.maxWaitSeconds(3); + +``` + +关于这块的详细文档请参考官网文档中的`高级特性->超时控制`。 + +## 特性 4:异步循环模式 + +经常群里的小伙伴会反映:LiteFLow 的循环体系只支持同步,如果能支持异步就好了。 + +这个版本我们支持了!对三种循环模式都予以了异步支持。 + +``` + + FOR(2).parallel(true).DO(THEN(a,b,c)); + + + WHILE(x).parallel(true).DO(THEN(a,b,c)); + + + ITERATOR(x).parallel(true).DO(THEN(a,b,c)); + +``` + +一个`parallel`关键字搞定。是不是很简单? + +关于异步循环的详细文档请参考官网文档中的`高级特性->异步循环模式`。 + +## 特性 5:组件回滚 + +这也就是所谓的逆操作。LiteFlow 从这个版本开始有逆向操作了!LiteFlow 会自动帮你记录执行的顺序,当某个组件抛出异常时,会自动按实际执行的顺序进行逆操作。 + +这一切的前提只需要你在组件中实现`rollback`方法。 + +在自动执行完回滚操作后,LiteFlow 的日志还会把回滚的步骤都打出来,让你一目了然。 + +``` +@LiteflowComponent("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + //do your biz + } + + @Override + public void rollback() throws Exception { + //do your biz + } +} +``` + +关于组件回滚的详细文档请参考官网文档中的`高级特性->组件回滚`。 + +## 测试用例增加到 1200 个左右 + +我相信,一个开源框架的上限是看这个框架所解决的问题和这个框架提供的特性和设计。而下限则是这个开源框架的测试用例的完备性。 + +我们不仅要开疆拓土,提供新特性和新的探索,也要守疆土,确保整体稳定性和质量。 + +我们这次将测试用例数量提高到近 1200 个,几乎覆盖到了所有的地方。 + +大家可以放心用。我们也有完备的社区和组织,社区目前拥有 3500 人左右。 + +## PPT 更新 + +官网提供的 PPT 同步更新到 v2.11.0 这个版本。 + +获取 PPT 的方式请参考:https://liteflow.cc/pages/8d6666/ + +## 完整更新列表 + +``` +更新列表 + +特性 #I7V6VB 建议增加Janino插件,支持java脚本的书写 + +https://gitee.com/dromara/liteFlow/issues/I7V6VB + +特性 #I7T53A 增加Redis存储规则的支持 + +https://gitee.com/dromara/liteFlow/issues/I7T53A + +特性 #I7I3LL 允许对EL中的每⼀个组件设置超时时间控制 + +https://gitee.com/dromara/liteFlow/issues/I7I3LL + +特性 #I7HJFX 循环表达式中支持异步模式 + +https://gitee.com/dromara/liteFlow/issues/I7HJFX + +特性 #I590JT 对于回滚流程的支持 + +https://gitee.com/dromara/liteFlow/issues/I590JT + +增强 #I7QC8V SQL插件格式化加入CDATA + +https://gitee.com/dromara/liteFlow/issues/I7QC8V + +增强 #I7Q8BX 部分代码有NPE风险 + +https://gitee.com/dromara/liteFlow/issues/I7Q8BX + +增强 #I7Q4BN 默认主线程池的名称拼写 + +https://gitee.com/dromara/liteFlow/issues/I7Q4BN + +修复 #I7WNDA SQLException: 使用druid+oracle会报出关闭的语句错误 + +https://gitee.com/dromara/liteFlow/issues/I7WNDA + +修复 #I7TYS3 当组件出现Exception的时候,afterProcess获取不到 + +https://gitee.com/dromara/liteFlow/issues/I7TYS3 +``` + +## 支持和赞助 LiteFlow + +开源一个项目并坚持 2 年并不容易,如果各位对 LiteFlow 这个项目有信心并且愿意支持的话,可以在官网首先点击 `给LiteFlow发电` 按钮。 + +但不管你是否选择赞助,我仍然会在社区里尽可能的解决你们的问题。 + +社区里的问题太多,如果没回答上,请多艾特我几遍。 + +## 如何加群 + +LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 + +关于加群的方式,请参考:https://liteflow.cc/pages/73c2c3/ + +你也可以直接以下这个码来加入新群: + + diff --git a/src/zh/news/LiteFlow-2.11.1.md b/src/zh/news/LiteFlow-2.11.1.md index 1e071bed74..9e8f2cec2e 100644 --- a/src/zh/news/LiteFlow-2.11.1.md +++ b/src/zh/news/LiteFlow-2.11.1.md @@ -1,269 +1,269 @@ ---- -title: LiteFlow v2.11.1发布!一个高速迭代,社区活跃的新一代规则引擎 -author: LiteFlow -tag: - - LiteFlow -date: 2023-10-16 -cover: /assets/img/news/LiteFlow-cover.png -head: - - - meta - - name: 新闻 ---- - -## LiteFlow 介绍 - -**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** - -LiteFlow 是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 - -同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3500 人。测试用例 1500 个,质量有保障。 - -如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: - -``` -项目官网: -https://liteflow.cc - -gitee 托管仓库: -https://gitee.com/dromara/liteFlow - -github 托管仓库: -https://github.com/dromara/liteflow -``` - -## 前言 - -上一个版本是 9 月 1 日发布的,经过了一个半月,LiteFlow 新的 v2.11.1 和大家见面了。 - -这个版本总共带来了 7 个大特性,3 个增强,2 个修复。总计 12 个 issue 的更新。 - -其实这已经比上一个大版本已经有过之而无不及了。 - -这一切归功于 LiteFlow 的团队小伙伴们,他们贡献了这个版本中的诸多的特性,感谢他们。尤其这个版本又有 2 位新成员加入团队,目前团队成员一共有 11 位,接下来的迭代将会变的更加迅速。 - -我们在确保新特性推出的同时,项目质量也同样出色。LiteFlow 截止到新版本,一共有 1515 个测试用例,每一个特性的提交,我们每一个成员都会全部通过测试用例,这也是我们每一次发版的底气,测试用例我们是认真的。并且我们的测试用例代码行覆盖率达到了非常高的 90%覆盖率。 - -![](/assets/img/news/LiteFlow-2.11.1-1.png) - -其实不吹不黑,在拥有 1500 个测试用例以上的开源项目中,90%的行覆盖率算得上是一个优秀的程度了。 - -## SQL 插件支持轮询自动更新模式 - -LiteFlow 支持把规则和脚本存入任意的关系型数据库,由于关系型数据库不像注册中心会进行推送变更,所以一直以来,规则和脚本在数据库中发生改变,是需要开发者自己手动的去刷新的。并且需要每个应用实例都进行刷新。这对开发者来说,是繁琐了点。 - -所以我们在 v2.11.1 中推出了数据库自动轮询更新模式。这一切只需要在你原来的配置中加入: - -``` -liteflow: -  rule-source-ext-data-map: -    ... -    #是否开启SQL数据轮询自动刷新机制 默认不开启    pollingEnabled: true -    ... -``` - -默认 LiteFlow 会每 1 分钟去进行 SHA 值的对比,由此来判断是否需要更新。 - -具体使用方式请参考`规则文件->SQL 数据库配置源`。 - -## WHEN 增加 must 语法 - -之前 `WHEN` 推出过 `any` 语法,意思是任意一个完成即继续,而忽略其他。但是在社区里有小伙伴碰到真实的场景,需要异步并行中对指定的节点先完成就忽略其他。 - -为此这次新版本中推出了全新的 `must` 语法,提供在并行编排中更多的多样性。 - -``` - -    THEN( -         a, -         WHEN(b, c, d).must(b, c), -         f -     ); - -``` - -也可以指定一个或多个表达式: - -``` - -    THEN( -         a, -         WHEN(b, THEN(c, d).id("t1"), e).must(b, "t1"), -         f -     ); - -``` - -具体使用方式请参考`EL规则的写法->并行编排->指定任意节点先执行完则忽略其他` - -## 推出 EL 表达式的动态组装 API - -之前 LiteFlow 推出过动态构建 chain 的 API,类似于这样: - -``` -LiteFlowChainELBuilder.createChain().setChainName("chain1").setEL( -   "THEN(a, b, WHEN(c, d))" -).build(); -``` - -但是 EL 表达式还是需要你自己以字符串的方式填入,这并不能算真正意义上动态。 - -这次我们推出了全新的 EL 表达式的动态组装 API,对于上面的 EL,你可以如下进行动态构建: - -``` -ThenELWrapper el = ELBus.then( -     "a","b",ELBus.when("c", "d") -);LiteFlowChainELBuilder.createChain().setChainName("chain1").setEL(el.toEL()).build(); -``` - -值得一说的是,用 java 语言构建 EL 表达式,几乎和 EL 的写法完全一致。如果你已经熟悉了 LiteFlow 的规则语法,应该是零成本上手的。 - -目前 API 支持了所有的 EL 语法。具体使用方式请参考`用代码构造规则->构造EL` - -## 链路继承 - -LiteFlow 每一个 chain 独立,之前的版本不存在继承关系。但在这个新版本中,我们推出了链路继承这个特性。 - -如同类的继承一样,链路可以继承,那么对于拥有复杂链路的业务系统,可以对链路进行抽象,得到一个非常优雅的表现方式。 - -我们定义的继承也非常容易看懂: - -``` - -    THEN(a, b, {0}, {1}); - - - -    {0}=IF(c, d, e); -    {1}=SWITCH(f).to(j,k); - -``` - -还可以多级继承: - -``` - - THEN(a, b, {0}, {1}); - - - - {0}=THEN(a,b,{3}); - {1}=SWITCH(f).to({4},k); - - - - {3}=THEN(a,b); - {4}=j; - -``` - -如果你有此方面的场景,不妨尝试下继承特性。 - -具体使用方式请参考高级特性->链路继承。 - -## 组件降级 - -LiteFlow 之前的`替补组件`全面升级成`组件降级`特性。 - -组件降级允许你定义各个类型组件的降级组件。新版本提供了`@FallbackCmp`注解用于定义。 - -在 EL 规则中,如果你用 `node` 关键字包裹组件,便开启了降级特性: - -``` - - THEN(node("a"), b, c); - -``` - -当 a 组件不存在时,便会走到用`@FallbackCmp` 注解定义的降级组件中去。 - -具体用法请参考`高级特定->组件降级` - -## 支持绝对路径的模糊匹配 - -LiteFlow 对项目内的规则文件的模糊匹配是早就支持的。但是之前的版本一直不支持绝对路径的模糊匹配。 - -这个版本,我们予以了支持。 - -你可以使用\*或者\*\*来模糊匹配多层级的任意名字的文件: - -``` -liteflow.rule-source=/data/lf/**/*Rule.xml -``` - -如果开启了文件监听功能,还能对模糊路径匹配到的每一个文件进行监听。当文件改变的时候,实现自动刷新功能。是不是很酷。 - -具体用法请参考` 规则文件->本地规则文件配置`和`高级功能->本地文件监听 ` - -## WHEN 线程池隔离 - -LiteFlow 在 v2.11.1 版本中新推出了一个配置,在执行 WHEN 中的并行组件时,每一个 when 的线程池隔离,在运行复杂的嵌套 WHEN 链路时,这个特性非常有用。可以有效提高运行速度并且避免死锁问题。 - -你只需要开启这个配置即可,默认是关闭的。 - -``` -liteflow.when-thread-pool-isolate=true -``` - -具体用法请参考 `EL规则的写法->并行编排->开启WHEN线程池隔离` - -## 完整的更新列表 - -``` -特性 #I7Y0Y1 SQL插件支持轮询模式 - -https://gitee.com/dromara/liteFlow/issues/I7Y0Y1 - -特性 #I7XAIB WHEN增加must语法 - -https://gitee.com/dromara/liteFlow/issues/I7XAIB - -特性 #I878WV EL表达式动态组装 - -https://gitee.com/dromara/liteFlow/issues/I878WV - -特性 #I7SVZF 支持chain的继承关系特性 - -https://gitee.com/dromara/liteFlow/issues/I7SVZF - -特性 #I7YYLF 组件降级特性 - -https://gitee.com/dromara/liteFlow/issues/I7YYLF - -特性 #I7ZJRH 支持绝对路径的模糊匹配 - -https://gitee.com/dromara/liteFlow/issues/I7ZJRH - -特性 #I883LB when线程池隔离支持 - -https://gitee.com/dromara/liteFlow/issues/I883LB - -增强 #I821F1 检测链路的循环依赖问题 - -https://gitee.com/dromara/liteFlow/issues/I821F1 - -增强 #I7G6BB 自定义异步线程池初始化存在并发问题 - -https://gitee.com/dromara/liteFlow/issues/I7G6BB - -增强 #I855YM sql 插件重构 - -https://gitee.com/dromara/liteFlow/issues/I855YM - -修复 #I82M4G 回滚组件无法获得tag的问题 - -https://gitee.com/dromara/liteFlow/issues/I82M4G - -修复 #I7ZMVM 普通组件isContinueOnError和isEnd为true时,process直接抛异常会导致isEnd失效 - -https://gitee.com/dromara/liteFlow/issues/I7ZMVM -``` - -## 如何加群 - -LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 - -关于加群的方式,请参考:https://liteflow.cc/pages/73c2c3/ - -你也可以直接以下这个码来加入新群: - -![](/assets/img/news/LiteFlow-2.11.1-2.png) +--- +title: LiteFlow v2.11.1发布!一个高速迭代,社区活跃的新一代规则引擎 +author: LiteFlow +tag: + - LiteFlow +date: 2023-10-16 +cover: /assets/img/news/LiteFlow-cover.png +head: + - - meta + - name: 新闻 +--- + +## LiteFlow 介绍 + +**LiteFlow 是一个开源编排式规则引擎,能够让你的系统逻辑任意编排,可选用脚本书写逻辑,支持多达 6 种脚本语言,支持丰富的第三方存储的支持,所有的逻辑和规则均可热变更。设计系统和重构系统的神器。** + +LiteFlow 是 Gitee 的高 star 项目,过去一年来保持了非常快的增长趋势。 + +同时 LiteFlow 也是国内优秀的社区驱动型开源项目,开源将近 3 年,目前已经被各大公司应用在核心系统上。特性以及支持度都非常好。社区人数超过 3500 人。测试用例 1500 个,质量有保障。 + +如果你是第一次知道这个项目,可以去官网或相关的主页进行了解: + +``` +项目官网: +https://liteflow.cc + +gitee 托管仓库: +https://gitee.com/dromara/liteFlow + +github 托管仓库: +https://github.com/dromara/liteflow +``` + +## 前言 + +上一个版本是 9 月 1 日发布的,经过了一个半月,LiteFlow 新的 v2.11.1 和大家见面了。 + +这个版本总共带来了 7 个大特性,3 个增强,2 个修复。总计 12 个 issue 的更新。 + +其实这已经比上一个大版本已经有过之而无不及了。 + +这一切归功于 LiteFlow 的团队小伙伴们,他们贡献了这个版本中的诸多的特性,感谢他们。尤其这个版本又有 2 位新成员加入团队,目前团队成员一共有 11 位,接下来的迭代将会变的更加迅速。 + +我们在确保新特性推出的同时,项目质量也同样出色。LiteFlow 截止到新版本,一共有 1515 个测试用例,每一个特性的提交,我们每一个成员都会全部通过测试用例,这也是我们每一次发版的底气,测试用例我们是认真的。并且我们的测试用例代码行覆盖率达到了非常高的 90%覆盖率。 + +![](/assets/img/news/LiteFlow-2.11.1-1.png) + +其实不吹不黑,在拥有 1500 个测试用例以上的开源项目中,90%的行覆盖率算得上是一个优秀的程度了。 + +## SQL 插件支持轮询自动更新模式 + +LiteFlow 支持把规则和脚本存入任意的关系型数据库,由于关系型数据库不像注册中心会进行推送变更,所以一直以来,规则和脚本在数据库中发生改变,是需要开发者自己手动的去刷新的。并且需要每个应用实例都进行刷新。这对开发者来说,是繁琐了点。 + +所以我们在 v2.11.1 中推出了数据库自动轮询更新模式。这一切只需要在你原来的配置中加入: + +``` +liteflow: +  rule-source-ext-data-map: +    ... +    #是否开启SQL数据轮询自动刷新机制 默认不开启    pollingEnabled: true +    ... +``` + +默认 LiteFlow 会每 1 分钟去进行 SHA 值的对比,由此来判断是否需要更新。 + +具体使用方式请参考`规则文件->SQL 数据库配置源`。 + +## WHEN 增加 must 语法 + +之前 `WHEN` 推出过 `any` 语法,意思是任意一个完成即继续,而忽略其他。但是在社区里有小伙伴碰到真实的场景,需要异步并行中对指定的节点先完成就忽略其他。 + +为此这次新版本中推出了全新的 `must` 语法,提供在并行编排中更多的多样性。 + +``` + +    THEN( +         a, +         WHEN(b, c, d).must(b, c), +         f +     ); + +``` + +也可以指定一个或多个表达式: + +``` + +    THEN( +         a, +         WHEN(b, THEN(c, d).id("t1"), e).must(b, "t1"), +         f +     ); + +``` + +具体使用方式请参考`EL规则的写法->并行编排->指定任意节点先执行完则忽略其他` + +## 推出 EL 表达式的动态组装 API + +之前 LiteFlow 推出过动态构建 chain 的 API,类似于这样: + +``` +LiteFlowChainELBuilder.createChain().setChainName("chain1").setEL( +   "THEN(a, b, WHEN(c, d))" +).build(); +``` + +但是 EL 表达式还是需要你自己以字符串的方式填入,这并不能算真正意义上动态。 + +这次我们推出了全新的 EL 表达式的动态组装 API,对于上面的 EL,你可以如下进行动态构建: + +``` +ThenELWrapper el = ELBus.then( +     "a","b",ELBus.when("c", "d") +);LiteFlowChainELBuilder.createChain().setChainName("chain1").setEL(el.toEL()).build(); +``` + +值得一说的是,用 java 语言构建 EL 表达式,几乎和 EL 的写法完全一致。如果你已经熟悉了 LiteFlow 的规则语法,应该是零成本上手的。 + +目前 API 支持了所有的 EL 语法。具体使用方式请参考`用代码构造规则->构造EL` + +## 链路继承 + +LiteFlow 每一个 chain 独立,之前的版本不存在继承关系。但在这个新版本中,我们推出了链路继承这个特性。 + +如同类的继承一样,链路可以继承,那么对于拥有复杂链路的业务系统,可以对链路进行抽象,得到一个非常优雅的表现方式。 + +我们定义的继承也非常容易看懂: + +``` + +    THEN(a, b, {0}, {1}); + + + +    {0}=IF(c, d, e); +    {1}=SWITCH(f).to(j,k); + +``` + +还可以多级继承: + +``` + + THEN(a, b, {0}, {1}); + + + + {0}=THEN(a,b,{3}); + {1}=SWITCH(f).to({4},k); + + + + {3}=THEN(a,b); + {4}=j; + +``` + +如果你有此方面的场景,不妨尝试下继承特性。 + +具体使用方式请参考高级特性->链路继承。 + +## 组件降级 + +LiteFlow 之前的`替补组件`全面升级成`组件降级`特性。 + +组件降级允许你定义各个类型组件的降级组件。新版本提供了`@FallbackCmp`注解用于定义。 + +在 EL 规则中,如果你用 `node` 关键字包裹组件,便开启了降级特性: + +``` + + THEN(node("a"), b, c); + +``` + +当 a 组件不存在时,便会走到用`@FallbackCmp` 注解定义的降级组件中去。 + +具体用法请参考`高级特定->组件降级` + +## 支持绝对路径的模糊匹配 + +LiteFlow 对项目内的规则文件的模糊匹配是早就支持的。但是之前的版本一直不支持绝对路径的模糊匹配。 + +这个版本,我们予以了支持。 + +你可以使用\*或者\*\*来模糊匹配多层级的任意名字的文件: + +``` +liteflow.rule-source=/data/lf/**/*Rule.xml +``` + +如果开启了文件监听功能,还能对模糊路径匹配到的每一个文件进行监听。当文件改变的时候,实现自动刷新功能。是不是很酷。 + +具体用法请参考` 规则文件->本地规则文件配置`和`高级功能->本地文件监听 ` + +## WHEN 线程池隔离 + +LiteFlow 在 v2.11.1 版本中新推出了一个配置,在执行 WHEN 中的并行组件时,每一个 when 的线程池隔离,在运行复杂的嵌套 WHEN 链路时,这个特性非常有用。可以有效提高运行速度并且避免死锁问题。 + +你只需要开启这个配置即可,默认是关闭的。 + +``` +liteflow.when-thread-pool-isolate=true +``` + +具体用法请参考 `EL规则的写法->并行编排->开启WHEN线程池隔离` + +## 完整的更新列表 + +``` +特性 #I7Y0Y1 SQL插件支持轮询模式 + +https://gitee.com/dromara/liteFlow/issues/I7Y0Y1 + +特性 #I7XAIB WHEN增加must语法 + +https://gitee.com/dromara/liteFlow/issues/I7XAIB + +特性 #I878WV EL表达式动态组装 + +https://gitee.com/dromara/liteFlow/issues/I878WV + +特性 #I7SVZF 支持chain的继承关系特性 + +https://gitee.com/dromara/liteFlow/issues/I7SVZF + +特性 #I7YYLF 组件降级特性 + +https://gitee.com/dromara/liteFlow/issues/I7YYLF + +特性 #I7ZJRH 支持绝对路径的模糊匹配 + +https://gitee.com/dromara/liteFlow/issues/I7ZJRH + +特性 #I883LB when线程池隔离支持 + +https://gitee.com/dromara/liteFlow/issues/I883LB + +增强 #I821F1 检测链路的循环依赖问题 + +https://gitee.com/dromara/liteFlow/issues/I821F1 + +增强 #I7G6BB 自定义异步线程池初始化存在并发问题 + +https://gitee.com/dromara/liteFlow/issues/I7G6BB + +增强 #I855YM sql 插件重构 + +https://gitee.com/dromara/liteFlow/issues/I855YM + +修复 #I82M4G 回滚组件无法获得tag的问题 + +https://gitee.com/dromara/liteFlow/issues/I82M4G + +修复 #I7ZMVM 普通组件isContinueOnError和isEnd为true时,process直接抛异常会导致isEnd失效 + +https://gitee.com/dromara/liteFlow/issues/I7ZMVM +``` + +## 如何加群 + +LiteFlow 的社区群已经有大约 3000 人以上了。你有任何问题,都可以在群里问。 + +关于加群的方式,请参考:https://liteflow.cc/pages/73c2c3/ + +你也可以直接以下这个码来加入新群: + +![](/assets/img/news/LiteFlow-2.11.1-2.png) diff --git a/src/zh/news/LiteFlow-2.11.4.md b/src/zh/news/LiteFlow-2.11.4.md index 36ecf515eb..9cde76acbd 100644 --- a/src/zh/news/LiteFlow-2.11.4.md +++ b/src/zh/news/LiteFlow-2.11.4.md @@ -1,180 +1,180 @@ ---- -title: LiteFlow v2.11.4正式版本发布!一个越来越强大的规则引擎 -author: 元人部落 -tag: - - LiteFlow -date: 2024-01-15 -cover: /assets/img/news/LiteFlow-2.11.4-0.png -head: - - - meta - - name: 新闻 ---- - -## 前言 - -2.11.4 经历了 BETA1,BETA2,BETA3 版本之后,正式版发布! - -2.11.4 总共有 18 个 issue 的更新,绝大多数为增强类 issue。在这个版本中,我们优化了底层,着重优化了性能。也提供了一些小特性的升级。 - -强烈建议还在用 2.11.3 版本的同学进行升级。 - -接下来挑几个更新的增强来详细说下。 - -## 重写了声明式的部分 - -此增强 issue 困扰我半个多月,一直以来,LiteFlow 声明式的底层代码比较混乱。不太好阅读,而且始终会在一些边缘场景莫名其妙的出问题。我一直想对其进行重写,这个版本,我阅读了大量了 spring bean 构造的底层代码,终于在底层实现了更加优化的改造。改造之后的声明式底层代码更加合理,结构层次也更加清晰,应该会摆脱一些边缘场景的问题。 - -改造之后的使用方式同之前一致,用户并不会感知到。 - -只是为了说明下,我们对底层代码是有追求的,并会为了追去极致的代码优雅而去不停努力。 - -希望你们去使用 LiteFlow,因为 LF 正在变得更强大。 - -## 解决了并行线程池的性能问题 - -源于社区的小伙伴在落地 LF 时发现,第一次请求会比后面的请求慢上一些。社区小伙伴也进行排查,发现 LF 是第一次请求时去初始化并行线程池,并报上了 issue。 - -这里确实是之前的版本没考虑周全,这一次,我们修复了这个问题。之后所有的线程池的初始化全都在启动时进行,包括用户自定义的线程池,也是如此。 - -## 并行线程池隔离 BUG 的修复 - -在 2.11.1 版本中曾经推出了并行线程池的隔离特性,旨在对一些并行分支非常多的进行线程池的隔离,以达到优化性能的目的,没想到这个特性一直存在 BUG。这次我们也深度进行了排查,并修复了这个问题。 - -## 增加映射关键字 SER 和 PAR - -曾经有一个 issue 我还记得,说是 LF 中的 THEN 和 WHEN 是表示串行和并行的意思,但是关键字意义却不精准。 - -虽然我承认 THEN 和 WHEN 表示串行和并行的确有些牵强,但是 LF 用户一直这么用,突然改变最主要的关键字也不太好。 - -所以这次新推出了`SER`和`PAR`关键字,等同于`THEN`和`WHEN`。当然继续使用`THEN`和`WHEN`也是可以的。 - -## CmpStep 中加入 startTime 和 endTime - -以前的 CmpStep 提供了耗时,但是没有组件的执行时间参数,这次提供了 startTime 和 EndTime。 - -## 加入了快速解析的参数 - -这个增强,也是源于社区内的一位落地的小伙伴,他和我说项目内有几万的流程。启动耗时非常久。 - -详细分析原因后,发现是 LF 的`CopyOnWriteHashMap`比较慢的缘故,当初用这个是为了平滑更新的因素。 - -所以,2.11.4 版本推出了`liteflow.fast-load`参数,当这个参数为 true 时。那么启动解析的速度回提升 4 到 5 倍。 - -我也进行了测试,1w 条流程(每个流程均有 14 个组件)耗时 3 秒,这个解析是线性的。所以在配置了这个参数后,解析速度还是可以接受的。 - -但是我并不推荐所有的人把快速 load 模式打开,因为快速 load 模式牺牲了热更新时的平滑性。换句话说就是,在正常模式下,如果当你热更新时正好有正在执行的流程,那么正在执行的流程是会用老的链路的,只有下一次才会用最新的链路。如果你打开了快速 load 模式,那么在热更新时,正好在执行过程中的流程有可能前半部分是老的流程,而后半部分有可能读到新的流程。这样就造成了不一致了。 - -当然这种场景是非常极端的场景,在普通的场景中,可能根本也不需要保持热更新时的平滑性。所以 fast-load 模式是有代价的。鱼和熊掌不可兼得。看项目要求了。 - -## Nacos 配置模式新增对阿里云 MSE 的鉴权方式 - -新版本的 LiteFlow 在 Nacos 层面可以支持阿里云的 MSE 鉴权方式了。 - -只需要这么配置就行了: - -``` -liteflow.rule-source-ext-data={\ - "serverAddr":"127.0.0.1:8848",\ - "dataId":"demo_rule",\ - "group":"DEFAULT_GROUP",\ - "namespace":"your namespace id",\ - "accessKey":"xxxxxxxxxx",\ - "secretKey":"xxxxxxxxxx"\ -} -``` - -## 2.11.4 全部更新列表 - -``` -增强 #I8UQR4 while组件本身加入loopIndex - -https://gitee.com/dromara/liteFlow/issues/I8UQR4 - -增强 #I8TZFQ 优化EL的check报错提示,代码优化 - -https://gitee.com/dromara/liteFlow/issues/I8TZFQ - -增强 #I8QJE1 增加映射关键字SER和PAR - -https://gitee.com/dromara/liteFlow/issues/I8QJE1 - -增强 #I8PWWO CmpStep中加入startTime和endTime - -https://gitee.com/dromara/liteFlow/issues/I8PWWO - -增强 #I8MXIB WHEN线程池可能存在第一次调用的时候初始化 - -https://gitee.com/dromara/liteFlow/issues/I8MXIB - -增强 #I8MXHX isAccess在WHEN中会被执行2次的问题 - -https://gitee.com/dromara/liteFlow/issues/I8MXHX - -增强 #I8J622 动态组件el语句data参数缺少单引号 - -https://gitee.com/dromara/liteFlow/issues/I8J622 - -增强 #I8IDE5 对声明式代理底层代码进行增强 - -https://gitee.com/dromara/liteFlow/issues/I8IDE5 - -增强 #I8H1LT 希望提供nacos对于阿里云MSE的鉴权方式 - -https://gitee.com/dromara/liteFlow/issues/I8H1LT - -增强 #I8FC4F 超大量规则场景下项目启动时间过长的问题 - -https://gitee.com/dromara/liteFlow/issues/I8FC4F - -增强 #I8FBBW chain及script新增enable字段后sql打印错误 - -https://gitee.com/dromara/liteFlow/issues/I8FBBW - -增强 #I8DW1J LiteFlowChainELBuilder.validate()依然存在bug - -https://gitee.com/dromara/liteFlow/issues/I8DW1J - -增强 #I8AE9G 超时控制增加maxWaitMilliseconds关键字,以增加更精细的时间控制 - -https://gitee.com/dromara/liteFlow/issues/I8AE9G - -修复 #I8TZTK EL语句构建的时候,如果出现了脚本节点。会出NPE的异常 - -https://gitee.com/dromara/liteFlow/issues/I8TZTK - -修复 #I8S75A 去除循环检测,低版本的jackson可能导致的内存溢出问题 - -https://gitee.com/dromara/liteFlow/issues/I8S75A - -修复 #I8RWGZ 开启WHEN线程池隔离线程池创建有问题 - -https://gitee.com/dromara/liteFlow/issues/I8RWGZ - -修复 #I8PEZ0 修复sql插件中script表中enable字段不起作用的bug - -https://gitee.com/dromara/liteFlow/issues/I8PEZ0 - -修复 #I8AF1O 修复redis的订阅模式mode解析的bug - -https://gitee.com/dromara/liteFlow/issues/I8AF1O -``` - -## 开设了 LF CLUB 付费社区 - -相信老用户都知道,我们在微信群努力帮大家答疑解惑,坚持了 2 年之多,目前微信群一共有 12 个群。 - -随着社区人越来越多,有些问题我有时也没有时间精力去一一回答。为了提高质量,为此我创建了`LF CLUB`的知识星球。 - -`LF CLUB`为付费知识星球,既然是付费星球,加入星球的用户拥有以下权益: - -- • **凡是在星球向我提问的问题,当天必定得到详细的回复和指导建议。** -- • **每个加入的用户每年提供 2 次远程问题排查,咨询类的服务。** -- • **每 1 到 2 天会分享 LF 目前的进度,以及下一个版本的重点。** -- • **每周发布一篇 LF 的解析和相关理念分析。** -- • **提供之后视频教程的折扣购买权益。** - -当然您也可以选择不加入星球,在微信群里向我提问,但是迫于精力问题,也不可能保证每位的问题都能得到细致的回复和指导。 - -加入星球,等于拥有了一位专家随时来帮助你更好的使用框架,希望加入`LF CLUB`的同学请扫以下二维码: - - +--- +title: LiteFlow v2.11.4正式版本发布!一个越来越强大的规则引擎 +author: 元人部落 +tag: + - LiteFlow +date: 2024-01-15 +cover: /assets/img/news/LiteFlow-2.11.4-0.png +head: + - - meta + - name: 新闻 +--- + +## 前言 + +2.11.4 经历了 BETA1,BETA2,BETA3 版本之后,正式版发布! + +2.11.4 总共有 18 个 issue 的更新,绝大多数为增强类 issue。在这个版本中,我们优化了底层,着重优化了性能。也提供了一些小特性的升级。 + +强烈建议还在用 2.11.3 版本的同学进行升级。 + +接下来挑几个更新的增强来详细说下。 + +## 重写了声明式的部分 + +此增强 issue 困扰我半个多月,一直以来,LiteFlow 声明式的底层代码比较混乱。不太好阅读,而且始终会在一些边缘场景莫名其妙的出问题。我一直想对其进行重写,这个版本,我阅读了大量了 spring bean 构造的底层代码,终于在底层实现了更加优化的改造。改造之后的声明式底层代码更加合理,结构层次也更加清晰,应该会摆脱一些边缘场景的问题。 + +改造之后的使用方式同之前一致,用户并不会感知到。 + +只是为了说明下,我们对底层代码是有追求的,并会为了追去极致的代码优雅而去不停努力。 + +希望你们去使用 LiteFlow,因为 LF 正在变得更强大。 + +## 解决了并行线程池的性能问题 + +源于社区的小伙伴在落地 LF 时发现,第一次请求会比后面的请求慢上一些。社区小伙伴也进行排查,发现 LF 是第一次请求时去初始化并行线程池,并报上了 issue。 + +这里确实是之前的版本没考虑周全,这一次,我们修复了这个问题。之后所有的线程池的初始化全都在启动时进行,包括用户自定义的线程池,也是如此。 + +## 并行线程池隔离 BUG 的修复 + +在 2.11.1 版本中曾经推出了并行线程池的隔离特性,旨在对一些并行分支非常多的进行线程池的隔离,以达到优化性能的目的,没想到这个特性一直存在 BUG。这次我们也深度进行了排查,并修复了这个问题。 + +## 增加映射关键字 SER 和 PAR + +曾经有一个 issue 我还记得,说是 LF 中的 THEN 和 WHEN 是表示串行和并行的意思,但是关键字意义却不精准。 + +虽然我承认 THEN 和 WHEN 表示串行和并行的确有些牵强,但是 LF 用户一直这么用,突然改变最主要的关键字也不太好。 + +所以这次新推出了`SER`和`PAR`关键字,等同于`THEN`和`WHEN`。当然继续使用`THEN`和`WHEN`也是可以的。 + +## CmpStep 中加入 startTime 和 endTime + +以前的 CmpStep 提供了耗时,但是没有组件的执行时间参数,这次提供了 startTime 和 EndTime。 + +## 加入了快速解析的参数 + +这个增强,也是源于社区内的一位落地的小伙伴,他和我说项目内有几万的流程。启动耗时非常久。 + +详细分析原因后,发现是 LF 的`CopyOnWriteHashMap`比较慢的缘故,当初用这个是为了平滑更新的因素。 + +所以,2.11.4 版本推出了`liteflow.fast-load`参数,当这个参数为 true 时。那么启动解析的速度回提升 4 到 5 倍。 + +我也进行了测试,1w 条流程(每个流程均有 14 个组件)耗时 3 秒,这个解析是线性的。所以在配置了这个参数后,解析速度还是可以接受的。 + +但是我并不推荐所有的人把快速 load 模式打开,因为快速 load 模式牺牲了热更新时的平滑性。换句话说就是,在正常模式下,如果当你热更新时正好有正在执行的流程,那么正在执行的流程是会用老的链路的,只有下一次才会用最新的链路。如果你打开了快速 load 模式,那么在热更新时,正好在执行过程中的流程有可能前半部分是老的流程,而后半部分有可能读到新的流程。这样就造成了不一致了。 + +当然这种场景是非常极端的场景,在普通的场景中,可能根本也不需要保持热更新时的平滑性。所以 fast-load 模式是有代价的。鱼和熊掌不可兼得。看项目要求了。 + +## Nacos 配置模式新增对阿里云 MSE 的鉴权方式 + +新版本的 LiteFlow 在 Nacos 层面可以支持阿里云的 MSE 鉴权方式了。 + +只需要这么配置就行了: + +``` +liteflow.rule-source-ext-data={\ + "serverAddr":"127.0.0.1:8848",\ + "dataId":"demo_rule",\ + "group":"DEFAULT_GROUP",\ + "namespace":"your namespace id",\ + "accessKey":"xxxxxxxxxx",\ + "secretKey":"xxxxxxxxxx"\ +} +``` + +## 2.11.4 全部更新列表 + +``` +增强 #I8UQR4 while组件本身加入loopIndex + +https://gitee.com/dromara/liteFlow/issues/I8UQR4 + +增强 #I8TZFQ 优化EL的check报错提示,代码优化 + +https://gitee.com/dromara/liteFlow/issues/I8TZFQ + +增强 #I8QJE1 增加映射关键字SER和PAR + +https://gitee.com/dromara/liteFlow/issues/I8QJE1 + +增强 #I8PWWO CmpStep中加入startTime和endTime + +https://gitee.com/dromara/liteFlow/issues/I8PWWO + +增强 #I8MXIB WHEN线程池可能存在第一次调用的时候初始化 + +https://gitee.com/dromara/liteFlow/issues/I8MXIB + +增强 #I8MXHX isAccess在WHEN中会被执行2次的问题 + +https://gitee.com/dromara/liteFlow/issues/I8MXHX + +增强 #I8J622 动态组件el语句data参数缺少单引号 + +https://gitee.com/dromara/liteFlow/issues/I8J622 + +增强 #I8IDE5 对声明式代理底层代码进行增强 + +https://gitee.com/dromara/liteFlow/issues/I8IDE5 + +增强 #I8H1LT 希望提供nacos对于阿里云MSE的鉴权方式 + +https://gitee.com/dromara/liteFlow/issues/I8H1LT + +增强 #I8FC4F 超大量规则场景下项目启动时间过长的问题 + +https://gitee.com/dromara/liteFlow/issues/I8FC4F + +增强 #I8FBBW chain及script新增enable字段后sql打印错误 + +https://gitee.com/dromara/liteFlow/issues/I8FBBW + +增强 #I8DW1J LiteFlowChainELBuilder.validate()依然存在bug + +https://gitee.com/dromara/liteFlow/issues/I8DW1J + +增强 #I8AE9G 超时控制增加maxWaitMilliseconds关键字,以增加更精细的时间控制 + +https://gitee.com/dromara/liteFlow/issues/I8AE9G + +修复 #I8TZTK EL语句构建的时候,如果出现了脚本节点。会出NPE的异常 + +https://gitee.com/dromara/liteFlow/issues/I8TZTK + +修复 #I8S75A 去除循环检测,低版本的jackson可能导致的内存溢出问题 + +https://gitee.com/dromara/liteFlow/issues/I8S75A + +修复 #I8RWGZ 开启WHEN线程池隔离线程池创建有问题 + +https://gitee.com/dromara/liteFlow/issues/I8RWGZ + +修复 #I8PEZ0 修复sql插件中script表中enable字段不起作用的bug + +https://gitee.com/dromara/liteFlow/issues/I8PEZ0 + +修复 #I8AF1O 修复redis的订阅模式mode解析的bug + +https://gitee.com/dromara/liteFlow/issues/I8AF1O +``` + +## 开设了 LF CLUB 付费社区 + +相信老用户都知道,我们在微信群努力帮大家答疑解惑,坚持了 2 年之多,目前微信群一共有 12 个群。 + +随着社区人越来越多,有些问题我有时也没有时间精力去一一回答。为了提高质量,为此我创建了`LF CLUB`的知识星球。 + +`LF CLUB`为付费知识星球,既然是付费星球,加入星球的用户拥有以下权益: + +- • **凡是在星球向我提问的问题,当天必定得到详细的回复和指导建议。** +- • **每个加入的用户每年提供 2 次远程问题排查,咨询类的服务。** +- • **每 1 到 2 天会分享 LF 目前的进度,以及下一个版本的重点。** +- • **每周发布一篇 LF 的解析和相关理念分析。** +- • **提供之后视频教程的折扣购买权益。** + +当然您也可以选择不加入星球,在微信群里向我提问,但是迫于精力问题,也不可能保证每位的问题都能得到细致的回复和指导。 + +加入星球,等于拥有了一位专家随时来帮助你更好的使用框架,希望加入`LF CLUB`的同学请扫以下二维码: + + diff --git a/src/zh/news/LiteFlow-v2.13.0.md b/src/zh/news/LiteFlow-v2.13.0.md new file mode 100644 index 0000000000..1dafa4094b --- /dev/null +++ b/src/zh/news/LiteFlow-v2.13.0.md @@ -0,0 +1,258 @@ +--- +title: LiteFlow v2.13.0 震撼发布!领航开源规则引擎新纪元 +author: 2025年02月25日 10:11 +date: 2025-02-25 +cover: /assets/img/news/LiteFlow-v2.13.0-0.png +head: + - - meta + - name: 新闻 +--- + +## +前言 + +这一次的新版本距离上一个版本时间有点久。 + +一方面确实这次更新的东西确实比较多,另一方面LF使用的公司和开发者越来越多,我需要很多的时间来收集社区反馈。有些特性我也需要细细思索去考虑合理性,底层代码也会经常审阅是否健壮。 + +再者我又比较佛系,做LF从来不给自己设时间期限,因为我觉得那样才是一种放松的状态,但凡一件事情有了时间紧迫感或KPI,我觉得都无法保证这个东西是发自内心的喜爱去驱动的。 + +在做LF的时候,对所有我自己写的代码,包括贡献者提交的代码,都需要审阅到变量的命名是否合理,测试用例是否缺少或不合理这个程度,并且文档是否合理,也在每次的发版审阅当中。所以LF从开源到现在,已经拥有了2200多个测试用例和相对比较规范的代码和文档。 + +LF也许不是发版最勤快的,但是LF一定是一个长期迭代且用心的开源项目,并且它已经坚持了4年多了。 + +废话说完,言归正传。 + +此次2.13.0总共更新了22个issue。其中包括7个特性,8个增强,7个修复。 + +此版本不是一个完全向下兼容的版本,但是能保证你在很短的时间内就可以升级完成。具体应该改哪里指南请参照官网的升级指南。 + +下面挑一些重要的更新点介绍下。 + +## +Javax-pro插件 + +原先有java插件,后来又出了javax插件。啥?现在又有javax-pro插件了?是不是以后还会有javax-pro-max呢?🤣 + +用过java脚本的同学应该知道,LF有基于Janino的`liteflow-script-java`插件和基于Liquor的`liteflow-script-javax`插件,前者只支持jdk6的语法,后者支持了所有jdk的语法。 + +而这次javax-pro插件应该就是最终形态了。它相比于`liteflow-script-javax`最大的不同就是:javax-pro支持的脚本完全同静态类的组件写法完全一致了。这意味着,在脚本中你可以覆盖超类中的方法了。 + +同时,我们退休了`liteflow-script-java`插件,这个插件在2.13.0中不会提供,所以在新版本中,java提供了2个插件: + +* liteflow-script-javax + +* liteflow-script-javax-pro + + +不过建议大家还是之后往javax-pro这个插件上迁移,因为`liteflow-script-javax`也只是为了兼容才留下来的。以后不排除会退休。 + +这里特别感谢Liquor的作者noear,在一次聊天中我和他吐槽了Janino,聊到了我在java脚本方面的困惑,他二话没说就为LF量身打造了java编译执行器,才使得LF在java脚本领域有了长足的进步。并且他把这个框架也开源了,大家可以关注下: + +https://gitee.com/noear/liquor + +## +bind关键字 + +LF以前支持对规则上的组件设置参数。涉及的关键字为`tag`和`data`。 + +这次新版本推出了bind关键字,这个关键字允许你在一个对象上去绑定一个key和value: + +``` +THEN(a.bind("k1","v1"), b) +``` + +它不仅可以作用于组件上,还可以作用于表达式上,子变量上,chain上。几乎是任何东西,都可以使用bind。 + + + +之所以有了`tag`和`data`这两个关键字,还要推出`bind`的原因是,`bind`还提供了动态绑定的特性,你可以使用占位符: + +``` +THEN(a, b).bind("k1", "${user.name}") +``` + +以上这个表达式LF会根据这个占位符表达式从你多个上下文中去匹配,然后把值同时赋给a和b。 + +bind关键字的用法丰富了组件参数的形态,bind关键字的用法有很多细节的地方,具体用法请参照官方文档。 + +## +线程池重塑 + +LF的以往版本在异步场景线程池这块,一直有点混乱。使用者并不知道在多样化的场景下,怎么去设置线程池。 + +所以这次我们重塑了线程池的设计。分为了3个维度: + +* 全局线程池 + +* Chain层面的线程池 + +* 表达式层面的线程池 + + +其中全局线程池的参数名和以前有所不同,且在Chain层面推出了新的线程池设置方式: + +``` + +    WHEN(a,b); + +``` + + + +可能这一次的线程池重塑,会不兼容以往的版本的参数配置。但是我一直觉得,比起兼容,合理和设计优雅才是更值得追求的。 + +况且,为了讲清楚LF中的线程池的结构,这次新文档专门开辟了一个大章节专门介绍线程池。并且在升级指南上专门会讲述如何在线程池这块升级到2.13.X。这一切不会耗费开发者太多时间。 + +具体相关用法和设置请参考文档。 + +## +脚本也可以延迟加载 + +LF在2.12.0的时候提供了一个全新的参数用来控制是否懒加载: + +``` +liteflow.parse-mode=PARSE_ONE_ON_FIRST_EXEC +``` + +但这个只对规则生效,而不对脚本生效。 + +经过社区里的反馈,有很多人也有脚本懒加载的需求。所以这次这个参数同时能控制规则和脚本了。 + +## +SQL插件支持多数据源指定 + +一直以来LF的SQL插件在多数据源的场景会出问题。总是无法自动检测出合适的数据源。 + +这一次,LF支持了市面上用的最多的两个数据源,分别为 + +* Baomidou社区的`dynamic-datasource`框架。 + +* Shardingsphere社区的`shardingsphere-jdbc`框架。 + + +并且大幅优化了取连接对象时的逻辑。 + +SQL插件作为官方首推的存储插件,其支持度也是最广的,新版本大家可以试试看。 + +## +Step步骤中支持加入自定义过程数据 + +现在你通过`this.setStepData(xx)`来进行添加自定义的步骤数据,来实现对上下文中一个数据变化过程的观测了。 + +这个功能其实很早就有人提PR了,但是我一直觉得没必要。 + +但是后来通过社区的反馈,我才意识到其实这个功能很有必要,所以社区的反馈有时也会纠正我的一些并不正确的想法。 + +## +Node实例Id的持久化 + +在新版本中,我们在Node对象中添加了一个叫instanceId的东西,这个id代表了唯一的编排id,只要你的规则不变,这个id恒久不变。哪怕是重启应用。 + +这个特性在本地规则文件以及SQL存储插件中都有实现。 + +但是你会发现这个特性并没有体现在文档中,但它却是2.13.0中非常重要的特性。因为这个特性更多的面对是二开开发者,纯粹是使用者可能并不需要知道这个特性。 + +可能这个特性会出现将要出现的**开发文档**中。 + +是的,LF以后不光有使用文档,还会考虑制作开发文档,以帮助想二开LF的开发者。 + +## +全部更新列表 + +``` +特性 #IBI6A3 新的基于原生态形式的javax-pro插件 + +https://gitee.com/dromara/liteFlow/issues/IBI6A3 + +特性 #IBL9CK 增加bind关键字,能够在任何地方bind key和value + +https://gitee.com/dromara/liteFlow/issues/IBL9CK + +特性 #IB2BKP 使PARSE_ONE_ON_FIRST_EXEC这个对脚本也生效 + +https://gitee.com/dromara/liteFlow/issues/IB2BKP + +特性 #IBLJ4A step中能够加入自定义的数据 + +https://gitee.com/dromara/liteFlow/issues/IBLJ4A + +特性 #IAPI07 按照chain维度来做线程池隔离 + +https://gitee.com/dromara/liteFlow/issues/IAPI07 + +特性 #IAUS2R sql插件希望支持指定数据源 + +https://gitee.com/dromara/liteFlow/issues/IAUS2R + +特性 #IB0SJ1 Node 实例id持久性 + +https://gitee.com/dromara/liteFlow/issues/IB0SJ1 + +增强 #IBNPZN LiteFlow的线程池体系重塑 + +https://gitee.com/dromara/liteFlow/issues/IBNPZN + +增强 #IBJO4X 建立统一元数据操作类,所有元数据的操作的入口 + +https://gitee.com/dromara/liteFlow/issues/IBJO4X + +增强 #IBCLUJ Step信息展示改造 && 线程信息的记录 + +https://gitee.com/dromara/liteFlow/issues/IBCLUJ + +增强 #IBA89R 期望java脚本引擎支持继承特性 + +https://gitee.com/dromara/liteFlow/issues/IBA89R + +增强 #IB1YLX 简化规则中的注释,化繁为简 + +https://gitee.com/dromara/liteFlow/issues/IB1YLX + +增强 #IAWJG1 ELBus中构建el表达式中对node包装类型格局的重新设计 + +https://gitee.com/dromara/liteFlow/issues/IAWJG1 + +增强 #IAVYME Node实例希望新增一个实例Id,在步骤里体现 + +https://gitee.com/dromara/liteFlow/issues/IAVYME + +增强 #IAUS4H sql插件数据库连接获取优化 + +https://gitee.com/dromara/liteFlow/issues/IAUS4H + +修复 #IBLWJG 布尔节点设置isAccess为false,报错 + +https://gitee.com/dromara/liteFlow/issues/IBLWJG + +修复 #IBL9HC 解决EL表达式中在node后面紧跟操作符会多次clone的问题 + +https://gitee.com/dromara/liteFlow/issues/IBL9HC + +修复 #IBGBLN java异常:Comparison method violates its general contract + +https://gitee.com/dromara/liteFlow/issues/IBGBLN + +修复 #IBAI9U FlowBus的getNodesByChainId返回空值 + +https://gitee.com/dromara/liteFlow/issues/IBAI9U + +修复 #IB7EQJ SWITCH后的target如果加了maxWaitMilliseconds导致选不到节点的问题 + +https://gitee.com/dromara/liteFlow/issues/IB7EQJ + +修复 #IB0X4Q 修复2.12.4的异步循环产生的bug + +https://gitee.com/dromara/liteFlow/issues/IB0X4Q + +修复 #IB0K9Y 允许FlowExecutor传入为null的上下文 + +https://gitee.com/dromara/liteFlow/issues/IB0K9Y +``` + +## +知识星球 + +现在加入星球\[LF CLUB\],以极低的价格享受一年的高质量答疑。并且有星球独占的LF系列解析文章。感兴趣的小伙伴请扫码直达。 + +![](/assets/img/news/LiteFlow-v2.13.0-0.png) \ No newline at end of file diff --git a/src/zh/news/MaxKey-3.5.17.md b/src/zh/news/MaxKey-3.5.17.md index a69a985209..c625f26e1a 100644 --- a/src/zh/news/MaxKey-3.5.17.md +++ b/src/zh/news/MaxKey-3.5.17.md @@ -1,107 +1,107 @@ ---- -title: MaxKey单点登录认证系统3.5.17发布,开源IAM产品 -author: MaxKey -tag: - - MaxKey -date: 2023-04-25 -cover: /assets/img/news/MaxKey-cover.png -head: - - - meta - - name: 新闻 ---- - -## 概述 - -MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 - -官方网站 http://www.maxkey.top/ - -官方 QQ:1054466084 - -邮箱 email: support@maxsso.net - -代码托管 Gitee | GitHub - -## 产品特性 - -1. 标准协议 - -| 序号 | 协议 | 支持 | -| ---- | :----------------------- | :--- | -| 1.1 | OAuth 2.x/OpenID Connect | 高 | -| 1.2 | SAML 2.0 | 高 | -| 1.3 | JWT | 高 | -| 1.4 | CAS | 高 | -| 1.5 | SCIM 2.0 | 高 | -| 1.6 | FormBased | 中 | -| 1.7 | TokenBased(Post/Cookie) | 中 | -| 1.8 | ExtendApi | 低 | -| 1.9 | EXT | 低 | - -2. 登录支持 - -| 序号 | 登录方式 | 支持 | -| ---- | :--------- | :--------------------------------------------------------- | -| 2.1 | 动态验证码 | 字母/数字/算术 | -| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | -| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | -| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | -| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | -| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | -| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | -| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | - -3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 - -4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 - -5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 - -6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 - -7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 - -8. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 - -9. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 - -## 界面 - -![](/assets/img/news/MaxKey-4.0.2.png) - -## 下载 - -当前版本百度网盘下载, 历史版本 - -| 版本 | 日期 | 网盘(提取码) | Docker | -| -------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | -| v 3.5.17 | 2023/04/25 | 下载( **mxk9** ) | 链接 | - -## 版本说明 - -MaxKey v 3.5.17 GA 2023/04/25 - -``` - *(MAXKEY-230501) 4.0.0开发计划 - *(MAXKEY-230502) 增加跳转引导配置inducer参数 - *(MAXKEY-230503) 本地开发调试模式baseUrl改为本地地址 @tomsun28 - *(MAXKEY-230504) 优化同步API REST和SCIM接口日志打印 - *(MAXKEY-230505) GatewayApplication改为MaxKeyGatewayApplication - *(MAXKEY-230506) Gateway启动bug,netty 4.1.65.Final - *(MAXKEY-230507) lombok支持,目前是可选 @sudarshan-doiphode - *(MAXKEY-230508) 新增CAS明文适配器CasPlainAdapter - *(MAXKEY-230509) synchronizer 同步的空值处理 - *(MAXKEY-230510) 增加官方微信支持,方便开发者咨询 - *(MAXKEY-230511) Spring Security OAuth SSO demo地址调整 - *(MAXKEY-230512) 用户自定义凭证类型前后端不统一修复 - *(MAXKEY-230513) arm架构docker @tzk007 - *(MAXKEY-230514) .gitignore优化 @tzk007 - *(MAXKEY-230515) 社区版前端文件统一放在maxkey_frontend下maxkey_html、maxkey_mgt_html,配置文件参见conf下 - *(MAXKEY-230516) 依赖项引用、更新和升级 - spring 5.3.27 - springBoot 2.7.11 - netty 4.1.65.Final - springSecurity 5.7.8 - tomcat 9.0.74 - micrometercore 1.9.10 -``` +--- +title: MaxKey单点登录认证系统3.5.17发布,开源IAM产品 +author: MaxKey +tag: + - MaxKey +date: 2023-04-25 +cover: /assets/img/news/MaxKey-cover.png +head: + - - meta + - name: 新闻 +--- + +## 概述 + +MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 + +官方网站 http://www.maxkey.top/ + +官方 QQ:1054466084 + +邮箱 email: support@maxsso.net + +代码托管 Gitee | GitHub + +## 产品特性 + +1. 标准协议 + +| 序号 | 协议 | 支持 | +| ---- | :----------------------- | :--- | +| 1.1 | OAuth 2.x/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2. 登录支持 + +| 序号 | 登录方式 | 支持 | +| ---- | :--------- | :--------------------------------------------------------- | +| 2.1 | 动态验证码 | 字母/数字/算术 | +| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | +| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | +| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | +| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | +| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | + +3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 + +4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 + +6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 + +8. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 + +9. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 + +## 界面 + +![](/assets/img/news/MaxKey-4.0.2.png) + +## 下载 + +当前版本百度网盘下载, 历史版本 + +| 版本 | 日期 | 网盘(提取码) | Docker | +| -------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| v 3.5.17 | 2023/04/25 | 下载( **mxk9** ) | 链接 | + +## 版本说明 + +MaxKey v 3.5.17 GA 2023/04/25 + +``` + *(MAXKEY-230501) 4.0.0开发计划 + *(MAXKEY-230502) 增加跳转引导配置inducer参数 + *(MAXKEY-230503) 本地开发调试模式baseUrl改为本地地址 @tomsun28 + *(MAXKEY-230504) 优化同步API REST和SCIM接口日志打印 + *(MAXKEY-230505) GatewayApplication改为MaxKeyGatewayApplication + *(MAXKEY-230506) Gateway启动bug,netty 4.1.65.Final + *(MAXKEY-230507) lombok支持,目前是可选 @sudarshan-doiphode + *(MAXKEY-230508) 新增CAS明文适配器CasPlainAdapter + *(MAXKEY-230509) synchronizer 同步的空值处理 + *(MAXKEY-230510) 增加官方微信支持,方便开发者咨询 + *(MAXKEY-230511) Spring Security OAuth SSO demo地址调整 + *(MAXKEY-230512) 用户自定义凭证类型前后端不统一修复 + *(MAXKEY-230513) arm架构docker @tzk007 + *(MAXKEY-230514) .gitignore优化 @tzk007 + *(MAXKEY-230515) 社区版前端文件统一放在maxkey_frontend下maxkey_html、maxkey_mgt_html,配置文件参见conf下 + *(MAXKEY-230516) 依赖项引用、更新和升级 + spring 5.3.27 + springBoot 2.7.11 + netty 4.1.65.Final + springSecurity 5.7.8 + tomcat 9.0.74 + micrometercore 1.9.10 +``` diff --git a/src/zh/news/MaxKey-3.5.18.md b/src/zh/news/MaxKey-3.5.18.md index 8c2404ab5d..56f4d121ca 100644 --- a/src/zh/news/MaxKey-3.5.18.md +++ b/src/zh/news/MaxKey-3.5.18.md @@ -1,99 +1,99 @@ ---- -title: MaxKey单点登录认证系统3.5.18发布,开源IAM产品 -author: MaxKey -tag: - - MaxKey -date: 2023-06-06 -cover: /assets/img/news/MaxKey-cover.png -head: - - - meta - - name: 新闻 ---- - -## 概述 - -MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 - -官方网站 http://www.maxkey.top/ - -官方 QQ:1054466084 - -邮箱 email: support@maxsso.net - -代码托管 Gitee | GitHub - -## 产品特性 - -1. 标准协议 - -| 序号 | 协议 | 支持 | -| ---- | :----------------------- | :--- | -| 1.1 | OAuth 2.x/OpenID Connect | 高 | -| 1.2 | SAML 2.0 | 高 | -| 1.3 | JWT | 高 | -| 1.4 | CAS | 高 | -| 1.5 | SCIM 2.0 | 高 | -| 1.6 | FormBased | 中 | -| 1.7 | TokenBased(Post/Cookie) | 中 | -| 1.8 | ExtendApi | 低 | -| 1.9 | EXT | 低 | - -2. 登录支持 - -| 序号 | 登录方式 | 支持 | -| ---- | :--------- | :--------------------------------------------------------- | -| 2.1 | 动态验证码 | 字母/数字/算术 | -| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | -| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | -| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | -| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | -| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | -| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | -| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | - -3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 - -4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 - -5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 - -6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 - -7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 - -8. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 - -9. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 - -## 界面 - -![](/assets/img/news/MaxKey-4.0.2.png) - -## 下载 - -当前版本百度网盘下载, 历史版本 - -| 版本 | 日期 | 网盘(提取码) | Docker | -| -------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | -| v 3.5.18 | 2023/06/05 | 下载( **mxk9** ) | 链接 | - -## 版本说明 - -MaxKey v 3.5.18 GA 2023/06/06 - -``` - *(MAXKEY-230601) 组织机构和用户同步的REST和SCIM从maxkey-web-mgt中分离到新建maxkey-web-openapi - *(MAXKEY-230602) 腾讯企业邮件接口优化 - *(MAXKEY-230603) 修改认证系统 是否需要验证码的配置 - *(MAXKEY-230604) 同步组织机构和用户API REST新增/.search分页查询功能 - *(MAXKEY-230605) #I76SV6 更正JdbcUsersService的username - *(MAXKEY-230606) 增加一次性动态口令验证功能 - *(MAXKEY-230607) 修改项目介绍信息 - *(MAXKEY-230608) 依赖项引用、更新和升级 - spring 5.3.27 - springBoot 2.7.12 - commonsfileupload 1.5 - poi 5.2.3 - log4j 2.20.0 - tomcat 9.0.75 -``` +--- +title: MaxKey单点登录认证系统3.5.18发布,开源IAM产品 +author: MaxKey +tag: + - MaxKey +date: 2023-06-06 +cover: /assets/img/news/MaxKey-cover.png +head: + - - meta + - name: 新闻 +--- + +## 概述 + +MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 + +官方网站 http://www.maxkey.top/ + +官方 QQ:1054466084 + +邮箱 email: support@maxsso.net + +代码托管 Gitee | GitHub + +## 产品特性 + +1. 标准协议 + +| 序号 | 协议 | 支持 | +| ---- | :----------------------- | :--- | +| 1.1 | OAuth 2.x/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2. 登录支持 + +| 序号 | 登录方式 | 支持 | +| ---- | :--------- | :--------------------------------------------------------- | +| 2.1 | 动态验证码 | 字母/数字/算术 | +| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | +| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | +| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | +| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | +| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | + +3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 + +4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 + +6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 + +8. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 + +9. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 + +## 界面 + +![](/assets/img/news/MaxKey-4.0.2.png) + +## 下载 + +当前版本百度网盘下载, 历史版本 + +| 版本 | 日期 | 网盘(提取码) | Docker | +| -------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| v 3.5.18 | 2023/06/05 | 下载( **mxk9** ) | 链接 | + +## 版本说明 + +MaxKey v 3.5.18 GA 2023/06/06 + +``` + *(MAXKEY-230601) 组织机构和用户同步的REST和SCIM从maxkey-web-mgt中分离到新建maxkey-web-openapi + *(MAXKEY-230602) 腾讯企业邮件接口优化 + *(MAXKEY-230603) 修改认证系统 是否需要验证码的配置 + *(MAXKEY-230604) 同步组织机构和用户API REST新增/.search分页查询功能 + *(MAXKEY-230605) #I76SV6 更正JdbcUsersService的username + *(MAXKEY-230606) 增加一次性动态口令验证功能 + *(MAXKEY-230607) 修改项目介绍信息 + *(MAXKEY-230608) 依赖项引用、更新和升级 + spring 5.3.27 + springBoot 2.7.12 + commonsfileupload 1.5 + poi 5.2.3 + log4j 2.20.0 + tomcat 9.0.75 +``` diff --git a/src/zh/news/MaxKey-3.5.19.md b/src/zh/news/MaxKey-3.5.19.md index 137c9d1e27..31a2e1d1b9 100644 --- a/src/zh/news/MaxKey-3.5.19.md +++ b/src/zh/news/MaxKey-3.5.19.md @@ -1,100 +1,100 @@ ---- -title: MaxKey单点登录认证系统3.5.19发布,开源IAM产品 -author: MaxKey -tag: - - MaxKey -date: 2023-08-16 -cover: /assets/img/news/MaxKey-cover.png -head: - - - meta - - name: 新闻 ---- - -## 概述 - -MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 - -官方网站 http://www.maxkey.top/ - -官方 QQ:1054466084 - -邮箱 email: support@maxsso.net - -代码托管 Gitee | GitHub - -## 产品特性 - -1. 标准协议 - -| 序号 | 协议 | 支持 | -| ---- | :----------------------- | :--- | -| 1.1 | OAuth 2.x/OpenID Connect | 高 | -| 1.2 | SAML 2.0 | 高 | -| 1.3 | JWT | 高 | -| 1.4 | CAS | 高 | -| 1.5 | SCIM 2.0 | 高 | -| 1.6 | FormBased | 中 | -| 1.7 | TokenBased(Post/Cookie) | 中 | -| 1.8 | ExtendApi | 低 | -| 1.9 | EXT | 低 | - -2. 登录支持 - -| 序号 | 登录方式 | 支持 | -| ---- | :--------- | :--------------------------------------------------------- | -| 2.1 | 动态验证码 | 字母/数字/算术 | -| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | -| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | -| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | -| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | -| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | -| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | -| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | - -3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 - -4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 - -5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 - -6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 - -7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 - -8. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 - -9. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 - -## 界面 - -![](/assets/img/news/MaxKey-4.0.2.png) - -## 下载 - -当前版本百度网盘下载, 历史版本 - -| 版本 | 日期 | 网盘(提取码) | Docker | -| -------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | -| v 3.5.19 | 2023/08/15 | 下载( **mxk9** ) | 链接 | - -## 版本说明 - -MaxKey v 3.5.19 GA 2023/08/15 - -``` - *(MAXKEY-230701) 增加配置:每次构建docker镜像时,可同时构建arm64镜像 * 增加配置:每次构建docker镜像时,可同时构建arm64镜像 - *(MAXKEY-230702) 优化CAS注销 CasLogout - *(MAXKEY-230703) 修复资源管理无法添加资源 #I7EPYO - *(MAXKEY-230704) 新增角色失败,数据入库问题。 #I7KMSJ - *(MAXKEY-230705) 修复/sign/authz/cas/v1/tickets这个接口只要用户名正确,密码随便填都能通过 #I7LC07 - *(MAXKEY-230706) 日志记录采用异步方式写入 - *(MAXKEY-230707) 增加Dockerfile,方便镜像构建 - *(MAXKEY-230708) gradle升级到 8.0.2 - *(MAXKEY-230709) readme增加安装介绍链接 - *(MAXKEY-230710) 代码优化和整合 - *(MAXKEY-230711) 依赖项引用、更新和升级 - spring 5.3.29 - springBoot 2.7.14 - springSecurity 5.7.10 - tomcat 9.0.78 -``` +--- +title: MaxKey单点登录认证系统3.5.19发布,开源IAM产品 +author: MaxKey +tag: + - MaxKey +date: 2023-08-16 +cover: /assets/img/news/MaxKey-cover.png +head: + - - meta + - name: 新闻 +--- + +## 概述 + +MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 + +官方网站 http://www.maxkey.top/ + +官方 QQ:1054466084 + +邮箱 email: support@maxsso.net + +代码托管 Gitee | GitHub + +## 产品特性 + +1. 标准协议 + +| 序号 | 协议 | 支持 | +| ---- | :----------------------- | :--- | +| 1.1 | OAuth 2.x/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2. 登录支持 + +| 序号 | 登录方式 | 支持 | +| ---- | :--------- | :--------------------------------------------------------- | +| 2.1 | 动态验证码 | 字母/数字/算术 | +| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | +| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | +| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | +| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | +| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | + +3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 + +4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 + +6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 + +8. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 + +9. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 + +## 界面 + +![](/assets/img/news/MaxKey-4.0.2.png) + +## 下载 + +当前版本百度网盘下载, 历史版本 + +| 版本 | 日期 | 网盘(提取码) | Docker | +| -------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| v 3.5.19 | 2023/08/15 | 下载( **mxk9** ) | 链接 | + +## 版本说明 + +MaxKey v 3.5.19 GA 2023/08/15 + +``` + *(MAXKEY-230701) 增加配置:每次构建docker镜像时,可同时构建arm64镜像 * 增加配置:每次构建docker镜像时,可同时构建arm64镜像 + *(MAXKEY-230702) 优化CAS注销 CasLogout + *(MAXKEY-230703) 修复资源管理无法添加资源 #I7EPYO + *(MAXKEY-230704) 新增角色失败,数据入库问题。 #I7KMSJ + *(MAXKEY-230705) 修复/sign/authz/cas/v1/tickets这个接口只要用户名正确,密码随便填都能通过 #I7LC07 + *(MAXKEY-230706) 日志记录采用异步方式写入 + *(MAXKEY-230707) 增加Dockerfile,方便镜像构建 + *(MAXKEY-230708) gradle升级到 8.0.2 + *(MAXKEY-230709) readme增加安装介绍链接 + *(MAXKEY-230710) 代码优化和整合 + *(MAXKEY-230711) 依赖项引用、更新和升级 + spring 5.3.29 + springBoot 2.7.14 + springSecurity 5.7.10 + tomcat 9.0.78 +``` diff --git a/src/zh/news/MaxKey-4.0.0.md b/src/zh/news/MaxKey-4.0.0.md index d4f843eae6..e841177afa 100644 --- a/src/zh/news/MaxKey-4.0.0.md +++ b/src/zh/news/MaxKey-4.0.0.md @@ -1,121 +1,121 @@ ---- -title: MaxKey单点登录认证系统4.0.0,JDK17重大升级 -author: MaxKey -tag: - - MaxKey -date: 2023-09-01 -cover: /assets/img/news/MaxKey-cover.png -head: - - - meta - - name: 新闻 ---- - -## 概述 - -MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 - -官方网站 http://www.maxkey.top/ - -官方 QQ:1054466084 - -邮箱 email: support@maxsso.net - -代码托管 Gitee | GitHub - -## 产品特性 - -1. 标准协议 - -| 序号 | 协议 | 支持 | -| ---- | :----------------------- | :--- | -| 1.1 | OAuth 2.x/OpenID Connect | 高 | -| 1.2 | SAML 2.0 | 高 | -| 1.3 | JWT | 高 | -| 1.4 | CAS | 高 | -| 1.5 | SCIM 2.0 | 高 | -| 1.6 | FormBased | 中 | -| 1.7 | TokenBased(Post/Cookie) | 中 | -| 1.8 | ExtendApi | 低 | -| 1.9 | EXT | 低 | - -2. 登录支持 - -| 序号 | 登录方式 | 支持 | -| ---- | :--------- | :--------------------------------------------------------- | -| 2.1 | 动态验证码 | 字母/数字/算术 | -| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | -| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | -| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | -| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | -| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | -| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | -| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | - -3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 - -4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 - -5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 - -6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 - -7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 - -8. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 - -9. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 - -## 界面 - -![](/assets/img/news/MaxKey-4.0.2.png) - -## 下载 - -当前版本百度网盘下载, 历史版本 - -| 版本 | 日期 | 网盘(提取码) | Docker | -| ------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | -| v 4.0.0 | 2023/09/01 | 下载( **mxk9** ) | 链接 | - -## 版本说明 - -MaxKey v 4.0.0 GA 2023/09/01 - -``` - *(MAXKEY-230801) JDK 17 支持 - *(MAXKEY-230802) SpringBoot 3.0+ 支持 - *(MAXKEY-230803) Spring 6.0+ 支持 - *(MAXKEY-230804) javax.修改为换为jakarta. - *(MAXKEY-230805) Jakarta EE 10 - *(MAXKEY-230806) org.maxkey改为org.dromara.maxkey - *(MAXKEY-230807) queryPageResults -> fetchPageResults - *(MAXKEY-230808) 调整获取accessToken次序 - *(MAXKEY-230809) 前端代码适配springboot3微调 - *(MAXKEY-230810) 日志打印优化 - *(MAXKEY-230811) 新增身份管理一级菜单包含组织、用户、用户组、用户组成员 - *(MAXKEY-230812) 角色调整为用户组,后续为应用级权限控制 - *(MAXKEY-230813) Mybatis JPA Etra优化,类似JPA实体定义,开发更加简洁 - *(MAXKEY-230814) 增加前端打包镜像的dockerfile暴露8527、8526端口 @iwc_2011911 - *(MAXKEY-230815) jar 打包baseName -> archiveBaseName - *(MAXKEY-230816) 依赖项引用、更新和升级 - JDK 17 - spring 6.0.11 - springBoot 3.1.2 - springSecurity 6.1.2 - springData 3.0.3 - springkafka 3.0.6 - springcloud 4.0.4 - springcloudalibaba 2022.0.0.0 - httpcomponentscore5 5.2.1 - tomcat 10.1.11 - mybatisjpaextra 3.2.1 - jibGradlePlugin 3.3.2 -``` - -## Roadmap - -### 增强身份管理 - -### 数据同步优化 - -### 权限管理优化 +--- +title: MaxKey单点登录认证系统4.0.0,JDK17重大升级 +author: MaxKey +tag: + - MaxKey +date: 2023-09-01 +cover: /assets/img/news/MaxKey-cover.png +head: + - - meta + - name: 新闻 +--- + +## 概述 + +MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 + +官方网站 http://www.maxkey.top/ + +官方 QQ:1054466084 + +邮箱 email: support@maxsso.net + +代码托管 Gitee | GitHub + +## 产品特性 + +1. 标准协议 + +| 序号 | 协议 | 支持 | +| ---- | :----------------------- | :--- | +| 1.1 | OAuth 2.x/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2. 登录支持 + +| 序号 | 登录方式 | 支持 | +| ---- | :--------- | :--------------------------------------------------------- | +| 2.1 | 动态验证码 | 字母/数字/算术 | +| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | +| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | +| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | +| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | +| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | + +3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 + +4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 + +6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 + +8. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 + +9. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 + +## 界面 + +![](/assets/img/news/MaxKey-4.0.2.png) + +## 下载 + +当前版本百度网盘下载, 历史版本 + +| 版本 | 日期 | 网盘(提取码) | Docker | +| ------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| v 4.0.0 | 2023/09/01 | 下载( **mxk9** ) | 链接 | + +## 版本说明 + +MaxKey v 4.0.0 GA 2023/09/01 + +``` + *(MAXKEY-230801) JDK 17 支持 + *(MAXKEY-230802) SpringBoot 3.0+ 支持 + *(MAXKEY-230803) Spring 6.0+ 支持 + *(MAXKEY-230804) javax.修改为换为jakarta. + *(MAXKEY-230805) Jakarta EE 10 + *(MAXKEY-230806) org.maxkey改为org.dromara.maxkey + *(MAXKEY-230807) queryPageResults -> fetchPageResults + *(MAXKEY-230808) 调整获取accessToken次序 + *(MAXKEY-230809) 前端代码适配springboot3微调 + *(MAXKEY-230810) 日志打印优化 + *(MAXKEY-230811) 新增身份管理一级菜单包含组织、用户、用户组、用户组成员 + *(MAXKEY-230812) 角色调整为用户组,后续为应用级权限控制 + *(MAXKEY-230813) Mybatis JPA Etra优化,类似JPA实体定义,开发更加简洁 + *(MAXKEY-230814) 增加前端打包镜像的dockerfile暴露8527、8526端口 @iwc_2011911 + *(MAXKEY-230815) jar 打包baseName -> archiveBaseName + *(MAXKEY-230816) 依赖项引用、更新和升级 + JDK 17 + spring 6.0.11 + springBoot 3.1.2 + springSecurity 6.1.2 + springData 3.0.3 + springkafka 3.0.6 + springcloud 4.0.4 + springcloudalibaba 2022.0.0.0 + httpcomponentscore5 5.2.1 + tomcat 10.1.11 + mybatisjpaextra 3.2.1 + jibGradlePlugin 3.3.2 +``` + +## Roadmap + +### 增强身份管理 + +### 数据同步优化 + +### 权限管理优化 diff --git a/src/zh/news/MaxKey-4.0.1.md b/src/zh/news/MaxKey-4.0.1.md index d968992de1..9ce6e5d6ec 100644 --- a/src/zh/news/MaxKey-4.0.1.md +++ b/src/zh/news/MaxKey-4.0.1.md @@ -1,106 +1,106 @@ ---- -title: MaxKey单点登录认证系统4.0.1,修复重要漏洞 -author: MaxKey -tag: - - MaxKey -date: 2023-09-19 -cover: /assets/img/news/MaxKey-cover.png -head: - - - meta - - name: 新闻 ---- - -## 概述 - -MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 - -官方网站 http://www.maxkey.top/ - -官方 QQ:1054466084 - -邮箱 email: support@maxsso.net - -代码托管 Gitee | GitHub - -## 产品特性 - -1. 标准协议 - -| 序号 | 协议 | 支持 | -| ---- | :----------------------- | :--- | -| 1.1 | OAuth 2.x/OpenID Connect | 高 | -| 1.2 | SAML 2.0 | 高 | -| 1.3 | JWT | 高 | -| 1.4 | CAS | 高 | -| 1.5 | SCIM 2.0 | 高 | -| 1.6 | FormBased | 中 | -| 1.7 | TokenBased(Post/Cookie) | 中 | -| 1.8 | ExtendApi | 低 | -| 1.9 | EXT | 低 | - -2. 登录支持 - -| 序号 | 登录方式 | 支持 | -| ---- | :--------- | :--------------------------------------------------------- | -| 2.1 | 动态验证码 | 字母/数字/算术 | -| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | -| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | -| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | -| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | -| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | -| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | -| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | - -3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 - -4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 - -5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 - -6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 - -7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 - -8. 配置化的密码策略、访问策略;支持 Ip2region 或 GeoLite2 地理库精准 IP 定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 - -9. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 - -10. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 - -## 界面 - -![](/assets/img/news/MaxKey-4.0.2.png) - -## 下载 - -当前版本百度网盘下载, 历史版本 - -| 版本 | 日期 | 网盘(提取码) | Docker | -| ------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | -| v 4.0.1 | 2023/09/19 | 下载( **mxk9** ) | 链接 | - -## 版本说明 - -MaxKey v 4.0.1 GA 2023/09/019 - -``` - *(MAXKEY-230901) !24 fix:shell启动脚本错误 * fix:启动脚本的JAVA_MAINCLASS有误 @tzk007 - *(MAXKEY-230902) 组权限问题修复 - *(MAXKEY-230903) !25修复错误的表别名使用 - *(MAXKEY-230904) GroupMemberMapper.xml 下memberNotIn sql 错误 @OomiyaShinobu - *(MAXKEY-230905) 增加 scripts,用于处理安装依赖与镜像问题 @徐晓伟 - *(MAXKEY-230906) 修改启动getProperty("server.port") - *(MAXKEY-230907) 修复用户组bug - *(MAXKEY-230908) 应用访问空值问题 - *(MAXKEY-230909) 用户组-资源绑定关系 - *(MAXKEY-230910) 删除历史sql文件 - *(MAXKEY-230911) 依赖项引用、更新和升级 -``` - -## Roadmap - -### 增强身份管理 - -### 数据同步优化 - -### 权限管理优化 +--- +title: MaxKey单点登录认证系统4.0.1,修复重要漏洞 +author: MaxKey +tag: + - MaxKey +date: 2023-09-19 +cover: /assets/img/news/MaxKey-cover.png +head: + - - meta + - name: 新闻 +--- + +## 概述 + +MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 + +官方网站 http://www.maxkey.top/ + +官方 QQ:1054466084 + +邮箱 email: support@maxsso.net + +代码托管 Gitee | GitHub + +## 产品特性 + +1. 标准协议 + +| 序号 | 协议 | 支持 | +| ---- | :----------------------- | :--- | +| 1.1 | OAuth 2.x/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2. 登录支持 + +| 序号 | 登录方式 | 支持 | +| ---- | :--------- | :--------------------------------------------------------- | +| 2.1 | 动态验证码 | 字母/数字/算术 | +| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | +| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | +| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | +| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | +| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | + +3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 + +4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 + +6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 + +8. 配置化的密码策略、访问策略;支持 Ip2region 或 GeoLite2 地理库精准 IP 定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 + +10. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 + +## 界面 + +![](/assets/img/news/MaxKey-4.0.2.png) + +## 下载 + +当前版本百度网盘下载, 历史版本 + +| 版本 | 日期 | 网盘(提取码) | Docker | +| ------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| v 4.0.1 | 2023/09/19 | 下载( **mxk9** ) | 链接 | + +## 版本说明 + +MaxKey v 4.0.1 GA 2023/09/019 + +``` + *(MAXKEY-230901) !24 fix:shell启动脚本错误 * fix:启动脚本的JAVA_MAINCLASS有误 @tzk007 + *(MAXKEY-230902) 组权限问题修复 + *(MAXKEY-230903) !25修复错误的表别名使用 + *(MAXKEY-230904) GroupMemberMapper.xml 下memberNotIn sql 错误 @OomiyaShinobu + *(MAXKEY-230905) 增加 scripts,用于处理安装依赖与镜像问题 @徐晓伟 + *(MAXKEY-230906) 修改启动getProperty("server.port") + *(MAXKEY-230907) 修复用户组bug + *(MAXKEY-230908) 应用访问空值问题 + *(MAXKEY-230909) 用户组-资源绑定关系 + *(MAXKEY-230910) 删除历史sql文件 + *(MAXKEY-230911) 依赖项引用、更新和升级 +``` + +## Roadmap + +### 增强身份管理 + +### 数据同步优化 + +### 权限管理优化 diff --git a/src/zh/news/MaxKey-4.0.2.md b/src/zh/news/MaxKey-4.0.2.md index f11f7b4d1d..c203886fe2 100644 --- a/src/zh/news/MaxKey-4.0.2.md +++ b/src/zh/news/MaxKey-4.0.2.md @@ -1,109 +1,109 @@ ---- -title: MaxKey单点登录认证系统4.0.2,精准IP定位 -author: MaxKey -tag: - - MaxKey -date: 2023-10-12 -cover: /assets/img/news/MaxKey-cover.png -head: - - - meta - - name: 新闻 ---- - -## 概述 - -MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 - -官方网站 http://www.maxkey.top/ - -官方 QQ:1054466084 - -邮箱 email: support@maxsso.net - -代码托管 Gitee | GitHub - -## 产品特性 - -1. 标准协议 - -| 序号 | 协议 | 支持 | -| ---- | :----------------------- | :--- | -| 1.1 | OAuth 2.x/OpenID Connect | 高 | -| 1.2 | SAML 2.0 | 高 | -| 1.3 | JWT | 高 | -| 1.4 | CAS | 高 | -| 1.5 | SCIM 2.0 | 高 | -| 1.6 | FormBased | 中 | -| 1.7 | TokenBased(Post/Cookie) | 中 | -| 1.8 | ExtendApi | 低 | -| 1.9 | EXT | 低 | - -2. 登录支持 - -| 序号 | 登录方式 | 支持 | -| ---- | :--------- | :--------------------------------------------------------- | -| 2.1 | 动态验证码 | 字母/数字/算术 | -| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | -| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | -| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | -| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | -| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | -| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | -| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | - -3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 - -4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 - -5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 - -6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 - -7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 - -8. 配置化的密码策略、访问策略;支持 Ip2region 或 GeoLite2 地理库精准 IP 定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 - -9. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 - -10. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 - -## 界面 - -![](/assets/img/news/MaxKey-4.0.2.png) - -## 下载 - -当前版本百度网盘下载, 历史版本 - -| 版本 | 日期 | 网盘(提取码) | Docker | -| ------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | -| v 4.0.2 | 2023/10/11 | 下载( **mxk9** ) | 链接 | - -## 版本说明 - -MaxKey v 4.0.2 GA 2023/10/12 - -``` - *(MAXKEY-231001) shenyu网关插件 @Saiph - *(MAXKEY-231002) ip2region & GeoLite2 ip地址转换,支持国家、省、市、地域 - *(MAXKEY-231003) #I844NY消除Session与Authentication来回嵌套 - *(MAXKEY-231004) #I83BUJ4.0.1版本 建应用,凭证类型选 user_defined用户自定义。值不一样 - *(MAXKEY-231005) 删除 session getFormattedId - *(MAXKEY-231006) 用户组显示bug修复 - *(MAXKEY-231007) pig整合MAXKEY方案 @RJ - *(MAXKEY-231008) 日志打印优化!31fix:log4j2.xml - *(MAXKEY-231009) 新增ProductEnvironment的显示 - *(MAXKEY-231010) #I8651R新建了应用,但在认证系统里面看不到 - *(MAXKEY-231011) 修复动态用户组出现bug #I85U4I - *(MAXKEY-231012) 依赖项引用、更新和升级 - spring 6.0.12 - springBoot 3.1.4 -``` - -## Roadmap - -### 增强身份管理 - -### 数据同步优化 - -### 权限管理优化 +--- +title: MaxKey单点登录认证系统4.0.2,精准IP定位 +author: MaxKey +tag: + - MaxKey +date: 2023-10-12 +cover: /assets/img/news/MaxKey-cover.png +head: + - - meta + - name: 新闻 +--- + +## 概述 + +MaxKey单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是业界领先的 IAM-IDaas 身份管理和认证产品,支持 OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM 等标准协议,提供安全、标准和开放的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC 权限管理和资源管理等;开源、安全、自主可控。 + +官方网站 http://www.maxkey.top/ + +官方 QQ:1054466084 + +邮箱 email: support@maxsso.net + +代码托管 Gitee | GitHub + +## 产品特性 + +1. 标准协议 + +| 序号 | 协议 | 支持 | +| ---- | :----------------------- | :--- | +| 1.1 | OAuth 2.x/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2. 登录支持 + +| 序号 | 登录方式 | 支持 | +| ---- | :--------- | :--------------------------------------------------------- | +| 2.1 | 动态验证码 | 字母/数字/算术 | +| 2.2 | 双因素认证 | 短信/时间令牌/邮件 | +| 2.3 | 短信认证 | 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 时间令牌 | Google/Microsoft Authenticator/FreeOTP/支持 TOTP 或者 HOTP | +| 2.5 | 域认证 | Kerberos/SPNEGO/AD 域 | +| 2.6 | LDAP | OpenLDAP/ActiveDirectory/标准 LDAP 服务器 | +| 2.7 | 社交账号 | 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 | 企业微信/钉钉/飞书扫码登录 | + +3. 提供标准的认证接口以便于其他应用集成 SSO,安全的移动接入,安全的 API、第三方认证和互联网认证的整合。 + +4. 提供用户生命周期管理,支持 SCIM 2 协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5. 简化微软 Active Directory 域控、标准 LDAP 服务器机构和账号管理,密码自助服务重置密码。 + +6. IDaas 多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7. 认证中心具有平台无关性、环境多样性,支持 Web、手机、移动设备等, 如 Apple iOS,Andriod 等,将认证能力从 B/S 到移动应用全面覆盖。 + +8. 配置化的密码策略、访问策略;支持 Ip2region 或 GeoLite2 地理库精准 IP 定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9. 基于 Java EE 平台,微服务架构,采用 Spring、MySQL、Tomcat、Redis、MQ 等开源技术,扩展性强。 + +10. 开源、安全、自主可控,许可证 Apache 2.0 License & MaxKey 版权声明。 + +## 界面 + +![](/assets/img/news/MaxKey-4.0.2.png) + +## 下载 + +当前版本百度网盘下载, 历史版本 + +| 版本 | 日期 | 网盘(提取码) | Docker | +| ------- | :--------- | :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| v 4.0.2 | 2023/10/11 | 下载( **mxk9** ) | 链接 | + +## 版本说明 + +MaxKey v 4.0.2 GA 2023/10/12 + +``` + *(MAXKEY-231001) shenyu网关插件 @Saiph + *(MAXKEY-231002) ip2region & GeoLite2 ip地址转换,支持国家、省、市、地域 + *(MAXKEY-231003) #I844NY消除Session与Authentication来回嵌套 + *(MAXKEY-231004) #I83BUJ4.0.1版本 建应用,凭证类型选 user_defined用户自定义。值不一样 + *(MAXKEY-231005) 删除 session getFormattedId + *(MAXKEY-231006) 用户组显示bug修复 + *(MAXKEY-231007) pig整合MAXKEY方案 @RJ + *(MAXKEY-231008) 日志打印优化!31fix:log4j2.xml + *(MAXKEY-231009) 新增ProductEnvironment的显示 + *(MAXKEY-231010) #I8651R新建了应用,但在认证系统里面看不到 + *(MAXKEY-231011) 修复动态用户组出现bug #I85U4I + *(MAXKEY-231012) 依赖项引用、更新和升级 + spring 6.0.12 + springBoot 3.1.4 +``` + +## Roadmap + +### 增强身份管理 + +### 数据同步优化 + +### 权限管理优化 diff --git a/src/zh/news/MaxKey-4.1.2.md b/src/zh/news/MaxKey-4.1.2.md new file mode 100644 index 0000000000..6b099827f3 --- /dev/null +++ b/src/zh/news/MaxKey-4.1.2.md @@ -0,0 +1,136 @@ +--- +title: MaxKey单点登录认证系统4.1.2,国庆版重大更新 +author: maxkey +tag: + - MaxKey +date: 2024-09-30 +cover: /assets/img/news/MaxKey-4.1.2-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/MaxKey-4.1.2-0.png) + +**业界领先的IAM-IDaas身份管理和认证产品** + +# 概述 + +**MaxKey**单点登录认证系统,谐音马克思的钥匙寓意是最大钥匙,是**业界领先的IAM-IDaas身份管理和认证产品**,支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供**安全、标准和开放**的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等;开源、安全、合规、自主可控。 + +官方网站: **http://www.maxkey.top** + +官方QQ:**1054466084** + +邮箱email:support@maxsso.net + +代码托管: + +Gitee:  https://gitee.com/dromara/MaxKey  + +GitHub:  https://github.com/dromara/MaxKey + +# 产品特性 + +1.标准协议 + +| 序号 | 协议 | 支持 | +| --- | --- | --- | +| 1.1 | OAuth 2.0/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2.登录支持 + +| 序号 | 登录方式 | +| --- | --- | +| 2.1 | 动态验证码 字母/数字/算术 | +| 2.2 | 双因素认证 | +| 2.3 | 短信认证 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 登录易/Google/Microsoft Authenticator/FreeOTP/支持TOTP或者HOTP | +| 2.5 | Kerberos/SPNEGO/AD域 | +| 2.6 | OpenLDAP/ActiveDirectory/标准LDAP服务器 | +| 2.7 | 社交账号 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 企业微信/钉钉/飞书扫码登录 | + +3.提供标准的认证接口以便于其他应用集成SSO,安全的移动接入,安全的API、第三方认证和互联网认证的整合。 + +4.提供用户生命周期管理,支持SCIM 2协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5.简化微软Active Directory域控、标准LDAP服务器机构和账号管理,密码自助服务重置密码。 + +6.IDaas多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7.认证中心具有平台无关性、环境多样性,支持Web、手机、移动设备等, 如Apple iOS,Andriod等,将认证能力从B/S到移动应用全面覆盖。 + +8.配置化的密码策略、访问策略;支持Ip2region或GeoLite2地理库精准IP定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9.基于Java EE平台,微服务架构,采用Spring、MySQL、Tomcat、Redis、MQ等开源技术,扩展性强。 + +10.开源、安全、合规、自主可控,许可证 Apache 2.0 License 。 + +# 界面 + +![](/assets/img/news/MaxKey-4.1.2-1.png) + +# 下载 + +当前版本网盘下载 + +| 版本 | 日期 | 下载地址 | +| --- | --- | --- | +| v 4.1.2 | 2024/09/30 | 下载 | + +# 版本说明 + +MaxKey v 4.1.2 GA 2024/09/30 + +``` +    *(MAXKEY-240601) timebased otp 优化 +    *(MAXKEY-240602) appCategoryService优化 +    *(MAXKEY-240603) 开源暑期 Flutter认证APP项目 +    *(MAXKEY-240604) 开源暑期同步器增加属性配置管理项目 +    *(MAXKEY-240605) APP扫码登录功能 +    *(MAXKEY-240606) maxkey-web-openapi访问路径/maxkey-openapi +    *(MAXKEY-240607) APP扫码登录支持 +    *(MAXKEY-240608) 组和角色优化,增加角色动态任务,删除resumeTime和suspendTime +    *(MAXKEY-240609) ProvisionAction 改为ProvisionAct +    *(MAXKEY-240610) MaxKeyGatewayApplication移除exclude  DruidDataSourceAutoConfigure.class +    *(MAXKEY-240611) 登录后台跳转功能修复 +    *(MAXKEY-240612) Oauth20Client代码优化,移除Oauth20ClientAutoConfiguration +    *(MAXKEY-240613) core Repository移动到maxkey-persistence,以后的版本进行合并 +    *(MAXKEY-240613) 依赖项引用、更新和升级 +        mybatis-jpa-extra               3.3.1 +        springVersion                   6.1.13 +        springBootVersion               3.3.4 +        springDataVersion               3.3.4 +        tomcatVersion                   10.1.30 +``` + +# Roadmap + +**增强身份管理** + +**数据同步优化** + +**权限管理优化** + + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/MaxKey-4.1.2-2.webp) \ No newline at end of file diff --git a/src/zh/news/MaxKey-4.1.3.md b/src/zh/news/MaxKey-4.1.3.md new file mode 100644 index 0000000000..1e74823055 --- /dev/null +++ b/src/zh/news/MaxKey-4.1.3.md @@ -0,0 +1,143 @@ +--- +title: MaxKey单点登录认证系统4.1.3,持久层重大更新 +author: MaxKey +date: 2024-11-29 +cover: /assets/img/news/MaxKey-4.1.3-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/MaxKey-4.1.3-0.png) + +**业界领先的IAM-IDaas身份管理和认证产品** + +# 概述 + +**MaxKey**单点登录认证系统是**业界领先的IAM-IDaas身份管理和认证产品**,谐音为马克思的钥匙,寓意它能够像一把万能钥匙(最大钥匙)一样,解锁复杂的企业安全需求,提供简洁而高效的解决方案。产品支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供**安全、标准和开放**的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等。 + +MaxKey注重企业级场景下的性能、安全和易用性,广泛应用于医疗、金融、政府和制造等行业。 + +官方网站:**官网** + +官方QQ:**1054466084** + +邮箱email:\*\*support@maxsso.net\*\* + +代码托管:**Gitee**|**GitHub** + +# 产品特性 + +1.标准协议 + +| 序号 | 协议 | 支持 | +| --- | --- | --- | +| 1.1 | OAuth 2.0/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2.登录支持 + +| 序号 | 登录方式 | +| --- | --- | +| 2.1 | 动态验证码 字母/数字/算术 | +| 2.2 | 双因素认证 | +| 2.3 | 短信认证 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 登录易/Google/Microsoft Authenticator/FreeOTP/支持TOTP或者HOTP | +| 2.5 | Kerberos/SPNEGO/AD域 | +| 2.6 | OpenLDAP/ActiveDirectory/标准LDAP服务器 | +| 2.7 | 社交账号 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 企业微信/钉钉/飞书扫码登录 | + +3.提供标准的认证接口以便于其他应用集成SSO,安全的移动接入,安全的API、第三方认证和互联网认证的整合。 + +4.提供用户生命周期管理,支持SCIM 2协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5.简化微软Active Directory域控、标准LDAP服务器机构和账号管理,密码自助服务重置密码。 + +6.IDaas多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7.认证中心具有平台无关性、环境多样性,支持Web、手机、移动设备等, 如Apple iOS,Andriod等,将认证能力从B/S到移动应用全面覆盖。 + +8.配置化的密码策略、访问策略;支持Ip2region或GeoLite2地理库精准IP定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9.基于Java EE平台,微服务架构,采用Spring、MySQL、Tomcat、Redis、MQ等开源技术,扩展性强。 + +10.开源、安全、合规、自主可控,许可证 Apache 2.0 License 。 + +# 界面 + +![](/assets/img/news/MaxKey-4.1.3-1.png) + +# 下载 + +当前版本网盘下载 + +| 版本 | 日期 | 下载地址 | +| --- | --- | --- | +| v 4.1.3 | 2024/11/29 | 下载 | + +# 版本说明 + +MaxKey v 4.1.3 GA 2024/11/29 + +``` +    *(MAXKEY-240701) #IAZNZS oauth2单点注销没有生效修复 +    *(MAXKEY-240702) #238 飞书扫码登录失败 +    *(MAXKEY-240703) 升级mybatis-jpa-extra-3.3.2 增加service接口和impl实现 +    *(MAXKEY-240704) 把activedirectory相关移动到org.dromara.maxkey.ldap.activedirectory下 +    *(MAXKEY-240705) 增加 maxkey-authentication-provider-mgt +    *(MAXKEY-240706) maxkey.auth.session.timeout rename to maxkey.auth.jwt.refresh.expires +    *(MAXKEY-240707) 增加公共规则 ConstsRegex +    *(MAXKEY-240708) 认证后跳转判断优化 +    *(MAXKEY-240709) #IB1BC9 客户端调用单点注销接口或者maxkey-hmt管理后台页面退出报错:Circular view path [logout] +    *(MAXKEY-240710) oidc wellknown接口优化 +    *(MAXKEY-240711) #IAEWN7 well-known/openid-configuration 接口 返回的response_types_supported问题 +    *(MAXKEY-240712) 短信验证码服务代码优化 +    *(MAXKEY-240713) 分离登录扫码接口 +    *(MAXKEY-240714) 登录会话和日志查询优化,增加IP地址归属地显示,删除loginUrl字段 +    *(MAXKEY-240715) 宝塔面板安装部署接入 +    *(MAXKEY-240716) bean初始化判断的优化 +    *(MAXKEY-240717) ALL Project UTF-8 +    *(MAXKEY-240718) maxkey-authentications\maxkey-authentication-social -> maxkey-starter\maxkey-starter-social +    *(MAXKEY-240719) 数据库字段自动填充instId,createdBy,createdDate,modifiedBy,modifiedDate +    *(MAXKEY-240720) #235 from khangdc2/circleci-project-setup +    *(MAXKEY-240721) docker-compose & docker 部署更新和优化 +    *(MAXKEY-240722) gradle配置文件优化 +    *(MAXKEY-240723) 删除代码中无效引用,代码规范化 +    *(MAXKEY-240724) HistorySignOnAppInterceptor 改名 HistorySingleSignOnInterceptor  +    *(MAXKEY-240725) mysql升级8.4.2 +    *(MAXKEY-240726) #IB71WC 前端本地启动 404 问题 +    *(MAXKEY-240727) saml metadata emailAddress support@maxsso.net +    *(MAXKEY-240728) 依赖项引用、更新和升级 +            spring 6.2.0 +            springBoot 3.3.6 +            springSecurity 6.4.1 +            springData 3.4.0 +            springkafka 3.3.0 +            springcloud 4.1.4 +            httpcomponentscore5 5.3.1 +            httpcomponentsclient5 5.4.1 +            tomcat 10.1.33 +            log4j 2.24.1 +            gson 2.11.0 +            freemarker  2.3.33 +``` + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/MaxKey-4.1.3-2.webp) \ No newline at end of file diff --git a/src/zh/news/MaxKey-4.1.4.md b/src/zh/news/MaxKey-4.1.4.md new file mode 100644 index 0000000000..e44d973c4c --- /dev/null +++ b/src/zh/news/MaxKey-4.1.4.md @@ -0,0 +1,140 @@ +--- +title: MaxKey单点登录认证系统4.1.4,访问地图优化 +author: MaxKey +date: 2024-12-27 +cover: /assets/img/news/MaxKey-4.1.4-0.webp +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/MaxKey-4.1.4-0.webp) + +**业界领先的IAM-IDaas身份管理和认证产品** + +# 概述 + +Dromara **MaxKey**单点登录认证系统是**业界领先的IAM-IDaas身份管理和认证产品**,谐音为马克思的钥匙,寓意它能够像一把万能钥匙(最大钥匙)一样,解锁复杂的企业安全需求,提供简洁而高效的解决方案。产品支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供**安全、标准和开放**的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等。 + +MaxKey注重企业级场景下的性能、安全和易用性,广泛应用于医疗、金融、政府和制造等行业。 + +MaxKey **遵循 Apache License, Version 2.0 开源免费**,开源、安全、合规、自主可控。 + +官方网站:**官网https://www.maxkey.top/** + +官方QQ:**1054466084** + +邮箱email:support@maxsso.net + +代码托管: + +Gitee:  https://gitee.com/dromara/MaxKey  + +GitHub:  https://github.com/dromara/MaxKey + + + +# 产品特性 + +1.标准协议 + +| 序号 | 协议 | 支持 | +| --- | --- | --- | +| 1.1 | OAuth 2.0/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2.登录支持 + +| 序号 | 登录方式 | +| --- | --- | +| 2.1 | 动态验证码 字母/数字/算术 | +| 2.2 | 双因素认证 | +| 2.3 | 短信认证 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 登录易/Google/Microsoft Authenticator/FreeOTP/支持TOTP或者HOTP | +| 2.5 | Kerberos/SPNEGO/AD域 | +| 2.6 | OpenLDAP/ActiveDirectory/标准LDAP服务器 | +| 2.7 | 社交账号 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 企业微信/钉钉/飞书扫码登录 | + +3.提供标准的认证接口以便于其他应用集成SSO,安全的移动接入,安全的API、第三方认证和互联网认证的整合。 + +4.提供用户生命周期管理,支持SCIM 2协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5.简化微软Active Directory域控、标准LDAP服务器机构和账号管理,密码自助服务重置密码。 + +6.IDaas多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7.认证中心具有平台无关性、环境多样性,支持Web、手机、移动设备等, 如Apple iOS,Andriod等,将认证能力从B/S到移动应用全面覆盖。 + +8.配置化的密码策略、访问策略;支持Ip2region或GeoLite2地理库精准IP定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9.基于Java EE平台,微服务架构,采用Spring、MySQL、Tomcat、Redis、MQ等开源技术,扩展性强。 + +10.开源、安全、合规、自主可控,许可证 Apache 2.0 License https://www.maxkey.top/zh/about/licenses.html。 + +# 界面 + +![](/assets/img/news/MaxKey-4.1.4-1.png) + +# 下载 + +当前版本网盘下载 + +| 版本 | 日期 | 下载地址 | +| --- | --- | --- | +| v 4.1.4 | 2024/12/27 | 下载 | + +# 版本说明 + +MaxKey v 4.1.4 GA 2024/12/27 + +``` + +    *(MAXKEY-240801) 部署在线演示系统 +    *(MAXKEY-240802) mysql数据库连接参数 serverTimezone=GMT+8 +    *(MAXKEY-240803) map地图优化 +    *(MAXKEY-240804) 国际化翻译 +    *(MAXKEY-240805) 登录日志增加参数category 区分认证1和管理端5 +    *(MAXKEY-240806) 认证端到管理端的单点登录改为CAS +    *(MAXKEY-240807) 增加Dromara标识 +    *(MAXKEY-240808) 仪表盘优化和更新 +    *(MAXKEY-240809) 仪表盘优化-增加国内地图显示 +    *(MAXKEY-240810) 会话管理优化 +    *(MAXKEY-240811) CAS适配器名称修正 +    *(MAXKEY-240812) #IB8ZT6 认证平台无法修改密码 +    *(MAXKEY-240813) 登录日志默认InstId +    *(MAXKEY-240814) InstitutionsService 优化和整合 +    *(MAXKEY-240815) DefaultTokenServices RefreshToken优化 +    *(MAXKEY-240816) 整合LoginHistoryRepository 到 HistoryLoginService +    *(MAXKEY-240817) 认证代码整合优化 +    *(MAXKEY-240818) 登录LoginRepository 整合优化LoginServiceImpl   +    *(MAXKEY-240819) 在线文档升级和优化 +    *(MAXKEY-240820) 依赖项引用、更新和升级 +            springVersion                   6.2.1 +            springBootVersion               3.4.1 +            springSecurityVersion           6.4.2 +            springretryVersion              2.0.11 +            log4jVersion                    2.24.3 +            jacksonVersion                  2.18.2 +            springcloudVersion              4.2.0 +            springcloudalibabaVersion       2023.0.1.2 +``` + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/MaxKey-4.1.4-2.webp) \ No newline at end of file diff --git a/src/zh/news/MaxKey-4.1.5.md b/src/zh/news/MaxKey-4.1.5.md new file mode 100644 index 0000000000..1ab5cde492 --- /dev/null +++ b/src/zh/news/MaxKey-4.1.5.md @@ -0,0 +1,110 @@ +--- +title: MaxKey单点登录认证系统4.1.5,Swagger漏洞修复 +author: maxkey +date: 2025-01-09 +cover: /assets/img/news/MaxKey-4.1.5-0.webp +head: + - - meta + - name: 新闻 +--- + + + +![](/assets/img/news/MaxKey-4.1.5-0.webp) + +**业界领先的IAM-IDaas身份管理和认证产品** + +# 概述 + +Dromara **MaxKey**单点登录认证系统是**业界领先的IAM-IDaas身份管理和认证产品**,谐音为马克思的钥匙,寓意它能够像一把万能钥匙(最大钥匙)一样,解锁复杂的企业安全需求,提供简洁而高效的解决方案。产品支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供**安全、标准和开放**的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等。 + +MaxKey注重企业级场景下的性能、安全和易用性,广泛应用于医疗、金融、政府和制造等行业。 + +MaxKey **遵循 Apache License, Version 2.0 开源免费**,开源、安全、合规、自主可控。 + +官方网站:**https://www.maxkey.top/** + +官方QQ:**1054466084** + +邮箱email:support@maxsso.net + +代码托管: + +Gitee:  https://gitee.com/dromara/MaxKey  + +GitHub:  https://github.com/dromara/MaxKey + + + +# 产品特性 + +1.标准协议 + +| 序号 | 协议 | 支持 | +| --- | --- | --- | +| 1.1 | OAuth 2.0/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| 1.3 | JWT | 高 | +| 1.4 | CAS | 高 | +| 1.5 | SCIM 2.0 | 高 | +| 1.6 | FormBased | 中 | +| 1.7 | TokenBased(Post/Cookie) | 中 | +| 1.8 | ExtendApi | 低 | +| 1.9 | EXT | 低 | + +2.登录支持 + +| 序号 | 登录方式 | +| --- | --- | +| 2.1 | 动态验证码 字母/数字/算术 | +| 2.2 | 双因素认证 | +| 2.3 | 短信认证 腾讯云短信/阿里云短信/网易云信 | +| 2.4 | 登录易/Google/Microsoft Authenticator/FreeOTP/支持TOTP或者HOTP | +| 2.5 | Kerberos/SPNEGO/AD域 | +| 2.6 | OpenLDAP/ActiveDirectory/标准LDAP服务器 | +| 2.7 | 社交账号 微信/QQ/微博/钉钉/Google/Facebook/其他 | +| 2.8 | 扫码登录 企业微信/钉钉/飞书扫码登录 | + +3.提供标准的认证接口以便于其他应用集成SSO,安全的移动接入,安全的API、第三方认证和互联网认证的整合。 + +4.提供用户生命周期管理,支持SCIM 2协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5.简化微软Active Directory域控、标准LDAP服务器机构和账号管理,密码自助服务重置密码。 + +6.IDaas多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7.认证中心具有平台无关性、环境多样性,支持Web、手机、移动设备等, 如Apple iOS,Andriod等,将认证能力从B/S到移动应用全面覆盖。 + +8.配置化的密码策略、访问策略;支持Ip2region或GeoLite2地理库精准IP定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9.基于Java EE平台,微服务架构,采用Spring、MySQL、Tomcat、Redis、MQ等开源技术,扩展性强。 + +10.开源、安全、合规、自主可控,许可证 Apache 2.0 License https://www.maxkey.top/zh/about/licenses.html。 + +# 界面 + +![](/assets/img/news/MaxKey-4.1.5-1.png) + +# 下载 + +当前版本网盘下载 + +| 版本 | 日期 | 下载地址 | +| --- | --- | --- | +| v 4.1.5 | 2025/01/09 | 下载 | + +# 版本说明 + +MaxKey v 4.1.5 GA 2025/01/09 + +``` + +    *(MAXKEY-250101) 登录验证码配置优化,默认配置启用 +    *(MAXKEY-250102) SQL语法表达式增强 +    *(MAXKEY-250103) unlockdate 修改为 unlocktime +    *(MAXKEY-250104) Xss 安全防护优化 +    *(MAXKEY-250105) #IBFGSI knife4j bug by zwj +    *(MAXKEY-250106) 在线文档升级和优化 +    *(MAXKEY-250107) 依赖项引用、更新和升级 +            mybatis-jpa-extra  3.3.3 +``` \ No newline at end of file diff --git a/src/zh/news/MaxKey-4.1.6.md b/src/zh/news/MaxKey-4.1.6.md new file mode 100644 index 0000000000..9780fd53ac --- /dev/null +++ b/src/zh/news/MaxKey-4.1.6.md @@ -0,0 +1,145 @@ +--- +title: MaxKey单点登录认证系统4.1.6,世界地图访问统计 +author: MaxKey +date: 2025-02-20 +cover: /assets/img/news/MaxKey-4.1.6-0.png +head: + - - meta + - name: 新闻 +--- + + +**![](/assets/img/news/MaxKey-4.1.6-1.webp)** + +**业界领先的IAM-IDaas身份管理和认证产品** + +# +概述 + +Dromara **MaxKey**单点登录认证系统是**业界领先的IAM-IDaas身份管理和认证产品**,谐音为马克思的钥匙,寓意它能够像一把万能钥匙(最大钥匙)一样,解锁复杂的企业安全需求,提供简洁而高效的解决方案。产品支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供**安全、标准和开放**的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等。 + +MaxKey注重企业级场景下的性能、安全和易用性,广泛应用于医疗、金融、政府和制造等行业。 + +MaxKey **遵循 Apache License, Version 2.0 开源免费**,开源、安全、合规、自主可控。 + +官方网站:**官网https://www.maxkey.top/** + +官方QQ:**1054466084** + +邮箱email:support@maxsso.net + +代码托管: + +Gitee:  https://gitee.com/dromara/MaxKey  + +GitHub:  https://github.com/dromara/MaxKey + + + +# +产品特性 + +1.标准协议 + +|序号|协议|支持| +|----|----|----| +|1.1|OAuth 2.0/OpenID Connect|高| +|1.2|SAML 2.0|高| +|1.3|JWT|高| +|1.4|CAS|高| +|1.5|SCIM 2.0|高| +|1.6|FormBased|中| +|1.7|TokenBased(Post/Cookie)|中| +|1.8|ExtendApi|低| +|1.9|EXT|低| + +|序号|登录方式| +|----|----| +|2.1|动态验证码 字母/数字/算术| +|2.2|双因素认证| +|2.3|短信认证 腾讯云短信/阿里云短信/网易云信| +|2.4|登录易/Google/Microsoft Authenticator/FreeOTP/支持TOTP或者HOTP| +|2.5|Kerberos/SPNEGO/AD域| +|2.6|OpenLDAP/ActiveDirectory/标准LDAP服务器| +|2.7|社交账号 微信/QQ/微博/钉钉/Google/Facebook/其他| +|2.8|扫码登录 企业微信/钉钉/飞书扫码登录| + + + +3.提供标准的认证接口以便于其他应用集成SSO,安全的移动接入,安全的API、第三方认证和互联网认证的整合。 + +4.提供用户生命周期管理,支持SCIM 2协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5.简化微软Active Directory域控、标准LDAP服务器机构和账号管理,密码自助服务重置密码。 + +6.IDaas多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7.认证中心具有平台无关性、环境多样性,支持Web、手机、移动设备等, 如Apple iOS,Andriod等,将认证能力从B/S到移动应用全面覆盖。 + +8.配置化的密码策略、访问策略;支持Ip2region或GeoLite2地理库精准IP定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9.基于Java EE平台,微服务架构,采用Spring、MySQL、Tomcat、Redis、MQ等开源技术,扩展性强。 + +10.开源、安全、合规、自主可控,许可证 Apache 2.0 License https://www.maxkey.top/zh/about/licenses.html。 + +# +界面 + +![](/assets/img/news/MaxKey-4.1.6-2.png) + +# +下载 + +| 版本 | 日期 | 下载地址 | +|--------|------------|-------------------------| +| v 4.1.6 | 2025/02/20 | 下载 | + +# +版本说明 + +MaxKey v 4.1.6 GA 2025/02/20 + +``` + +    *(MAXKEY-250201) 无效请求地址全局优化 +    *(MAXKEY-250202) loginConfig,captchaType属性补写,修复openApi运行失败问题 by zhangzhongjie +    *(MAXKEY-250203) #IBGVOI LoginConfig 设置默认值 +    *(MAXKEY-250204) 增加30日世界地图访问 +    *(MAXKEY-250205) JWT验证代码优化 +    *(MAXKEY-250206) 审计查询优化 +    *(MAXKEY-250207) 读取当前机构优化 +    *(MAXKEY-250208) #IBI3NO fetch 查询条件不能为空么问题 +    *(MAXKEY-250209) 登录成功信息更新优化 +    *(MAXKEY-250210) 认证端终止会话问题优化 +    *(MAXKEY-250211) 当已存在token时也需要保存visited到当前session呀   by zwj +    *(MAXKEY-250212) 修复因converter处理顺序导致string类型被mappingJacksonHttpMessageConverter当成字符串处理 by 宁鹏涛 +    *(MAXKEY-250213) 图片计算验证码增加计算复杂度优化 +    *(MAXKEY-250214) 管理端增加源码的连接地址 +    *(MAXKEY-250215) 依赖项引用、更新和升级 +            springDataVersion           3.4.2 +            springkafkaVersion          3.3.2 +            freemarkerVersion           2.3.34 +            tomcatVersion               10.1.34 +            slf4jVersion                2.0.16 +            guavaVersion                33.4.0-jre +            zxingcoreVersion            3.5.3 +            gsonVersion                 2.12.1 +            jakartaannotationVersion    3.0.0 +            jakartaactivationVersion    2.1.3 +            jakartamailapiVersion       2.1.3 +            jakartavalidationapiVersion 3.1.1 +            jakartaxmlbindapiVersion    4.0.2 +            mybatis-jpa-extra           3.3.4 +``` + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/MaxKey-4.1.6-3.webp) \ No newline at end of file diff --git a/src/zh/news/MaxKey-4.1.7.md b/src/zh/news/MaxKey-4.1.7.md new file mode 100644 index 0000000000..bf9a919e28 --- /dev/null +++ b/src/zh/news/MaxKey-4.1.7.md @@ -0,0 +1,118 @@ +--- +title: MaxKey单点登录认证系统4.1.7,重要BUG修复 +author: MaxKey +date: 2025-04-01 +cover: /assets/img/news/MaxKey-4.1.7-0.webp +head: + - - meta + - name: 新闻 +--- + +**![](/assets/img/news/MaxKey-4.1.7-0.webp)** + +**业界领先的IAM-IDaas身份管理和认证产品** + +# 概述 + +Dromara **MaxKey**单点登录认证系统是**业界领先的IAM-IDaas身份管理和认证产品**,谐音为马克思的钥匙,寓意它能够像一把万能钥匙(最大钥匙)一样,解锁复杂的企业安全需求,提供简洁而高效的解决方案。产品支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供**安全、标准和开放**的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等。 + +MaxKey注重企业级场景下的性能、安全和易用性,广泛应用于医疗、金融、政府和制造等行业。 + +MaxKey **遵循 Apache License, Version 2.0 开源免费**,开源、安全、合规、自主可控。 + +官方网站:**官网https://www.maxkey.top/** + +官方QQ:**1054466084** + +邮箱email:support@maxsso.net + +代码托管: + +Gitee:  https://gitee.com/dromara/MaxKey  + +GitHub:  https://github.com/dromara/MaxKey + + + +# 产品特性 + +1.标准协议 + +| 序号 | 协议 | 支持 | +| :----: | :------------------------: | :----: | +| 1.1 | OAuth 2.0/OpenID Connect | 高 | +| 1.2 | SAML 2.0 | 高 | +| ... | ... | ... | + + +2.登录支持 + +| 序号 | 登录方式 | +| ---- | ---- | +| 2.1 | 动态验证码(字母/数字/算术) | +| 2.2 | 双因素认证 | +| 2.3 | 短信认证(腾讯云短信/阿里云短信/网易云信) | +| 2.4 | 登录易/Google/Microsoft Authenticator/FreeOTP(支持TOTP或者HOTP) | +| 2.5 | Kerberos/SPNEGO/AD域 | +| 2.6 | OpenLDAP/ActiveDirectory/标准LDAP服务器 | +| 2.7 | 社交账号(微信/QQ/微博/钉钉/Google/Facebook/其他) | +| 2.8 | 扫码登录(企业微信/钉钉/飞书扫码登录) | + +3.提供标准的认证接口以便于其他应用集成SSO,安全的移动接入,安全的API、第三方认证和互联网认证的整合。 + +4.提供用户生命周期管理,支持SCIM 2协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5.简化微软Active Directory域控、标准LDAP服务器机构和账号管理,密码自助服务重置密码。 + +6.IDaas多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7.认证中心具有平台无关性、环境多样性,支持Web、手机、移动设备等, 如Apple iOS,Andriod等,将认证能力从B/S到移动应用全面覆盖。 + +8.配置化的密码策略、访问策略;支持Ip2region或GeoLite2地理库精准IP定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9.基于Java EE平台,微服务架构,采用Spring、MySQL、Tomcat、Redis、MQ等开源技术,扩展性强。 + +10.开源、安全、合规、自主可控,许可证 Apache 2.0 License https://www.maxkey.top/zh/about/licenses.html。 + +# 界面 + +![](/assets/img/news/MaxKey-4.1.7-1.png) + +# 下载 + +当前版本网盘下载 + +| 版本 | 日期 | 下载地址 | +|--------|------------|-------------------------| +| v 4.1.7 | 2025/04/01 | 下载 | + +# 版本说明 + +MaxKey v 4.1.7 GA 2025/04/01 + +``` + +    *(MAXKEY-250301) #IBQEYU 使用 AccessToken 验证身份报错session is null +    *(MAXKEY-250302) org.dromara.maxkey.web.component to org.dromara.maxkey.entity +    *(MAXKEY-250303) remove query submitLoading +    *(MAXKEY-250304) 编译后可能导致图无法显示问题 +    *(MAXKEY-250305) sessionStatus优化 +    *(MAXKEY-250306) 菜单名称优化 +    *(MAXKEY-250307) 新增用户密码生成功能 +    *(MAXKEY-250308) 管理端前端存储INST值改为inst_mgt +    *(MAXKEY-250309) 代码优化 +    *(MAXKEY-250310) 依赖项引用、更新和升级 +            springVersion                 6.2.5 +            springBootVersion             3.4.4 +            springSecurityVersion         6.4.4 +            springDataVersion             3.4.4 +            springkafkaVersion            3.3.4 +            tomcatVersion                 10.1.39 +            slf4jVersion                  2.0.17 +            jacksonVersion                2.18.3 +            druidVersion                  1.2.24 +            druidspringbootstarterVersion 1.2.24 +            mybatisVersion                3.5.19 +            mybatisspringVersion          3.0.4 +            mybatis-jpa-extra             3.3.5 +``` \ No newline at end of file diff --git a/src/zh/news/MaxKey-4.1.8.md b/src/zh/news/MaxKey-4.1.8.md new file mode 100644 index 0000000000..cef1d74a0c --- /dev/null +++ b/src/zh/news/MaxKey-4.1.8.md @@ -0,0 +1,119 @@ +--- +title: MaxKey 单点登录认证系统4.1.8,二次认证来袭 +author: maxkey +date: 2025-08-02 +cover: /assets/img/news/MaxKey-4.1.8-0.webp +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/MaxKey-4.1.8-0.webp) + +**业界领先的IAM-IDaas身份管理和认证产品** + +# 概述 + +Dromara **MaxKey**单点登录认证系统是**业界领先的IAM-IDaas身份管理和认证产品**,谐音为马克思的钥匙,寓意它能够像一把万能钥匙(最大钥匙)一样,解锁复杂的企业安全需求,提供简洁而高效的解决方案。产品支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供**安全、标准和开放**的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等。 + +MaxKey注重企业级场景下的性能、安全和易用性,广泛应用于医疗、金融、政府和制造等行业。 + +MaxKey **遵循 Apache-2.0 开源免费**,开源、安全、合规、自主可控。 + +官方网站:**官网https://www.maxkey.top/** + +官方QQ:**1054466084** + +邮箱email:\*\*support@maxsso.net\*\* + +代码托管: + +Gitee:  https://gitee.com/dromara/MaxKey  + +GitHub:  https://github.com/dromara/MaxKey + + + +# 产品特性 + +1.标准协议 + +|序号|协议|支持| +|----|----|----| +|1.1|OAuth 2.0/OpenID Connect|高| +|1.2|SAML 2.0|高| +|1.3|JWT|高| +|1.4|CAS|高| +|1.5|SCIM 2.0|高| +|1.6|Form Based|中| +|1.7|Token Based(Post/Cookie)|中| +|1.8|ExtendApi|低| +|1.9|EXT|低| + +2.登录支持 +|序号|登录方式| +|----|----| +|2.1|动态验证码(字母/数字/算术)| +|2.2|双因素认证| +|2.3|短信认证(腾讯云短信/阿里云短信/网易云信)| +|2.4|登录易/Google/Microsoft Authenticator/FreeOTP(支持TOTP或者HOTP)| +|2.5|Kerberos/SPNEGO/AD域| +|2.6|OpenLDAP/ActiveDirectory/标准LDAP服务器| +|2.7|社交账号(微信/QQ/微博/钉钉/Google/Facebook/其他)| +|2.8|扫码登录(企业微信/钉钉/飞书扫码登录)| + +3.提供标准的认证接口以便于其他应用集成SSO,安全的移动接入,安全的API、第三方认证和互联网认证的整合。 + +4.提供用户生命周期管理,支持SCIM 2协议;开箱即用的连接器(Connector)实现身份供给同步。 + +5.简化微软Active Directory域控、标准LDAP服务器机构和账号管理,密码自助服务重置密码。 + +6.IDaas多租户功能,支持集团下多企业独立管理或企业下不同部门数据隔离的,降低运维成本。 + +7.认证中心具有平台无关性、环境多样性,支持Web、手机、移动设备等, 如Apple iOS,Andriod等,将认证能力从B/S到移动应用全面覆盖。 + +8.配置化的密码策略、访问策略;支持Ip2region或GeoLite2地理库精准IP定位 ,强大安全审计,对用户全生命周期审计、访问行为记录追溯审计、安全合规审计、安全风险预警。 + +9.基于Java EE平台,微服务架构,采用Spring、MySQL、Tomcat、Redis、MQ等开源技术,扩展性强。 + +10.开源、安全、合规、自主可控,许可证 Apache 2.0 License https://www.maxkey.top/zh/about/licenses.html。 + +# 界面 + +![](/assets/img/news/MaxKey-4.1.8-1.png) + +# 下载 + +当前版本网盘下载 + + + +# 版本说明 + +MaxKey v 4.1.8 GA 2025/08/01 + +``` +    *(MAXKEY-250401) 二次认证 +    *(MAXKEY-250402) MaxKeyOpenApiApplication name MaxKeyApiApplication +    *(MAXKEY-250403) 缓存优化 +    *(MAXKEY-250404) maxkey-core 拆分 maxkey-entity maxkey-cache +    *(MAXKEY-250405) maxkey-common 拆分maxkey-crypto、maxkey-ldap & maxkey-commons  +    *(MAXKEY-250406) 缓存验证码过期异常问题 +    *(MAXKEY-250407) 二次认证的配置 +    *(MAXKEY-250408) /functionList 获取应用功能权限清单 +    *(MAXKEY-250409) #IBY0OL 长时间未操作,验证码无法登录 +    *(MAXKEY-250410) 用户和组织的排序优化 +    *(MAXKEY-250411) 密码修改入口优化 +    *(MAXKEY-250412) Groups.service.ts rename groups.service.ts +    *(MAXKEY-250413) 代码整合和优化 +    *(MAXKEY-250414) 开源之夏任务讲解和具体事项说明 +    *(MAXKEY-250415) angular.json optimize +    *(MAXKEY-250416) 依赖项引用、更新和升级 +            springVersion                   6.2.9 +            springBootVersion               3.4.8 +            springSecurityVersion           6.5.2 +            springDataVersion               3.5.2 +            springkafkaVersion              3.3.8 +            tomcatVersion                   10.1.43 +            jacksonVersion                  2.18.4 +``` \ No newline at end of file diff --git a/src/zh/news/Mayfly-Go-1.10.0.md b/src/zh/news/Mayfly-Go-1.10.0.md new file mode 100644 index 0000000000..233d9d74e7 --- /dev/null +++ b/src/zh/news/Mayfly-Go-1.10.0.md @@ -0,0 +1,93 @@ +--- +title: Mayfly-Go 1.10.0 新版本发布,统一IT管控平台 +author: mayfly go +date: 2025-05-30 +cover: /assets/img/news/Mayfly-Go-1.10.0-0.jpg +head: + - - meta + - name: 新闻 +--- + +## **简介** + +Web版 统一管理操作平台,集成了对Linux系统的全面操作支持(包括终端管理\[终端回放、命令过滤\]、文件管理、脚本执行、进程监控及计划任务设置),同时提供了多种数据库(如 MySQL、PostgreSQL、Oracle、SQL Server、达梦、高斯、SQLite 等)的数据操作、数据同步与数据迁移功能。此外,还支持 Redis(单机、哨兵、集群模式)以及 MongoDB、ES 的操作管理,并结合工单流程审批功能,为企业提供一站式的运维与管理解决方案。 + +![](/assets/img/news/Mayfly-Go-1.10.0-0.jpg) + +## **功能介绍** + +* **linux**: ssh终端(终端操作记录回放),文件查看(可根据常见后缀名高亮显示关键词等)、修改、上传、下载、删除等,脚本管理执行,计划任务、进程操作,运行状态查看等(可当做堡垒机使用)。 + +* **dbms(目前支持****mysql postgres oracle sqlserver sqlite 高斯 达梦 kingbase vastbase**。**)**: 可视化数据增删改查,sql语句提示,表信息、索引信息、建表语句查看,建表等(类似mini版navicat)。 + +* **dbms-数据同步**: 支持异构数据库之间数据同步 + +* **dbms-数据库迁移**: 支持异构数据库迁移(迁移至异构数据库文件等) + +* **redis(单机、哨兵、集群)**: 增删改查redis数据,redis基本信息查看,如版本,内存,cpu等使用情况、集群信息节点查看。 + +* **mongo**: 增删改查mongo文档数据,数据库、集合状态查看,新建删除集合等。 + +* **es**: 增删改查elasticsearch 数据,状态查看,索引操作等。 + +* **支持ssh tunnel访问**: linux机器、数据库、redis、mongo、es都支持ssh隧道访问操作。 + +* **支持工单流程审批**: Dbms、Redis等写入类相关危险操作支持工单流程审批执行。 + +* **系统管理**: 同时拥有完善的账号、角色、资源权限控制、系统配置(oauth2、ldap登录、登录验证码、双因素校验、水印等),也可基于该项目进行二次开发作为后台管理系统。 + + +## **项目信息** + +项目文档: https://www.yuque.com/may-fly/mayfly-go + +gitee: https://gitee.com/dromara/mayfly-go + +github: https://github.com/dromara/mayfly-go + +## **开发语言&主要框架** + +* 前端:typescript、 vue3、 element-plus + +* 后端:golang、 gin、 gorm + + +## **特点** + +* 对前后端进行了大部分通用功能的封装,使用起来更加简洁,功能逻辑清晰,能快速上手学习开发,并进行二次开发或者用于后台管理系统。 + +* 项目使用的Go语言开发,使用更小的内存及资源运行更高效的应用,二进制文件部署,方便快捷。 + + +## **升级** + +* 🌟 新增支持ES相关操作 + +* 🌟 工单流程优化,支持或签/会签、流程图设计等 + +* 🌟 前后端依赖组件升级、部分代码重构优化 + +* 🐞 若干问题修复 + + +## ES相关操作 + + + +![](/assets/img/news/Mayfly-Go-1.10.0-1.png) + + + + + +![](/assets/img/news/Mayfly-Go-1.10.0-2.png) + + + +## 工单流程优化 + + + +![](/assets/img/news/Mayfly-Go-1.10.0-3.png) + +![](/assets/img/news/Mayfly-Go-1.10.0-4.png) \ No newline at end of file diff --git a/src/zh/news/Mayfly-Go-1.9.0.md b/src/zh/news/Mayfly-Go-1.9.0.md new file mode 100644 index 0000000000..3b38a42bce --- /dev/null +++ b/src/zh/news/Mayfly-Go-1.9.0.md @@ -0,0 +1,103 @@ +--- +title: Mayfly-Go 1.9.0 新版本发布,统一IT管控平台 +author: Mayfly-Go +tag: + - MaxKey +date: 2024-10-25 +cover: /assets/img/news/Mayfly-Go-1.9.0-0.png +head: + - - meta + - name: 新闻 +--- + +## **简介** + +mayfly-go是一个web版linux、数据库、redis、mongo、集工单流程审批于一体的统一管理操作平台,旨在为用户提供统一的操作和管理体验,助力组织实现资源的高效利用和风险管控,提高系统的安全性、合规性、降低风险,同时增强团队的协作和责任感。 + +## **功能介绍** + +* **linux:** ssh终端(终端操作记录回放),文件查看(可根据常见后缀名高亮显示关键词等)、修改、上传、下载、删除等,脚本管理执行,计划任务、进程操作,运行状态查看等(可当做堡垒机使用)。 + +* **dbms(目前支持** **mysql postgres oracle sqlserver sqlite 高斯 达梦 kingbase vastbase。** **):** 可视化数据增删改查,sql语句提示,表信息、索引信息、建表语句查看,建表等(类似mini版navicat)。 + +* **dbms-数据同步:** 支持异构数据库之间数据同步 + +* **dbms-数据库迁移:** 支持异构数据库迁移(迁移至异构数据库文件等) + +* **redis(单机、哨兵、集群):** 增删改查redis数据,redis基本信息查看,如版本,内存,cpu等使用情况、集群信息节点查看。 + +* **mongo:** 增删改查mongo文档数据,数据库、集合状态查看,新建删除集合等。 + +* **支持ssh tunnel访问:** linux机器、数据库、redis、mongo都支持ssh隧道访问操作。 + +* **支持工单流程审批:** Dbms、Redis等写入类相关危险操作支持工单流程审批执行。 + +* **系统管理:** 同时拥有完善的账号、角色、资源权限控制、系统配置(oauth2、ldap登录、登录验证码、双因素校验、水印等),也可基于该项目进行二次开发作为后台管理系统。 + + +## **项目信息** + +项目文档: https://www.yuque.com/may-fly/mayfly-go + +gitee: https://gitee.com/objs/mayfly-go + +github: https://github.com/may-fly/mayfly-go + +## **开发语言&主要框架** + +* 前端:typescript、 vue3、 element-plus + +* 后端:golang、 gin、 gorm + + +## **特点** + +* 对前后端进行了大部分通用功能的封装,使用起来更加简洁,功能逻辑清晰,能快速上手学习开发,并进行二次开发或者用于后台管理系统。 + +* 项目使用的Go语言开发,使用更小的内存及资源运行更高效的应用,二进制文件部署,方便快捷。 + + +## **升级** + +* 🌟 DBMS、机器、redis、mongo编辑表单移除code(编号) + +* 🌟 新增统一文件基础路径配置,移除机器等相关配置中关于文件路径的配置项 + +* 🌟 流程定义支持根据指定条件触发审批操作 + +* 🌟 DBMS、Redis工单申请统一转移至 ”工单流程-我的流程“ 发起 + +* 🌟 DBMS - 数据库迁移支持定时迁移至异构数据库文件(可以当备份与恢复使用) + +* 🌟 DBMS - 查询框支持一次执行多条SQL(多条sql对应多个结果集tab) + +* 🌟 DBMS - sql解析器替换为antlr4自行解析 + +* 🐞 调整sql加密方式为AES,避免使用base64可能被拦截或其他问题 + + +### 新增统一文件基础路径配置 + +![](/assets/img/news/Mayfly-Go-1.9.0-0.png) + +### 流程定义支持根据指定条件触发审批操作 + +![](/assets/img/news/Mayfly-Go-1.9.0-1.png) + +### DBMS、Redis工单申请统一转移至 ”工单流程-我的流程“ 发起 + +![](/assets/img/news/Mayfly-Go-1.9.0-2.png) + +### DBMS-数据库迁移支持定时迁移至异构数据库文件(可以当备份与恢复使用) + +![](/assets/img/news/Mayfly-Go-1.9.0-3.png) + +![](/assets/img/news/Mayfly-Go-1.9.0-4.png) + +### DBMS-查询框支持一次执行多条SQL(多条sql对应多个结果集tab) + +![](/assets/img/news/Mayfly-Go-1.9.0-5.png) + +Github: https://github.com/dromara/mayfly-go + +Gitee: https://gitee.com/dromara/mayfly-go \ No newline at end of file diff --git a/src/zh/news/Mayfly-Go-1.9.4.md b/src/zh/news/Mayfly-Go-1.9.4.md new file mode 100644 index 0000000000..48851e7364 --- /dev/null +++ b/src/zh/news/Mayfly-Go-1.9.4.md @@ -0,0 +1,72 @@ +--- +title: Mayfly-Go 1.9.4 新版本发布,统一IT管控平台 +author: mayfly-go +date: 2025-04-17 +cover: /assets/img/news/Mayfly-Go-1.9.4-0.png +head: + - - meta + - name: 新闻 +--- + +## **简介**  + +Web版 统一管理操作平台,集成了对Linux系统的全面操作支持(包括终端管理\[终端回放、命令过滤\]、文件管理、脚本执行、进程监控及计划任务设置),同时提供了多种数据库(如 MySQL、PostgreSQL、Oracle、SQL Server、达梦、高斯、SQLite 等)的数据操作、数据同步与数据迁移功能。此外,还支持 Redis(单机、哨兵、集群模式)以及 MongoDB 的操作管理,并结合工单流程审批功能,为企业提供一站式的运维与管理解决方案。 + +![](/assets/img/news/Mayfly-Go-1.9.4-0.png) + +## **功能介绍**  + +* **linux**: ssh终端(终端操作记录回放),文件查看(可根据常见后缀名高亮显示关键词等)、修改、上传、下载、删除等,脚本管理执行,计划任务、进程操作,运行状态查看等(可当做堡垒机使用)。 + +* **dbms(目前支持** **mysql postgres oracle sqlserver sqlite 高斯 达梦 kingbase vastbase**。 **)**: 可视化数据增删改查,sql语句提示,表信息、索引信息、建表语句查看,建表等(类似mini版navicat)。 + +* **dbms-数据同步**: 支持异构数据库之间数据同步 + +* **dbms-数据库迁移**: 支持异构数据库迁移(迁移至异构数据库文件等) + +* **redis(单机、哨兵、集群)**: 增删改查redis数据,redis基本信息查看,如版本,内存,cpu等使用情况、集群信息节点查看。 + +* **mongo**: 增删改查mongo文档数据,数据库、集合状态查看,新建删除集合等。 + +* **支持ssh tunnel访问**: linux机器、数据库、redis、mongo都支持ssh隧道访问操作。 + +* **支持工单流程审批**: Dbms、Redis等写入类相关危险操作支持工单流程审批执行。 + +* **系统管理**: 同时拥有完善的账号、角色、资源权限控制、系统配置(oauth2、ldap登录、登录验证码、双因素校验、水印等),也可基于该项目进行二次开发作为后台管理系统。 + + +## **项目信息**  + +项目文档: https://www.yuque.com/may-fly/mayfly-go + +gitee: https://gitee.com/dromara/mayfly-go + +github: https://github.com/dromara/mayfly-go + +## **开发语言&主要框架**  + +* 前端:typescript、 vue3、 element-plus + +* 后端:golang、 gin、 gorm + + +## **特点**  + +* 对前后端进行了大部分通用功能的封装,使用起来更加简洁,功能逻辑清晰,能快速上手学习开发,并进行二次开发或者用于后台管理系统。 + +* 项目使用的Go语言开发,使用更小的内存及资源运行更高效的应用,二进制文件部署,方便快捷。 + + +## **升级**  + +* 🌟 新增消息渠道管理。支持邮件、钉钉机器人、企业微信机器人、飞书机器人通知 + +* 🌟 新增消息模板管理。支持txt、markdown、html模板设置 + +* 🌟 流程定义支持关联消息模板用于工单审批消息的通知 + +* 🌟 前后端依赖组件升级、部分代码优化 + +* 🐞 数据库迁移longblob转为带长度的longblog修复 + +* 🐞 postgres decimal类型前端展示精度丢失问题修复 \ No newline at end of file diff --git a/src/zh/news/MayflyGo-1.10.2.md b/src/zh/news/MayflyGo-1.10.2.md new file mode 100644 index 0000000000..57a7696340 --- /dev/null +++ b/src/zh/news/MayflyGo-1.10.2.md @@ -0,0 +1,101 @@ +--- +title: mayfly-go 1.10.2新版本发布,统一IT管控平台 +author: mayfly-go +date: 2025-09-02 +cover: /assets/img/news/MayflyGo-1.10.2-0.png +head: + - - meta + - name: 新闻 +--- + +## **简介** + +Web 版统一运维管理平台,全面支持 Linux 系统运维(终端管理、文件管理、脚本执行、进程监控等)、容器操作及多种数据库(MySQL、PostgreSQL、Oracle、SQL Server、达梦、高斯、SQLite)的数据操作与迁移。同时提供 Redis(单机/哨兵/集群)、MongoDB、ES 的全生命周期管理,并集成工单审批流程,打造企业一站式 IT 运维解决方案。   + +## **功能介绍** + +* **linux**: ssh 终端(终端操作记录回放),文件查看(可根据常见后缀名高亮显示关键词等)、修改、上传、下载、删除等,脚本管理执行,计划任务、进程操作,运行状态查看等(可当做堡垒机使用)。 + +* **容器操作**: 支持容器/镜像管理等。 + +* **dbms(目前支持****mysql postgres oracle sqlserver sqlite 高斯 达梦 kingbase vastbase**。**)**: 可视化数据增删改查,sql 语句提示,表信息、索引信息、建表语句查看,建表等(类似 mini 版 navicat)。 + +* **dbms-数据同步**: 支持异构数据库之间数据同步 + +* **dbms-数据库迁移**: 支持异构数据库迁移(迁移至异构数据库文件等) + +* **redis(单机、哨兵、集群)**: 增删改查 redis 数据,redis 基本信息查看,如版本,内存,cpu 等使用情况、集群信息节点查看。 + +* **mongo**: 增删改查 mongo 文档数据,数据库、集合状态查看,新建删除集合等。 + +* **es**: 增删改查 elasticsearch 数据,状态查看,索引操作等。 + +* **支持 ssh tunnel 访问**: linux 机器、数据库、redis、mongo、es 都支持 ssh 隧道访问操作。 + +* **支持工单流程审批**: Dbms、Redis 等写入类相关危险操作支持工单流程审批执行。 + +* **系统管理**: 同时拥有完善的账号、角色、资源权限控制、系统配置(oauth2、ldap 登录、登录验证码、双因素校验、水印等),也可基于该项目进行二次开发作为后台管理系统。 + + +## **项目信息** + +项目文档: https://www.yuque.com/may-fly/mayfly-go + +gitee: https://gitee.com/dromara/mayfly-go + +github: https://github.com/dromara/mayfly-go + +## **开发语言&主要框架** + +* 前端:typescript、 vue3、 element-plus + +* 后端:golang、 gin、 gorm + + +## **特点** + +* 模块化设计,前后端通用功能封装完善,代码结构清晰易懂,无论是学习上手还是二次开发都能快速开展,轻松构建专属后台管理系统 + +* 采用 Go 语言开发,具备低内存占用、高运行效率的优势,通过二进制文件一键部署,极大简化了部署流程 + + +## **升级** + +* 🌟 资源操作入口统一 + +* 🌟 初步支持容器操作 + +* 🌟 消息通知模块优化重构 + +* 🐞 优化与修复若干其他问题 + + +## 资源操作入口统一 + + + +![](/assets/img/news/MayflyGo-1.10.2-0.png) + + + + + +![](/assets/img/news/MayflyGo-1.10.2-1.png) + + + +输入图片说明 + +## 初步支持容器操作 + +![](/assets/img/news/MayflyGo-1.10.2-2.png) + +![](/assets/img/news/MayflyGo-1.10.2-3.png) + +![](/assets/img/news/MayflyGo-1.10.2-4.png) + +## 消息通知模块优化重构 + + + +![](/assets/img/news/MayflyGo-1.10.2-5.png) \ No newline at end of file diff --git a/src/zh/news/Mica-MQTT-0.md b/src/zh/news/Mica-MQTT-0.md new file mode 100644 index 0000000000..bf8ce3a4f2 --- /dev/null +++ b/src/zh/news/Mica-MQTT-0.md @@ -0,0 +1,177 @@ +--- +title: Mica-MQTT 正式加入 Dromara 社区开源社区 +author: mica +date: 2024-11-26 +cover: /assets/img/news/Mica-MQTT-0-0.png +head: + - - meta + - name: 新闻 +--- + +# Mica-MQTT 正式加入 Dromara 社区大家庭啦! + +## 一、新的起点 + +感谢 Dromara 社区 `MaxKey` 作者 `达思` 邀请,mica-mqtt 已正式加入了 Dromara 社区。一个人可以走的很快,一群人才能走的更远。相信在 Dromara 社区的指导下,mica-mqtt 会发展得越来越好。 + +Mcia-MQTT 是基于 java aio 实现,开源、简单、易用、低延迟、高性能百万级 java mqtt client 组件和 java mqtt broker 服务。诞生于笔者刚转入物联网,需要对 mqtt 有个深入的了解,于是诞生造这么一个轮子。通过 mica-mqtt 的开源,笔者也收获不少,当然也踩了不少坑,到 2.2.x 才算开始正真稳定下来(彻底解决编解码问题)。感谢那些积极使用、反馈和贡献的朋友,也是有你们才让 mica-mqtt 变得更加稳定和好用!感谢 `@冷月宫主`、`@willianfu`、`@Symous`、`@hjkJOJO`、`@hongfeng11`、`@胡萝博`、`@一片小雨滴`、`@杨钊`、`@iTong`、`@hpz`、`@tan90`、`@DoubleH`、`@那一刹的容颜`、`@皮球`、`@powerxie`、`@yinyuncan`、`@zkname`、`@彭蕾`、`@litongjava`、`@YYGuo`、`@xiaochonzi`、`@HY`、`@tangjj`、`@peigen` 等谢谢你们!!! + +mica-mqtt **2.4.0-M1** 已经正式发布,该版本将 maven groupId 迁移到了 `org.dromara.mica-mqtt`,将包名切换到了 `org.dromara`,并且切换到 central sonatype(central sonatype 不支持快照版)其他均和老版本保持一致。 + +![](/assets/img/news/Mica-MQTT-0-0.png) + +## 二、功能 + +* 支持 MQTT v3.1、v3.1.1 以及 v5.0 协议。 + +* 支持 websocket mqtt 子协议(支持 mqtt.js)。 + +* 支持 http rest api,`http api 文档详见:` https://gitee.com/dromara/mica-mqtt/blob/master/docs/http-api.md + +* 支持 MQTT client 客户端。 + +* 支持 MQTT server 服务端。 + +* 支持 MQTT 遗嘱消息。 + +* 支持 MQTT 保留消息。 + +* 支持自定义消息(mq)处理转发实现集群。 + +* MQTT 客户端 阿里云 mqtt 连接 demo。 + +* 支持 GraalVM 编译成本机可执行程序。 + +* 支持 Spring boot、Solon 和 JFinal 项目快速接入。 + +* mica-mqtt-spring-boot-starter 支持对接 Prometheus + Grafana。 + +* 基于 redis Stream 实现集群,详见 `mica-mqtt-broker 模块:` https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-broker)。 + + +## 三、使用场景 + +* 物联网(云端 mqtt broker) + +* 物联网(边缘端消息通信) + +* 群组类 IM + +* 消息推送 + +* 简单、易用的 mqtt client 客户端 + + +## 四、快速上手 + +### Spring boot 项目 + +**客户端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-client-spring-boot-starter +  ${mica-mqtt.version} + +``` + +**mica-mqtt-client-spring-boot-starter 使用文档:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-spring-boot-starter/README.md + +**服务端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-server-spring-boot-starter +  ${mica-mqtt.version} + +``` + +**mica-mqtt-server-spring-boot-starter使用文档:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-spring-boot-starter/README.md + +### solon 项目 + +**客户端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-client-solon-plugin +  ${mica-mqtt.version} + +``` + +**mica-mqtt-client-solon-plugin 使用文档:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-solon-plugin/README.md + +**服务端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-server-solon-plugin +  ${mica-mqtt.version} + +``` + +**mica-mqtt-server-solon-plugin 使用文档:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-solon-plugin/README.md + +### JFinal 项目 + +**客户端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-client-jfinal-plugin +  ${mica-mqtt.version} + +``` + +**mica-mqtt-client-jfinal-plugin 使用文档:** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-jfinal-plugin/README.md + +**服务端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-server-jfinal-plugin +  ${mica-mqtt.version} + +``` + +**mica-mqtt-server-jfinal-plugin 使用文档** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-jfinal-plugin/README.md + +### 其他项目 + +**客户端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-client +  ${mica-mqtt.version} + +``` + +**mica-mqtt-client 使用文档**:https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-client/README.md + +**服务端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-server +  ${mica-mqtt.version} + +``` + +**mica-mqtt-server 使用文档:** https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-server/README.md + +## 开源地址 + +* Gitee:https://gitee.com/dromara/mica-mqtt + +* GitHub:https://github.com/dromara/mica-mqtt + +* GitCode:https://gitcode.com/dromara/mica-mqtt \ No newline at end of file diff --git a/src/zh/news/MilvusPlus-v2.2.1.md b/src/zh/news/MilvusPlus-v2.2.1.md new file mode 100644 index 0000000000..c7bd3c317b --- /dev/null +++ b/src/zh/news/MilvusPlus-v2.2.1.md @@ -0,0 +1,53 @@ +--- +title: MilvusPlus向量数据库增强操作库v2.2.1,文本搜索重大更新 +author: Coder建设 +date: 2024-12-02 +cover: /assets/img/news/MilvusPlus-v2.2.1-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/MilvusPlus-v2.2.1-0.png) + +![](/assets/img/news/MilvusPlus-v2.2.1-1.gif) + +🔥🔥🔥MilvusPlus(简称 MP)是一个 Milvus 的操作工具,旨在简化与 Milvus 向量数据库的交互 + +## + + + +版本 + +MilvusPlus v 2.2.1 GA 2024/11/29 + +新增功能:(注意数据库支持版本需要2.5.x) + +1、Sparse-BM25文本搜索 + +2、Tantivy文本匹配 + + + + + +![](/assets/img/news/MilvusPlus-v2.2.1-2.png) + + + +![](/assets/img/news/MilvusPlus-v2.2.1-3.png) + + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/MilvusPlus-v2.2.1-4.png) \ No newline at end of file diff --git a/src/zh/news/Neutrino-Proxy-1.9.0.md b/src/zh/news/Neutrino-Proxy-1.9.0.md index 3c609b3da6..a31432717c 100644 --- a/src/zh/news/Neutrino-Proxy-1.9.0.md +++ b/src/zh/news/Neutrino-Proxy-1.9.0.md @@ -1,174 +1,174 @@ ---- -title: 内网穿透神器NeutrinoProxy 1.9.0版本发布 -author: NeutrinoProxy -date: 2023-09-25 -cover: /assets/img/news/Neutrino-Proxy-1.9.0-1.png -head: - - - meta - - name: 新闻 ---- - -## 内网穿透神器 NeutrinoProxy 1.9.0 版本发布 - -## 更新内容 - -> 与 1.8.0 版本对比,更新内容如下: - -- 核心功能 - -- 域名映射支持 HTTPS -- 支持 UDP 协议代理 -- 客户端断开连接时,记录日志空指针异常问题修复 - -- 基础优化 - -- 客户端重连逻辑优化,支持配置文件指定重连间隔,是否开启无限重连 -- 增加对 mariadb 的支持 -- 服务端/客户端,支持配置文件、启动参数指定日志级别 -- 后台端口池管理支持批量删除 -- 后台下拉选择 license、用户支持模糊搜索 -- 端口映射下拉选择端口支持搜索、分野 -- 客户端/服务端配置增加心跳日志开关,有需要时开启,方便排查问题 -- 端口映射 HTTP(S)新增打开网页快捷操作,优先使用绑定域名打开 - -## 升级须知 - -> 从 1.9.0 之前的版本升级需要注意: - -- 在项目`neutrino-proxy-server/src/main/resources/sql`目录下找到 mysql/sqlite 的增量 sql 文件`UPDATE-20230922.SQL`,执行更新自己的中微子代理数据库 -- 参照官网服务端配置调整现有的服务端配置 -- 参照官网客户端配置吊证现有的客户端配置 -- 从仓库发行版下载最新版本的客户端(neutrino-proxy-client.jar)、服务端(neutrino-proxy-server.jar、 neutrino-proxy-admin.zip)部署文件。若为服务端是 docker 镜像部署,则更新镜像重启即可 - -## 项目简介 - -- 中微子代理(neutrino-proxy) 是一款基于 netty 的内网穿透神器。该项目采用最为宽松的 MIT 协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。 -- Gitee 地址:https://gitee.com/dromara/neutrino-proxy -- 官网地址:http://neutrino-proxy.dromara.org -- 服务端管理后台截图: - -![](/assets/img/news/Neutrino-Proxy-1.9.0-1.png) - -## 主要特点: - -- 1、流量监控:首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。 -- 2、用户/License:支持多用户、多客户端使用。后台禁用实时生效。 -- 3、端口池:对外端口统一管理,支持用户、License 独占端口。 -- 4、端口映射:新增、编辑、删除、禁用实时生效。 -- 5、Docker:服务端支持 Docker 一键部署。 -- 6、SSL 证书:支持 SSL,保护您的信息安全。 -- 7、域名映射:支持绑定子域名,方便本地调试三方回调 -- 8、多协议:支持代理 TCP、HTTP(S)、UDP 多种协议 -- 9、采用最为宽松的 MIT 协议,免去你的后顾之忧 - -## 快速使用 - -> 更多使用姿势、细节请通过官网或结尾微信二维码加我备注"中微子代理"入群交流。 - -### 1、 部署服务端 - -#### 1.1、 Docker 一键部署 - -> 当前最新版本为 1.9.0,下面的脚本中,可以使用:`registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.9.0` 指定版本安装,推荐使用`latest`直接安装最新版。 - -##### 使用默认 sqlite 数据库 - -``` -docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 \ --d --restart=always --name neutrino-proxy \ -registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest -``` - -##### 指定自己的 mysql 数据库 - -- 在服务器上创建目录:/root/neutrino-proxy/config -- 在该目录下创建`app.yml`文本文件,并配置如下内容: - -``` -neutrino: - data: - db: - type: mysql - # 自己的数据库实例,创建一个空的名为'neutrino-proxy'的数据库即可,首次启动服务端会自动初始化 - url: jdbc:mysql://xxxx:3306/neutrino-proxy?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false - driver-class: com.mysql.jdbc.Driver - # 数据库帐号 - username: xxx - # 数据库密码 - password: xxx -``` - -- 然后执行如下命令: - -``` -docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 \ --v /root/neutrino-proxy/config:/root/neutrino-proxy/config \ --d --restart=always --name neutrino \ -registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest -``` - -#### 1.2、使用 jar 包自行部署 - -- 首先确保服务器上已安装 java8 运行环境 -- 打开发行版页面,下载最新的 release 包:`neutrino-proxy-server.jar`、`neutrino-proxy-admin.zip` -- 在服务器上新建部署目录:`/work/projects/neutrino-proxy-server` -- 将 `neutrino-proxy-server.jar`、`neutrino-proxy-admin.zip`上传至服务器部署目录。 -- 解压`neutrino-proxy-admin.zip`文件 -- 执行命令`java -jar neutrino-proxy-server.jar`启动服务端完成部署,默认使用 sqlite 数据库。 -- 若需要指定自己的 mysql 数据库,同样的需要在当前目录下新建`app.yml`文件,文件内容同上。执行命令`java -jar neutrino-proxy-server.jar config=app.yml`启动服务端完成部署 -- 可参照 https://gitee.com/dromara/neutrino-proxy/blob/master/bin/server\_start.sh 使用 shell 脚本启动服务端。 - -### 2、管理后台配置 - -- 服务端部署成功后,访问`http://{服务端IP}:8888`打开后台管理页面。 -- 使用默认的管理员帐号登录:admin/123456 -- 打开`代理配置>License管理`页面,可以看到系统已经自动为管理员初始化了一条 License 记录,复制该`LicenseKey`备用,后续客户端配置需要。 -- 打开`代理配置>端口映射`页面,可以看到系统已经自动为初始化了几条端口映射。可根据需要自行添加、修改。这里我们以`9101 -> 127.0.0.1:8080`映射为例 - -### 3、启动客户端 - -- 首先确保本地已安装 java8 运行环境 -- 打开发行版页面,下载最新的 release 包:`neutrino-proxy-client.jar` -- 在本地`neutrino-proxy-client.jar`同级别目录下新建`app.yml`文件,并配置如下内容: - -``` -neutrino: - proxy: - tunnel: - # ssl证书密钥(使用jjar包内自带的证书,则此处无需修改) - key-store-password: 123456 - # ssl证书管理密钥(使用jjar包内自带的证书,则此处无需修改。自定义证书,则此处配置对应的路径) - jks-path: classpath:/test.jks - # 代理服务端IP - server-ip: localhost - # 代理服务端IP, 若是非ssl端口,则ssl-enable需要配置为false - server-port: 9002 - # 是否启用ssl - ssl-enable: true - # licenseKey,客户端凭证。此处需要配置刚刚从管理后台复制的LicenseKey - license-key: xxxx -``` - -- 执行命令`java -jar neutrino-proxy-client.jar`启动客户端 -- 查看服务端 License 管理,刷新页面,对应的 License 在线状态为`在线`,则表明客户端已正常连接。 - -### 4、代理验证 - -- 本地启动被代理服务,如:redis、本地 web 项目、本地 mysql 等等 -- 先确保本地能正常访问被代理服务,如果本地都不能访问,不用想代理更不可能!!! -- 通过服务端 IP+9101(上面 License 配置的端口映射重的服务端端口)访问本地被代理服务 - -## 联系我们 - -笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。 - -对项目有什么想法或者建议,可以加我微信拉交流群,或者创建 issues,一起完善项目 - -- 微信号:yuyunshize -- Email: aoshiguchen@dromara.org -- 中微子代理官网:http://neutrino-proxy.dromara.org -- 中微子代理仓库:https://gitee.com/dromara/neutrino-proxy - - 微信二维码: - - +--- +title: 内网穿透神器NeutrinoProxy 1.9.0版本发布 +author: NeutrinoProxy +date: 2023-09-25 +cover: /assets/img/news/Neutrino-Proxy-1.9.0-1.png +head: + - - meta + - name: 新闻 +--- + +## 内网穿透神器 NeutrinoProxy 1.9.0 版本发布 + +## 更新内容 + +> 与 1.8.0 版本对比,更新内容如下: + +- 核心功能 + +- 域名映射支持 HTTPS +- 支持 UDP 协议代理 +- 客户端断开连接时,记录日志空指针异常问题修复 + +- 基础优化 + +- 客户端重连逻辑优化,支持配置文件指定重连间隔,是否开启无限重连 +- 增加对 mariadb 的支持 +- 服务端/客户端,支持配置文件、启动参数指定日志级别 +- 后台端口池管理支持批量删除 +- 后台下拉选择 license、用户支持模糊搜索 +- 端口映射下拉选择端口支持搜索、分野 +- 客户端/服务端配置增加心跳日志开关,有需要时开启,方便排查问题 +- 端口映射 HTTP(S)新增打开网页快捷操作,优先使用绑定域名打开 + +## 升级须知 + +> 从 1.9.0 之前的版本升级需要注意: + +- 在项目`neutrino-proxy-server/src/main/resources/sql`目录下找到 mysql/sqlite 的增量 sql 文件`UPDATE-20230922.SQL`,执行更新自己的中微子代理数据库 +- 参照官网服务端配置调整现有的服务端配置 +- 参照官网客户端配置吊证现有的客户端配置 +- 从仓库发行版下载最新版本的客户端(neutrino-proxy-client.jar)、服务端(neutrino-proxy-server.jar、 neutrino-proxy-admin.zip)部署文件。若为服务端是 docker 镜像部署,则更新镜像重启即可 + +## 项目简介 + +- 中微子代理(neutrino-proxy) 是一款基于 netty 的内网穿透神器。该项目采用最为宽松的 MIT 协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。 +- Gitee 地址:https://gitee.com/dromara/neutrino-proxy +- 官网地址:http://neutrino-proxy.dromara.org +- 服务端管理后台截图: + +![](/assets/img/news/Neutrino-Proxy-1.9.0-1.png) + +## 主要特点: + +- 1、流量监控:首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。 +- 2、用户/License:支持多用户、多客户端使用。后台禁用实时生效。 +- 3、端口池:对外端口统一管理,支持用户、License 独占端口。 +- 4、端口映射:新增、编辑、删除、禁用实时生效。 +- 5、Docker:服务端支持 Docker 一键部署。 +- 6、SSL 证书:支持 SSL,保护您的信息安全。 +- 7、域名映射:支持绑定子域名,方便本地调试三方回调 +- 8、多协议:支持代理 TCP、HTTP(S)、UDP 多种协议 +- 9、采用最为宽松的 MIT 协议,免去你的后顾之忧 + +## 快速使用 + +> 更多使用姿势、细节请通过官网或结尾微信二维码加我备注"中微子代理"入群交流。 + +### 1、 部署服务端 + +#### 1.1、 Docker 一键部署 + +> 当前最新版本为 1.9.0,下面的脚本中,可以使用:`registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.9.0` 指定版本安装,推荐使用`latest`直接安装最新版。 + +##### 使用默认 sqlite 数据库 + +``` +docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 \ +-d --restart=always --name neutrino-proxy \ +registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest +``` + +##### 指定自己的 mysql 数据库 + +- 在服务器上创建目录:/root/neutrino-proxy/config +- 在该目录下创建`app.yml`文本文件,并配置如下内容: + +``` +neutrino: + data: + db: + type: mysql + # 自己的数据库实例,创建一个空的名为'neutrino-proxy'的数据库即可,首次启动服务端会自动初始化 + url: jdbc:mysql://xxxx:3306/neutrino-proxy?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false + driver-class: com.mysql.jdbc.Driver + # 数据库帐号 + username: xxx + # 数据库密码 + password: xxx +``` + +- 然后执行如下命令: + +``` +docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 \ +-v /root/neutrino-proxy/config:/root/neutrino-proxy/config \ +-d --restart=always --name neutrino \ +registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest +``` + +#### 1.2、使用 jar 包自行部署 + +- 首先确保服务器上已安装 java8 运行环境 +- 打开发行版页面,下载最新的 release 包:`neutrino-proxy-server.jar`、`neutrino-proxy-admin.zip` +- 在服务器上新建部署目录:`/work/projects/neutrino-proxy-server` +- 将 `neutrino-proxy-server.jar`、`neutrino-proxy-admin.zip`上传至服务器部署目录。 +- 解压`neutrino-proxy-admin.zip`文件 +- 执行命令`java -jar neutrino-proxy-server.jar`启动服务端完成部署,默认使用 sqlite 数据库。 +- 若需要指定自己的 mysql 数据库,同样的需要在当前目录下新建`app.yml`文件,文件内容同上。执行命令`java -jar neutrino-proxy-server.jar config=app.yml`启动服务端完成部署 +- 可参照 https://gitee.com/dromara/neutrino-proxy/blob/master/bin/server\_start.sh 使用 shell 脚本启动服务端。 + +### 2、管理后台配置 + +- 服务端部署成功后,访问`http://{服务端IP}:8888`打开后台管理页面。 +- 使用默认的管理员帐号登录:admin/123456 +- 打开`代理配置>License管理`页面,可以看到系统已经自动为管理员初始化了一条 License 记录,复制该`LicenseKey`备用,后续客户端配置需要。 +- 打开`代理配置>端口映射`页面,可以看到系统已经自动为初始化了几条端口映射。可根据需要自行添加、修改。这里我们以`9101 -> 127.0.0.1:8080`映射为例 + +### 3、启动客户端 + +- 首先确保本地已安装 java8 运行环境 +- 打开发行版页面,下载最新的 release 包:`neutrino-proxy-client.jar` +- 在本地`neutrino-proxy-client.jar`同级别目录下新建`app.yml`文件,并配置如下内容: + +``` +neutrino: + proxy: + tunnel: + # ssl证书密钥(使用jjar包内自带的证书,则此处无需修改) + key-store-password: 123456 + # ssl证书管理密钥(使用jjar包内自带的证书,则此处无需修改。自定义证书,则此处配置对应的路径) + jks-path: classpath:/test.jks + # 代理服务端IP + server-ip: localhost + # 代理服务端IP, 若是非ssl端口,则ssl-enable需要配置为false + server-port: 9002 + # 是否启用ssl + ssl-enable: true + # licenseKey,客户端凭证。此处需要配置刚刚从管理后台复制的LicenseKey + license-key: xxxx +``` + +- 执行命令`java -jar neutrino-proxy-client.jar`启动客户端 +- 查看服务端 License 管理,刷新页面,对应的 License 在线状态为`在线`,则表明客户端已正常连接。 + +### 4、代理验证 + +- 本地启动被代理服务,如:redis、本地 web 项目、本地 mysql 等等 +- 先确保本地能正常访问被代理服务,如果本地都不能访问,不用想代理更不可能!!! +- 通过服务端 IP+9101(上面 License 配置的端口映射重的服务端端口)访问本地被代理服务 + +## 联系我们 + +笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。 + +对项目有什么想法或者建议,可以加我微信拉交流群,或者创建 issues,一起完善项目 + +- 微信号:yuyunshize +- Email: aoshiguchen@dromara.org +- 中微子代理官网:http://neutrino-proxy.dromara.org +- 中微子代理仓库:https://gitee.com/dromara/neutrino-proxy + + 微信二维码: + + diff --git a/src/zh/news/Neutrino-Proxy-2.0.0.md b/src/zh/news/Neutrino-Proxy-2.0.0.md index a1a3b6ff90..cb4164f196 100644 --- a/src/zh/news/Neutrino-Proxy-2.0.0.md +++ b/src/zh/news/Neutrino-Proxy-2.0.0.md @@ -1,83 +1,83 @@ ---- -title: 内网穿透神器-中微子代理(NeutrinoProxy)2.0.0重磅发布 -author: NeutrinoProxy -date: 2023-10-30 -cover: /assets/img/news/Neutrino-Proxy-1.9.0-1.png -head: - - - meta - - name: 新闻 ---- - -## 中微子代理(NeutrinoProxy)2.0.0 重磅发布 - -## 项目简介 - -- 中微子代理(neutrino-proxy) 是一款基于 netty 的内网穿透神器。该项目采用最为宽松的 MIT 协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。 -- 市面上基于内网穿透的常见产品有:花生壳、TeamView、cpolar 等。 -- 常见的使用场景: - -- 本地开发调试第三方回调 -- 本地开发异地接口连调 -- 远程登录内网 windows 机器 -- 将本地服务映射到外网,用于演示 - -- Gitee 仓库:https://gitee.com/dromara/neutrino-proxy -- Github 仓库:https://github.com/dromara/neutrino-proxy -- 官网地址 1: https://neutrino-proxy.dromara.org -- 官网地址 2: https://dromara.gitee.io/neutrino-proxy - -## 主要特点: - -- 1、流量监控:首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。 -- 2、用户/License:支持多用户、多客户端使用。后台禁用实时生效。 -- 3、端口池:对外端口统一管理,支持用户、License 独占端口。 -- 4、端口映射:新增、编辑、删除、禁用实时生效。 -- 5、Docker:服务端/客户端支持 Docker 一键部署。 -- 6、SSL 证书:隧道通信支持 SSL 加密,保护您的数据安全。 -- 7、域名映射:支持绑定子域名,方便本地调试三方回调 -- 8、多种协议:支持代理 TCP、HTTP、HTTPS、UDP 协议 -- 9、原生部署:支持编译为原生可执行文件,更低部署门槛、更少内存占用 -- 10、采用最为宽松的 MIT 协议,免去你的后顾之忧 - -## 本次更新内容 - -- solon 版本升级为`2.5.12-M1` -- jdk 版本升级为 17 -- 支持原生编译改造 -- 默认支持的数据库由 sqlite 改为 h2 -- 官方默认使用的 docker 镜像仓库从阿里云改为 dockerhub - -## 安装使用说明 - -- 快速上手:点击这里 -- 升级须知: - -- jdk 版本升级为了 jdk17,jar 部署时请注意 -- 去掉了默认的 sqlite 数据库,改为了 h2。如果之前使用 sqlite,请自行处理数据迁移 -- 配置文件做了较大调整,请参照官网使用须知中的`服务端配置`、`客户端配置`进行更新 - -## 运行示例 - -#### 本地原生启动截图 - -![](/assets/img/news/Neutrino-Proxy-2.0.0-1.png) - -#### 管理后台首页 - -![](/assets/img/news/Neutrino-Proxy-1.9.0-1.png) - -#### 端口映射 - -![](/assets/img/news/Neutrino-Proxy-2.0.0-2.png) - -## 联系我们 - -笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。 - -对项目有什么想法或者建议,可以加我微信拉交流群,或者创建 issues,一起完善项目 - -- 微信号:yuyunshize -- Email: aoshiguchen@dromara.org -- 微信二维码(添加时请备注"中微子进群"): - - +--- +title: 内网穿透神器-中微子代理(NeutrinoProxy)2.0.0重磅发布 +author: NeutrinoProxy +date: 2023-10-30 +cover: /assets/img/news/Neutrino-Proxy-1.9.0-1.png +head: + - - meta + - name: 新闻 +--- + +## 中微子代理(NeutrinoProxy)2.0.0 重磅发布 + +## 项目简介 + +- 中微子代理(neutrino-proxy) 是一款基于 netty 的内网穿透神器。该项目采用最为宽松的 MIT 协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。 +- 市面上基于内网穿透的常见产品有:花生壳、TeamView、cpolar 等。 +- 常见的使用场景: + +- 本地开发调试第三方回调 +- 本地开发异地接口连调 +- 远程登录内网 windows 机器 +- 将本地服务映射到外网,用于演示 + +- Gitee 仓库:https://gitee.com/dromara/neutrino-proxy +- Github 仓库:https://github.com/dromara/neutrino-proxy +- 官网地址 1: https://neutrino-proxy.dromara.org +- 官网地址 2: https://dromara.gitee.io/neutrino-proxy + +## 主要特点: + +- 1、流量监控:首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。 +- 2、用户/License:支持多用户、多客户端使用。后台禁用实时生效。 +- 3、端口池:对外端口统一管理,支持用户、License 独占端口。 +- 4、端口映射:新增、编辑、删除、禁用实时生效。 +- 5、Docker:服务端/客户端支持 Docker 一键部署。 +- 6、SSL 证书:隧道通信支持 SSL 加密,保护您的数据安全。 +- 7、域名映射:支持绑定子域名,方便本地调试三方回调 +- 8、多种协议:支持代理 TCP、HTTP、HTTPS、UDP 协议 +- 9、原生部署:支持编译为原生可执行文件,更低部署门槛、更少内存占用 +- 10、采用最为宽松的 MIT 协议,免去你的后顾之忧 + +## 本次更新内容 + +- solon 版本升级为`2.5.12-M1` +- jdk 版本升级为 17 +- 支持原生编译改造 +- 默认支持的数据库由 sqlite 改为 h2 +- 官方默认使用的 docker 镜像仓库从阿里云改为 dockerhub + +## 安装使用说明 + +- 快速上手:点击这里 +- 升级须知: + +- jdk 版本升级为了 jdk17,jar 部署时请注意 +- 去掉了默认的 sqlite 数据库,改为了 h2。如果之前使用 sqlite,请自行处理数据迁移 +- 配置文件做了较大调整,请参照官网使用须知中的`服务端配置`、`客户端配置`进行更新 + +## 运行示例 + +#### 本地原生启动截图 + +![](/assets/img/news/Neutrino-Proxy-2.0.0-1.png) + +#### 管理后台首页 + +![](/assets/img/news/Neutrino-Proxy-1.9.0-1.png) + +#### 端口映射 + +![](/assets/img/news/Neutrino-Proxy-2.0.0-2.png) + +## 联系我们 + +笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。 + +对项目有什么想法或者建议,可以加我微信拉交流群,或者创建 issues,一起完善项目 + +- 微信号:yuyunshize +- Email: aoshiguchen@dromara.org +- 微信二维码(添加时请备注"中微子进群"): + + diff --git a/src/zh/news/Neutrino-Proxy-2.0.1.md b/src/zh/news/Neutrino-Proxy-2.0.1.md index 5c3facab74..4209a8d5c5 100644 --- a/src/zh/news/Neutrino-Proxy-2.0.1.md +++ b/src/zh/news/Neutrino-Proxy-2.0.1.md @@ -1,88 +1,88 @@ ---- -title: NeutrinoProxy2.0.1发布,新增IP访问拦截+限速支持 -author: NeutrinoProxy -date: 2023-12-21 -cover: /assets/img/news/Neutrino-Proxy-2.0.1-0.png -head: - - - meta - - name: 新闻 ---- - -# NeutrinoProxy2.0.1 发布,新增 IP 访问拦截+限速支持 - -## 项目简介 - -- 中微子代理(neutrino-proxy) 是一款基于 Solon、Netty 的内网穿透神器。该项目采用最为宽松的 MIT 协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。 -- 市面上基于内网穿透的常见产品有:花生壳、TeamView、cpolar 等。 -- 常见的使用场景: - -- 本地开发调试第三方回调 -- 本地开发异地接口连调 -- 远程登录内网 windows 机器 -- 将本地服务映射到外网,用于演示 - -- Gitee 仓库:https://gitee.com/dromara/neutrino-proxy -- Github 仓库:https://github.com/dromara/neutrino-proxy -- 官网地址 1: https://neutrino-proxy.dromara.org -- 官网地址 2: https://dromara.gitee.io/neutrino-proxy - -## 主要特点: - -- 1、流量监控:首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。 -- 2、用户/License:支持多用户、多客户端使用。后台禁用实时生效。 -- 3、端口池:对外端口统一管理,支持用户、License 独占端口。 -- 4、端口映射:新增、编辑、删除、禁用实时生效。 -- 5、Docker:服务端/客户端支持 Docker 一键部署。 -- 6、SSL 证书:隧道通信支持 SSL 加密,保护您的数据安全。 -- 7、域名映射:支持绑定子域名,方便本地调试三方回调 -- 8、多种协议:支持代理 TCP、HTTP、HTTPS、UDP 协议 -- 9、原生部署:支持编译为原生可执行文件,更低部署门槛、更少内存占用 -- 10、安全组:支持黑/白名单 IP 访问限制 -- 11、限速:支持对 License、端口映射限制上传/下载速度 -- 12、采用最为宽松的 MIT 协议,免去你的后顾之忧 - -## 本次更新内容 - -- jdk 版本升级为 21 -- 新增安全组模块,支持黑名单、白名单限制 -- 支持对用户、license 限速 -- 修复 HTTP(S)映射时使用 tcp 端口访问正常,使用域名访问偶现一直 loading 的问题 -- 修复 HTTP(S)映射时使用映射的域名上传文件时,连接中断的问题 - -## 安装使用说明 - -- 快速上手:https://neutrino-proxy.dromara.org/neutrino-proxy/pages/793dcb/ -- 升级须知: - -- jdk 版本升级为了 jdk21,jar 部署时请注意 -- 涉及到表结构变更,执行增量 SQL:https://gitee.com/dromara/neutrino-proxy/blob/master/neutrino-proxy-server/src/main/resources/sql/mysql/update/UPDATE-20231215.SQL - -## 运行示例 - -#### License 限速 - -![](/assets/img/news/Neutrino-Proxy-2.0.1-1.png) - -#### 端口映射限速 - -![](/assets/img/news/Neutrino-Proxy-2.0.1-2.png) - -#### 安全组 - -![](/assets/img/news/Neutrino-Proxy-2.0.1-3.png) - -![](/assets/img/news/Neutrino-Proxy-2.0.1-4.png) - -## - -联系我们 - -笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。 - -对项目有什么想法或者建议,可以加我微信拉交流群,或者创建 issues,一起完善项目 - -- 微信号:yuyunshize -- Email: aoshiguchen@dromara.org -- 微信二维码(添加时请备注"中微子进群"): - - +--- +title: NeutrinoProxy2.0.1发布,新增IP访问拦截+限速支持 +author: NeutrinoProxy +date: 2023-12-21 +cover: /assets/img/news/Neutrino-Proxy-2.0.1-0.png +head: + - - meta + - name: 新闻 +--- + +# NeutrinoProxy2.0.1 发布,新增 IP 访问拦截+限速支持 + +## 项目简介 + +- 中微子代理(neutrino-proxy) 是一款基于 Solon、Netty 的内网穿透神器。该项目采用最为宽松的 MIT 协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。 +- 市面上基于内网穿透的常见产品有:花生壳、TeamView、cpolar 等。 +- 常见的使用场景: + +- 本地开发调试第三方回调 +- 本地开发异地接口连调 +- 远程登录内网 windows 机器 +- 将本地服务映射到外网,用于演示 + +- Gitee 仓库:https://gitee.com/dromara/neutrino-proxy +- Github 仓库:https://github.com/dromara/neutrino-proxy +- 官网地址 1: https://neutrino-proxy.dromara.org +- 官网地址 2: https://dromara.gitee.io/neutrino-proxy + +## 主要特点: + +- 1、流量监控:首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。 +- 2、用户/License:支持多用户、多客户端使用。后台禁用实时生效。 +- 3、端口池:对外端口统一管理,支持用户、License 独占端口。 +- 4、端口映射:新增、编辑、删除、禁用实时生效。 +- 5、Docker:服务端/客户端支持 Docker 一键部署。 +- 6、SSL 证书:隧道通信支持 SSL 加密,保护您的数据安全。 +- 7、域名映射:支持绑定子域名,方便本地调试三方回调 +- 8、多种协议:支持代理 TCP、HTTP、HTTPS、UDP 协议 +- 9、原生部署:支持编译为原生可执行文件,更低部署门槛、更少内存占用 +- 10、安全组:支持黑/白名单 IP 访问限制 +- 11、限速:支持对 License、端口映射限制上传/下载速度 +- 12、采用最为宽松的 MIT 协议,免去你的后顾之忧 + +## 本次更新内容 + +- jdk 版本升级为 21 +- 新增安全组模块,支持黑名单、白名单限制 +- 支持对用户、license 限速 +- 修复 HTTP(S)映射时使用 tcp 端口访问正常,使用域名访问偶现一直 loading 的问题 +- 修复 HTTP(S)映射时使用映射的域名上传文件时,连接中断的问题 + +## 安装使用说明 + +- 快速上手:https://neutrino-proxy.dromara.org/neutrino-proxy/pages/793dcb/ +- 升级须知: + +- jdk 版本升级为了 jdk21,jar 部署时请注意 +- 涉及到表结构变更,执行增量 SQL:https://gitee.com/dromara/neutrino-proxy/blob/master/neutrino-proxy-server/src/main/resources/sql/mysql/update/UPDATE-20231215.SQL + +## 运行示例 + +#### License 限速 + +![](/assets/img/news/Neutrino-Proxy-2.0.1-1.png) + +#### 端口映射限速 + +![](/assets/img/news/Neutrino-Proxy-2.0.1-2.png) + +#### 安全组 + +![](/assets/img/news/Neutrino-Proxy-2.0.1-3.png) + +![](/assets/img/news/Neutrino-Proxy-2.0.1-4.png) + +## + +联系我们 + +笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。 + +对项目有什么想法或者建议,可以加我微信拉交流群,或者创建 issues,一起完善项目 + +- 微信号:yuyunshize +- Email: aoshiguchen@dromara.org +- 微信二维码(添加时请备注"中微子进群"): + + diff --git a/src/zh/news/NeutrinoProxy-2.0.2.md b/src/zh/news/NeutrinoProxy-2.0.2.md new file mode 100644 index 0000000000..62aa1472ff --- /dev/null +++ b/src/zh/news/NeutrinoProxy-2.0.2.md @@ -0,0 +1,178 @@ +--- +title: NeutrinoProxy2.0.2发布,端口映射支持绑定多个域名 +author: neutrino +date: 2024-12-10 +cover: /assets/img/news/NeutrinoProxy-2.0.2-0.png +head: + - - meta + - name: 新闻 +--- + +## 项目简介 + +* 中微子代理(neutrino-proxy) 是一款基于Solon、Netty的内网穿透神器。该项目采用最为宽松的MIT协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。 + +* 市面上基于内网穿透的常见产品有:花生壳、TeamView、cpolar等。 + +* 常见的使用场景: + + +* 本地开发调试第三方回调 + +* 本地开发异地接口连调 + +* 远程登录内网windows机器 + +* 将本地服务映射到外网,用于演示 + + +* GitCode:https://gitcode.com/dromara/neutrino-proxy + +* Gitee仓库:https://gitee.com/dromara/neutrino-proxy + +* Github仓库:https://github.com/dromara/neutrino-proxy + +* 官网地址: https://neutrino-proxy.dromara.org + + + + + +* ![](/assets/img/news/NeutrinoProxy-2.0.2-0.png) + + +## 主要特点: + +* 1、流量监控:首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。 + +* 2、用户/License:支持多用户、多客户端使用。后台禁用实时生效。 + +* 3、端口池:对外端口统一管理,支持用户、License独占端口。 + +* 4、端口映射:新增、编辑、删除、禁用实时生效。 + +* 5、Docker:服务端/客户端支持Docker一键部署。 + +* 6、SSL证书:隧道通信支持SSL加密,保护您的数据安全。 + +* 7、域名映射:支持绑定多个子域名,方便本地调试三方回调 + +* 8、多种协议:支持代理TCP、HTTP、HTTPS、UDP协议 + +* 9、原生部署:支持编译为原生可执行文件,更低部署门槛、更少内存占用 + +* 10、安全组:支持黑/白名单IP访问限制 + +* 11、限速:支持对License、端口映射限制上传/下载速度 + +* 12、采用最为宽松的MIT协议,免去你的后顾之忧 + + +## 本次更新内容 + +* bug修复 + + +* 解决安全组在ipv6场景下误拦截导致域名映射访问不通的问题 + +* 解决native编译后,端口池批量删除报错的问题 + +* 解决客户端docker方式部署环境变量指定参数不生效的问题 + +* 服务端解决native下清理日志定时任务报错的问题 + +* 解决后台分页查询的已知问题 + +* 端口映射中的客户端ip字段加长一点,解决配置阿里云数据库域名字段不够的问题 + +* 解决服务端native部署时,访问后台用户列表,日志警告“you should use: nativeMetadata.registerField(field) at aot runtime”的问题 + + +* 新增功能 + + +* 新增后台域名管理,⽀持新增、修改、删除、禁⽤主域名,支持添加多级域名。 + +* 域名管理支持上传域名对应的SSL证书、⽀持设置强制HTTPS、⽀持域名映射⾃动加载对应的服务器证书。 + +* 端口映射支持选择切换不同的主域名,⽀持绑定多个⼦域名,单个端⼝⽀持绑定多个不同的主域名。 + + +## 安装使用说明 + +* 快速上手:https://neutrino-proxy.dromara.org/neutrino-proxy/pages/793dcb/ + +* 支持的部署方式 + + +* windows + +* linux + +* mac + +* 1、jar + +* 2、docker + +* 3、native + +* 4、docker-compose + + +* 升级须知: + + +* **域名和SSL证书的配置由配置文件配置改为后台域名管理页面配置。** + +* 去掉了配置文件声明服务器端域名和证书的方式,改为通过页面进行管理,请注意自行进行数据迁移。 + +* 数据库新增`域名表`和`域名映射中间表`,修改了`端口映射表`,涉及到表结构变更,执行增量SQL + + +## 运行示例 + +#### License限速 + +![](/assets/img/news/NeutrinoProxy-2.0.2-1.png) + +#### 端口映射限速 + +![](/assets/img/news/NeutrinoProxy-2.0.2-2.png) + +#### 端口映射绑定多个域名 + +![](/assets/img/news/NeutrinoProxy-2.0.2-3.png) + +#### 安全组 + +![](/assets/img/news/NeutrinoProxy-2.0.2-4.png) + +![](/assets/img/news/NeutrinoProxy-2.0.2-5.png) + +## 联系我们 + +笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。 + +对项目有什么想法或者建议,可以加我微信拉交流群,或者创建issues,一起完善项目 + +* 微信号:yuyunshize + +* Email: aoshiguchen@dromara.org + +* 微信二维码(添加时请备注"中微子进群"): + + +![](/assets/img/news/NeutrinoProxy-2.0.2-6.jpg) + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/NeutrinoProxy-2.0.2-7.png) \ No newline at end of file diff --git a/src/zh/news/Northstar-6.0.md b/src/zh/news/Northstar-6.0.md index a92c81283b..43abef3731 100644 --- a/src/zh/news/Northstar-6.0.md +++ b/src/zh/news/Northstar-6.0.md @@ -1,65 +1,65 @@ ---- -title: AI量化平台前传——Northstar盈富量化平台v6.0.Final发布 -author: Kevin黄伟亮 -date: 2023-06-28 -cover: /assets/img/news/Northstar-6.0-7.png -head: - - - meta - - name: 新闻 ---- - -##### **【导读】自从 AlphaGo 问世以来,我一直在思考打造一个可以集成 AI 计算能力的量化交易平台。但交易智能化的前提,是交易的程序化与自动化。只有实现了交易的程序化与自动化,才有可能谈智能化。Northstar 盈富量化平台从 v1.0 至今 v6.0,前前后后摸索了两三年时间,一直在做架构迭代。务求把基础架构打牢,才有可能在智能化应用中得到快速发展。如今 v6.0 正式版经历了一个多月的实盘反复测试,确认了架构的稳定性与可扩展性得到验证。** - -一个量化交易平台的基础需求无非是四个: - -- 策略研发 -- 策略回测 -- 策略模拟运行 -- 策略实盘运行 - -## 策略研发 - -策略研发的关键在于,策略编写的难易度。而策略编写则主要取决于指标框架开发的难易程度与策略逻辑编写的难易程度。 - -先给大伙看看,我生产环境正在跑的策略模组。 - -![](/assets/img/news/Northstar-6.0-1.png) -一个策略从构思到完成编码,大概只需要几天的时间。目前我正在跑的策略有两个。一个策略的代码逻辑大概是三百行。由于 v6.0 的指标框架做了重构,使得现在要开发一个新的指标就像搭乐高一样简单。把复杂的交易逻辑拆分成指标计算与指标操作,再把不同周期的指标操作再封装起来,使得一个复杂的多周期交易逻辑能在三百行左右的。 - -加上平台对指标的可视化处理,能轻松直观地查看指标计算是否正确。 - -![](/assets/img/news/Northstar-6.0-2.gif) - -## 策略回测 - -平台提供了灵活通用的历史回放功能来实现策略回测,从而使得多合约回测变得像看视频一样简单、可控。 - -给大伙看看我那两策略的一些回测绩效曲线。 - -![](/assets/img/news/Northstar-6.0-3.jpg) - -在开发完一个策略后,我还可以编写一个全自动模组回测功能对全市场的品种进行遍历测试,从而找到最合适的品种,大大地提升了工作效率。 - -## 策略模拟运行 - -![](/assets/img/news/Northstar-6.0-4.png) - -模拟盘在很多的交易平台上都是个要收费的功能,但实际上这个功能说白了就是个本地账本。所以盈富平台免费开放出来给大伙使用,让大家能像打游戏一样玩量化。 - -## 策略实盘运行 - -![](/assets/img/news/Northstar-6.0-5.png) - -通过引入不同的实盘网关,可以轻松地实现实盘网关的任意扩展。而引入的方式也是相当直观的。以下便是我服务器上的目录结构。 - -![](/assets/img/news/Northstar-6.0-6.png) - -看完直观的展示,最后来总结一下整个基础架构: - -![](/assets/img/news/Northstar-6.0-7.png) - -也许有人想问,AI 部分在哪里? - -在策略层,我们可以用强化学习来优化策略并生产信号。只能先说这么多了,卖个关子,v6.1 见! - -作者:黄伟亮,Northstar 盈富量化平台作者 +--- +title: AI量化平台前传——Northstar盈富量化平台v6.0.Final发布 +author: Kevin黄伟亮 +date: 2023-06-28 +cover: /assets/img/news/Northstar-6.0-7.png +head: + - - meta + - name: 新闻 +--- + +##### **【导读】自从 AlphaGo 问世以来,我一直在思考打造一个可以集成 AI 计算能力的量化交易平台。但交易智能化的前提,是交易的程序化与自动化。只有实现了交易的程序化与自动化,才有可能谈智能化。Northstar 盈富量化平台从 v1.0 至今 v6.0,前前后后摸索了两三年时间,一直在做架构迭代。务求把基础架构打牢,才有可能在智能化应用中得到快速发展。如今 v6.0 正式版经历了一个多月的实盘反复测试,确认了架构的稳定性与可扩展性得到验证。** + +一个量化交易平台的基础需求无非是四个: + +- 策略研发 +- 策略回测 +- 策略模拟运行 +- 策略实盘运行 + +## 策略研发 + +策略研发的关键在于,策略编写的难易度。而策略编写则主要取决于指标框架开发的难易程度与策略逻辑编写的难易程度。 + +先给大伙看看,我生产环境正在跑的策略模组。 + +![](/assets/img/news/Northstar-6.0-1.png) +一个策略从构思到完成编码,大概只需要几天的时间。目前我正在跑的策略有两个。一个策略的代码逻辑大概是三百行。由于 v6.0 的指标框架做了重构,使得现在要开发一个新的指标就像搭乐高一样简单。把复杂的交易逻辑拆分成指标计算与指标操作,再把不同周期的指标操作再封装起来,使得一个复杂的多周期交易逻辑能在三百行左右的。 + +加上平台对指标的可视化处理,能轻松直观地查看指标计算是否正确。 + +![](/assets/img/news/Northstar-6.0-2.gif) + +## 策略回测 + +平台提供了灵活通用的历史回放功能来实现策略回测,从而使得多合约回测变得像看视频一样简单、可控。 + +给大伙看看我那两策略的一些回测绩效曲线。 + +![](/assets/img/news/Northstar-6.0-3.jpg) + +在开发完一个策略后,我还可以编写一个全自动模组回测功能对全市场的品种进行遍历测试,从而找到最合适的品种,大大地提升了工作效率。 + +## 策略模拟运行 + +![](/assets/img/news/Northstar-6.0-4.png) + +模拟盘在很多的交易平台上都是个要收费的功能,但实际上这个功能说白了就是个本地账本。所以盈富平台免费开放出来给大伙使用,让大家能像打游戏一样玩量化。 + +## 策略实盘运行 + +![](/assets/img/news/Northstar-6.0-5.png) + +通过引入不同的实盘网关,可以轻松地实现实盘网关的任意扩展。而引入的方式也是相当直观的。以下便是我服务器上的目录结构。 + +![](/assets/img/news/Northstar-6.0-6.png) + +看完直观的展示,最后来总结一下整个基础架构: + +![](/assets/img/news/Northstar-6.0-7.png) + +也许有人想问,AI 部分在哪里? + +在策略层,我们可以用强化学习来优化策略并生产信号。只能先说这么多了,卖个关子,v6.1 见! + +作者:黄伟亮,Northstar 盈富量化平台作者 diff --git a/src/zh/news/Northstar-6.1.md b/src/zh/news/Northstar-6.1.md index 33d5231f53..e3292e3e48 100644 --- a/src/zh/news/Northstar-6.1.md +++ b/src/zh/news/Northstar-6.1.md @@ -1,60 +1,60 @@ ---- -title: Northstar盈富量化平台v6.1版本发布 -author: Kevin黄伟亮 -date: 2023-07-27 -cover: /assets/img/news/Northstar-6.1-2.png -head: - - - meta - - name: 新闻 ---- - -##### **【导读】v6.1 版本最大的变化是增加了监控台对移动端的适配,使得用户可以通过手机来监控自动化模组的运行状态。之前提到的策略集成强化学习,会有的,下半年重点就搞这个,敬请期待!** - -### 新增 - -- #I7JL8B:\[功能建议\]: 增加模组图表指标下载功能 - -![](/assets/img/news/Northstar-6.1-1.gif) - -- #I7JLC9:\[功能建议\]: 增加年化收益率统计 - -![](/assets/img/news/Northstar-6.1-2.png) - -- #I7JLEZ:\[功能建议\]: 增加模组回撤统计与盈亏分布图 - -![](/assets/img/news/Northstar-6.1-3.png) - -- #I7JO5A:\[功能建议\]: 增加移动端样式适配 - -![](/assets/img/news/Northstar-6.1-3.png) - -### 优化 - -- 模组列表自动刷新 -- 资金曲线加入手续费扣减 -- 限制模组持仓状态下修改模组配置,避免可能出现的异常问题 -- 避免可能出现的人工干预与模组订单同时出现时导致的下单负手数问题 -- 偶尔可能出现的模组开仓时找不到合约导致的空指针问题 - -### 修复 - -- #I7JHVQ:\[Bug\]: 模组平仓时,若持仓同时存在今仓与昨仓,会出现平仓异常 -- #I7JL3S:\[Bug\]: 模组策略选择框无正确显示的问题 -- #I7KF0N:\[Bug\]: 模组修改分配资金并保存,模组的分配金额没有更新 -- #I7KZWL:\[Bug\]: 开发环境下,当模组没完全加载时,启停全部模组会出现异常的问题 -- #I7KP8T:\[Bug\]: 在 idea 中启动项目有异常抛出 - ---- - -### 更多有意思的课题进行中 - -- 币安,OKX 网关对接 -- 外盘老虎证券网关对接 -- 关于使用 GAN 生成仿真行情数据的研究 -- 关于使用强化学习来进行策略运算的研究 - -欢迎感兴趣的小伙伴加入,一起来探索平台更多的可能性。 - -作者:黄伟亮,Northstar 盈富量化平台作者 - -——   完  —— +--- +title: Northstar盈富量化平台v6.1版本发布 +author: Kevin黄伟亮 +date: 2023-07-27 +cover: /assets/img/news/Northstar-6.1-2.png +head: + - - meta + - name: 新闻 +--- + +##### **【导读】v6.1 版本最大的变化是增加了监控台对移动端的适配,使得用户可以通过手机来监控自动化模组的运行状态。之前提到的策略集成强化学习,会有的,下半年重点就搞这个,敬请期待!** + +### 新增 + +- #I7JL8B:\[功能建议\]: 增加模组图表指标下载功能 + +![](/assets/img/news/Northstar-6.1-1.gif) + +- #I7JLC9:\[功能建议\]: 增加年化收益率统计 + +![](/assets/img/news/Northstar-6.1-2.png) + +- #I7JLEZ:\[功能建议\]: 增加模组回撤统计与盈亏分布图 + +![](/assets/img/news/Northstar-6.1-3.png) + +- #I7JO5A:\[功能建议\]: 增加移动端样式适配 + +![](/assets/img/news/Northstar-6.1-3.png) + +### 优化 + +- 模组列表自动刷新 +- 资金曲线加入手续费扣减 +- 限制模组持仓状态下修改模组配置,避免可能出现的异常问题 +- 避免可能出现的人工干预与模组订单同时出现时导致的下单负手数问题 +- 偶尔可能出现的模组开仓时找不到合约导致的空指针问题 + +### 修复 + +- #I7JHVQ:\[Bug\]: 模组平仓时,若持仓同时存在今仓与昨仓,会出现平仓异常 +- #I7JL3S:\[Bug\]: 模组策略选择框无正确显示的问题 +- #I7KF0N:\[Bug\]: 模组修改分配资金并保存,模组的分配金额没有更新 +- #I7KZWL:\[Bug\]: 开发环境下,当模组没完全加载时,启停全部模组会出现异常的问题 +- #I7KP8T:\[Bug\]: 在 idea 中启动项目有异常抛出 + +--- + +### 更多有意思的课题进行中 + +- 币安,OKX 网关对接 +- 外盘老虎证券网关对接 +- 关于使用 GAN 生成仿真行情数据的研究 +- 关于使用强化学习来进行策略运算的研究 + +欢迎感兴趣的小伙伴加入,一起来探索平台更多的可能性。 + +作者:黄伟亮,Northstar 盈富量化平台作者 + +——   完  —— diff --git a/src/zh/news/Northstar-7.0.md b/src/zh/news/Northstar-7.0.md index e275a051c5..a6a2d40794 100644 --- a/src/zh/news/Northstar-7.0.md +++ b/src/zh/news/Northstar-7.0.md @@ -1,129 +1,129 @@ ---- -title: 量化软件行业的搅局者,northstar 7.0正式版强势登场 -author: Kevin黄伟亮 -date: 2024-02-01 -cover: /assets/img/news/Northstar-7.0-0.png -head: - - - meta - - name: 新闻 ---- - -一起来看看这个搅局者都有哪些本事,敢来叫板一众商业量化软件。 - -## 一站式平台 - -炒外汇的都知道用 MT4、MT5;做国内期货的都知道用文华、MC、开拓者、金字塔;做股票的都听过 PTrade、QMT……每切换一次交易平台,都是一次费时费力的重新学习。让人不禁要问,你们这些平台为什么就不能做成一站式的? - -northstar 作者本人就曾经深受其苦。因此,northstar 从诞生之初就立志要做成一个一站式的平台,誓要让用户重获主导权;而不是让咱们不断地去迎合不同的软件商与交易所。 - -为此,northstar 的总体设计图如下: - -![](/assets/img/news/Northstar-7.0-0.png) - -由图可见,不管是什么交易所,只要它提供了公开的 API 接口,便可以通过在 northstar 实现一个适配器模块对交易所进行接入,从而能实现对任意的交易所进行对接。 - -目前已实现对接的有国内期货 CTP、币安、老虎证券。 - -## 私有化部署 - -除了以上提到的很多客户端商业软件外,如今也有不少是以 SaaS 租户的形式提供着量化服务,例如某宽、某筐之类的。而 SaaS 服务最致命的问题便是交易策略的安全性问题。不管平台如何强调交易策略的部署是安全的,也不如可以私有化部署来得放心。 - -northstar 从基因上就决定了项目的代码由用户绝对可控。部署环节也是全权由用户自行处理。从根本上免除了代码泄漏的风险隐患。 - -为了同时兼顾主程序通用框架的更新迭代,以及用户策略代码的私密性,主程序采用了灵活的可插拔设计。只要用户把策略代码包与主程序包放在同一个目录下运行,主程序便可以自动加载用户策略。整个过程非常简单且自然。(下图中,northstar 为主程序包,northstar-external 为用户策略包) - -![](/assets/img/news/Northstar-7.0-1.png) - -如图所示,不单单是用户的策略代码可以享受这般丝滑的可插拔功能,其余的可扩展模块均能借此来实现更好的扩展性。 - -## 全链路监控 - -一个成熟的量化交易程序必然是部署在服务器上长时间不间断地运行,为此一套全链路的监控手段必不可少。得益于成熟的 web 应用生态,northstar 也打造了一整套监控体系为交易保驾护航。 - -事前,用户可以通过电脑端或移动端的 web 页面来创建自动化交易模组。 - -![](/assets/img/news/Northstar-7.0-2.png) - -上图为电脑端界面 - -![](/assets/img/news/Northstar-7.0-3.png) - -上图为手机端界面 - -事中,可以借助微信或电子邮件,及时接收到交易动态以及程序的异常提示。 - -![](/assets/img/news/Northstar-7.0-4.png) - -事后,可以通过程序日志与模组运行日志分别进行复盘检查。 - -![](/assets/img/news/Northstar-7.0-5.png) - -由此打造出了一套让人放心的自动化交易体系。 - -## 智能化交易 - -智能化交易是下一代量化软件的必备门槛(至少我是这么理解的)。随着人工智能算法的普及,越来越多的交易场景需要应用到深度学习框架。 - -为此,northstar 7.0 把集成人工智能能力作为大版本升级的一大核心功能,借助比较普及的 Tensorflow 框架,使 northstar 可以调用已经训练好的神经网络模型。得益于 northstar 良好的可扩展性设计,要集成人工智能的能力不敢说是易如反掌,至少也是一马平川。 - -![](/assets/img/news/Northstar-7.0-6.png) - -如上图所示,新增的人工智能能力本质上是统一了数据采样的方法,并外挂了一个预训练模型。数据采样方法完全由自定义策略去实现。在训练阶段,通过框架提供的回放引擎驱动采样数据的收集,使 Tensorflow 可以有足够的数据用于训练;在实盘阶段,则预先加载好预训练模型,然后便能轻易地得出神经网络的推理结果。从而实现了人工智能算法对策略的增强。 - -northstar 支持的交易模式包含但不限于 CTA 指标交易、合约价差交易、高频 TICK 算法交易、跨市场套利交易等等。在不同的交易模式上叠加了人工智能算法的加成,可以大大提升交易的胜率。 - -## 低成本运行 - -northstar 光凭以上智能化交易那轻描淡写的功能特性,放市场上随便一款软件,每年的软件使用费没个小一两万块钱基本没戏。更不谦虚地说,要集齐以上四个特性的,几乎没有。而令用户更加惊艳的是,它的使用成本仅仅需要大概不到十分之一的价格。 - -由于 northstar 项目本身是免费的开源项目,相当于软件是可以免费获得的。毕竟软件产品的边际成本几乎为零,各大量化软件厂商的软件费用之所以昂贵,主要是在于营销费用与企业管理费用比较高。正是因为苦于高昂的软件费用,northstar 作者才有了打造出 northstar 的冲动。因此,northstar 在诞生之初就自带草根屠龙属性。正如那句著名的话一样,“从群众中来,到群众中去”。 - -凡事都有两面,northstar 最大的成本便是用户的学习成本。因此,作为 northstar 的用户需要有一定的 JAVA 编程能力,如果要用到 AI 能力时也需要用户具备 AI 工程师同等的技术能力。但站在 northstar 作者的角度看,这些都是作为一名量化交易员的必备技能。因此我相信,但凡是有意向入坑量化交易的用户,都会对自己所面临的挑战有充分的心理预期。 - -除了学习成本外,还有便是那微不足道的服务器成本、数据服务成本等等。 - -northstar 项目作为一个免费开源项目,运营的思路根本不是在卖软件。站在 northstar 作者的角度看,更像是在做一个网红 IP,旨在打造一个草根量化生态社区。欢迎有能力的参与者在社区中提供有价值的付费增值服务,更需要更多的用户支持购买社区的增值服务,以帮助社区发展壮大。 - -## 个性化定制 - -northstar 拥有良好的可扩展性设计,允许用户按需进行自行扩展。 - -社区汇集了众多优秀的开发者,能提供收费的定制服务,欢迎甲方爸爸来体验 VIP 定制服务。 - ---- - -## northstar 7.0 版本升级说明 - -> 介绍完了产品竞争力,是时候该向老粉们仔细交代一下此次 v7.0 升级的几项重大改进了。 - -- **重构了核心类,使内存占用率得以优化** - -为了更好地验证优化效果,我特意做了一个 A/B 测试。针对同一个回测周期的同一批模组进行回测,并观测其内存使用情况。 - -![](/assets/img/news/Northstar-7.0-7.jpg) - -![](/assets/img/news/Northstar-7.0-8.jpg) - -优化前的内存运行情况如下: - -![](/assets/img/news/Northstar-7.0-9.jpg) - -优化后的内存运行情况如下: - -![](/assets/img/news/Northstar-7.0-10.jpg) - -可以明显看出优化后的内存增长速度明显降低,最终占用的物理内存也显著下降。 - -- **升级了 JDK21,引入了虚拟线程** - -对主程序所有涉及 IO 操作的逻辑,都进行了虚拟线程优化。比如保存 K 线数据、发送即时行情等。 - -- 集成了 Tensorflow 深度学习能力 - -如前文提到的,策略可以加载 Tensorflow 的预训练模型,来优化策略的胜率。 - -- **注意事项** - -由于 springboot3 的升级,引发了 H2 数据库升级导致数据不兼容,必须要移除数据文件,或进行数据迁移。 - -—— 完 —— +--- +title: 量化软件行业的搅局者,northstar 7.0正式版强势登场 +author: Kevin黄伟亮 +date: 2024-02-01 +cover: /assets/img/news/Northstar-7.0-0.png +head: + - - meta + - name: 新闻 +--- + +一起来看看这个搅局者都有哪些本事,敢来叫板一众商业量化软件。 + +## 一站式平台 + +炒外汇的都知道用 MT4、MT5;做国内期货的都知道用文华、MC、开拓者、金字塔;做股票的都听过 PTrade、QMT……每切换一次交易平台,都是一次费时费力的重新学习。让人不禁要问,你们这些平台为什么就不能做成一站式的? + +northstar 作者本人就曾经深受其苦。因此,northstar 从诞生之初就立志要做成一个一站式的平台,誓要让用户重获主导权;而不是让咱们不断地去迎合不同的软件商与交易所。 + +为此,northstar 的总体设计图如下: + +![](/assets/img/news/Northstar-7.0-0.png) + +由图可见,不管是什么交易所,只要它提供了公开的 API 接口,便可以通过在 northstar 实现一个适配器模块对交易所进行接入,从而能实现对任意的交易所进行对接。 + +目前已实现对接的有国内期货 CTP、币安、老虎证券。 + +## 私有化部署 + +除了以上提到的很多客户端商业软件外,如今也有不少是以 SaaS 租户的形式提供着量化服务,例如某宽、某筐之类的。而 SaaS 服务最致命的问题便是交易策略的安全性问题。不管平台如何强调交易策略的部署是安全的,也不如可以私有化部署来得放心。 + +northstar 从基因上就决定了项目的代码由用户绝对可控。部署环节也是全权由用户自行处理。从根本上免除了代码泄漏的风险隐患。 + +为了同时兼顾主程序通用框架的更新迭代,以及用户策略代码的私密性,主程序采用了灵活的可插拔设计。只要用户把策略代码包与主程序包放在同一个目录下运行,主程序便可以自动加载用户策略。整个过程非常简单且自然。(下图中,northstar 为主程序包,northstar-external 为用户策略包) + +![](/assets/img/news/Northstar-7.0-1.png) + +如图所示,不单单是用户的策略代码可以享受这般丝滑的可插拔功能,其余的可扩展模块均能借此来实现更好的扩展性。 + +## 全链路监控 + +一个成熟的量化交易程序必然是部署在服务器上长时间不间断地运行,为此一套全链路的监控手段必不可少。得益于成熟的 web 应用生态,northstar 也打造了一整套监控体系为交易保驾护航。 + +事前,用户可以通过电脑端或移动端的 web 页面来创建自动化交易模组。 + +![](/assets/img/news/Northstar-7.0-2.png) + +上图为电脑端界面 + +![](/assets/img/news/Northstar-7.0-3.png) + +上图为手机端界面 + +事中,可以借助微信或电子邮件,及时接收到交易动态以及程序的异常提示。 + +![](/assets/img/news/Northstar-7.0-4.png) + +事后,可以通过程序日志与模组运行日志分别进行复盘检查。 + +![](/assets/img/news/Northstar-7.0-5.png) + +由此打造出了一套让人放心的自动化交易体系。 + +## 智能化交易 + +智能化交易是下一代量化软件的必备门槛(至少我是这么理解的)。随着人工智能算法的普及,越来越多的交易场景需要应用到深度学习框架。 + +为此,northstar 7.0 把集成人工智能能力作为大版本升级的一大核心功能,借助比较普及的 Tensorflow 框架,使 northstar 可以调用已经训练好的神经网络模型。得益于 northstar 良好的可扩展性设计,要集成人工智能的能力不敢说是易如反掌,至少也是一马平川。 + +![](/assets/img/news/Northstar-7.0-6.png) + +如上图所示,新增的人工智能能力本质上是统一了数据采样的方法,并外挂了一个预训练模型。数据采样方法完全由自定义策略去实现。在训练阶段,通过框架提供的回放引擎驱动采样数据的收集,使 Tensorflow 可以有足够的数据用于训练;在实盘阶段,则预先加载好预训练模型,然后便能轻易地得出神经网络的推理结果。从而实现了人工智能算法对策略的增强。 + +northstar 支持的交易模式包含但不限于 CTA 指标交易、合约价差交易、高频 TICK 算法交易、跨市场套利交易等等。在不同的交易模式上叠加了人工智能算法的加成,可以大大提升交易的胜率。 + +## 低成本运行 + +northstar 光凭以上智能化交易那轻描淡写的功能特性,放市场上随便一款软件,每年的软件使用费没个小一两万块钱基本没戏。更不谦虚地说,要集齐以上四个特性的,几乎没有。而令用户更加惊艳的是,它的使用成本仅仅需要大概不到十分之一的价格。 + +由于 northstar 项目本身是免费的开源项目,相当于软件是可以免费获得的。毕竟软件产品的边际成本几乎为零,各大量化软件厂商的软件费用之所以昂贵,主要是在于营销费用与企业管理费用比较高。正是因为苦于高昂的软件费用,northstar 作者才有了打造出 northstar 的冲动。因此,northstar 在诞生之初就自带草根屠龙属性。正如那句著名的话一样,“从群众中来,到群众中去”。 + +凡事都有两面,northstar 最大的成本便是用户的学习成本。因此,作为 northstar 的用户需要有一定的 JAVA 编程能力,如果要用到 AI 能力时也需要用户具备 AI 工程师同等的技术能力。但站在 northstar 作者的角度看,这些都是作为一名量化交易员的必备技能。因此我相信,但凡是有意向入坑量化交易的用户,都会对自己所面临的挑战有充分的心理预期。 + +除了学习成本外,还有便是那微不足道的服务器成本、数据服务成本等等。 + +northstar 项目作为一个免费开源项目,运营的思路根本不是在卖软件。站在 northstar 作者的角度看,更像是在做一个网红 IP,旨在打造一个草根量化生态社区。欢迎有能力的参与者在社区中提供有价值的付费增值服务,更需要更多的用户支持购买社区的增值服务,以帮助社区发展壮大。 + +## 个性化定制 + +northstar 拥有良好的可扩展性设计,允许用户按需进行自行扩展。 + +社区汇集了众多优秀的开发者,能提供收费的定制服务,欢迎甲方爸爸来体验 VIP 定制服务。 + +--- + +## northstar 7.0 版本升级说明 + +> 介绍完了产品竞争力,是时候该向老粉们仔细交代一下此次 v7.0 升级的几项重大改进了。 + +- **重构了核心类,使内存占用率得以优化** + +为了更好地验证优化效果,我特意做了一个 A/B 测试。针对同一个回测周期的同一批模组进行回测,并观测其内存使用情况。 + +![](/assets/img/news/Northstar-7.0-7.jpg) + +![](/assets/img/news/Northstar-7.0-8.jpg) + +优化前的内存运行情况如下: + +![](/assets/img/news/Northstar-7.0-9.jpg) + +优化后的内存运行情况如下: + +![](/assets/img/news/Northstar-7.0-10.jpg) + +可以明显看出优化后的内存增长速度明显降低,最终占用的物理内存也显著下降。 + +- **升级了 JDK21,引入了虚拟线程** + +对主程序所有涉及 IO 操作的逻辑,都进行了虚拟线程优化。比如保存 K 线数据、发送即时行情等。 + +- 集成了 Tensorflow 深度学习能力 + +如前文提到的,策略可以加载 Tensorflow 的预训练模型,来优化策略的胜率。 + +- **注意事项** + +由于 springboot3 的升级,引发了 H2 数据库升级导致数据不兼容,必须要移除数据文件,或进行数据迁移。 + +—— 完 —— diff --git a/src/zh/news/Northstar-7.x.md b/src/zh/news/Northstar-7.x.md index aee2505b58..1a684857c4 100644 --- a/src/zh/news/Northstar-7.x.md +++ b/src/zh/news/Northstar-7.x.md @@ -1,76 +1,76 @@ ---- -title: Northstar-v7.x 从此能用AI来做量化交易 -author: Kevin黄伟亮 -date: 2024-01-13 -cover: /assets/img/news/Northstar-7.x-0.png -head: - - - meta - - name: 新闻 ---- - -##### **【导读】两年前,大概还是在 v4.x 版本的时候,我就已经意识到,量化交易的实践路径必须是从程序化走向智能化。但如何把 AI 能力集成到 Northstar 平台是一个难点。经过了一系列的 PoC 探索,如今终于找到了一个可以落地的端到端架构方案...(此处应该有掌声)** - -有一段时间没发文章了,老粉们应该都知道,我肯定又在憋大招了。这不,当年吹的牛逼终于给实现了:Northstar 盈富量化平台终于可以用 AI 来做量化交易了。 - -在之前的版本里,正如绝大多数的商业量化软件一样,Northstar 只能做到交易逻辑的程序化与自动化。比如要把开平仓的条件与逻辑都要写得很清楚,这会导致不是欠拟合就是过拟合。随着深度学习方法论的普及,我发现这也许是解决以上问题的新思路。于是,从两年多前就开始意识到,量化交易的最终演化必然是从初级的程序化与自动化,向高级的智能化过渡。 - -## 界定问题 - -既然确定了演化方向,那么如何把人工智能的能力集成到 Northstar 这个平台上呢?首先面临的就是建模问题。这两年多的时间来,我一直把量化交易理解成为一个强化学习问题。 - -![](/assets/img/news/Northstar-7.x-0.png) - -在强化学习模型中,“智能体”对“环境”采取某个动作,然后“环境”把新的状态与奖励反馈给“智能体”。放到量化交易的语境下就是交易策略发出交易指令,然后实际的持仓盈亏就是市场给予的“奖励”反馈。 - -这个建模思路看上去是多么完美。因此,我一直都认为就该这么落地的。 - -## 思路的转变 - -直到深入地学习了强化学习后、准备动手落地时,便发现了不妥之处。或者说,根本没必要把简单问题复杂化。与强化学习相比,监督学习的模型会更简单,也更好落地。 - -我们很多时候不得不采用强化学习方法,仅仅是因为我们没有足够的数据样本,因此才不得不通过建立一个强化学习模型来对环境数据进行采样。因此,交互的本质是在做数据采样,从而得到在什么样的环境状态下,做出什么样行动所得到的期望奖励值分布。但在量化交易这个场景下,根本不需要花这功夫,因为历史行情是固定的,我们只需要把环境状态与期望的后续动作建立起关联性,就能得到监督学习所需要的数据。有了数据,便能通过深度学习方法进行拟合。对!不管是强化学习还是监督学习,最终都是拿着历史数据对拟合而已,并没有什么高大上的秘密。 - -把问题从强化学习转变为监督学习,那实现难度直接降了不止一个数量级。因为强化学习要考虑的问题实在太多,随便一个都有可能引入了新的问题导致训练的结果不如预期。而监督学习就很简单明了,历史行情就摆在那里,期望模型什么时候做多、什么时候做空是显而易见的,这样我们可以在进行数据集处理时便能更容易地发现问题。 - -## 架构设计 - -得益于之前版本良好的可扩展性设计,Northstar v7.x 要集成 AI 能力并不需要做过多的修改,仅仅是需要增加一个数据采样的数据出口,及一个预训练模型的入口。实际的框架选型时,选择了既有广泛用户基础又能提供 PYTHON 与 JAVA 兼容的 Tensorflow 作为预模型加载接口的实现。 - -![](/assets/img/news/Northstar-7.x-1.jpg) - -如上架构示意图所示,黄色模块就是 Northstar 的系统模块,而带有红色边框的便是这次新增加的模块。在实现时,只需要在策略层实现采样器接口,便可以实现适应任意策略的数据采样,可以采样的数据包括但不限于基础的行情 K 线数据、任意指标的二次加工数据。 - -数据被采样后会自动保存为 CSV 文件,然后可以在 jupyter notebook 对数据做任意的训练与探索,包括但不限于构建回归模型、CNN 模型、RNN 模型等,并最终以 Tensorflow 预训练模型的形式保存。然后到了生产环境下,策略层可以通过预训练加载器把模型加载到内存以供实盘调用。 - -## 实战与研究属性兼备 - -放眼整个量化交易软件行业,但凡能进行实际交易且能够应用 AI 能力的平台,软件年费不会低于 10000 块钱。如今 Northstar 作为开源项目,把整个交易智能化的端到端方案实现并成功落地,可以说是直接对行业同类软件发起挑战。除了能进行实际交易外,Northstar 的历史回放功能为人工智能在量化交易领域的应用研究提供了一个很方便的一站式环境。其开放可拓展的设计可以让用户针对自己的个性化需求进行定制。 - -**欢迎更多想学习量化交易、智能交易的朋友加入并支持我们社区的发展,一起来玩转量化!** - -**关于如何用 AI 来优化交易策略的案例,将会在我的知识星球里做更详尽的分享,欢迎来交个朋友加入我的知识星球。 -** - -![](/assets/img/news/Northstar-7.x-2.jpg) - ---- - -## Northstar 盈富量化平台介绍 - -> Northstar 盈富量化平台是一个能平替文华、MC、金字塔等商业化量化交易软件的开源量化交易专业软件。它能对接任意开放的交易所接口,例如 CTP、币安、老虎证券等,来进行自动化、智能化的量化交易。 -> -> Northstar 截止到目前最新的 v7.0.0-Beta 测试版本,已经在生产环境有多样的实践案例,例如半自动/全自动 CTA 交易、跨期套利交易。未来会继续实践更多的不同的交易场景与策略模式,成为个人量化交易中的超级武器。 - -![](/assets/img/news/Northstar-7.x-3.png) - -![](/assets/img/news/Northstar-7.x-4.gif) - -![](/assets/img/news/Northstar-7.x-5.jpg) - -项目地址:https://gitee.com/dromara/northstar - -作者:黄伟亮,Northstar 盈富量化平台作者 - -关注公众号,带你看更多量化交易的干货 - -—— 完 —— +--- +title: Northstar-v7.x 从此能用AI来做量化交易 +author: Kevin黄伟亮 +date: 2024-01-13 +cover: /assets/img/news/Northstar-7.x-0.png +head: + - - meta + - name: 新闻 +--- + +##### **【导读】两年前,大概还是在 v4.x 版本的时候,我就已经意识到,量化交易的实践路径必须是从程序化走向智能化。但如何把 AI 能力集成到 Northstar 平台是一个难点。经过了一系列的 PoC 探索,如今终于找到了一个可以落地的端到端架构方案...(此处应该有掌声)** + +有一段时间没发文章了,老粉们应该都知道,我肯定又在憋大招了。这不,当年吹的牛逼终于给实现了:Northstar 盈富量化平台终于可以用 AI 来做量化交易了。 + +在之前的版本里,正如绝大多数的商业量化软件一样,Northstar 只能做到交易逻辑的程序化与自动化。比如要把开平仓的条件与逻辑都要写得很清楚,这会导致不是欠拟合就是过拟合。随着深度学习方法论的普及,我发现这也许是解决以上问题的新思路。于是,从两年多前就开始意识到,量化交易的最终演化必然是从初级的程序化与自动化,向高级的智能化过渡。 + +## 界定问题 + +既然确定了演化方向,那么如何把人工智能的能力集成到 Northstar 这个平台上呢?首先面临的就是建模问题。这两年多的时间来,我一直把量化交易理解成为一个强化学习问题。 + +![](/assets/img/news/Northstar-7.x-0.png) + +在强化学习模型中,“智能体”对“环境”采取某个动作,然后“环境”把新的状态与奖励反馈给“智能体”。放到量化交易的语境下就是交易策略发出交易指令,然后实际的持仓盈亏就是市场给予的“奖励”反馈。 + +这个建模思路看上去是多么完美。因此,我一直都认为就该这么落地的。 + +## 思路的转变 + +直到深入地学习了强化学习后、准备动手落地时,便发现了不妥之处。或者说,根本没必要把简单问题复杂化。与强化学习相比,监督学习的模型会更简单,也更好落地。 + +我们很多时候不得不采用强化学习方法,仅仅是因为我们没有足够的数据样本,因此才不得不通过建立一个强化学习模型来对环境数据进行采样。因此,交互的本质是在做数据采样,从而得到在什么样的环境状态下,做出什么样行动所得到的期望奖励值分布。但在量化交易这个场景下,根本不需要花这功夫,因为历史行情是固定的,我们只需要把环境状态与期望的后续动作建立起关联性,就能得到监督学习所需要的数据。有了数据,便能通过深度学习方法进行拟合。对!不管是强化学习还是监督学习,最终都是拿着历史数据对拟合而已,并没有什么高大上的秘密。 + +把问题从强化学习转变为监督学习,那实现难度直接降了不止一个数量级。因为强化学习要考虑的问题实在太多,随便一个都有可能引入了新的问题导致训练的结果不如预期。而监督学习就很简单明了,历史行情就摆在那里,期望模型什么时候做多、什么时候做空是显而易见的,这样我们可以在进行数据集处理时便能更容易地发现问题。 + +## 架构设计 + +得益于之前版本良好的可扩展性设计,Northstar v7.x 要集成 AI 能力并不需要做过多的修改,仅仅是需要增加一个数据采样的数据出口,及一个预训练模型的入口。实际的框架选型时,选择了既有广泛用户基础又能提供 PYTHON 与 JAVA 兼容的 Tensorflow 作为预模型加载接口的实现。 + +![](/assets/img/news/Northstar-7.x-1.jpg) + +如上架构示意图所示,黄色模块就是 Northstar 的系统模块,而带有红色边框的便是这次新增加的模块。在实现时,只需要在策略层实现采样器接口,便可以实现适应任意策略的数据采样,可以采样的数据包括但不限于基础的行情 K 线数据、任意指标的二次加工数据。 + +数据被采样后会自动保存为 CSV 文件,然后可以在 jupyter notebook 对数据做任意的训练与探索,包括但不限于构建回归模型、CNN 模型、RNN 模型等,并最终以 Tensorflow 预训练模型的形式保存。然后到了生产环境下,策略层可以通过预训练加载器把模型加载到内存以供实盘调用。 + +## 实战与研究属性兼备 + +放眼整个量化交易软件行业,但凡能进行实际交易且能够应用 AI 能力的平台,软件年费不会低于 10000 块钱。如今 Northstar 作为开源项目,把整个交易智能化的端到端方案实现并成功落地,可以说是直接对行业同类软件发起挑战。除了能进行实际交易外,Northstar 的历史回放功能为人工智能在量化交易领域的应用研究提供了一个很方便的一站式环境。其开放可拓展的设计可以让用户针对自己的个性化需求进行定制。 + +**欢迎更多想学习量化交易、智能交易的朋友加入并支持我们社区的发展,一起来玩转量化!** + +**关于如何用 AI 来优化交易策略的案例,将会在我的知识星球里做更详尽的分享,欢迎来交个朋友加入我的知识星球。 +** + +![](/assets/img/news/Northstar-7.x-2.jpg) + +--- + +## Northstar 盈富量化平台介绍 + +> Northstar 盈富量化平台是一个能平替文华、MC、金字塔等商业化量化交易软件的开源量化交易专业软件。它能对接任意开放的交易所接口,例如 CTP、币安、老虎证券等,来进行自动化、智能化的量化交易。 +> +> Northstar 截止到目前最新的 v7.0.0-Beta 测试版本,已经在生产环境有多样的实践案例,例如半自动/全自动 CTA 交易、跨期套利交易。未来会继续实践更多的不同的交易场景与策略模式,成为个人量化交易中的超级武器。 + +![](/assets/img/news/Northstar-7.x-3.png) + +![](/assets/img/news/Northstar-7.x-4.gif) + +![](/assets/img/news/Northstar-7.x-5.jpg) + +项目地址:https://gitee.com/dromara/northstar + +作者:黄伟亮,Northstar 盈富量化平台作者 + +关注公众号,带你看更多量化交易的干货 + +—— 完 —— diff --git a/src/zh/news/README.md b/src/zh/news/README.md index 8047344777..58b0cd343b 100644 --- a/src/zh/news/README.md +++ b/src/zh/news/README.md @@ -1,33 +1,9 @@ ---- -title: 新闻 -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - - - - - - +--- +title: 新闻 +pageInfo: false +contributors: false +editLink: false +sidebar: false +layout: siteLayout +--- + diff --git a/src/zh/news/RedisFront-0.md b/src/zh/news/RedisFront-0.md new file mode 100644 index 0000000000..b3f85b8047 --- /dev/null +++ b/src/zh/news/RedisFront-0.md @@ -0,0 +1,41 @@ +--- +title: RedisFront 2025.1 正式发布 +author: RedisFront +date: 2025-03-26 +cover: /assets/img/news/RedisFront-0-0.png +head: + - - meta + - name: 新闻 +--- + +> 【🚀RedisFront 2025.1 正式发布】历经数月的深度打磨,带来了一次脱胎换骨的升级! + +## 内容  + +RedisFront 是一款基于 Java Swing 开发的跨平台 Redis 客户端工具,兼容多种主流操作系统 (包括 Windows, macOS 和 Linux) , 通过可视化界面实现 Redis 数据管理与服务器状态监测,适用于开发调试与生产运维场景。 + +_本次更新聚焦两大核心:_ + +**✨ 焕新设计** + +* 重构 80% UI 界面 + +* 支持暗黑 / 明亮主题切换 + +* 数据图表可视化增强 + + +**🔧 性能跃升** + +* 新增动态连接池提速 50% + + +![](/assets/img/news/RedisFront-0-0.png) + +![](/assets/img/news/RedisFront-0-1.png) + +![](/assets/img/news/RedisFront-0-2.png) + + + +👉 立即体验:https://gitee.com/dromara/RedisFront/releases/tag/2025.1 \ No newline at end of file diff --git a/src/zh/news/RuoYi-Cloud-Plus-2.1.2.md b/src/zh/news/RuoYi-Cloud-Plus-2.1.2.md index efb6619e81..525f03e76e 100644 --- a/src/zh/news/RuoYi-Cloud-Plus-2.1.2.md +++ b/src/zh/news/RuoYi-Cloud-Plus-2.1.2.md @@ -1,111 +1,111 @@ ---- -title: RuoYi-Cloud-Plus 发布 2.1.2 版本 2023 最后一版 -author: 疯狂的狮子Li -date: 2023-12-27 -cover: /assets/img/news/RuoYi-Cloud-Plus-2.1.2-0.png -head: - - - meta - - name: 新闻 ---- - -# 更新日志 - -### 依赖升级 - -- update springboot 3.1.5 => 3.1.7 -- update springboot 2.7.17 => 2.7.18(扩展服务升级到 boot2 最终版本) -- update mybatis-boot 3.0.2 => 3.0.3 优化依赖传递 -- update powerjob 4.3.3 => 4.3.6 -- update easyexcel 3.3.2 => 3.3.3 -- update transmittable-thread-local 2.14.2 => 2.14.4 -- update justauth 1.16.5 => 1.16.6 -- update redisson 3.24.1 => 3.24.3 修复订阅重启连接超时问题 -- update easy-es 1.1.1 => 2.0.0-beta4 - -### 功能更新 - -- update 优化 oss 远程调用 支持降级处理 -- update 优化 丰富 RedisUtils 对 List Set 类型的操作 -- update 优化 为 admin 模块 单独增加 ratelimiter 模块 -- update 优化 验证码接口 增加限流配置 -- update 优化 excel 合并注解会根据第一合并列的结果来决定后续的列合并 (感谢 Simple) -- update 优化 SocialUtils 代码 -- update 优化 删除无用异常类 -- update 优化 补全三方登录校验国际化 -- update 优化 sms 组件 预留自动配置类 -- update 更新 关于数据库的说明 -- update 优化 sms 组件 预留自动配置类 -- update 优化 将 OSS 配置 改为全局模式 降低使用难度 保留 sql 便于用户自行扩展(常规项目用不上配置分多租户) -- update 优化 细化 oss 配置管理权限控制 -- update 优化 开启 redisson 脚本缓存 减少网络传输 -- update 优化 删除 hikaricp 官方不推荐使用的配置 jdbc4 协议自带校验方法 -- update 优化 减少 PlusSaTokenDao 不必要的查询优化性能 -- update 优化 更新用户异常提示 使用登录账号 -- update 优化 使用登录用户判断是否登录 提高效率 -- update 优化 重构 LoginHelper 将本地存储代码操作封装 -- update 优化 getTenantId 判断是否开启多租户 -- update 优化 Dockerfile 使用 shell 模式 支持环境变量传入 jvm 参数 -- update 优化 WebSocketUtils 连接关闭改为警告 -- update 优化 excel 多 sheet 页导出 (感谢 May) -- update 优化 删除无用接口实现 -- update 优化 jvm 参数调整 全面启用 zgc -- update 优化 使用动态租户重构业务对租户的逻辑 -- update 优化 TenantHelper 动态租户支持函数式方法 -- update 优化 支持多租户绑定相同的三方登录 -- update 优化 更新用户登录信息方法忽略数据权限 -- update 优化 补全三方绑定时间字段 删除无用 excel 注解 -- update 优化 将登录记录抽取到监听器统一处理 -- update 优化 登录消息推送异常拦截(未启动 resource 也不耽误用) -- update 优化 租户插件 ignoreTable 方法支持动态租户 - -### 新增功能 - -- add 新增 RedisUtils.setObjectIfExists 如果存在则设置方法 -- add 新增 丰富 RedisUtils 对 List Set 类型的操作 -- add 新增 翻译组件 用户昵称翻译实现 -- add 新增 响应加密功能 支持注解强制加密接口数据 (感谢 MichelleChung) -- add 新增 common-ratelimiter 限流模块 用于自定义业务限流 与 sentinel 不冲突 - -### 问题修复 - -- fix 修复 stream-mq 测试服务未导入租户模块 导致鉴权不一致问题 -- fix 修复 使用 zgc 导致 seata 报错(未知原因 将 alibaba 组件全还原) -- fix 修复 sentinel 镜像添加了多余接口参数 -- fix 修复 注册接口获取开关未在租户范围内问题 -- fix 修复 seata-server logback 版本冲突问题 -- fix 修复 selectDictTypeByType 查询方法错误问题 -- fix 修复 一些不正常类无法加载报错问题 -- fix 修复 powerjob sql 脚本针对其他数据库转义符问题 (感谢 branches) -- fix 修复 MybatisSystemException 空指针问题 -- fix 修复 excel 合并注解会根据第一合并列的结果来决定后续的列合并 -- fix 修复 session 多账号共用覆盖问题 改为 tokenSession 独立存储 -- fix 修复 token 失效后 登录获取用户 null 问题 -- fix 修复 powerjob 部署方案 高版本 nginx 不生效问题 -- fix 修复 OssFactory 并发多创建实例问题 -- fix 修复 延迟队列在投递消息未到达时间的时候 服务死机导致重启收不到消息 - -### 前端改动 - -- update 优化 用户头像 img 变量无确定类型问题 -- update 优化 细化 oss 配置管理权限控制 -- update 优化 明确打包命令 -- update 优化 代码中存在的警告 -- update 优化 前端白名单页面放行逻辑 -- update 优化 页面关于权限标识符说明 -- fix 修复 append-to-body 编写错误 (感谢 Ai3\_刘小龙) -- fix 关闭动态路由 tab 页签时不清理组件缓存 (感谢 NickLuo) -- fix 删除重复环境变量 ElUploadInstance (感谢 棉花) -- fix 修复 在线用户 强推按钮点击取消控制台警告问题 -- fix 修复 字典使用 default 样式报警告问题 - -## 框架文档 - -使用框架前请仔细阅读文档重点注意事项 - -参考文档: https://plus\-doc.dromara.org - -## 加公开群 - -加微信备注加群 - -![](/assets/img/news/RuoYi-Vue-Plus-5.1.0-1.png) +--- +title: RuoYi-Cloud-Plus 发布 2.1.2 版本 2023 最后一版 +author: 疯狂的狮子Li +date: 2023-12-27 +cover: /assets/img/news/RuoYi-Cloud-Plus-2.1.2-0.png +head: + - - meta + - name: 新闻 +--- + +# 更新日志 + +### 依赖升级 + +- update springboot 3.1.5 => 3.1.7 +- update springboot 2.7.17 => 2.7.18(扩展服务升级到 boot2 最终版本) +- update mybatis-boot 3.0.2 => 3.0.3 优化依赖传递 +- update powerjob 4.3.3 => 4.3.6 +- update easyexcel 3.3.2 => 3.3.3 +- update transmittable-thread-local 2.14.2 => 2.14.4 +- update justauth 1.16.5 => 1.16.6 +- update redisson 3.24.1 => 3.24.3 修复订阅重启连接超时问题 +- update easy-es 1.1.1 => 2.0.0-beta4 + +### 功能更新 + +- update 优化 oss 远程调用 支持降级处理 +- update 优化 丰富 RedisUtils 对 List Set 类型的操作 +- update 优化 为 admin 模块 单独增加 ratelimiter 模块 +- update 优化 验证码接口 增加限流配置 +- update 优化 excel 合并注解会根据第一合并列的结果来决定后续的列合并 (感谢 Simple) +- update 优化 SocialUtils 代码 +- update 优化 删除无用异常类 +- update 优化 补全三方登录校验国际化 +- update 优化 sms 组件 预留自动配置类 +- update 更新 关于数据库的说明 +- update 优化 sms 组件 预留自动配置类 +- update 优化 将 OSS 配置 改为全局模式 降低使用难度 保留 sql 便于用户自行扩展(常规项目用不上配置分多租户) +- update 优化 细化 oss 配置管理权限控制 +- update 优化 开启 redisson 脚本缓存 减少网络传输 +- update 优化 删除 hikaricp 官方不推荐使用的配置 jdbc4 协议自带校验方法 +- update 优化 减少 PlusSaTokenDao 不必要的查询优化性能 +- update 优化 更新用户异常提示 使用登录账号 +- update 优化 使用登录用户判断是否登录 提高效率 +- update 优化 重构 LoginHelper 将本地存储代码操作封装 +- update 优化 getTenantId 判断是否开启多租户 +- update 优化 Dockerfile 使用 shell 模式 支持环境变量传入 jvm 参数 +- update 优化 WebSocketUtils 连接关闭改为警告 +- update 优化 excel 多 sheet 页导出 (感谢 May) +- update 优化 删除无用接口实现 +- update 优化 jvm 参数调整 全面启用 zgc +- update 优化 使用动态租户重构业务对租户的逻辑 +- update 优化 TenantHelper 动态租户支持函数式方法 +- update 优化 支持多租户绑定相同的三方登录 +- update 优化 更新用户登录信息方法忽略数据权限 +- update 优化 补全三方绑定时间字段 删除无用 excel 注解 +- update 优化 将登录记录抽取到监听器统一处理 +- update 优化 登录消息推送异常拦截(未启动 resource 也不耽误用) +- update 优化 租户插件 ignoreTable 方法支持动态租户 + +### 新增功能 + +- add 新增 RedisUtils.setObjectIfExists 如果存在则设置方法 +- add 新增 丰富 RedisUtils 对 List Set 类型的操作 +- add 新增 翻译组件 用户昵称翻译实现 +- add 新增 响应加密功能 支持注解强制加密接口数据 (感谢 MichelleChung) +- add 新增 common-ratelimiter 限流模块 用于自定义业务限流 与 sentinel 不冲突 + +### 问题修复 + +- fix 修复 stream-mq 测试服务未导入租户模块 导致鉴权不一致问题 +- fix 修复 使用 zgc 导致 seata 报错(未知原因 将 alibaba 组件全还原) +- fix 修复 sentinel 镜像添加了多余接口参数 +- fix 修复 注册接口获取开关未在租户范围内问题 +- fix 修复 seata-server logback 版本冲突问题 +- fix 修复 selectDictTypeByType 查询方法错误问题 +- fix 修复 一些不正常类无法加载报错问题 +- fix 修复 powerjob sql 脚本针对其他数据库转义符问题 (感谢 branches) +- fix 修复 MybatisSystemException 空指针问题 +- fix 修复 excel 合并注解会根据第一合并列的结果来决定后续的列合并 +- fix 修复 session 多账号共用覆盖问题 改为 tokenSession 独立存储 +- fix 修复 token 失效后 登录获取用户 null 问题 +- fix 修复 powerjob 部署方案 高版本 nginx 不生效问题 +- fix 修复 OssFactory 并发多创建实例问题 +- fix 修复 延迟队列在投递消息未到达时间的时候 服务死机导致重启收不到消息 + +### 前端改动 + +- update 优化 用户头像 img 变量无确定类型问题 +- update 优化 细化 oss 配置管理权限控制 +- update 优化 明确打包命令 +- update 优化 代码中存在的警告 +- update 优化 前端白名单页面放行逻辑 +- update 优化 页面关于权限标识符说明 +- fix 修复 append-to-body 编写错误 (感谢 Ai3\_刘小龙) +- fix 关闭动态路由 tab 页签时不清理组件缓存 (感谢 NickLuo) +- fix 删除重复环境变量 ElUploadInstance (感谢 棉花) +- fix 修复 在线用户 强推按钮点击取消控制台警告问题 +- fix 修复 字典使用 default 样式报警告问题 + +## 框架文档 + +使用框架前请仔细阅读文档重点注意事项 + +参考文档: https://plus\-doc.dromara.org + +## 加公开群 + +加微信备注加群 + +![](/assets/img/news/RuoYi-Vue-Plus-5.1.0-1.png) diff --git a/src/zh/news/RuoYi-Vue-Plus-4.7.0.md b/src/zh/news/RuoYi-Vue-Plus-4.7.0.md index 501e57a256..758b6f12d5 100644 --- a/src/zh/news/RuoYi-Vue-Plus-4.7.0.md +++ b/src/zh/news/RuoYi-Vue-Plus-4.7.0.md @@ -1,169 +1,169 @@ ---- -title: RuoYi-Vue-Plus 4.7.0 发布 稳定性版本 -author: 疯狂的狮子Li -date: 2023-05-08 -cover: /assets/img/news/RuoYi-Vue-Plus-5.1.0-cover.png -head: - - - meta - - name: 新闻 ---- - -## 更新日志 - -### 依赖升级 - -- update springboot 2.7.9 => 2.7.11 修复 DoS 漏洞 -- update xxljob 2.3.1 => 2.4.0 -- update minio 升级至最新版 避免低版本信息泄漏问题 -- update hutool 5.8.15 => 5.8.18 -- update redisson 3.20.0 => 3.20.1 - -### 功能更新 - -- update 优化 更改 sys_oss_config 表注释 避免误解 -- update 项目正式入驻 dromara 开源社区 更改项目地址 -- update 全新 logo 全新背景图(设计师打造) -- update 优化代码生成 同步操作使用批量处理 -- update 重写项目 readme 说明 -- update 修改 controller 中校验直接返回 R.fail -- update 更换默认用户头像 -- update 优化 限流注解 key 支持简单 spel 表达式 -- update 优化弹窗后导航栏偏移的问题 -- update 优化$tab.closePage 后存在非首页页签时不应该跳转首页 -- update delete build style -- update 优化选择图标组件 -- update 移除 vue-multiselect 样式 -- update 优化固定头部页签滚动条被隐藏的问题 -- update 按代码规范补全重写注解 -- update 优化 极端情况获取 LoginUser 可能为 null 问题 -- update 优化 更改系统所有服务日志配置文件命名为 logback-plus.xml 避免与其他框架默认配置冲突 -- update 优化 加解密模块 将 null 判断下推防止任何可能的 null 出现 -- update 优化 调整配置文件错误注释 -- update 优化 在线用户 token 获取方式 -- update 优化 用户更改角色 踢掉角色相关所有在线用户 -- update 优化 下拉图标选择组件优化:1.已选择图标高亮回显 2.滚动条采用 el-scrollbar -- update 优化 Vue 的 DictTag 组件 当 value 没有匹配的值时 展示空 value -- update 优化 恢复翻页/切换路由滚动功能 - -### 新增功能 - -- add 新增 ip2region 实现离线 IP 地址定位库 -- add 增加 邮箱验证码发送接口 -- add 增加 邮箱登陆接口 -- add 增加 EncryptUtils 加解密安全工具类 可以处理 base64,aes,sm4,sm2,rsa,md5,sha256 加解密 -- add 增加 EncryptUtils 类中增加国密 sm3 的不可逆加密算法 -- add 新增 忽略数据权限写法 防止异常不执行关闭问题 - -### 问题修复 - -- fix 修复 代码生成 点选按钮不生效问题 -- fix 修复 用户密码更新无效问题 -- fix 修复 findInSet 在 mysql 下方法搜索非数字字段时 无引号报错问题 -- fix 修复 oracle postgres 数据库日志表索引创建错误 -- fix 角色列表关联多表 sort 值都一样 导致排序不稳定、临时表没有原来的主键顺序 -- fix 修复 DefaultExcelResult 单词拼写错误 -- fix 修复页面切换时布局错乱的问题 -- fix 修复 tab 栏“关闭其他”异常的问题 -- fix 修复 加解密拦截器 对象属性为 null 问题 -- fix 修复 取消 oss 预览状态修改 图标变化不正常问题 -- fix 修复 开启 TopNav 后一级菜单路由参数设置无效问题 -- fix 修复 路由跳转被阻止时 vue-router 内部产生报错信息问题 -- fix 修复 缓存列表:多次清除操作,提示不变的问题 - -## 平台简介 - -> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群` 场景全方位升级(不兼容原框架) - -> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可 -> 活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 - -## 本框架与 RuoYi 的功能差异 - -| 功能 | 本框架 | RuoYi | -| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| 前端项目 | 基于 vue3-element-admin 开源项目重写 Vue3 + TS + ElementPlus | 基于 Vue2/Vue3 + JS | -| 后端项目结构 | 采用插件化 + 扩展包形式 结构解耦 易于扩展 | 模块相互注入耦合严重难以扩展 | -| 后端代码风格 | 严格遵守 Alibaba 规范与项目统一配置的代码格式化 | 代码书写与常规结构不同阅读障碍大 | -| Web 容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat | -| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 | -| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic 校验、忽略校验。 角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 | -| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer 。可同时使用异构切换 | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 | -| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 | -| Redis 客户端 | 采用 Redisson Redis 官方推荐 基于 Netty 的客户端工具 。支持 Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys 被转换为 scan 。支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐 。连接池采用 common-pool Bug 多经常性出问题 | -| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能 。例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写 Redis 代码逻辑 | -| ORM 框架 | 采用 Mybatis-Plus 基于对象几乎不用写 SQL 全 java 操作 功能强大插件众多。例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于 XML 需要手写 SQL | -| SQL 监控 | 采用 p6spy 可输出完整 SQL 与执行时间监控 | log 输出 需手动拼接 sql 与参数无法快速查看调试问题 | -| 数据分页 | 采用 Mybatis-Plus 分页插件 。框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序 | 采用 PageHelper 仅支持单查询分页 参数只能从 param 传 只能单排序 功能扩展性差 体验不好 | -| 数据权限 | 采用 Mybatis-Plus 插件 自行分析拼接 SQL 无感式过滤。 只需为 Mapper 设置好注解条件 支持多种自定义 不限于部门角色 | 采用 注解+aop 实现 基于部门角色 生成的 sql 兼容性差 不支持其他业务扩展。生成 sql 后需手动拼接到具体业务 sql 上 对于多个 Mapper 查询不起作用 | -| 数据脱敏 | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件 。支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展 | 无 | -| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密 。支持多种策略 如 BASE64、AES、RSA、SM2、SM4 等 | 无 | -| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译。 支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 | -| 多数据源框架 | 采用 dynamic-datasource 支持世面大部分数据库 。通过 yml 配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源 。支持 spel 表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | -| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 | -| 数据库连接池 | 采用 HikariCP Spring 官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug 众多 社区维护差 活跃度低 配置众多繁琐性能一般 | -| 数据库主键 | 采用 雪花 ID 基于时间戳的 有序增长 唯一 ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增 ID 支持数据量有限 不支持多数据源主键唯一 | -| WebSocket 协议 | 基于 Spring 封装的 WebSocket 协议 扩展了 Token 鉴权与分布式会话同步 不再只是基于单机的废物 | 无 | -| 序列化 | 采用 Jackson Spring 官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 | -| 分布式幂等 | 参考美团 GTIS 防重系统简化实现(细节可看文档) | 手动编写注解基于 aop 实现 | -| 分布式任务调度 | 采用 Xxl-Job 天生支持分布式 统一的管理中心 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 | -| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储。支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 | -| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持 S3 协议的厂家 | 不支持 | -| 短信 | 支持 阿里、腾讯 只需在 yml 配置好厂家密钥即可使用 接口化支持扩展其他厂家 | 不支持 | -| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 | -| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于 java 注释。只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 | -| 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 | -| Excel 框架 | 采用 Alibaba EasyExcel 基于插件化 。框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | -| 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖 90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 | -| 监控框架 | 采用 SpringBoot-Admin 基于 SpringBoot 官方 actuator 探针机制。实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 | -| 链路追踪 | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗 。 用了它即可实时查看请求经过的每一处每一个节点 | 无 | -| 代码生成器 | 只需设计好表结构 一键生成所有 crud 代码与页面 。降低 80%的开发量 把精力都投入到业务设计上 。 框架为其适配 MP、SpringDoc 规范化代码 同时支持动态多数据源代码生成 | 代码生成原生结构 只支持单数据源生成 | -| 部署方式 | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼 | 原生 jar 部署 其他环境需手动下载安装 自行搭建 | -| 项目路径修改 | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的 | 需要做很多改造 文档说明有限 | -| 国际化 | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化 | 只提供基础功能 其他需自行编写扩展 | -| 代码单例测试 | 提供单例测试 使用方式编写方法与 maven 多环境单测插件 | 只提供基础功能 其他需自行编写扩展 | -| Demo 案例 | 提供框架功能的实际使用案例 单独一个模块提供了很多很全 | 无 | - -## 本框架与 RuoYi 的业务差异 - -| 业务 | 功能说明 | 本框架 | RuoYi | -| ------------ | --------------------------------------------------------------- | ------ | ------------------------------ | -| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 | -| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 | -| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 | -| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 | -| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 | -| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 | -| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 | -| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 | -| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 | -| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 | -| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 | -| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 | -| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 | -| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 | -| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持 CRUD 下载 | 支持 | 仅支持单数据源 | -| 系统接口 | 根据业务代码自动生成相关的 api 接口文档 | 支持 | 支持 | -| 服务监控 | 监视集群系统 CPU、内存、磁盘、堆栈、在线日志、Spring 相关配置等 | 支持 | 仅支持单机 CPU、内存、磁盘监控 | -| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 | -| 在线构建器 | 拖动表单元素生成相应的 HTML 代码。 | 支持 | 支持 | -| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 | - -## 参考文档 - -使用框架前请仔细阅读文档重点注意事项 - -> 初始化项目 必看 -> -> > https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort\_id=4164117&doc\_id=1469725 -> -> 专栏与视频 入门必看 -> -> > https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort\_id=5473272&doc\_id=1469725 -> -> 部署项目 必看 -> -> > https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort\_id=4219382&doc\_id=1469725 -> -> 参考文档 Wiki -> -> > https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages +--- +title: RuoYi-Vue-Plus 4.7.0 发布 稳定性版本 +author: 疯狂的狮子Li +date: 2023-05-08 +cover: /assets/img/news/RuoYi-Vue-Plus-5.1.0-cover.png +head: + - - meta + - name: 新闻 +--- + +## 更新日志 + +### 依赖升级 + +- update springboot 2.7.9 => 2.7.11 修复 DoS 漏洞 +- update xxljob 2.3.1 => 2.4.0 +- update minio 升级至最新版 避免低版本信息泄漏问题 +- update hutool 5.8.15 => 5.8.18 +- update redisson 3.20.0 => 3.20.1 + +### 功能更新 + +- update 优化 更改 sys_oss_config 表注释 避免误解 +- update 项目正式入驻 dromara 开源社区 更改项目地址 +- update 全新 logo 全新背景图(设计师打造) +- update 优化代码生成 同步操作使用批量处理 +- update 重写项目 readme 说明 +- update 修改 controller 中校验直接返回 R.fail +- update 更换默认用户头像 +- update 优化 限流注解 key 支持简单 spel 表达式 +- update 优化弹窗后导航栏偏移的问题 +- update 优化$tab.closePage 后存在非首页页签时不应该跳转首页 +- update delete build style +- update 优化选择图标组件 +- update 移除 vue-multiselect 样式 +- update 优化固定头部页签滚动条被隐藏的问题 +- update 按代码规范补全重写注解 +- update 优化 极端情况获取 LoginUser 可能为 null 问题 +- update 优化 更改系统所有服务日志配置文件命名为 logback-plus.xml 避免与其他框架默认配置冲突 +- update 优化 加解密模块 将 null 判断下推防止任何可能的 null 出现 +- update 优化 调整配置文件错误注释 +- update 优化 在线用户 token 获取方式 +- update 优化 用户更改角色 踢掉角色相关所有在线用户 +- update 优化 下拉图标选择组件优化:1.已选择图标高亮回显 2.滚动条采用 el-scrollbar +- update 优化 Vue 的 DictTag 组件 当 value 没有匹配的值时 展示空 value +- update 优化 恢复翻页/切换路由滚动功能 + +### 新增功能 + +- add 新增 ip2region 实现离线 IP 地址定位库 +- add 增加 邮箱验证码发送接口 +- add 增加 邮箱登陆接口 +- add 增加 EncryptUtils 加解密安全工具类 可以处理 base64,aes,sm4,sm2,rsa,md5,sha256 加解密 +- add 增加 EncryptUtils 类中增加国密 sm3 的不可逆加密算法 +- add 新增 忽略数据权限写法 防止异常不执行关闭问题 + +### 问题修复 + +- fix 修复 代码生成 点选按钮不生效问题 +- fix 修复 用户密码更新无效问题 +- fix 修复 findInSet 在 mysql 下方法搜索非数字字段时 无引号报错问题 +- fix 修复 oracle postgres 数据库日志表索引创建错误 +- fix 角色列表关联多表 sort 值都一样 导致排序不稳定、临时表没有原来的主键顺序 +- fix 修复 DefaultExcelResult 单词拼写错误 +- fix 修复页面切换时布局错乱的问题 +- fix 修复 tab 栏“关闭其他”异常的问题 +- fix 修复 加解密拦截器 对象属性为 null 问题 +- fix 修复 取消 oss 预览状态修改 图标变化不正常问题 +- fix 修复 开启 TopNav 后一级菜单路由参数设置无效问题 +- fix 修复 路由跳转被阻止时 vue-router 内部产生报错信息问题 +- fix 修复 缓存列表:多次清除操作,提示不变的问题 + +## 平台简介 + +> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群` 场景全方位升级(不兼容原框架) + +> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可 +> 活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 + +## 本框架与 RuoYi 的功能差异 + +| 功能 | 本框架 | RuoYi | +| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| 前端项目 | 基于 vue3-element-admin 开源项目重写 Vue3 + TS + ElementPlus | 基于 Vue2/Vue3 + JS | +| 后端项目结构 | 采用插件化 + 扩展包形式 结构解耦 易于扩展 | 模块相互注入耦合严重难以扩展 | +| 后端代码风格 | 严格遵守 Alibaba 规范与项目统一配置的代码格式化 | 代码书写与常规结构不同阅读障碍大 | +| Web 容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat | +| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 | +| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic 校验、忽略校验。 角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 | +| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer 。可同时使用异构切换 | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 | +| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 | +| Redis 客户端 | 采用 Redisson Redis 官方推荐 基于 Netty 的客户端工具 。支持 Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys 被转换为 scan 。支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐 。连接池采用 common-pool Bug 多经常性出问题 | +| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能 。例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写 Redis 代码逻辑 | +| ORM 框架 | 采用 Mybatis-Plus 基于对象几乎不用写 SQL 全 java 操作 功能强大插件众多。例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于 XML 需要手写 SQL | +| SQL 监控 | 采用 p6spy 可输出完整 SQL 与执行时间监控 | log 输出 需手动拼接 sql 与参数无法快速查看调试问题 | +| 数据分页 | 采用 Mybatis-Plus 分页插件 。框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序 | 采用 PageHelper 仅支持单查询分页 参数只能从 param 传 只能单排序 功能扩展性差 体验不好 | +| 数据权限 | 采用 Mybatis-Plus 插件 自行分析拼接 SQL 无感式过滤。 只需为 Mapper 设置好注解条件 支持多种自定义 不限于部门角色 | 采用 注解+aop 实现 基于部门角色 生成的 sql 兼容性差 不支持其他业务扩展。生成 sql 后需手动拼接到具体业务 sql 上 对于多个 Mapper 查询不起作用 | +| 数据脱敏 | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件 。支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展 | 无 | +| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密 。支持多种策略 如 BASE64、AES、RSA、SM2、SM4 等 | 无 | +| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译。 支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 | +| 多数据源框架 | 采用 dynamic-datasource 支持世面大部分数据库 。通过 yml 配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源 。支持 spel 表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | +| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 | +| 数据库连接池 | 采用 HikariCP Spring 官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug 众多 社区维护差 活跃度低 配置众多繁琐性能一般 | +| 数据库主键 | 采用 雪花 ID 基于时间戳的 有序增长 唯一 ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增 ID 支持数据量有限 不支持多数据源主键唯一 | +| WebSocket 协议 | 基于 Spring 封装的 WebSocket 协议 扩展了 Token 鉴权与分布式会话同步 不再只是基于单机的废物 | 无 | +| 序列化 | 采用 Jackson Spring 官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 | +| 分布式幂等 | 参考美团 GTIS 防重系统简化实现(细节可看文档) | 手动编写注解基于 aop 实现 | +| 分布式任务调度 | 采用 Xxl-Job 天生支持分布式 统一的管理中心 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 | +| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储。支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 | +| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持 S3 协议的厂家 | 不支持 | +| 短信 | 支持 阿里、腾讯 只需在 yml 配置好厂家密钥即可使用 接口化支持扩展其他厂家 | 不支持 | +| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 | +| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于 java 注释。只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 | +| 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 | +| Excel 框架 | 采用 Alibaba EasyExcel 基于插件化 。框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | +| 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖 90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 | +| 监控框架 | 采用 SpringBoot-Admin 基于 SpringBoot 官方 actuator 探针机制。实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 | +| 链路追踪 | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗 。 用了它即可实时查看请求经过的每一处每一个节点 | 无 | +| 代码生成器 | 只需设计好表结构 一键生成所有 crud 代码与页面 。降低 80%的开发量 把精力都投入到业务设计上 。 框架为其适配 MP、SpringDoc 规范化代码 同时支持动态多数据源代码生成 | 代码生成原生结构 只支持单数据源生成 | +| 部署方式 | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼 | 原生 jar 部署 其他环境需手动下载安装 自行搭建 | +| 项目路径修改 | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的 | 需要做很多改造 文档说明有限 | +| 国际化 | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化 | 只提供基础功能 其他需自行编写扩展 | +| 代码单例测试 | 提供单例测试 使用方式编写方法与 maven 多环境单测插件 | 只提供基础功能 其他需自行编写扩展 | +| Demo 案例 | 提供框架功能的实际使用案例 单独一个模块提供了很多很全 | 无 | + +## 本框架与 RuoYi 的业务差异 + +| 业务 | 功能说明 | 本框架 | RuoYi | +| ------------ | --------------------------------------------------------------- | ------ | ------------------------------ | +| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 | +| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 | +| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 | +| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 | +| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 | +| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 | +| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 | +| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 | +| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 | +| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 | +| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 | +| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 | +| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 | +| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 | +| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持 CRUD 下载 | 支持 | 仅支持单数据源 | +| 系统接口 | 根据业务代码自动生成相关的 api 接口文档 | 支持 | 支持 | +| 服务监控 | 监视集群系统 CPU、内存、磁盘、堆栈、在线日志、Spring 相关配置等 | 支持 | 仅支持单机 CPU、内存、磁盘监控 | +| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 | +| 在线构建器 | 拖动表单元素生成相应的 HTML 代码。 | 支持 | 支持 | +| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 | + +## 参考文档 + +使用框架前请仔细阅读文档重点注意事项 + +> 初始化项目 必看 +> +> > https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort\_id=4164117&doc\_id=1469725 +> +> 专栏与视频 入门必看 +> +> > https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort\_id=5473272&doc\_id=1469725 +> +> 部署项目 必看 +> +> > https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort\_id=4219382&doc\_id=1469725 +> +> 参考文档 Wiki +> +> > https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages diff --git a/src/zh/news/RuoYi-Vue-Plus-5.1.0.md b/src/zh/news/RuoYi-Vue-Plus-5.1.0.md index f83cbea83a..9be27a2f6a 100644 --- a/src/zh/news/RuoYi-Vue-Plus-5.1.0.md +++ b/src/zh/news/RuoYi-Vue-Plus-5.1.0.md @@ -1,154 +1,154 @@ ---- -title: RuoYi-Vue-Plus 5.1.0 发布 客户端授权、三方授权、传输加密、sms4j、powerjob密集来袭 -author: 疯狂的狮子Li -date: 2023-09-06 -cover: /assets/img/news/RuoYi-Vue-Plus-5.1.0-cover.png -head: - - - meta - - name: 新闻 ---- - -## 开发历程 - -- 2023 年 5 月 开始 5.1.0 计划 历经 1 个月的设计与讨论 -- 2023 年 6 月 开始着手开发 历经 2 个多月的开发 特别感谢团队的小伙伴与一些热心的粉丝 参与功能开发与测试 -- 2023 年 8 月 开始公测 历经将近 1 个月的公测与修复工作(期间成功支持多位使用者生产使用) -- 2023 年 9 月初 正式发布(经过多个小伙伴的生产实践 已基本可尝试生产使用) - -> 关于 4.X 的说明 由于 SpringBoot2.X 与 vue2.X 均在 11 月底停止维护 -> 故而咱们 vue 版本 4.X 也无法再继续更新 -> 介于 4.X 的用户量特别庞大 功能也非常的稳定 -> 计划于 11 月底同 Boot2.X 一同停止更新但还会持续维护修复 bug(修复的形式为直接提交到 4.X 分支停止发版) - -## 视频介绍 - -为了更好的让大家了解 5.1.0 作者录制了相关的视频 供大家快速了解上手 - -- 5.1.0 新功能与变更介绍: https://www.bilibili.com/video/BV1fj411y71X/ -- 搭建与运行: https://www.bilibili.com/video/BV1Fg4y137JK/ -- 生产环境搭建部署: https://www.bilibili.com/video/BV1mL411e7ha/ - -## 更新日志 - -### 重大更新 - -- \[重大更新\] 优化 相关代码 完成代码生成多数据源统一存储(感谢 WangBQ !pr349) -- \[不兼容更新\] 移除 原短信功能 集成更强大的 sms4j 短信工具包(感谢 友杰 !pr367) -- \[不兼容更新\] 对接 powerjob 实现分布式任务调度 删除原有 xxljob 原因为社区不更新功能太少只支持 mysql(感谢 yhan219 !pr359) -- \[重大更新\] 新增 三方授权绑定登录功能 基于 justauth 支持市面上大部分三方登录(感谢 三个三 !pr370) -- \[不兼容更新\] 新增 客户端授权功能 不需要更改任何代码即可完成多端动态对接(感谢 Michelle.Chung !pr379) -- \[重大更新\] 新增 前后端接口请求加密传输 基于 AES+RSA 动态高强度加密(感谢 wdhcr !pr377) -- \[重大更新\] 新增 三方授权登录 对接 maxkey 单点登录 -- \[不兼容更新\] 优化 redis 序列化配置 更改为通用格式(升级需清除 redis 所有数据) - -### 依赖升级 - -- update springboot 3.0.7 => 3.1.3 -- update springboot-admin 3.1.3 => 3.1.5 -- update springdoc 2.1.0 => 2.2.0 -- update spring-mybatis 3.0.1 => 3.0.2 -- update mybatis-plus 3.5.3.1 => 3.5.3.2 -- update easyexcel 3.2.1 => 3.3.2 -- update mapstruct-plus 1.2.3 => 1.3.5 解决修改实体类 idea 不编译问题 -- update satoken 1.34.0 => 1.35.0.RC 优化过期配置 支持多端 token 自定义有效期 -- update dynamic-ds 3.6.1 => 4.1.3 支持 SpringBoot3 -- update sms4j 2.2.0 -- update hutool 5.8.18 => 5.8.20 -- update redisson 3.20.1 => 3.23.4 -- update lock4j 2.2.4 => 2.2.5 -- update aws-java-sdk-s3 1.12.400 => 1.12.540 -- update maven-surefire-plugin 3.0.0 => 3.1.2 - -### 功能更新 - -- update 优化 excel 导出合并 在初始化类时进行数据的处理 -- update 优化 简化 flatten 插件语法写法 -- update 优化 支持本地虚拟域名调试(感谢 代星登 !pr363) -- update 重构 将框架内的 swagger 命名更改为 springdoc 命名避免误解 -- update 重构 将系统内置配置放置到 common 包内独立加载 不允许用户随意修改 -- update 优化 切换 maven 仓库到 华为云(aliyun 依赖不更新拉取不到) -- update 优化 升级 satoken 支持多端 token 自定义有效期功能 -- update 优化 RepeatSubmitAspect 逻辑避免并发请求问题 -- update 优化 在全局异常拦截器中增加两类异常处理 -- update 优化 提供 powerjob 完整 sql 脚本 降低用户使用难度 -- update 优化 StreamUtils 其他方法过滤 null 值(感谢 bleachtred !pr390) -- update 优化 powerjob 端口随着主应用端口飘逸 避免集群冲突 -- update 优化 角色权限支持仅本人权限查看 解决无法查看自己创建的角色问题 -- update 修改代码生成模版,日期范围统一采用 addDateRange 方法(感谢 LiuHao !pr397) -- update 优化 树表生成前端缺少 children 字段 -- update 优化 CryptoFilter null 判断工具 -- update 优化 websocket 路径与 cloud 版本保持一致 -- update 优化 更新登录策略返回值(感谢 zlyx) -- update 修改代码生成模板,调整列表打开对话框和接口请求顺序 -- update 优化 SaInterceptor 拦截器判断 token 客户端 id 是否有效(感谢 zlyx !pr402) -- update 优化 excel 导出字典默认转为下拉框 -- update 优化 兼容 clientid 通过 param 传输 -- update 优化 excel 导出字典转下拉框 无需标记 index 自动处理(感谢 一夏 coco) -- update 优化 简化线程池配置 -- update 优化 屏蔽 powerjob 无用的心跳日志 -- update 优化 适配 mysql 8.0.34 升级连接机制 -- update 优化 加密实现 使用 EncryptUtils 统一处理 -- update 优化 删除字典无用状态字段(基本用不上 禁用后还会导致回显问题) -- update 优化 部门与角色如果绑定了用户则不允许禁用 -- update 优化 岗位如果绑定了用户则不允许禁用 -- update 优化 用户管理 只查询未禁用的部门角色岗位数据 -- update 优化 登录用户增加昵称返回 -- update 优化 将部门管理 负责人选项改为下拉框选择(感谢 Lionel !pr410) -- update 优化 全局异常处理器 业务异常不输出具体堆栈信息 减少无用日志存储 -- update 优化 登录用户缓存 去除冗余统一存储 -- update 优化 放宽菜单权限 角色关联菜单无需管理员 - -### 新增功能 - -- add 增加 RedisUtils 批量删除 hash key 方法 -- add 新增 Oss 上传 File 文件方法(感谢 jenn !pr362) -- add 增加 excel 导出下拉框功能 -- add 新增 RedisUtils.setObjectIfAbsent 如果不存在则设置方法 - -### 修复问题 - -- fix 修复 脱敏注解标记位置错误 -- fix 修复 OssClient 实例多租户相同 key 缓存覆盖问题 -- fix 修复 关闭多租户 脱敏判断问题 -- fix 修复 OssClient 切换服务 实例不正确问题(感谢 jenn !pr360) -- fix 修复 传参类型不正确导致 postgreSql 同步套餐报错问题 -- fix 修复 参数类型修改 未修改校验注解 -- fix 修复 登录校验错误次数未达到上限时 错误次数缓存未设置有效时间问题(感谢 konbai !pr366) -- fix 修复 common-core 包使用 aop 注解 但未添加 aop 实现类导致单独使用报错问题 -- fix 修复 Mapper 多参数未加 @Param 注解问题 -- fix 修复 邮箱登录 查询值错误问题 -- fix 修复 用户篡改管理员角色标识符越权问题 -- fix 修复 字典缓存注解使用错误问题 -- fix 修复 查询部门下拉树未过滤数据权限问题 -- fix 修复 CacheName 缓存 key 存储错误问题 -- fix 修复 代码生成 前端添加或修改表单修改列生成问题 -- fix 修复 新增角色使用内置管理员标识符问题 -- fix 修复 代码生成 前端添加或修改表单修改列生成问题 -- fix 修复 token 过期登出无法清理在线用户问题 -- fix 修复 加密模块数据转换异常问题 -- fix 修复 可能导致异常类无法反序列化问题 -- fix 修复 代码生成 编辑按钮刷新列表问题 -- fix 修复 客户端编辑时授权类型变更未保存的问题(感谢 David Wei !pr400) -- fix 修复 有界队列与优先队列 错误使用问题 -- fix 修复 修复树模板父级编码变量错误 -- fix 修复 部署部分系统出现乱码问题 -- fix 修复 一级菜单无法显示问题 -- fix 修复 可能会存在的越权行为(感谢 丶 Stone !pr416) -- fix 修复 代码生成页面参数缺少逗号问题 - -### 移除功能 - -- remove 移除原有短信功能(建议使用 sms4j) -- remove 移除 xxljob 功能(建议使用 powerjob) - -## 框架文档 - -使用框架前请仔细阅读文档重点注意事项 - -参考文档: https://plus-doc.dromara.org - -## 加公开群 - -加微信备注加群 - -![](/assets/img/news/RuoYi-Vue-Plus-5.1.0-1.png) +--- +title: RuoYi-Vue-Plus 5.1.0 发布 客户端授权、三方授权、传输加密、sms4j、powerjob密集来袭 +author: 疯狂的狮子Li +date: 2023-09-06 +cover: /assets/img/news/RuoYi-Vue-Plus-5.1.0-cover.png +head: + - - meta + - name: 新闻 +--- + +## 开发历程 + +- 2023 年 5 月 开始 5.1.0 计划 历经 1 个月的设计与讨论 +- 2023 年 6 月 开始着手开发 历经 2 个多月的开发 特别感谢团队的小伙伴与一些热心的粉丝 参与功能开发与测试 +- 2023 年 8 月 开始公测 历经将近 1 个月的公测与修复工作(期间成功支持多位使用者生产使用) +- 2023 年 9 月初 正式发布(经过多个小伙伴的生产实践 已基本可尝试生产使用) + +> 关于 4.X 的说明 由于 SpringBoot2.X 与 vue2.X 均在 11 月底停止维护 +> 故而咱们 vue 版本 4.X 也无法再继续更新 +> 介于 4.X 的用户量特别庞大 功能也非常的稳定 +> 计划于 11 月底同 Boot2.X 一同停止更新但还会持续维护修复 bug(修复的形式为直接提交到 4.X 分支停止发版) + +## 视频介绍 + +为了更好的让大家了解 5.1.0 作者录制了相关的视频 供大家快速了解上手 + +- 5.1.0 新功能与变更介绍: https://www.bilibili.com/video/BV1fj411y71X/ +- 搭建与运行: https://www.bilibili.com/video/BV1Fg4y137JK/ +- 生产环境搭建部署: https://www.bilibili.com/video/BV1mL411e7ha/ + +## 更新日志 + +### 重大更新 + +- \[重大更新\] 优化 相关代码 完成代码生成多数据源统一存储(感谢 WangBQ !pr349) +- \[不兼容更新\] 移除 原短信功能 集成更强大的 sms4j 短信工具包(感谢 友杰 !pr367) +- \[不兼容更新\] 对接 powerjob 实现分布式任务调度 删除原有 xxljob 原因为社区不更新功能太少只支持 mysql(感谢 yhan219 !pr359) +- \[重大更新\] 新增 三方授权绑定登录功能 基于 justauth 支持市面上大部分三方登录(感谢 三个三 !pr370) +- \[不兼容更新\] 新增 客户端授权功能 不需要更改任何代码即可完成多端动态对接(感谢 Michelle.Chung !pr379) +- \[重大更新\] 新增 前后端接口请求加密传输 基于 AES+RSA 动态高强度加密(感谢 wdhcr !pr377) +- \[重大更新\] 新增 三方授权登录 对接 maxkey 单点登录 +- \[不兼容更新\] 优化 redis 序列化配置 更改为通用格式(升级需清除 redis 所有数据) + +### 依赖升级 + +- update springboot 3.0.7 => 3.1.3 +- update springboot-admin 3.1.3 => 3.1.5 +- update springdoc 2.1.0 => 2.2.0 +- update spring-mybatis 3.0.1 => 3.0.2 +- update mybatis-plus 3.5.3.1 => 3.5.3.2 +- update easyexcel 3.2.1 => 3.3.2 +- update mapstruct-plus 1.2.3 => 1.3.5 解决修改实体类 idea 不编译问题 +- update satoken 1.34.0 => 1.35.0.RC 优化过期配置 支持多端 token 自定义有效期 +- update dynamic-ds 3.6.1 => 4.1.3 支持 SpringBoot3 +- update sms4j 2.2.0 +- update hutool 5.8.18 => 5.8.20 +- update redisson 3.20.1 => 3.23.4 +- update lock4j 2.2.4 => 2.2.5 +- update aws-java-sdk-s3 1.12.400 => 1.12.540 +- update maven-surefire-plugin 3.0.0 => 3.1.2 + +### 功能更新 + +- update 优化 excel 导出合并 在初始化类时进行数据的处理 +- update 优化 简化 flatten 插件语法写法 +- update 优化 支持本地虚拟域名调试(感谢 代星登 !pr363) +- update 重构 将框架内的 swagger 命名更改为 springdoc 命名避免误解 +- update 重构 将系统内置配置放置到 common 包内独立加载 不允许用户随意修改 +- update 优化 切换 maven 仓库到 华为云(aliyun 依赖不更新拉取不到) +- update 优化 升级 satoken 支持多端 token 自定义有效期功能 +- update 优化 RepeatSubmitAspect 逻辑避免并发请求问题 +- update 优化 在全局异常拦截器中增加两类异常处理 +- update 优化 提供 powerjob 完整 sql 脚本 降低用户使用难度 +- update 优化 StreamUtils 其他方法过滤 null 值(感谢 bleachtred !pr390) +- update 优化 powerjob 端口随着主应用端口飘逸 避免集群冲突 +- update 优化 角色权限支持仅本人权限查看 解决无法查看自己创建的角色问题 +- update 修改代码生成模版,日期范围统一采用 addDateRange 方法(感谢 LiuHao !pr397) +- update 优化 树表生成前端缺少 children 字段 +- update 优化 CryptoFilter null 判断工具 +- update 优化 websocket 路径与 cloud 版本保持一致 +- update 优化 更新登录策略返回值(感谢 zlyx) +- update 修改代码生成模板,调整列表打开对话框和接口请求顺序 +- update 优化 SaInterceptor 拦截器判断 token 客户端 id 是否有效(感谢 zlyx !pr402) +- update 优化 excel 导出字典默认转为下拉框 +- update 优化 兼容 clientid 通过 param 传输 +- update 优化 excel 导出字典转下拉框 无需标记 index 自动处理(感谢 一夏 coco) +- update 优化 简化线程池配置 +- update 优化 屏蔽 powerjob 无用的心跳日志 +- update 优化 适配 mysql 8.0.34 升级连接机制 +- update 优化 加密实现 使用 EncryptUtils 统一处理 +- update 优化 删除字典无用状态字段(基本用不上 禁用后还会导致回显问题) +- update 优化 部门与角色如果绑定了用户则不允许禁用 +- update 优化 岗位如果绑定了用户则不允许禁用 +- update 优化 用户管理 只查询未禁用的部门角色岗位数据 +- update 优化 登录用户增加昵称返回 +- update 优化 将部门管理 负责人选项改为下拉框选择(感谢 Lionel !pr410) +- update 优化 全局异常处理器 业务异常不输出具体堆栈信息 减少无用日志存储 +- update 优化 登录用户缓存 去除冗余统一存储 +- update 优化 放宽菜单权限 角色关联菜单无需管理员 + +### 新增功能 + +- add 增加 RedisUtils 批量删除 hash key 方法 +- add 新增 Oss 上传 File 文件方法(感谢 jenn !pr362) +- add 增加 excel 导出下拉框功能 +- add 新增 RedisUtils.setObjectIfAbsent 如果不存在则设置方法 + +### 修复问题 + +- fix 修复 脱敏注解标记位置错误 +- fix 修复 OssClient 实例多租户相同 key 缓存覆盖问题 +- fix 修复 关闭多租户 脱敏判断问题 +- fix 修复 OssClient 切换服务 实例不正确问题(感谢 jenn !pr360) +- fix 修复 传参类型不正确导致 postgreSql 同步套餐报错问题 +- fix 修复 参数类型修改 未修改校验注解 +- fix 修复 登录校验错误次数未达到上限时 错误次数缓存未设置有效时间问题(感谢 konbai !pr366) +- fix 修复 common-core 包使用 aop 注解 但未添加 aop 实现类导致单独使用报错问题 +- fix 修复 Mapper 多参数未加 @Param 注解问题 +- fix 修复 邮箱登录 查询值错误问题 +- fix 修复 用户篡改管理员角色标识符越权问题 +- fix 修复 字典缓存注解使用错误问题 +- fix 修复 查询部门下拉树未过滤数据权限问题 +- fix 修复 CacheName 缓存 key 存储错误问题 +- fix 修复 代码生成 前端添加或修改表单修改列生成问题 +- fix 修复 新增角色使用内置管理员标识符问题 +- fix 修复 代码生成 前端添加或修改表单修改列生成问题 +- fix 修复 token 过期登出无法清理在线用户问题 +- fix 修复 加密模块数据转换异常问题 +- fix 修复 可能导致异常类无法反序列化问题 +- fix 修复 代码生成 编辑按钮刷新列表问题 +- fix 修复 客户端编辑时授权类型变更未保存的问题(感谢 David Wei !pr400) +- fix 修复 有界队列与优先队列 错误使用问题 +- fix 修复 修复树模板父级编码变量错误 +- fix 修复 部署部分系统出现乱码问题 +- fix 修复 一级菜单无法显示问题 +- fix 修复 可能会存在的越权行为(感谢 丶 Stone !pr416) +- fix 修复 代码生成页面参数缺少逗号问题 + +### 移除功能 + +- remove 移除原有短信功能(建议使用 sms4j) +- remove 移除 xxljob 功能(建议使用 powerjob) + +## 框架文档 + +使用框架前请仔细阅读文档重点注意事项 + +参考文档: https://plus-doc.dromara.org + +## 加公开群 + +加微信备注加群 + +![](/assets/img/news/RuoYi-Vue-Plus-5.1.0-1.png) diff --git a/src/zh/news/RuoYi-Vue-Plus-5.1.2.md b/src/zh/news/RuoYi-Vue-Plus-5.1.2.md index c97163151f..7da2b78218 100644 --- a/src/zh/news/RuoYi-Vue-Plus-5.1.2.md +++ b/src/zh/news/RuoYi-Vue-Plus-5.1.2.md @@ -1,100 +1,100 @@ ---- -title: RuoYi-Vue-Plus 发布 5.1.2 版本 2023 最后一版 -author: 疯狂的狮子Li -date: 2023-12-27 -cover: /assets/img/news/RuoYi-Vue-Plus-5.1.2-0.png -head: - - - meta - - name: 新闻 ---- - -# 更新日志 - -### 依赖升级 - -- update springboot 3.1.5 => 3.1.7 -- update mybatis-boot 3.0.2 => 3.0.3 优化依赖传递 -- update powerjob 4.3.3 => 4.3.6 -- update easyexcel 3.3.2 => 3.3.3 -- update transmittable-thread-local 2.14.2 => 2.14.4 -- update justauth 1.16.5 => 1.16.6 -- update redisson 3.24.1 => 3.24.3 修复订阅重启连接超时问题 - -### 功能更新 - -- update 优化 为 admin 模块 单独增加 ratelimiter 模块 -- update 优化 验证码接口 增加限流配置 -- update 优化 excel 合并注解会根据第一合并列的结果来决定后续的列合并 (感谢 Simple) -- update 优化 SocialUtils 代码 -- update 优化 删除无用异常类 -- update 优化 补全三方登录校验国际化 -- update 优化 sms 组件 预留自动配置类 -- update 更新 关于数据库的说明 -- update 优化 sms 组件 预留自动配置类 -- update 优化 将 OSS 配置 改为全局模式 降低使用难度 保留 sql 便于用户自行扩展(常规项目用不上配置分多租户) -- update 优化 细化 oss 配置管理权限控制 -- update 优化 开启 redisson 脚本缓存 减少网络传输 -- update 优化 删除 hikaricp 官方不推荐使用的配置 jdbc4 协议自带校验方法 -- update 优化 减少 PlusSaTokenDao 不必要的查询优化性能 -- update 优化 更新用户异常提示 使用登录账号 -- update 优化 使用登录用户判断是否登录 提高效率 -- update 优化 重构 LoginHelper 将本地存储代码操作封装 -- update 优化 getTenantId 判断是否开启多租户 -- update 优化 Dockerfile 使用 shell 模式 支持环境变量传入 jvm 参数 -- update 优化 WebSocketUtils 连接关闭改为警告 -- update 优化 excel 多 sheet 页导出 (感谢 May) -- update 优化 删除无用接口实现 -- update 优化 jvm 参数调整 全面启用 zgc -- update 优化 使用动态租户重构业务对租户的逻辑 -- update 优化 TenantHelper 动态租户支持函数式方法 -- update 优化 支持多租户绑定相同的三方登录 -- update 优化 更新用户登录信息方法忽略数据权限 -- update 优化 补全三方绑定时间字段 删除无用 excel 注解 -- update 优化 将登录记录抽取到监听器统一处理 -- update 优化 租户插件 ignoreTable 方法支持动态租户 - -### 新增功能 - -- add 新增 RedisUtils.setObjectIfExists 如果存在则设置方法 -- add 新增 丰富 RedisUtils 对 List Set 类型的操作 -- add 新增 翻译组件 用户昵称翻译实现 -- add 新增 响应加密功能 支持注解强制加密接口数据 (感谢 MichelleChung) - -### 问题修复 - -- fix 修复 selectDictTypeByType 查询方法错误问题 -- fix 修复 一些不正常类无法加载报错问题 -- fix 修复 powerjob sql 脚本针对其他数据库转义符问题 (感谢 branches) -- fix 修复 MybatisSystemException 空指针问题 -- fix 修复 excel 合并注解会根据第一合并列的结果来决定后续的列合并 -- fix 修复 session 多账号共用覆盖问题 改为 tokenSession 独立存储 -- fix 修复 token 失效后 登录获取用户 null 问题 -- fix 修复 powerjob 部署方案 高版本 nginx 不生效问题 -- fix 修复 OssFactory 并发多创建实例问题 -- fix 修复 延迟队列在投递消息未到达时间的时候 服务死机导致重启收不到消息 - -### 前端改动 - -- update 优化 用户头像 img 变量无确定类型问题 -- update 优化 细化 oss 配置管理权限控制 -- update 优化 明确打包命令 -- update 优化 代码中存在的警告 -- update 优化 前端白名单页面放行逻辑 -- update 优化 页面关于权限标识符说明 -- fix 修复 append-to-body 编写错误 (感谢 Ai3\_刘小龙) -- fix 关闭动态路由 tab 页签时不清理组件缓存 (感谢 NickLuo) -- fix 删除重复环境变量 ElUploadInstance (感谢 棉花) -- fix 修复 在线用户 强推按钮点击取消控制台警告问题 -- fix 修复 字典使用 default 样式报警告问题 - -## 框架文档 - -使用框架前请仔细阅读文档重点注意事项 - -参考文档: https://plus\-doc.dromara.org - -## 加公开群 - -加微信备注加群 - -![](/assets/img/news/RuoYi-Vue-Plus-5.1.0-1.png) +--- +title: RuoYi-Vue-Plus 发布 5.1.2 版本 2023 最后一版 +author: 疯狂的狮子Li +date: 2023-12-27 +cover: /assets/img/news/RuoYi-Vue-Plus-5.1.2-0.png +head: + - - meta + - name: 新闻 +--- + +# 更新日志 + +### 依赖升级 + +- update springboot 3.1.5 => 3.1.7 +- update mybatis-boot 3.0.2 => 3.0.3 优化依赖传递 +- update powerjob 4.3.3 => 4.3.6 +- update easyexcel 3.3.2 => 3.3.3 +- update transmittable-thread-local 2.14.2 => 2.14.4 +- update justauth 1.16.5 => 1.16.6 +- update redisson 3.24.1 => 3.24.3 修复订阅重启连接超时问题 + +### 功能更新 + +- update 优化 为 admin 模块 单独增加 ratelimiter 模块 +- update 优化 验证码接口 增加限流配置 +- update 优化 excel 合并注解会根据第一合并列的结果来决定后续的列合并 (感谢 Simple) +- update 优化 SocialUtils 代码 +- update 优化 删除无用异常类 +- update 优化 补全三方登录校验国际化 +- update 优化 sms 组件 预留自动配置类 +- update 更新 关于数据库的说明 +- update 优化 sms 组件 预留自动配置类 +- update 优化 将 OSS 配置 改为全局模式 降低使用难度 保留 sql 便于用户自行扩展(常规项目用不上配置分多租户) +- update 优化 细化 oss 配置管理权限控制 +- update 优化 开启 redisson 脚本缓存 减少网络传输 +- update 优化 删除 hikaricp 官方不推荐使用的配置 jdbc4 协议自带校验方法 +- update 优化 减少 PlusSaTokenDao 不必要的查询优化性能 +- update 优化 更新用户异常提示 使用登录账号 +- update 优化 使用登录用户判断是否登录 提高效率 +- update 优化 重构 LoginHelper 将本地存储代码操作封装 +- update 优化 getTenantId 判断是否开启多租户 +- update 优化 Dockerfile 使用 shell 模式 支持环境变量传入 jvm 参数 +- update 优化 WebSocketUtils 连接关闭改为警告 +- update 优化 excel 多 sheet 页导出 (感谢 May) +- update 优化 删除无用接口实现 +- update 优化 jvm 参数调整 全面启用 zgc +- update 优化 使用动态租户重构业务对租户的逻辑 +- update 优化 TenantHelper 动态租户支持函数式方法 +- update 优化 支持多租户绑定相同的三方登录 +- update 优化 更新用户登录信息方法忽略数据权限 +- update 优化 补全三方绑定时间字段 删除无用 excel 注解 +- update 优化 将登录记录抽取到监听器统一处理 +- update 优化 租户插件 ignoreTable 方法支持动态租户 + +### 新增功能 + +- add 新增 RedisUtils.setObjectIfExists 如果存在则设置方法 +- add 新增 丰富 RedisUtils 对 List Set 类型的操作 +- add 新增 翻译组件 用户昵称翻译实现 +- add 新增 响应加密功能 支持注解强制加密接口数据 (感谢 MichelleChung) + +### 问题修复 + +- fix 修复 selectDictTypeByType 查询方法错误问题 +- fix 修复 一些不正常类无法加载报错问题 +- fix 修复 powerjob sql 脚本针对其他数据库转义符问题 (感谢 branches) +- fix 修复 MybatisSystemException 空指针问题 +- fix 修复 excel 合并注解会根据第一合并列的结果来决定后续的列合并 +- fix 修复 session 多账号共用覆盖问题 改为 tokenSession 独立存储 +- fix 修复 token 失效后 登录获取用户 null 问题 +- fix 修复 powerjob 部署方案 高版本 nginx 不生效问题 +- fix 修复 OssFactory 并发多创建实例问题 +- fix 修复 延迟队列在投递消息未到达时间的时候 服务死机导致重启收不到消息 + +### 前端改动 + +- update 优化 用户头像 img 变量无确定类型问题 +- update 优化 细化 oss 配置管理权限控制 +- update 优化 明确打包命令 +- update 优化 代码中存在的警告 +- update 优化 前端白名单页面放行逻辑 +- update 优化 页面关于权限标识符说明 +- fix 修复 append-to-body 编写错误 (感谢 Ai3\_刘小龙) +- fix 关闭动态路由 tab 页签时不清理组件缓存 (感谢 NickLuo) +- fix 删除重复环境变量 ElUploadInstance (感谢 棉花) +- fix 修复 在线用户 强推按钮点击取消控制台警告问题 +- fix 修复 字典使用 default 样式报警告问题 + +## 框架文档 + +使用框架前请仔细阅读文档重点注意事项 + +参考文档: https://plus\-doc.dromara.org + +## 加公开群 + +加微信备注加群 + +![](/assets/img/news/RuoYi-Vue-Plus-5.1.0-1.png) diff --git a/src/zh/news/RuoYi-Vue-Plus-5.3.0.md b/src/zh/news/RuoYi-Vue-Plus-5.3.0.md new file mode 100644 index 0000000000..00aa8e2f2e --- /dev/null +++ b/src/zh/news/RuoYi-Vue-Plus-5.3.0.md @@ -0,0 +1,400 @@ +--- +title: RuoYi-Vue-Plus 发布 5.3.0 新春版 warm-flow 强强联合 +author: 2025年01月25日 09:20 +date: 2025-01-25 +cover: /assets/img/news/RuoYi-Vue-Plus-5.3.0-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/RuoYi-Vue-Plus-5.3.0-0.png) + +* * * + +# 更新日志 + +### 重大更新 + +* 重构数据权限实现逻辑 支持任意mapper方法标注注解 无需再找真实mapper标注 + +* 重写工作流模块 接入warm-flow工作流 移除flowable工作流(过于复杂 用不明白的人太多) + + +### 依赖升级 + +* update springboot 3.2.11 => 3.4.1 + +* update springboot-admin 3.2.3 => 3.4.1 + +* update mybatis-plus 3.5.8 => 3.5.9 + +* update snailjob 1.1.2 => 1.3.0(感谢 dhb52) + +* update springdoc 2.6.0 => 2.8.3 + +* update redisson 3.37.0 => 3.43.0 + +* update justauth 1.16.6 => 1.16.7 支持多种登录方式 不限于三方登录 + +* update mybatis-plus 3.5.9 => 3.5.10 + +* update hutool 5.8.31 => 5.8.35 + +* update mapstruct-plus 1.4.5 => 1.4.6 + +* update lombok 1.18.34 => 1.18.36 + +* update anyline 20241022 => 20250101 + + +### 功能更新 + +* update 优化 查询oss图片url接口改为query标识符 + +* update 优化 绑定三方与解绑三方校验token是否存在 + +* update 优化 OSS私有桶的临时URL获取方法(感谢 秋辞未寒) + +* update 优化 ws模块替换session的时候关闭session连接 + +* update 优化 数据权限 判断当前注解不满足模板则跳过 + +* update 优化 使用request存储动态租户 避免单请求多次查询redis获取 + +* update 优化 修改部门信息增加事务(感谢 AprilWind) + +* update 优化 增加菜单选择拓展参数(感谢 玲娜贝er) + +* update 优化 jdk21环境开启虚拟线程时的定时任务池(感谢 秋辞未寒) + +* update 优化 sse 如果获取token列表为空 删除userid对应的存储 + +* update 优化 数据权限处理器 增加默认值处理 针对于表达式变量与注解不对应或者表达式变量为null的情况 + +* update 优化 关闭sse后 使用工具报错 + +* update 优化 增加mybatis-plus一键开启/关闭逻辑删除功能 + +* update 优化 修改日志时间展示颜色(感谢 疯狂的牛子Li) + +* update 适配 TOPIAM 2.0 单点登录(感谢 马铃薯头) + +* update 优化 完善微信小程序登录接口逻辑 + +* update 优化 重构DateUtils工具类 更加实用 + +* update 优化 为部门角色岗位用户增加一些常用查询方法 + +* update 优化 登录用户增加岗位数据 + +* update 优化 去除部门查询状态校验 改为前端过滤 便于查看禁用部门下的其他数据 + +* update 优化 部门树增加禁用标志位 + +* update 优化 workflow 模块增加接口文档生成功能 + +* update 优化 代码生成 增加buildQueryWrapper默认排序规则 + +* update 优化 代码生成 创建更新时间被覆盖问题 + +* update 优化 代码生成排序问题(感谢 AprilWind) + +* update 优化 在线用户查询 优先查询租户下数据 减少数据量 + +* update 优化 租户域名使用忽略大小写匹配 + +* update 优化 代码生成器 将数据库字段默认转为小写 避免某些数据库大写出现的问题 + +* update 优化 由于sse重试机制导致经常输出认证失败日志过多 将sse失败改为debug + +* update 优化 有界队列销毁方式 应该使用特殊销毁方法 + +* update 优化 redis序列化 支持更快的apache二进制跨语言序列化方案 + +* update 优化 租户日志模块名 + +* update 优化 增加默认数据权限 "部门及以下或本人数据权限" 选项 + +* update 优化 代码生成器 pg数据库 主键获取不精确问题 + +* update 优化 代码生成器类型获取 + +* update 优化 个人中心强退设备接口路径 + +* update 优化 Dockerfile 消除warn警告 + +* update 优化 补充客户端工具类注释(感谢 AprilWind) + +* update 优化 补充Undertow自定义配置信息注释(感谢 AprilWind) + +* update 优化 拦截爬虫跟踪等垃圾请求 + +* update 优化 将Log记录异常长度改为5000 + +* update 优化 将Log记录参数长度扩充为5000更符合实际需求 + +* update 优化 xss包装器 Parameter 处理 兼容某些容器不允许改参数的情况 + +* update 优化 支持脱敏传多角色多权限标识 + +* update 优化 角色删除清理缓存 + +* update 优化 使用ObjectUtils新增方法封装代码 + +* update 优化 数据权限查询增加缓存 + +* update 优化 代码生成器数字类别判断 + +* update 优化 逻辑删除状态改为1 避免误解 + +* update 重构 将UserConstants改为SystemConstants 统一常量使用 降低使用难度避免误解 + +* update 优化 封装部门基于父id查询方法 + +* update 优化 不传用户id不校验数据权限 + +* update 优化 部门树多基点展示问题 支持相同名称节点并排展示 + +* update 优化 去除OSS桶检测 桶不存在自然会报错无需额外检测 + +* update 优化 限流注解增加固定清理时间 + +* update 优化 sys\_social表 租户id增加默认值 + +* update 优化 jackson 过期方法 + +* update 优化 多租户插件初始化流程 + +* update 优化 去除GenUtils设置createby逻辑 统一走自动注入设置 + +* update 优化 替换RedisUtils中的废弃方法getKeysStreamByPattern及trySetRate(感谢 Lucien\_Lu) + +* update 优化 删除桶自动创建代码逻辑(云厂商限制不允许操作桶) + +* update 优化 角色清理在线用户代码逻辑 + + +### 功能新增 + +* add 新增 导出模板必填、备注注解实现(感谢 liyang) + +* add 新增 基于Redisson的发号器工具(感谢 秋辞未寒) + +* add 新增 validation支持枚举校验(感谢 秋辞未寒) + +* add 新增 validation支持枚举校验(感谢 秋辞未寒) + +* add 新增 对象工具类(感谢 秋辞未寒) + +* add 增加 邮件多附件demo + + +### 问题修复 + +* fix 修复 文件下载 设置content-length无效问题 + +* fix 修复 satoken dao层获取timeout为秒导致丢失毫秒进度问题(临时修复 等satoken官方解决) + +* fix 修复 postgresql的表元数据没有创建时间这个东西(好奇葩) 只能new Date代替 + +* fix 修复 数据权限 多角色多注解包含忽略权限标识符逻辑不正确问题 + +* fix 修复 未开启sse 找不到bean问题 + +* fix 修复 数据权限导致的个人中心的修改头像和修改密码接口错误(感谢 QianRj) + +* fix 修复 部门数据权限缓存错误(感谢 QianRj) + +* fix 修复 三方授权工具部分网站授权缺失参数问题 + +* fix 修复 代码生成 表名中间带有特殊字符被过滤问题 改为开头过滤 + +* fix 修复 字段长度超出数据库限制问题 + +* fix 修复 过滤器正则错误 + +* fix 修复 monitor 设置 context-path 导致退出重新登录404问题 + +* fix 修复 数据权限多角色与权限标识符共用导致的问题 https://gitee.com/dromara/RuoYi-Vue-Plus/issues/IB4CS4 + +* fix 修复 排除websocket包内包含的tomcat依赖(导致一些问题) + +* fix 修复 PageQuery 转json报错问题 + +* fix 修复 sse 关闭接口无法断连问题 + +* fix 修复 PlusSmsDao#clean 方法书写错误 + +* fix 修复 excel级联下拉框数据错误(感谢 Emil.Zhang) + +* fix 修复 某些模块不存在 mp 依赖导致方法报错问题 + +* fix 修复 新版本mp默认使用最新 sqlserver 语法导致代码生成分页报错问题 + +* fix 修复 OssClient 回滚错误修改 + +* fix 修复 注册日志记录状态错误 + + +### 前端改动 + +* update typescript 5.4.5 => 5.7.2 + +* update vite 5.2.12 => 5.4.11 + +* update vue 3.4.34 => 3.5.13 + +* update element-plus 2.7.8 => 2.8.8 + +* update eslint 升级v9版本(感谢 玲娜贝er) + +* update vue-i18n 10.0.5 + +* update 优化 parseTime 提示报错问题 + +* update 优化 国际化 变量提示 + +* update 优化 重写工作流相关页面 + +* update 优化 主题色在深色模式下显示亮度(感谢 LiuHao) + +* update 优化 hasRoles 方法增加超管判断 + +* update 优化 用户页面 增加导入到处权限标识 + +* update 优化 TopNav内链菜单点击没有高亮 + +* update 优化 新增编辑用户 过滤禁用的部门 + +* update 优化 白名单增加正则匹配示例 + +* update 优化 白名单支持对通配符路径匹配 + +* update 优化 i18n $t方法支持ts类型提示(感谢 玲娜贝er) + +* update 优化 登录页多语言按钮样式 + +* update 优化 补充登录页与注册页的国际化内容并添加切换语言按钮(感谢 QianRj) + +* update 优化 eslint升级v9版本 & 更新一些不符合校验规则的代码(感谢 玲娜贝er) + +* update 优化 全代码规范化处理 + +* update 优化 代码生成导入下拉框默认值处理 + +* update 优化 菜单面包屑导航支持多层级显示 + +* update 优化 参数键值更换为多行文本 + +* update 优化 增加默认数据权限 "部门及以下或本人数据权限" 选项 + +* update 优化 permission loadView避免整个modules循环 允许view中间有views文件夹(感谢 admin\_lijinfu) + +* update 优化 个人中心强退设备接口路径 + +* update 优化 直接从@/lang/\*.ts后缀的i18n文件中读取各国语言包信息(感谢 QianRj) + +* update 优化 将同步字典功能迁移到租户管理内 + +* update 优化 重构操作日志详情样式(感谢 玲娜贝er) + +* update 优化 字典缓存使用Map代替Array更高效(感谢 月夜) + +* update 优化 校检文件名是否包含特殊字符 + +* update 优化 getTenantList 接口动态决定是否传token + +* fix 修复 切换租户 tabs过多导致卡住问题 + +* fix 修复 用户管理界面修改按钮权限字符串错误(感谢 QianRj) + +* fix 修复 oss配置页 展示配置key 隐藏主键id + +* fix 修复 页面api过期警告 + +* fix 修复 代码生成列表加载问题你 + +* fix 修复 修复默认关闭Tags-Views时,内链页面打不开 + +* fix 修复 用户选择组件 id类型不统一问题 + +* fix 修复 代码生成 编辑之后查两遍列表的问题 + +* fix 修复 登录无redirect参数404问题 + +* fix 修复 monitor 设置 context-path 导致退出重新登录404问题 + +* fix 修复 手动登出与token过期登出跳转行为不一致问题 + +* fix 修复 关闭sse功能 登出还是会发送sse关闭请求导致报错问题 + +* fix 修复 内嵌页面数据缓存导致与外部页面不一致问题 + + +## 平台简介 + +> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架) + +> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可 +> 活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 + +> 系统演示: https://plus-doc.dromara.org/#/common/demo\_system + +> 前端项目地址: https://gitee.com/JavaLionLi/plus-ui +> 成员前端项目地址: 基于vben5 https://gitee.com/dapppp/ruoyi-plus-vben5 + +> 文档地址: https://plus-doc.dromara.org + +# 本框架与RuoYi的功能差异 + +以下是修复后的Markdown表格,优化了格式对齐、去除了冗余空行,并确保表头与内容对应清晰,可读性大幅提升: + +| 功能 | 本框架 | RuoYi | +| -------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | +| 前端项目 | 采用 Vue3 + TS + ElementPlus 重写 | 基于Vue2/Vue3 + JS | +| 后端项目结构 | 采用插件化 + 扩展包形式,结构解耦,易于扩展 | 模块相互注入耦合严重,难以扩展 | +| 后端代码风格 | 严格遵守Alibaba规范与项目统一配置的代码格式化 | 代码书写与常规结构不同,阅读障碍大 | +| Web容器 | 采用 Undertow(基于 XNIO 的高性能容器) | 采用 Tomcat | +| 权限认证 | 采用 Sa-Token、Jwt,静态使用功能齐全,低耦合、高扩展 | Spring Security 配置繁琐,扩展性极差 | +| 权限注解 | 采用 Sa-Token 支持多类型校验(登录/角色/权限等),支持 `AND/OR` 等复杂表达式 | 只支持是否存在匹配 | +| 三方鉴权 | 采用 JustAuth 第三方登录组件,支持微信、钉钉等数十种三方认证 | 无 | +| 关系数据库支持 | 原生支持 MySQL/Oracle/PostgreSQL/SQLServer,可异构切换(支持达梦、金仓等) | 支持 MySQL、Oracle,不支持同时使用、不支持异构切换 | +| 缓存数据库 | 支持 Redis 5-7,支持分布式限流、分布式队列等新特性 | Redis 仅支持简单 get/set 操作 | +| Redis客户端 | 采用 Redisson(Redis官方推荐),支持多模式,底层优化规避不当用法 | Lettuce + RedisTemplate,支持模式少,连接池(common-pool)Bug多 | +| 缓存注解 | 基于 Spring-Cache 扩展,支持过期时间、最大空闲时间等,注解化实现 | 需手动编写Redis代码逻辑 | +| ORM框架 | 采用 MyBatis-Plus,基于对象操作无需手写SQL,插件丰富 | 采用 MyBatis,基于XML需手写SQL | +| SQL监控 | 采用 p6spy,可输出完整SQL与执行时间监控 | log输出需手动拼接SQL与参数,调试困难 | +| 数据分页 | 基于 MyBatis-Plus 扩展,支持多排序、复杂传参 | 采用 PageHelper,仅支持单查询分页、单排序,扩展性差 | +| 数据权限 | 基于 MyBatis-Plus 插件,无感式过滤,支持自定义规则 | 基于注解+AOP,仅支持部门角色,SQL兼容性差,扩展受限 | +| 数据脱敏 | 注解 + jackson 序列化脱敏,支持多策略(身份证/手机号等),可扩展 | 无 | +| 数据加解密 | 注解 + mybatis 拦截器,支持 BASE64/AES/RSA/SM2/SM4 等 | 无 | +| 接口传输加密 | 动态 AES + RSA 加密请求body,每次请求秘钥不同 | 无 | +| 数据翻译 | 注解 + jackson 序列化翻译,支持映射/直接翻译等,可扩展 | 无 | +| 多数据源框架 | 采用 dynamic-datasource,yml配置管理,支持spel表达式切换 | 基于 druid 手动配置,配置繁琐,支持性差 | +| 多数据源事务 | 采用 dynamic-datasource,支持异构数据库事务回滚 | 不支持 | +| 数据库连接池 | 采用 HikariCP(Spring官方内置),配置简单,性能稳定 | 采用 druid,社区维护差,配置繁琐,性能一般 | +| 数据库主键 | 采用雪花ID,有序唯一,适配分库分表 | 采用数据库自增ID,不支持多数据源主键唯一 | +| WebSocket协议 | 基于 Spring 封装,扩展Token鉴权与分布式会话同步 | 无 | +| SSE推送 | 采用 Spring SSE 实现,扩展Token鉴权与分布式会话同步 | 无 | +| 序列化 | 采用 Jackson(Spring官方内置),稳定可靠 | 采用 fastjson,Bug较多 | +| 分布式幂等 | 参考美团GTIS防重系统简化实现 | 手动编写注解基于AOP实现 | +| 分布式锁 | 采用 Lock4j(底层基于 Redisson) | 无 | +| 分布式任务调度 | 采用 SnailJob,支持分布式、DAG任务流等 | 采用 Quartz,基于数据库锁性能差,集群配置复杂 | +| 文件存储 | 采用 Minio 分布式存储,支持权限管理、加密存储 | 采用本机文件存储,易丢失泄漏,无集群支持 | +| 云存储 | 采用 AWS S3 协议客户端,支持七牛、阿里、腾讯等云厂商 | 不支持 | +| 短信 | 采用 sms4j 融合包,支持数十种厂商,yml配置即可使用 | 不支持 | +| 邮件 | 采用 mail-api 通用协议,支持大部分邮件厂商 | 不支持 | +| 接口文档 | 采用 SpringDoc + javadoc,无注解零入侵(基于Java注释生成) | 采用 Springfox(已停止维护),需编写大量注解 | +| 校验框架 | 采用 Validation,支持注解/工具类校验,注解支持国际化 | 仅支持注解,且不支持国际化 | +| Excel框架 | 采用 Alibaba EasyExcel,扩展自动合并、字典翻译等功能 | 基于 POI 手写实现,功能有限,扩展性差 | +| 工作流支持 | 支持审批、转办、会签等复杂流程操作 | 无 | +| 工具类框架 | 采用 Hutool + Lombok,覆盖90%需求,注解简化代码(get/set等) | 手写工具稳定性差,数量有限,代码臃肿需手动编写get/set | +| 监控框架 | 采用 SpringBoot-Admin,基于actuator,扩展在线日志监控 | 无 | +| 链路追踪 | 采用 Apache SkyWalking,实时查看请求节点与问题定位 | 无 | +| 代码生成器 | 一键生成CRUD代码与页面,适配MP/SpringDoc,支持多数据源生成 | 仅支持单数据源,生成原生结构代码 | +| 部署方式 | 支持 Docker 编排,一键搭建环境 | 原生jar部署,其他环境需手动搭建 | +| 项目路径修改 | 提供详细方案文档,修改简单 | 需大量改造,文档说明有限 | +| 国际化 | 基于请求头动态返回多语种,支持注解国际化,开发难度低 | 仅提供基础功能,需自行扩展 | +| 代码单例测试 | 提供单例测试方法与maven多环境单测插件 | 仅提供基础功能,需自行扩展 | +| Demo案例 | 单独模块提供完整功能使用案例 | 无 | \ No newline at end of file diff --git a/src/zh/news/SMS4j-3.3.4.md b/src/zh/news/SMS4j-3.3.4.md new file mode 100644 index 0000000000..3a16816387 --- /dev/null +++ b/src/zh/news/SMS4j-3.3.4.md @@ -0,0 +1,68 @@ +--- +title: 短信聚合框架 SMS4j 3.3.4 更新公告 +author: SMS4j +date: 2025-03-10 +cover: /assets/img/news/SMS4j-3.3.4-0.png +head: + - - meta + - name: 新闻 +--- + +## SMS4j 3.3.4 更新公告  + +> 即 v3.3.3版本发布到现在,已经过去了近半年的时间,这半年里,作者可谓是“多灾多难”,先是家人生病,然后是自己生病,差十分钟就见到了阎王爷,紧接着是遇到了“毕业”。在这里,也希望大家多注意身体,身体才是革命的本钱。什么内卷什么加班,各位还是悠着来,毕竟小命没了啥都没了。好在现在一切安好,蒸蒸日上。 + +![](/assets/img/news/SMS4j-3.3.4-0.png) + +**今朝有酒今朝醉,明日愁来明日愁** + +> 在这里,也祝愿大家:身体健康,从不脱发,年年加薪,好事不断! + +* * * + +以下是这次的更新内容: + +## 修复  + +* 修复 OA发送消息时,线程池资源未能释放的问题 + +* 修复 调用百度时,缺少请求头的问题 + +* 修复 两个版本邮件代码不同步问题 + +* 修复 luosimao短信手机不需要+86前缀 + +* 修复 异常状态下某些厂商短信失败无限重试问题 + +* 修复 OA与sms同时存在时的Bean冲突问题 + + +## 优化  + +* solon支持升级为3.0.1 + +* 每日最大发送量计时截止为每日0点,而非24小时计时 + +* 部分依赖重复的导入 + +* OA的yml案例示例 + +* 优化部分代码结构 + + +## 新增  + +* 联通一信通平台短信 + +* 短信发送支持http代理配置 + + +最后,希望大家多多支持,如有什么建议还请在issues中告诉我们,我们会尽力优化。只要我还没挂,sms4j就会一直更新下去。还请大家用你发财的手,给我们点一个不要钱的star,您的支持是我们更新的最大动力。 + +Gitee仓库地址 https://gitee.com/dromara/sms4j + +Github仓库地址 https://github.com/dromara/sms4j + +GitCode仓库地址 https://gitcode.com/dromara/SMS4J + +官方文档地址 https://sms4j.com \ No newline at end of file diff --git a/src/zh/news/SQLREST-0.md b/src/zh/news/SQLREST-0.md new file mode 100644 index 0000000000..12c7e76c3e --- /dev/null +++ b/src/zh/news/SQLREST-0.md @@ -0,0 +1,93 @@ +--- +title: 新晋项目SQLREST:一款完全开源的SQL转RESTful API的工具 +author: sqlrest +date: 2025-08-05 +cover: /assets/img/news/SQLREST-0-0.png +head: + - - meta + - name: 新闻 +--- + +#        一款完全开源的SQL转RESTful API的工具 + +## 作者介绍 + +* 网名:三胖(inrgihc) + +* dromara 开源组织成员,项目dromara/dbswitch和dromara/sqlrest作者 + +* 项目地址:https://gitee.com/dromara/sqlrest + + +## 项目诞生 + +作为一个java程序员,快速编写前端调用的CRUD的RESTfull接口,是一项必备的体力劳动工作,事实上需要设计的工作也就是设计库表结构和构思接口SQL实现了; + +当今大数据火热的背景下,常常需要将数据平台中的数据对外提供给其他系统访问,以实现数据的访问共享; + +## 项目概述 + +SQLREST 是一个开源项目,旨在提供一种简单而强大的方式来将 SQL 操作转化为 RESTful API。它支持多种数据库,允许用户通过配置 SQL 语句来创建 API,无需编写复杂的后端逻辑,用户只需选择数据源、输入SQL或脚本、简单path配置即可快速生成API接口。 + +SQLREST的功能包括: + +* SQL直接构建API:通过配置增删改查SQL和参数即可生成 RESTful API。 + +* 多数据库支持:支持常见的20+种数据库,其中包含多款国产数据库。 + +* MyBatis语法支持:支持MyBatis的动态SQL语法。 + +* Groovy脚本支持:支持groovy语法构建复杂场景下的接口。 + +* 参数类型支持:支持整型/浮点型/时间/日期/布尔/字符串/对象等多种类型。 + +* ContentType支持:支持application/x-www-form-urlencoded及application/json等多种请求格式。 + +* 身份认证支持:提供基于 Token 的认证机制,保护 API 安全。 + +* Swagger在线文档:支持自动生成swagger-ui的在线接口文档。 + +* 缓存配置支持:支持 Hazelcast 和 Redis 缓存,提升 API 访问性能。 + +* 流控配置管理:通过 Sentinel 支持流量控制,防止系统过载。 + +* 统一告警对接:支持统一告警系统的对接与触发。 + +* 大模型MCP服务:支持简单配置即可创建MCP的tool。 + + +## 部署体验 + +### 1、系统部署指南 + +x86架构联网CentOS系统一键安装(基于docker-compose): + +``` +curl -k -sSL https://gitee.com/inrgihc/sqlrest/attach_files/2241027/download -o /tmp/sr.sh && systemctl stop firewalld && bash /tmp/sr.sh && rm -f /tmp/sr.sh +``` + +详细参考:https://gitee.com/dromara/sqlrest/tree/master/build-docker/install + +### 2、系统使用说明文档 + +使用说明文档: + +https://www.yuque.com/sanpang-jq7te/nys82g/hur636mthgyhaodb + +![](/assets/img/news/SQLREST-0-0.png) + + + + + +![](/assets/img/news/SQLREST-0-1.png) + + + + + +## 关注sqlrest + +欢迎体验sqlrest工具,可加入sqlrest微信群内交流,sqlrest期望获得更多的代码开发爱好者参与代码贡献。 + +  Gitee: https://gitee.com/dromara/sqlrest \ No newline at end of file diff --git a/src/zh/news/Sa-Token-1.35.0.md b/src/zh/news/Sa-Token-1.35.0.md index c9269383be..30dd13fcfb 100644 --- a/src/zh/news/Sa-Token-1.35.0.md +++ b/src/zh/news/Sa-Token-1.35.0.md @@ -1,98 +1,98 @@ ---- -title: Sa-Token v1.35.0更新,新增动态 active-timeout 能力 -author: 省长 -date: 2023-06-25 -cover: /assets/img/news/Sa-Token-1.35.0.png -head: - - - meta - - name: 新闻 ---- - -## Sa-Token v1.35.0 更新,新增动态 active-timeout 能力 - -Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、分布式 Session 会话、单点登录、OAuth2.0 等一系列权限相关问题。 - -框架针对踢人下线、自动续签、前后台分离、分布式会话…… 等常见业务进行 N 多适配,通过 Sa-Token,你可以以一种极简的方式实现系统的权限认证部分 - -Sa-Token v1.35.0.RC   版本更新包括以下内容: - -- sa-token-core: - -- 优化:前端未提供 token 时,`getTokenSession()` 将抛出未登录异常,而不是返回 null。**\[不向下兼容\]** -- 新增:SaSession 新增字段:`type`、`loginType`、`loginId`、`token`。 -- 重构:全局过滤器抽离 SaFilter 统一接口。 -- 重构:全局过滤器 `includeList`、`excludeList` 改为 public,同时移除对应的 getter 方法。**\[不向下兼容\]** -- 重构:将全局过滤器的 BeforeAuth 认证设为不受 `includeList` 与 `excludeList` 的限制,所有请求都会进入。**\[不向下兼容\]** -- 新增:新增循环生成 token 的算法,用于确保 Token 的唯一性。**\[重要\]** -- 重构:API 接口签名所有方法均迁移至 core 核心模块。**\[重要\]** -- 新增:新增彩色日志打印,更方便的分辨不同日志等级。**\[重要\]** -- 重构:重构概念:临时有效期 -> token 最低活跃频率,过期后 token 冻结。 -- 重构:重构概念:`User-Session` -> `Account-Session`。 -- 新增:新增 `getTokenTimeout(String token)` 方法,获取任意 token 剩余有效期。 -- 优化:在登录时增加判断当前 StpLogic 是否支持 extra 扩展参数模式,如果不支持则打印警告信息。 -- 新增:NotLoginException 增加新场景值 -6、-7,提供更精确的未登录异常描述信息。 -- 新增:TokenSign 新增 tag 挂载参数,可在登录时方便的存储一些客户端特有数据。 **\[重要\]** -- 新增:新增 `SaStrategy#createStpLogic`,用于指定动态创建 StpLogic 时的算法策略。 -- 新增:新增 `@SaCheckOr` 批量注解鉴权:只要满足其中一个注解即可通过验证。 **\[重要\]** -- 重构:重命名:`SaStrategy.me` -> `SaStrategy.instance`。 -- 重构:在登录时强制性检查账号 id 是否为异常值,如果是则登录失败。 -- 重构:重构概念:`activity-timeout` -> `active-timeout`。 **\[重要\]** -- 新增:新增动态 `active-timeout` 能力,可在每次登录时指定 `active-timeout` 值。 **\[重要\]** -- 优化:将 `SaStrategy` 所有策略声明抽离为单独的函数式接口。 -- 新增:增加为 StpLogic 单独配置 `SaTokenConfig` 参数的能力。 - -- sa-token-sso: - -- 修复:在 SSO 模式三中 `ticket` 校验地址配错时,会出现 NPE 的问题 -- 新增:新增 `getData` 接口配置,在模式三拉取数据时可以传递任意参数。**\[重要\]** -- 重构:模式三秘钥配置更改:`sa-token.sso.secretkey=xxx` -> `sa-token.sign.secret-key=xxx`。**\[不向下兼容\]** -- 重构:模式三校验签名方法更改:`SaSsoUtil.checkSign(req)` -> `SaSignUtil.checkRequest(req)`。**\[不向下兼容\]** -- 新增:新增 `sa-token.sso.mode` 配置项,用于约定此系统使用的 SSO 模式。 -- 优化:优化校验 ticket 的逻辑。 - -- sa-token-jwt: - -- 修复:jwt 令牌的签名类型可以被篡改的问题。**\[重要\]** - -- 其它: - -- 优化:所有模块优化注释,更方便开发者阅读源码。 -- 优化:在所有 .java 文件中添加 license 头说明。 -- 优化:修复大部分代码警告。 -- 新增:新增 thymeleaf 标签方言命名空间,增强 ide 代码提示。**\[重要\]** -- 新增:定义 `sa-token-bom` 包,方便引入 sa-token 时对齐版本。 -- 新增:sa-token-dubbo3 插件新增代码示例。 -- 新增:新增跨域文章和示例:Header 参数版和第三方 Cookie 版。**\[重要\]** -- 修复:修复 `sa-token-alone-redis` 在低版本 springboot 下无法启动成功(缺少 `username` 属性)的问题。 - -- 新增插件: - -- 新增:新增 `sa-token-context-dubbo3` 插件。感谢 `@qiudaozhang` 提交的 pr。**\[重要\]** - -- 文档: - -- 新增:部分常见报错排查。 -- 新增:首页图片增加懒加载效果,节省流量。 -- 新增:增加 Cookie 配置示例。 -- 修复:整理 demo 结构目录结构,修复不正确的路径说明。 -- 新增:新增 api-sign 模块文档。 **\[重要\]** - -- 简化包名  **\[重要\]**  **\[不向下兼容\]** - -- `sa-token-dao-redis` -> `sa-token-redis` -- `sa-token-dao-redis-jackson` -> `sa-token-redis-jackson`。 -- `sa-token-dao-redis-fastjson` -> `sa-token-redis-fastjson`。 -- `sa-token-dao-redis-fastjson2` -> `sa-token-redis-fastjson2`。 -- `sa-token-dao-redisson-jackson` -> `sa-token-redis-jackson`。 -- `sa-token-dao-redisx` -> `sa-token-redisx`。 -- `sa-token-context-dubbo` -> `sa-token-dubbo`。 -- `sa-token-context-dubbo3` -> `sa-token-dubbo3`。 -- `sa-token-context-grpc` -> `sa-token-grpc`。 - -cn.dev33sa-token-spring-boot-starter1.35.0.RC - -#### 代码仓库:https://gitee.com/dromara/sa-token - -框架功能结构图 - -![](/assets/img/news/Sa-Token-1.35.0.png) +--- +title: Sa-Token v1.35.0更新,新增动态 active-timeout 能力 +author: 省长 +date: 2023-06-25 +cover: /assets/img/news/Sa-Token-1.35.0.png +head: + - - meta + - name: 新闻 +--- + +## Sa-Token v1.35.0 更新,新增动态 active-timeout 能力 + +Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、分布式 Session 会话、单点登录、OAuth2.0 等一系列权限相关问题。 + +框架针对踢人下线、自动续签、前后台分离、分布式会话…… 等常见业务进行 N 多适配,通过 Sa-Token,你可以以一种极简的方式实现系统的权限认证部分 + +Sa-Token v1.35.0.RC   版本更新包括以下内容: + +- sa-token-core: + +- 优化:前端未提供 token 时,`getTokenSession()` 将抛出未登录异常,而不是返回 null。**\[不向下兼容\]** +- 新增:SaSession 新增字段:`type`、`loginType`、`loginId`、`token`。 +- 重构:全局过滤器抽离 SaFilter 统一接口。 +- 重构:全局过滤器 `includeList`、`excludeList` 改为 public,同时移除对应的 getter 方法。**\[不向下兼容\]** +- 重构:将全局过滤器的 BeforeAuth 认证设为不受 `includeList` 与 `excludeList` 的限制,所有请求都会进入。**\[不向下兼容\]** +- 新增:新增循环生成 token 的算法,用于确保 Token 的唯一性。**\[重要\]** +- 重构:API 接口签名所有方法均迁移至 core 核心模块。**\[重要\]** +- 新增:新增彩色日志打印,更方便的分辨不同日志等级。**\[重要\]** +- 重构:重构概念:临时有效期 -> token 最低活跃频率,过期后 token 冻结。 +- 重构:重构概念:`User-Session` -> `Account-Session`。 +- 新增:新增 `getTokenTimeout(String token)` 方法,获取任意 token 剩余有效期。 +- 优化:在登录时增加判断当前 StpLogic 是否支持 extra 扩展参数模式,如果不支持则打印警告信息。 +- 新增:NotLoginException 增加新场景值 -6、-7,提供更精确的未登录异常描述信息。 +- 新增:TokenSign 新增 tag 挂载参数,可在登录时方便的存储一些客户端特有数据。 **\[重要\]** +- 新增:新增 `SaStrategy#createStpLogic`,用于指定动态创建 StpLogic 时的算法策略。 +- 新增:新增 `@SaCheckOr` 批量注解鉴权:只要满足其中一个注解即可通过验证。 **\[重要\]** +- 重构:重命名:`SaStrategy.me` -> `SaStrategy.instance`。 +- 重构:在登录时强制性检查账号 id 是否为异常值,如果是则登录失败。 +- 重构:重构概念:`activity-timeout` -> `active-timeout`。 **\[重要\]** +- 新增:新增动态 `active-timeout` 能力,可在每次登录时指定 `active-timeout` 值。 **\[重要\]** +- 优化:将 `SaStrategy` 所有策略声明抽离为单独的函数式接口。 +- 新增:增加为 StpLogic 单独配置 `SaTokenConfig` 参数的能力。 + +- sa-token-sso: + +- 修复:在 SSO 模式三中 `ticket` 校验地址配错时,会出现 NPE 的问题 +- 新增:新增 `getData` 接口配置,在模式三拉取数据时可以传递任意参数。**\[重要\]** +- 重构:模式三秘钥配置更改:`sa-token.sso.secretkey=xxx` -> `sa-token.sign.secret-key=xxx`。**\[不向下兼容\]** +- 重构:模式三校验签名方法更改:`SaSsoUtil.checkSign(req)` -> `SaSignUtil.checkRequest(req)`。**\[不向下兼容\]** +- 新增:新增 `sa-token.sso.mode` 配置项,用于约定此系统使用的 SSO 模式。 +- 优化:优化校验 ticket 的逻辑。 + +- sa-token-jwt: + +- 修复:jwt 令牌的签名类型可以被篡改的问题。**\[重要\]** + +- 其它: + +- 优化:所有模块优化注释,更方便开发者阅读源码。 +- 优化:在所有 .java 文件中添加 license 头说明。 +- 优化:修复大部分代码警告。 +- 新增:新增 thymeleaf 标签方言命名空间,增强 ide 代码提示。**\[重要\]** +- 新增:定义 `sa-token-bom` 包,方便引入 sa-token 时对齐版本。 +- 新增:sa-token-dubbo3 插件新增代码示例。 +- 新增:新增跨域文章和示例:Header 参数版和第三方 Cookie 版。**\[重要\]** +- 修复:修复 `sa-token-alone-redis` 在低版本 springboot 下无法启动成功(缺少 `username` 属性)的问题。 + +- 新增插件: + +- 新增:新增 `sa-token-context-dubbo3` 插件。感谢 `@qiudaozhang` 提交的 pr。**\[重要\]** + +- 文档: + +- 新增:部分常见报错排查。 +- 新增:首页图片增加懒加载效果,节省流量。 +- 新增:增加 Cookie 配置示例。 +- 修复:整理 demo 结构目录结构,修复不正确的路径说明。 +- 新增:新增 api-sign 模块文档。 **\[重要\]** + +- 简化包名  **\[重要\]**  **\[不向下兼容\]** + +- `sa-token-dao-redis` -> `sa-token-redis` +- `sa-token-dao-redis-jackson` -> `sa-token-redis-jackson`。 +- `sa-token-dao-redis-fastjson` -> `sa-token-redis-fastjson`。 +- `sa-token-dao-redis-fastjson2` -> `sa-token-redis-fastjson2`。 +- `sa-token-dao-redisson-jackson` -> `sa-token-redis-jackson`。 +- `sa-token-dao-redisx` -> `sa-token-redisx`。 +- `sa-token-context-dubbo` -> `sa-token-dubbo`。 +- `sa-token-context-dubbo3` -> `sa-token-dubbo3`。 +- `sa-token-context-grpc` -> `sa-token-grpc`。 + +cn.dev33sa-token-spring-boot-starter1.35.0.RC + +#### 代码仓库:https://gitee.com/dromara/sa-token + +框架功能结构图 + +![](/assets/img/news/Sa-Token-1.35.0.png) diff --git a/src/zh/news/Sa-Token-v1.41.0.md b/src/zh/news/Sa-Token-v1.41.0.md new file mode 100644 index 0000000000..7e578de908 --- /dev/null +++ b/src/zh/news/Sa-Token-v1.41.0.md @@ -0,0 +1,379 @@ +--- +title: Sa-Token v1.41.0 发布,来看看有没有令你心动的功能! +author: sa小风筝 +date: 2025-03-24 +cover: /assets/img/news/Sa-Token-v1.41.0-0.png +head: + - - meta + - name: 新闻 +--- + +Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:**登录认证**、**权限认证**、**单点登录**、**OAuth2.0**、**微服务网关鉴权** 等一系列权限相关问题。🔐 + +**目前最新版本 `v1.41.0` 已推送至 `Maven` 中央仓库** 🎉,大家可以通过如下方式引入: + +``` + + +    cn.dev33 +    sa-token-spring-boot-starter +    1.41.0 + +``` + +该版本包含大量 ⛏️️️新增特性、⛏️底层重构、⛏️️️代码优化 等,下面容我列举几条比较重要的更新内容供大家参阅: + +### 🛡️ 更新点1:防火墙模块新增 hooks 扩展机制 + +本次更新针对防火墙新增了多条校验规则,之前的规则为: + +* • path 白名单放行。 + +* • path 黑名单拦截。 + +* • path 危险字符校验。 + + +本次新增规则为: + +* • path 禁止字符校验。 + +* • path 目录遍历符检测(优化了检测算法)。 + +* • 请求 host 检测。 + +* • 请求 Method 检测。 + +* • 请求 Header 头检测。 + +* • 请求参数检测。 + + +并且本次更新开放了 hooks 机制,允许开发者注册自定义的校验规则 🛠️,参考如下: + +``` +@PostConstruct +public void saTokenPostConstruct() { +    // 注册新 hook 演示,拦截所有带有 pwd 参数的请求,拒绝响应  +    SaFirewallStrategy.instance.registerHook((req, res, extArg)->{ +        if(req.getParam("pwd") != null) { +            throw new FirewallCheckException("请求中不可包含 pwd 参数"); +        } +    }); +} +``` + +文档直达地址:Sa-Token 防火墙 🔗 + +### 💡 更新点2:新增基于 SPI 机制的插件体系 + +之前在 Sa-Token 中也有插件体系,不过都是利用 SpringBoot 的 SPI 机制完成组件注册的。 + +这种注册机制有一个问题,就是插件只能在 SpringBoot 环境下正常工作,在其它环境,比如 Solon 项目中,就只能手动注册插件才行 😫。 + +也就是说,严格来讲,这些插件只能算是 SpringBoot 的插件,而非 Sa-Token 框架的插件 🌐。 + +为了提高插件的通用性,Sa-Token 设计了自己的 SPI 机制,使得这些插件可以在更多的项目环境下正常工作 🚀。 + +第一步:实现插件注册类,此类需要 `implements SaTokenPlugin` 接口 👨💻: + +``` +/** + * SaToken 插件安装:插件作用描述 + */ +public class SaTokenPluginForXxx implements SaTokenPlugin { +    @Override +    public void install() { +        // 书写需要在项目启动时执行的代码,例如: +        // SaManager.setXxx(new SaXxxForXxx()); +    } +} +``` + +第二步:在项目的 `resources\META-INF\satoken\` 文件夹下 📂 创建 `cn.dev33.satoken.plugin.SaTokenPlugin` 文件,内容为该插件注册类的完全限定名: + +``` +cn.dev33.satoken.plugin.SaTokenPluginForXxx +``` + +这样便可以在项目启动时,被 Sa-Token 插件管理器加载到此插件,执行插件注册类的 install 方法,完成插件安装 ✅。 + +文档直达地址:Sa-Token 插件开发指南 🔗 + +### 🎛️ 更新点3:重构缓存体系,将数据读写与序列化操作分离 + +在之前的版本中,Redis 集成通常和具体的序列化方式耦合在一起,这不仅让 Redis 相关插件产生大量的重复冗余代码,也让大家在选择 Redis 插件时严重受限。⚠️ + +本次版本更新彻底重构了此模块,将数据读写与序列化操作分离,使其每一块都可以单独自定义实现类,做到灵活扩展 ✨,例如: + +* • 1️⃣ SaTokenDao 数据读写可以选择:RedisTemplate、Redisson、ConcurrentHashMap、Hutool-Timed-Cache 等不同实现类。 + +* • 2️⃣ SaSerializerTemplate 序列化器可以选择:Base64编码、Hex编码、ISO-8859-1编码、JSON序列化等不同方式。 + +* • 3️⃣ JSON 序列化可以选择:Jackson、Fastjson、Snack3 等组件。 + + +所有实现类均可以按需选择,自由搭配,大大提高灵活性🏗️。 + +### ⚙️️ 更新点4:SaLoginParameter 登录参数类新增大量配置项 + +SaLoginParameter (前SaLoginModel) 用于控制登录操作中的部分细节行为,本次新增的配置项有: + +* • isConcurrent:决定是否允许同一账号多地同时登录(为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)。🌍 + +* • isShare:在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)。🔄 + +* • maxLoginCount:同一账号最大登录数量,超出此数量的客户端将被自动注销,-1代表不限制数量。🚫 + +* • maxTryTimes:在创建 token 时的最高循环次数,用于保证 token 唯一性(-1=不循环尝试,直接使用。⏳ + +* • deviceId:此次登录的客户端设备id,用于判断后续某次登录是否为可信任设备。📱 + +* • terminalExtraData:本次登录挂载到 SaTerminalInfo 的自定义扩展数据。📦 + + +以上大部分配置项在之前的版本中也有支持,不过它们都被定义在了全局配置类 SaTokenConfig 之上,本次更新支持在 SaLoginParameter 中定义这些配置项, +这将让登录策略的控制变得更加灵活。✨ + +### 🚪 更新点5:新增 SaLogoutParameter 注销参数类 + +SaLogoutParameter 用于控制注销操作中的部分细节行为️,例如: + +通过 `Range` 参数决定注销范围 🎯: + +``` +// 注销范围: TOKEN=只注销当前 token 的会话,ACCOUNT=注销当前 token 指向的 loginId 其所有客户端会话 +StpUtil.logout(new SaLogoutParameter().setRange(SaLogoutRange.TOKEN)); +``` + +通过 `DeviceType` 参数决定哪些登录设备类型参与注销 💻: + +``` +// 指定 10001 账号,所有 PC 端注销下线,其它端如 APP 端不受影响  +StpUtil.logout(10001, new SaLogoutParameter().setDeviceType("PC")); +``` + +还有其它参数此处暂不逐一列举,文档直达地址:Sa-Token 登录参数 & 注销参数 🔗 + +### 🐞 更新点6:修复 `StpUtil.setTokenValue("xxx")`、`loginParameter.getIsWriteHeader()` 空指针的问题。 + +这个没啥好说的,有 bug 🐛 必须修复。 + +fix issue:#IBKSM0 🔗 + +### ✨ 更新点7:API 参数签名模块升级 + +* • 1、新增了 @SaCheckSign 注解,现在 API 参数签名模块也支持注解鉴权了。🆕 + +* • 2、新增自定义签名的摘要算法,现在不仅可以 md5 算法计算签名,也支持 sha1、sha256 等算法了。🔐 + +* • 3、新增多应用模式: + + +多应用模式就是指,允许在对接多个系统时分别使用不同的秘钥等配置项,配置示例如下 📝: + +``` +sa-token:  +    # API 签名配置 多应用模式 +    sign-many: +        # 应用1 +        xm-shop: +            secret-key:0123456789abcdefg +            digest-algo:md5 +        # 应用2 +        xm-forum: +            secret-key:0123456789hijklmnopq +            digest-algo:sha256 +        # 应用3 +        xm-video: +            secret-key:12341234aaaaccccdddd +            digest-algo: sha512 +``` + +然后在签名时通过指定 appid 的方式获取对应的 SignTemplate 进行操作 👨💻: + +``` +// 创建签名示例 +String paramStr = SaSignMany.getSignTemplate("xm-shop").addSignParamsAndJoin(paramMap); + +// 校验签名示例 +SaSignMany.getSignTemplate("xm-shop").checkRequest(SaHolder.getRequest()); +``` + +### ⚡ 更新点8:新增 sa-token-caffeine 插件,用于整合 Caffeine + +Caffeine 是一个基于 Java 的高性能本地缓存库,本次新增 sa-token-caffeine 插件用于将 Caffeine 作为 Sa-Token 的缓存层,存储会话鉴权数据。🚀 +这进一步丰富了 Sa-Token 的缓存层插件生态。🌱 + +``` + + +    cn.dev33 +    sa-token-caffeine +    1.41.0 + +``` + +### 🎪 更新点9:新增 sa-token-serializer-features 序列化扩展包 + +引入此插件可以为 Sa-Token 提供一些有意思的序列化方案。(娱乐向,不建议上生产 🎭) + +例如:以base64 编码,采用:元素周期表 🧪、特殊符号 🔣、或 emoji 😊 作为元字符集存储数据 : + +![](/assets/img/news/Sa-Token-v1.41.0-0.png) + +sa-custom-serializer-yszqb.png + +![](/assets/img/news/Sa-Token-v1.41.0-1.png) + +sa-custom-serializer-tsfh.png + +![](/assets/img/news/Sa-Token-v1.41.0-2.png) + +sa-custom-serializer-emoji.png + +![](/assets/img/news/Sa-Token-v1.41.0-3.png) + +sa-custom-serializer-emoji2.png + +### 📜 完整更新日志 + +除了以上提到的几点以外,还有更多更新点无法逐一详细介绍,下面是 v1.41.0 版本的完整更新日志: + +* • core: + + +* • 修复:修复 `StpUtil.setTokenValue("xxx")`、`loginParameter.getIsWriteHeader()` 空指针的问题。 fix: #IBKSM0 + +* • 修复:将 `SaDisableWrapperInfo.createNotDisabled()` 默认返回值封禁等级改为 -2,以保证向之前版本兼容。 + +* • 新增:新增基于 SPI 的插件体系。 **\[重要\]** + +* • 重构:JSON 转换器模块。 **\[重要\]** + +* • 新增:新增 serializer 序列化模块,控制 `Object` 与 `String` 的序列化方式。 **\[重要\]** + +* • 重构:重构防火墙模块,增加 hooks 机制。 **\[重要\]** + +* • 新增:防火墙新增:请求 path 禁止字符校验、Host 检测、请求 Method 检测、请求头检测、请求参数检测。重构目录遍历符检测算法。 + +* • 重构:重构 `SaTokenDao` 模块,将序列化与存储操作分离。 **\[重要\]** + +* • 重构:重构 `SaTokenDao` 默认实现类,优化底层设计。 + +* • 新增:`isLastingCookie` 配置项支持在全局配置中定义了。 + +* • 重构:`SaLoginModel` -> `SaLoginParameter`。 **\[不向下兼容\]** + +* • 重构:`TokenSign` -> `SaTerminalInfo`。 **\[不向下兼容\]** + +* • 新增:`SaTerminalInfo` 新增 `extraData` 自定义扩展数据设置。 + +* • 新增:`SaLoginParameter` 支持配置 `isConcurrent`、`isShare`、`maxLoginCount`、`maxTryTimes`。 + +* • 新增:新增 `SaLogoutParameter`,用于控制注销会话时的各种细节。 **\[重要\]** + +* • 新增:新增 `StpLogic#isTrustDeviceId` 方法,用于判断指定设备是否为可信任设备。 + +* • 新增:新增 `StpUtil.getTerminalListByLoginId(loginId)`、`StpUtil.forEachTerminalList(loginId)` 方法,以更方便的实现单账号会话管理。 + +* • 升级:API 参数签名配置支持自定义摘要算法。 + +* • 新增:新增 `@SaCheckSign` 注解鉴权,用于 API 签名参数校验。 + +* • 新增:API 参数签名模块新增多应用模式。 fix: #IAK2BI, #I9SPI1, #IAC0P9 **\[重要\]** + +* • 重构:全局配置 `is-share` 默认值改为 false。 **\[不向下兼容\]** + +* • 重构:踢人下线、顶人下线默认将删除对应的 token-session 对象。 + +* • 优化:优化注销会话相关 API。 + +* • 重构:登录默认设备类型值改为 DEF。 **\[不向下兼容\]** + +* • 重构:`BCrypt` 标注为 `@Deprecated`。 + +* • 新增:`sa-token-quick-login` 支持 `SpringBoot3` 项目。 fix: #IAFQNE、#673 + +* • 新增:`SaTokenConfig` 新增 `replacedRange`、`overflowLogoutMode`、`logoutRange`、`isLogoutKeepFreezeOps`、`isLogoutKeepTokenSession` 配置项。 + + +* • OAuth2: + + +* • 重构:重构 sa-token-oauth2 插件,使注解鉴权处理器的注册过程改为 SPI 插件加载。 + + +* • 插件: + + +* • 新增:`sa-token-serializer-features` 插件,用于实现各种形式的自定义字符集序列化方案。 + +* • 新增:`sa-token-fastjson` 插件。 + +* • 新增:`sa-token-fastjson2` 插件。 + +* • 新增:`sa-token-snack3` 插件。 + +* • 新增:`sa-token-caffeine` 插件。 + + +* • 单元测试: + + +* • 新增:`sa-token-json-test` json 模块单元测试。 + +* • 新增:`sa-token-serializer-test` 序列化模块单元测试。 + + +* • 文档: + + +* • 新增:QA “多个项目共用同一个 redis,怎么防止冲突?” + +* • 优化:补全 OAuth2 模块遗漏的相关配置项。 + +* • 优化:优化 OAuth2 简述章节描述文档。 + +* • 优化:完善 “SSO 用户数据同步 / 迁移” 章节文档。 + +* • 修正:补全项目目录结构介绍文档。 + +* • 新增:文档新增 “登录参数 & 注销参数” 章节。 + +* • 优化:优化“技术求助”按钮的提示文字。 + +* • 新增:新增 `preview-doc.bat` 文件,一键启动文档预览。 + +* • 完善:完善 Redis 集成文档。 + +* • 新增:新增单账号会话查询的操作示例。 + +* • 新增:新增顶人下线 API 介绍。 + +* • 新增:新增 自定义序列化插件 章节。 + + +* • 其它: + + +* • 新增:新增 `sa-token-demo/pom.xml` 以便在 idea 中一键导入所有 demo 项目。 + +* • 删除:删除不必要的 `.gitignore` 文件 + +* • 重构:重构 `sa-token-solon-plugin` 插件。 + +* • 新增:新增设备锁登录示例。 + + +更新日志在线文档直达链接:https://sa-token.cc/doc.html#/more/update-log + +### 🌟 其它 + +代码仓库地址:https://gitee.com/dromara/sa-token + +框架功能结构图: + +![](/assets/img/news/Sa-Token-v1.41.0-4.png) + +js \ No newline at end of file diff --git a/src/zh/news/Sa-Token-v1.42.0.md b/src/zh/news/Sa-Token-v1.42.0.md new file mode 100644 index 0000000000..26f967c7c3 --- /dev/null +++ b/src/zh/news/Sa-Token-v1.42.0.md @@ -0,0 +1,311 @@ +--- +title: Sa-Token v1.42.0 发布,新增 API Key、TOTP 验证码、RefreshToken 反查等能力 +author: 2025年04月13日 11:35 +date: 2025-04-13 +cover: /assets/img/news/Sa-Token-v1.42.0-0.png +head: + - - meta + - name: 新闻 +--- + +Sa-Token 是一款 **免费**、**开源** 的轻量级 Java 权限认证框架,主要解决:**登录认证**、**权限认证**、**单点登录**、**OAuth2.0**、**微服务网关鉴权** 等一系列权限相关问题。🔐 + +**目前最新版本 `v1.42.0` 已推送至 `Maven` 中央仓库** 🎉,大家可以通过如下方式引入: + +``` + + +    cn.dev33 +    sa-token-spring-boot-starter +    1.42.0 + +``` + +该版本包含大量 ⛏️️️新增特性、⛏️底层重构、⛏️️️代码优化 等,下面容我列举几条比较重要的更新内容供大家参阅: + +### 🗝️ 更新点1:新增 API Key 模块 + +如果你曾经对接过 ChatGPT、DeepSeek 等大模型平台的开放接口,那你一定对 API Key 不陌生。🤝 + +API Key 是一种接口调用密钥,类似于会话 token ,但比会话 token 具有更灵活的权限控制。🔑 + +本次更新带来了 API Key 的全流程管理,支持为指定账号签发、校验、禁用、删除 API Key 。 +同时每个 API Key 都可以单独设置不同的 scope 权限,以便在不同的场景下使用不同的 API Key,做到秘钥相互隔离,最小化授权。🛡️ + +为了更好的展示此模块的能力,我们专门制作了一个 demo 示例: + +![](/assets/img/news/Sa-Token-v1.42.0-0.png) + +sa-api-key + +示例仓库地址:sa-token-demo-apikey 🔗 + +在这个示例中,你可以登录测试不同的账号,并为它们签发 API Key,设置 scope 权限,并使用不同的 API Key 测试调用接口,观察响应结果。 🧪 + +框架默认将所有 API Key 信息保存在缓存中,这可以称之为“缓存模式”,在这种模式下,重启缓存库后,数据将会丢失。⚠️ + +框架预留了 SaApiKeyDataLoader 接口,以便你将数据的加载切换为 “数据库模式”,做到数据长久有效保存。 💾 + +在线文档直达地址:API Key 接口调用秘钥 🔗 + +### 🔍 更新点2:重构 TempToken 模块新增 value 反查机制 + +在 Sa-Token 文档中有一段这样的示例:📚 + +![](/assets/img/news/Sa-Token-v1.42.0-1.png) + +sa-refresh-token + +该示例演示了如何通过临时 Token 认证模块,创建 `RefreshToken` 为登录会话做到双 Token 的效果。🔄 + +但是有一天我在官网 sa-token 小助手接收到一位用户的咨询: 💬 + +![](/assets/img/news/Sa-Token-v1.42.0-2.png) + +sa-refresh-token-wnglian-zixun + +该用户指出,是否可以为 `RefreshToken` 提供反查机制,以便获取某个账号历史签发的 全部 `RefreshToken`。 + +必须安排!💪🏆 + +此次版本更新,允许程序在创建 refresh-token 时,指定第三个参数,该参数表示是否允许框架记录 Token 索引信息: + +``` +SaTempUtil.createToken("10001", 2592000, true); +``` + +指定为 false 代表不记录索引,只生成 token,指定为 true 代表记录索引信息,以便日后可以通过 value 反查历史签发的所有 token。🔍 + +例如我们可以通过 `SaTempUtil.getTempTokenList("xxx")` 方法获取指定账号所有历史签发的 `RefreshToken` 记录: + +``` +List refreshTokenList = SaTempUtil.getTempTokenList("10001"); +``` + +在线文档直达地址:临时 Token 令牌认证 🔗 + +### ⏱️ 更新点3:新增 TOTP 算法实现 + +TOTP 是一种动态密码算法,用于生成短暂有效的数字验证码(通常6-8位)️。它的核心原理是:结合密钥与当前时间,通过哈希运算生成一次性密码。⏱ + +TOTP 一般有以下应用场景: + +* • 1、登录时的双因子认证:用户输入账号密码后还需要再输入 TOTP 验证码才可以登录成功。 🔐 + +* • 2、敏感操作的二次认证:用户在进行一些高危敏感操作时,需要输入 TOTP 验证码才可以继续操作。🛡️ + +* • 3、替代短信验证码:TOTP 验证码无需网络,可离线计算生成,一定程度上可以替代短信验证码验证身份。📴 + + +本次版本新增了 TOTP 验证码的生成与校验功能,这将方便大家为自己的系统添加双因子认证能力。🚀 + +### ⚙️ 更新点4:重构升级 `SaTokenContext` 上下文读写策略 + +这可能是近几个版本中最底层的一次重构,几乎完全推翻了之前上下文模块的设计。💥 + +在之前的版本中,Sa-Token 对接不同的 Web 框架需要利用这些 Web 框架的原生上下文能力来构建 Sa-Token 的上下文。 🌐 + +本次更新 Sa-Token 利用 ThreadLocal 实现了自己的上下文存储机制,这将带来以下好处: + +* • 1、可以更方便、更简单的对接更多的 Web 框架。 + +* • 2、可在异步场景中临时 Mock 一个上下文,调用 Sa-Token 框架同步 API。 + +* • 3、彻底删除了二级上下文模块,做到了 Web 请求与 RPC 请求的上下文统一。 + +* • 4、在防火墙 hook 里也可以调用 Sa-Token 同步 API 了。 + + +### 🌐 更新点5:新增 `CORS` 跨域策略处理函数,提供不同架构下统一的跨域处理方案 + +在之前的版本中,跨域处理总是要写在全局鉴权过滤器中,属于“鉴权之下的额外补充操作”。⏳ + +新版本专门提供了一个 CORS 跨域处理策略组件,以后再也不用仅仅为了跨域就书写一个长长的鉴权过滤器组件了。🚀 + +``` +/** + * CORS 跨域处理 + */ +@Bean +public SaCorsHandleFunction corsHandle() { +    return (req, res, sto) -> { +        res. +                // 允许指定域访问跨域资源 +                setHeader("Access-Control-Allow-Origin", "*") +                // 允许所有请求方式 +                .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE") +                // 有效时间 +                .setHeader("Access-Control-Max-Age", "3600") +                // 允许的header参数 +                .setHeader("Access-Control-Allow-Headers", "*"); + +        // 如果是预检请求,则立即返回到前端 +        SaRouter.match(SaHttpMethod.OPTIONS) +                .free(r -> System.out.println("--------OPTIONS预检请求,不做处理")) +                .back(); +    }; +} +``` + +开源仓库示例:sa-token-demo-cross 🔗 + +### 🔑 更新点6:`sa-token-quick-login` 插件支持 `Http Basic` 方式通过认证 + +`sa-token-quick-login` 可以快速、方便的为项目注入一个登录页面,当我们引入依赖后: + +``` + +    cn.dev33 +    sa-token-quick-login +    1.42.0 + +``` + +启动类: + +``` +@SpringBootApplication +public class SaTokenQuickDemoApplication { +    public static void main(String[] args) { +        SpringApplication.run(SaTokenQuickDemoApplication.class, args); +         +        System.out.println("\n------ 启动成功 ------"); +        System.out.println("name: " + SaQuickManager.getConfig().getName()); +        System.out.println("pwd:  " + SaQuickManager.getConfig().getPwd()); +    } +} +``` + +测试 Controller + +``` +@RestController +publicclassTestController { +    @RequestMapping({"/", "/index"}) +    public String index() { +        Stringstr="
" +                + "

资源页 (登录后才可进入本页面)

" +                + "
" +                + "

Sa-Token " + SaTokenConsts.VERSION_NO + "

"; +        return str; +    } +} +``` + +启动项目,使用浏览器访问:`http://localhost:8081`,首次访问时,由于处于未登录状态,会被强制进入登录页面 🚪: + +![](/assets/img/news/Sa-Token-v1.42.0-3.png) + +登录 + +使用默认账号:`sa / 123456`进行登录,会看到资源页面 + +![](/assets/img/news/Sa-Token-v1.42.0-4.png) + +登录 + +新版本中更新了通过 Http Basic 的方式直接进行认证的能力: + +``` +http://sa:123456@localhost:8081/ +``` + +这将非常有助于大家在专门的 API 测试工具下进行 quick-login 相关资源接口的测试。🧪 + +### 📜 完整更新日志 + +除了以上提到的几点以外,还有更多更新点无法逐一详细介绍,下面是 v1.42.0 版本的完整更新日志: + +* • core: + + +* • 新增: 新增 `API Key` 模块。 **\[重要\]** + +* • 新增: 新增 `TOTP` 实现。 **\[重要\]** + +* • 重构:重构 `TempToken` 模块,新增 value 反查 token 机制。 **\[重要\]** + +* • 升级: 重构升级 `SaTokenContext` 上下文读写策略。 **\[重要\]** + +* • 新增: 新增 Mock 上下文模块。 **\[重要\]** + +* • 删除: 删除二级上下文模块。 + +* • 新增: 新增异步场景使用 demo。 **\[重要\]** + +* • 新增: 新增 `Base32` 编码工具类。 + +* • 新增:新增 `CORS` 跨域策略处理函数,提供不同架构下统一的跨域处理方案。 + +* • 新增:`renewTimeout` 续期方法增加 token 终端信息有效性校验。 + +* • 新增: 全局配置项 `cookieAutoFillPrefix`:cookie 模式是否自动填充 token 前缀。 + +* • 新增: 全局配置项 `rightNowCreateTokenSession`:在登录时,是否立即创建对应的 `Token-Session`。 + +* • 优化:优化 `Token-Session` 获取算法,减少缓存读取次数。 + +* • 新增:`SaLoginParameter` 支持配置 `SaCookieConfig`,以配置 Cookie 相关参数。 + +* • 修改:防火墙校验过滤器的注册顺序 修改为 -102。 + +* • 新增:防火墙 `hook` 注册新增 `registerHookToFirst`、`registerHookToSecond` 方法,以便更灵活的控制 hook 顺序。 + + +* • 插件: + + +* • 新增: `sa-token-quick-login` 插件支持 `Http Basic` 方式通过认证。 + + +* • 单元测试: + + +* • 补全:补全 `Temp Token` 模块单元测试。 + + +* • 文档: + + +* • 补全:补全赞助者名单。 + +* • 修复:修复 `Thymeleaf` 集成文档不正确的依赖示例说明。 + +* • 修复:修复 `unionid` 章节错误描述。 + +* • 优化:采用更细致的描述优化SSO模式三单点注销步骤。 + +* • 新增:登录认证文档添加 Cookie 查看步骤演示图。 + +* • 新增:多账号模式新增注意点:运行时不可更改 `LoginType`。 + +* • 新增: 多账号模式QA:在一个接口里获取是哪个体系的账号正在登录。 + +* • 新增:新增QA:解决低版本 `SpringBoot (<2.2.0)` 引入 Sa-Token 报错的问题。 + +* • 新增:新增QA:前后端一体项目下,在拦截未登录进入登录页面时,如何登录完成后原路返回? + +* • 新增:新增QA:Sa-Token 集成 Redis 如何集群? + +* • 新增:新增QA:如何自定义框架读取 token 的方式? + +* • 新增:新增QA:修改 `hosts` 文件无效可能原因排查。 + +* • 新增:新增QA:如何防止 CSRF 攻击。 + +* • 新增: “异步 & Mock 上下文” 章节。 + +* • 升级:升级“自定义 SaTokenContext 指南”章节文档。 + + +更新日志在线文档直达链接:https://sa-token.cc/doc.html#/more/update-log + +### 🌟 其它 + +代码仓库地址:https://gitee.com/dromara/sa-token + +框架功能结构图: + +![](/assets/img/news/Sa-Token-v1.42.0-5.png) + +js \ No newline at end of file diff --git a/src/zh/news/Sa-Token-v1.43.0.md b/src/zh/news/Sa-Token-v1.43.0.md new file mode 100644 index 0000000000..f1d4d981f9 --- /dev/null +++ b/src/zh/news/Sa-Token-v1.43.0.md @@ -0,0 +1,375 @@ +--- +title: Sa-Token v1.43.0 发布,新增 SSO 单设备注销、消息推送,多 Access-Token 并存能力 +author: 2025年05月21日 09:28 +date: 2025-05-21 +cover: /assets/img/news/Sa-Token-v1.43.0-0.png +head: + - - meta + - name: 新闻 +--- + +Sa-Token 是一款 **免费**、**开源** 的轻量级 Java 权限认证框架,主要解决:**登录认证**、**权限认证**、**单点登录**、**OAuth2.0**、**微服务网关鉴权** 等一系列权限相关问题。🔐 + +![](/assets/img/news/Sa-Token-v1.43.0-0.png) + +sa-token-jss--tran + +**目前最新版本 `v1.43.0` 已推送至 `Maven` 中央仓库** 🎉,大家可以通过如下方式引入: + +``` + + +    cn.dev33 +    sa-token-spring-boot-starter +    1.43.0 + +``` + +该版本包含大量 ⛏️️️新增特性、⛏️底层重构、⛏️️️代码优化 等,下面容我列举几条比较重要的更新内容供大家参阅: + +### ⛏️️️️ 更新点1:单点登录模块新增“单设备注销”模式 + +有单点登录就必然要有单点注销,目前 SSO 单点登录模块包含:单应用注销、单设备注销、全端注销 三种模式,怎么理解三者的差异呢? + +举个例子,用户在 Chrome 浏览器 登录了 应用A、应用B、应用C,又在 Firefox 浏览器登录了应用A、应用B,那么此时他: + +* • 点击“单应用注销”后:他只会在应用A注销下线,其它应用依然会保持登录状态。 + +* • 点击“单设备注销”后:他在 Chrome 浏览器登录的所有应用会一起下线,但是 Firefox 浏览器登录的应用不受影响。 + +* • 点击“全端注销”后:用户在所有浏览器登录的所以会话一起注销下线。 + + +以上几种注销方式仅需通过一行代码或者调用一个接口即可实现,详细可参考在线文档:Sa-Token SSO 单点注销。 + +### ⛏️️️️ 更新点2:单点登录模块新增消息推送机制 + +此提案来源于社区交流群中的一位开发者咨询: + +![](/assets/img/news/Sa-Token-v1.43.0-1.png) + +sa-sso-message-push-tian.png + +该开发者指出是否可以在 SSO 模块增加各个系统互相通知 token 续期的功能,以便做到系统之间会话有效期的强同步。 + +本次更新没有直接增加这两个 API,而是从底层构建了一套消息推送体系,允许 sso-client 端按照特点格式构建一个 http 请求,调用 sso-server 端的 /sso/pushS 接口,sso-server 接收到消息后做出处理回应 sso-client 端。 + +消息推送是相互的,sso-server 端也可以构建 http 请求,调用 sso-client 端的 /sso/pushC 接口。 + +消息推送机制是应用与认证中心相互沟通的桥梁,ticket 校验、单点注销等行为都是依赖消息推送机制来实现的。 + +当然你也可以通过自定义消息处理器的方式,来扩展消息推送能力,这将非常有助于你完成一些应用与认证中心的自定义数据交互。 + +假设我们现在有如下需求:在 sso-client 获取 sso-server 端指定账号 id 的昵称、头像等信息,即:用户资料的拉取。 + +首先,我们需要在 sso-server 实现一个消息处理器: + +``` +@RestController +publicclassSsoServerController { + +    // 配置SSO相关参数  +    @Autowired +    privatevoidconfigSso(SaSsoServerTemplate ssoServerTemplate) { + +        // 添加消息处理器:userinfo (获取用户资料) (用于为 client 端开放拉取数据的接口) +        ssoServerTemplate.messageHolder.addHandle("userinfo", (ssoTemplate, message) -> { +            System.out.println("收到消息:" + message); + +            // 自定义返回结果(模拟) +            return SaResult.ok() +                    .set("id", message.get("loginId")) +                    .set("name", "LinXiaoLin") +                    .set("sex", "女") +                    .set("age", 18); +        }); + +    } + +} +``` + +在 sso-client 端配置文件里要配置上消息推送的具体地址 + +``` +# sa-token配置  +sa-token: +    # sso-client 相关配置 +    sso-client: +        # 应用标识 +        client: sso-client3 +        # sso-server 端推送消息地址 +        push-url: http://sa-sso-server.com:9000/sso/pushS +        # API 接口调用秘钥 +        secret-key: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor +``` + +然后在需要拉取资料的地方: + +``` +// 查询我的账号信息:sso-client 前端 -> sso-center 后端 -> sso-server 后端 +@RequestMapping("/sso/myInfo") +public Object myInfo() { +    // 如果尚未登录 +    if( ! StpUtil.isLogin()) { +        return"尚未登录,无法获取"; +    } + +    // 获取本地 loginId +    ObjectloginId= StpUtil.getLoginId(); + +    // 构建消息对象  +    SaSsoMessagemessage=newSaSsoMessage(); +    message.setType("userinfo"); +    message.set("loginId", loginId); +     +    // 推送至 sso-server,并接收响应数据  +    SaResultresult= SaSsoClientUtil.pushMessageAsSaResult(message); + +    // 返回给前端 +    return result; +} +``` + +详细可参考在线文档:Sa-Token SSO 消息推送机制 。 + +### ⛏️️️️ 更新点3:单点登录新增 resdk 对接模式 + +Sa-Token SSO 模块一直是支持非 Sa-Token 技术栈、甚至非 java 项目对接的,在之前版本给出的对接方式是 NoSdk 模式。 + +NoSdk 模式就是指不集成 Sa-Token,直接通过 http 工具类调用接口的方式来对接 SSO-Server。 + +参考 demo:sa-token-demo-sso3-client-nosdk + +该 demo 假设应用端没有使用任何“权限认证框架”,使用最基础的 ServletAPI 进行会话管理,模拟了 `/sso/login`、 `/sso/logout`、 `/sso/logoutCall` 三个接口的处理逻辑。 + +但是基于以下原因,NoSdk 示例将不再维护: + +* • 1、NoSdk demo 相当于通过 http 工具类再次重写了一遍 Sa-Token SSO 模块代码,繁琐且冗余。 + +* • 2、重写的代码无法拥有 Sa-Token SSO 模块全部能力,仅能完成基本对接,算是一个简化版 SDK。 + + +最新版我们推荐使用 ReSdk 方式进行对接:ReSdk 模式(重写SDK部分方法):通过重写框架关键步骤点,来对接 SSO-Server。 + +参考 demo:sa-token-demo-sso3-client-resdk + +ReSdk 模式优点: + +1、依然支持客户端使用任意技术栈。 +2、仅重写少量部分关键代码,即可完成对接。几乎可以得到 Sa-Token SSO 模块全量能力。 + +### ⛏️️️️ 更新点4:OAuth2新增多 Access-Token 并存能力。 + +点击非常高的一个提案:`OAuth2 模块每次生成新的 access_token 时能否保留旧 access_token 依然有效`。 + +fix issues: #IBHFD1 、 #IBLL4Q 、#724 + +![](/assets/img/news/Sa-Token-v1.43.0-2.png) + +sa-many-access-token-issue1 + +![](/assets/img/news/Sa-Token-v1.43.0-3.png) + +sa-many-access-token-issue2 + +![](/assets/img/news/Sa-Token-v1.43.0-4.png) + +sa-many-access-token-issue3 + +在之前的版本中,因索引数据结构设计所限,每次申请 `access_token` 时会导致旧 `access_token` 立即失效,新版本重构了索引数据结构,增加了多 `Access-Token` 并存能力。 + +相关 API 展示: + +``` +// 获取 AccessTokenModel,无效的 AccessToken 会返回 null +SaOAuth2Util.getAccessToken(accessToken); + +// 校验 Access-Token,成功返回 AccessTokenModel,失败则抛出异常 +SaOAuth2Util.checkAccessToken(accessToken); + +// 获取 Access-Token 列表:此应用下 对 某个用户 签发的所有 Access-token +SaOAuth2Util.getAccessTokenValueList(clientId, loginId); + +// 判断:指定 Access-Token 是否具有指定 Scope 列表,返回 true 或 false +SaOAuth2Util.hasAccessTokenScope(accessToken, ...scopes); + +// 校验:指定 Access-Token 是否具有指定 Scope 列表,如果不具备则抛出异常 +SaOAuth2Util.checkAccessTokenScope(accessToken, ...scopes); + +// 获取 Access-Token 所代表的LoginId +SaOAuth2Util.getLoginIdByAccessToken(accessToken); + +// 获取 Access-Token 所代表的 clientId +SaOAuth2Util.getClientIdByAccessToken(accessToken); + +// 回收一个 Access-Token +SaOAuth2Util.revokeAccessToken(accessToken); + +// 回收全部 Access-Token:指定应用下 指定用户 的全部 Access-Token +SaOAuth2Util.revokeAccessTokenByIndex(clientId, loginId); +``` + +在线文档相关章节:Sa-Token-OAuth2 常用方法 + +### ⛏️️️️ 更新点5:简化 core 核心包功能模块,进行拆包 + +在社区群聊中有同学提到目前 sa-token-core 核心包功能过于拥挤复杂。应要求本次将部分功能模块进行删减,转移到了 plugin 包下: + +* • 拆分:API Key 模块拆分独立插件包:`sa-token-apikey`。 + +* • 拆分:API Sign 模块拆分独立插件包:`sa-token-sign`。 + + +并且本次更新新增了以下插件包: + +* • 新增:新增 `sa-token-forest` 插件,用于在 Http 请求处理器模块整合 Forest。 + +* • 新增:新增 `sa-token-okhttps` 插件,用于在 Http 请求处理器模块整合 OkHttps。 + + +### 📜 完整更新日志 + +除了以上提到的几点以外,还有更多更新点无法逐一详细介绍,下面是 v1.43.0 版本的完整更新日志: + +* • core: + + +* • 新增:`SaLogoutParameter` 新增 `deviceId` 参数,用于控制指定设备 id 的注销。 **\[重要\]** + +* • 新增:新增 `SaHttpTemplate` 请求处理器模块。 + +* • 新增:TOTP 增加 `issuer` 字段。 merge: pr 329 + +* • 修复:修复 `Http Digest` 认证时 url 上带有查询参数时认证无法通过的问题。merge: pr 334 + +* • 新增:@SaCheckOr 注解添加 `append` 字段,用于抓取未预先定义的注解类型进行批量注解鉴权。 + +* • 新增:侦听器 `doRenewTimeout` 方法添加 loginType 参数。 + +* • 新增:`SaInterceptor` 新增 `beforeAuth` 认证前置函数。 + + +* • SSO: + + +* • 新增:单点注销支持单设备注销。 **\[重要\]** fix: #IA6ZK0 、#747 + +* • 新增:新增消息推送机制。 **\[重要\]** fix: #IBGXA7 + +* • 新增:配置项 clients 用于单独配置每个 client 的授权信息。 **\[重要\]** + +* • 新增:配置项 `allowAnonClient` 决定是否启用匿名 client。 + +* • 新增:SSO 模块新增配置文件方式启用“不同 client 不同秘钥”能力。 + +* • 重构:sso-client 封装化获取 client 标识值。 + +* • 新增:新增 SSO Strategy 策略类。 + +* • 新增:sso-client 新增 `convertCenterIdToLoginId`、`convertLoginIdToCenterId` 策略函数,用于描述本地 LoginId 与认证中心 loginId 的转换规则。 + +* • 新增:sso-server 新增 `jumpToRedirectUrlNotice` 策略,用于授权重定向跳转之前的通知。 + +* • 优化:调整整体 SSO 示例代码。 + +* • 新增:新增 ReSdk 模式对接示例:`sa-token-demo-sso3-client-resdk`。 **\[重要\]** + +* • 新增:新增匿名应用模式对接示例:`sa-token-demo-sso3-client-anon`。 **\[重要\]** + + +* • OAuth2: + + +* • 新增:`SaClientModel` 新增 `isAutoConfirm` 配置项,用于决定是否允许应用可以自动确认授权。 **\[重要\]** + +* • 新增:多 `Access-Token` 并存、多 `Refresh-Token` 并存、多 `Client-Token` 并存能力。 **\[重要\]** fix: #IBHFD1 、 #IBLL4Q 、#724 + +* • 新增:Scope 分割符支持加号。merge: pr 333 + +* • 修复:修复 oidc 协议下,当用户数据变动后,id\_token 仍是旧信息的问题。 + +* • 优化:对 `OAuth2 Password` 认证模式需要重写处理器添加强提醒。 + +* • 优化:将认证流程回调从 `SaOAuth2ServerConfig` 转移到 `SaOAuth2Strategy`。 + +* • 新增:新增 `SaOAuth2Strategy.instance.userAuthorizeClientCheck` 策略,用于检查指定用户是否可以授权指定应用。fix: #553 + +* • 优化:优化调整 `sa-token-oauth2` 模块代码结构及注释。 + +* • 新增:`currentAccessToken()`、`currentClientToken()`,简化读取 `access_token`、`client_token` 步骤 + + +* • 插件: + + +* • 新增:新增 `sa-token-forest` 插件,用于在 Http 请求处理器模块整合 Forest。 + +* • 新增:新增 `sa-token-okhttps` 插件,用于在 Http 请求处理器模块整合 OkHttps。 + +* • 拆分:API Key 模块拆分独立插件包:`sa-token-apikey`。 + +* • 拆分:API Sign 模块拆分独立插件包:`sa-token-sign`。 + +* • 修复:修复 `sa-token-dubbo` 插件部分场景上下文控制出错的问题。 + +* • 修复:修复 `sa-token-sanck3` `SaSessionForSnack3Customized:getModel` 接收 map 值时会出错的问题。 merge: pr 330 + +* • 修复:修复使用 `sa-token-redis-template-jdk-serializer` 时反序列化错误。merge: pr 331 + +* • 修复:`sa-token-snack3` 优化 `objectToJson` 序列化处理(增加类名,但不增加根类名)。 + +* • 重构:重构 `sa-token-redis-template`、`sa-token-redis-template-jdk-serializer` 插件中 update 方法 ttl 获取方式改为毫秒,以减少 update 时的 ttl 计算误差。 **\[重要\]** + + +* • 示例: + + +* • 新增:新增 SSE 鉴权示例。 + + +* • 文档: + + +* • 新增:新增文档离线版下载。 + +* • 新增:新增框架功能列表插图。 + +* • 新增:新增示例:如何在响应式环境下的 Filter 里调用 Sa-Token 同步 API。 + +* • 新增:新增 QA:在 idea 导入源码,运行报错:java: 程序包cn.dev33.satoken.oauth2不存在。 + +* • 新增:新增 QA:新增QA:报错:SaTokenContext 上下文尚未初始化。 + +* • 新增:新增 QA:在 idea 导入源码,运行报错:java: 程序包cn.dev33.satoken.oauth2不存在。 + +* • 新增:重写路由匹配算法修正为最新写法。 + +* • 新增:修复 OAuth2 UnionId 章节相关不正确描述。 + +* • 优化:完善 QA:访问了一个不存在的路由,报错:SaTokenContext 上下文尚未初始化。 fix: #771 + +* • 优化:补充 sso 模块遗漏的配置字段介绍。 + +* • 优化:OAuth2-Server 示例添加真正表单。 + +* • 新增:文档新增重写 `PasswordGrantTypeHandler` 处理器示例。 + +* • 新增:sso 章节和 oauth2 章节文档增加可重写策略说明。 + + +* • 其它: + + +* • 新增:readme 新增框架功能介绍图。 + +* • 新增:SSO 模块新增思维导图说明。 + +* • 新增:readme 新增 Forest 的友情链接。 + + +更新日志在线文档直达链接:https://sa-token.cc/doc.html#/more/update-log + +### 🌟 其它 + +代码仓库地址:https://gitee.com/dromara/sa-token \ No newline at end of file diff --git a/src/zh/news/Skyeye-April-update.md b/src/zh/news/Skyeye-April-update.md new file mode 100644 index 0000000000..37c71b459d --- /dev/null +++ b/src/zh/news/Skyeye-April-update.md @@ -0,0 +1,57 @@ +--- +title: Skyeye云4月份版本更新内容 +author: 资源站点 +date: 2025-04-21 +cover: /assets/img/news/Skyeye-April-update-1.png +head: + - - meta + - name: 新闻 +--- + +Skyeye云4月份版本更新内容 + +VUE版本重构:共计 299 个功能,目前已重构 80%;(具体功能列表如下) + +VUE 版额外新增多个小工具:代码格式化,JSON 格式化,图片压缩,图片水印,图片背景替换,图片灰度转换,文本转换; + +移动端新增聊天功能(PS:目前第一版只支持普通的消息发送和接收,后续会完善); + +云校园 + 表白墙 版本预计共 49 个功能;(具体功能列表如下) + +移动端界面优化; + +低代码平台新增虚拟属性,支持动态添加属性的功能,目前支持文本和数字两种数据类型; + +修复若干问题单; + +新增多租户底层逻辑代码处理(PS:待整合业务); + +Layui版本框架整体优化,例如:添加输入框文本长度限制、部分组件的必填限制、文本超长的显示等; + +![](/assets/img/news/Skyeye-April-update-0.png) + +**多租户** + +01 + +预售价已开启,进入星球后联系作者采购 + +02 + +将于2025年10月份发布 + +03 + +预售价截止时间:2025年5月31日 + +VUE版本: + +新增VUE版本的PC前端,和Layui版本同一套后端,目前已重构80%,剩余20%将在下载版本开发,具体可参考下方图片。 + +![](/assets/img/news/Skyeye-April-update-1.png) + +云校园: + +打造校园教学与娱乐平台,第一版功能如下: + +![](/assets/img/news/Skyeye-April-update-2.png) \ No newline at end of file diff --git a/src/zh/news/Skyeye-v2.5.8.md b/src/zh/news/Skyeye-v2.5.8.md new file mode 100644 index 0000000000..4d481e04c0 --- /dev/null +++ b/src/zh/news/Skyeye-v2.5.8.md @@ -0,0 +1,145 @@ +--- +title: 【Skyeye云重磅更新】10 大模块全面升级!多租户架构 + 智能财务 + 智慧门店,效率倍增新体验 +author: 资源站点 +date: 2025-08-11 +cover: /assets/img/news/Skyeye-v2.5.8-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/Skyeye-v2.5.8-0.png) + + + + + +本次版本更新聚焦于系统核心功能增强,通过多租户架构支持、灵活的打印模板系统、前端框架全面升级以及各业务模块的深度优化,为用户提供更高效、更智能、更专业的使用体验。 + +项目地址:https://gitee.com/dromara/skyeye + +功能清单:https://www.kdocs.cn/l/cbf2cgCLrUyz + +01# + +核心功能更新 + +CORE FUNCTIONS + +![](/assets/img/news/Skyeye-v2.5.8-1.png) + +1\. 多租户功能 + +实现了全新的多租户架构支持,为企业级应用提供更安全、更灵活的数据管理方案。 + +•新增多租户架构支持,实现数据隔离与权限控制 + +•支持租户个性化配置与独立管理界面 + +•优化租户资源分配与性能监控机制 + +2\. 打印模板系统 + +引入灵活的自定义打印模板设计器,满足多样化的文档输出需求。 + +•新增自定义打印模板设计器 + +•支持多种格式导出(PDF/Excel/Word) + +•模板权限精细化管理与版本控制 + +3\. VUE 100%全覆盖 + +完成前端框架全面升级与组件化重构,显著提升用户体验与系统性能。 + +•前端框架全面升级至Vue最新版本 + +•实现所有业务模块的Vue组件化重构 + +•提升页面加载速度与交互响应性能 + +4\. 生产管理模块 + +强化生产全流程管理能力,优化计划排程与资源配置效率。 + +•新增生产计划排程与进度跟踪功能 + +•优化物料需求计算与库存预警机制 + +•生产质量追溯与异常处理流程优化 + +5\. 排班管理系统 + +升级排班管理系统,提供智能化排班与便捷的人员管理体验。 + +•智能排班算法优化,支持多维度规则配置 + +•员工考勤与排班冲突自动检测 + +•移动端排班查看与一键换班功能 + +系统性能整体优化,平均响应速度提升30%,安全防护机制增强,用户体验细节优化,操作流程简化。 + +02# + +其他优化 + +OTHER IMPROVEMENTS + +![](/assets/img/news/Skyeye-v2.5.8-2.png) + +本次版本还包含以下重要功能模块的增强与优化,覆盖多个业务场景,提升系统整体效能: + +6\. 排班管理系统(新增子功能) + +•**工位管理**:实现工位可视化分配与实时状态监控 + +•**班次管理**:自定义班次模板与轮班规则配置 + +•**排班管理**:智能排班算法优化,支持多维度规则适配 + +•**临时工请假申请**:移动端快捷请假流程与审批管理 + +•**临时工月薪明细**:工时自动核算与薪资明细生成 + +7\. ERP模块升级 + +•**销售换货**:完整换货流程跟踪与库存联动 + +•**销售退货**:退货原因分类统计与退款时效优化 + +•**借出管理**:资产借出登记与到期提醒 + +•**归还管理**:归还验收流程与状态变更记录 + +8\. 智慧门店系统 + +•**门店订单管理**:线上线下订单一体化处理 + +•**运费模板计费配置**:多维度运费规则自定义与智能计算 + +•**商城美化**:店铺装修模板库更新,支持可视化编辑 + +9\. 财务系统增强 + +•**收支管理/财务账号/账套管理/会计科目** + +•**凭证管理/凭证上传/收付款管理/资金统计** + +•**应收账款/应付账款/开具发票/发票收取** + +•**发票统计/费用申请/费用报销/费用分析** + +•**借款管理/还款管理/借还统计** + +10\. 招聘模块 + +**招聘分析**:多维度招聘数据看板,包含渠道效能、简历转化率、招聘周期分析。 + +系统版本 | V2.5.8 + +发布日期 | 2025-8月 + +更新范围 | 全系统功能升级 + +建议反馈 | 微信交流群 \ No newline at end of file diff --git a/src/zh/news/Skyeye-v3.14.17.md b/src/zh/news/Skyeye-v3.14.17.md new file mode 100644 index 0000000000..d9f892a9b6 --- /dev/null +++ b/src/zh/news/Skyeye-v3.14.17.md @@ -0,0 +1,215 @@ +--- +title: Skyeye云 v3.14.17 源代码大版本发布 +author: 资源站点 +date: 2024-12-16 +cover: /assets/img/news/Skyeye-v3.14.17-0.png +head: + - - meta + - name: 新闻 +--- + +Skyeye 云智能制造,采用 Springboot + winUI 的低代码平台、移动端采用 UNI-APP。包含 30 多个应用模块、50 多种电子流程,CRM、PM、ERP、MES、ADM、EHR、笔记、知识库、项目、门店、商城、财务、多班次考勤、薪资、招聘、云售后、论坛、公告、问卷、报表设计、工作流、日程、云盘等全面管理,实现智能制造行业一体化管理。实现管理流程 “客户关系 -> 线上 / 线下报价 -> 销售报价 -> 销售合同 -> 生产计划 -> 商品设计 -> 采购 -> 加工制造 -> 入库 -> 发货 -> 售后服务” 的高效运作,同时实现企业员工的管理以及内部运作的流程操作,完善了员工从 “入职 -> 培训 -> 转正 -> 办公 -> 离职” 等多项功能。 + +**Skyeye 云【源代码】针对 {星球用户} 开源。拿到源码后可进行学习、毕设、企业等使用。** + +**目前支付已做,作者由于是个人,还未真正进行联调,需要客户自己进行联调。** + +Skyeye 云智能制造 v3.14.17 发布 ,发布内容如下: + +* **新增与其他软件的对比表:https://docs.qq.com/sheet/DYUtPdWhTbVBITlpL?tab=000001** + +* **Skyeye 云已加入 Dromara 社区** + +* **商城(支持PC端和移动端)** + + +* **新增区域套餐管理** + +* **新增快递公司管理** + +* **新增快递模版管理** + +* **新增会员管理** + +* **新增广告位管理** + +* **会员管理模块,支持与ERP会员同步,支持注册、登录等。** + +* **个人中心,支持用户中心,账号安全,位置等信息维护** + +* **订单中心,支持下单,取消订单等操作** + +* **订单评价/追评** + +* **新增优惠券管理,我的优惠券等** + +* **新增地址管理** + +* **新增首页轮播图** + +* **新增品牌管理** + +* **支持门店搜索以及商品搜索** + +* 新增购物车管理 + + +* Skyeye云 + + +* 新增支付应用管理 + +* 新增支付渠道管理 + +* 新增支持文件管理 + +* 新增短信渠道管理 + +* 新增短信模板管理 + +* SpringBoot版本升级,部分组件升级到对应版本 + + +* ERP + MES + + +* 新增商品上架商城管理 + +* 预生产计划结合商品来源类型(自产/外购),可以转采购订单,生产计划单 + + +* VUE版Skyeye云已添加 80+ 组件,预计2025年11月发布,凡是采购了移动端且星球未过期的用户,皆可获取VUE版。 + +* 新增离线文档项目管理 + +* **源代码对星球用户开放** + +* 解决若干问题。 + + +Skyeye 具备**低代码、快捷开发、可视化设计、微服务**等特点,方便客户二次开发,极大的提高了开发效率。 + +地址1: https://gitee.com/doc\_wei01/skyeye + +地址2: https://gitee.com/dromara/skyeye + +**效果图** + +| +效果图 + + | + +效果图 + + | +| --- | --- | +| + +![](/assets/img/news/Skyeye-v3.14.17-0.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-1.png) + + | +| + +![](/assets/img/news/Skyeye-v3.14.17-2.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-3.png) + + | +| + +![](/assets/img/news/Skyeye-v3.14.17-4.png) + + | + + | +| + +![](/assets/img/news/Skyeye-v3.14.17-5.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-6.png) + + | +| + +![](/assets/img/news/Skyeye-v3.14.17-7.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-8.png) + + | + +**效果图** + +| +效果图 + + | + +效果图 + + | + +效果图 + + | + +效果图 + + | +| --- | --- | --- | --- | +| + +![](/assets/img/news/Skyeye-v3.14.17-9.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-10.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-11.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-12.png) + + | +| + +![](/assets/img/news/Skyeye-v3.14.17-13.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-14.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-15.png) + + | + +![](/assets/img/news/Skyeye-v3.14.17-16.png) + + | + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/Skyeye-v3.14.17-17.png) \ No newline at end of file diff --git a/src/zh/news/Skyeye-v3.15.14.md b/src/zh/news/Skyeye-v3.15.14.md new file mode 100644 index 0000000000..c47d981f57 --- /dev/null +++ b/src/zh/news/Skyeye-v3.15.14.md @@ -0,0 +1,109 @@ +--- +title: Skyeye 云智能制造办公系统 VUE 版本 v3.15.14 发布 +author: 资源站点 +date: 2025-03-27 +cover: /assets/img/news/Skyeye-v3.15.14-0.png +head: + - - meta + - name: 新闻 +--- + +Skyeye 云智能制造,采用 Springboot + winUI 的低代码平台、移动端采用 UNI-APP。包含 30 多个应用模块、50 多种电子流程,CRM、PM、ERP、MES、ADM、EHR、笔记、知识库、项目、门店、商城、财务、多班次考勤、薪资、招聘、云售后、论坛、公告、问卷、报表设计、工作流、日程、云盘等全面管理,实现智能制造行业一体化管理。实现管理流程 “客户关系 -> 线上 / 线下报价 -> 销售报价 -> 销售合同 -> 生产计划 -> 商品设计 -> 采购 -> 加工制造 -> 入库 -> 发货 -> 售后服务” 的高效运作,同时实现企业员工的管理以及内部运作的流程操作,完善了员工从 “入职 -> 培训 -> 转正 -> 办公 -> 离职” 等多项功能。 + +**常见问题**     **开发文档      视频教程**     **功能点** + +**Skyeye 云【源代码】针对 {星球用户} 开源。拿到源码后可进行学习、毕设、企业等使用**。 + +Skyeye 云智能制造 v3.15.14 发布 ,发布内容如下: + +* **Skyeye 云已加入 Dromara 社区** +* **VUE 版 Skyeye 云** + +* 已重构 **65** 个组件,VUE 版重构进度可参考:https://kdocs.cn/l/cbf2cgCLrUyz + +* VUE 版本预计共 **289** 个功能,目前已重构 **80%**,达到 4 月份版本的标准。 + +* VUE 版本预计共 **289** 个功能,已转测 **231** 个功能,目前测试已完成 **226** 个功能。 + +* 解决 Layui 版本存在的问题 + +* 学校模块 - 移动端新增聊天、好友功能 + +* 学校模块 - VUE 版试卷,完善编辑,设计(单选矩阵、多选矩阵、填空矩阵、评分矩阵、分页、分数设计) + +* 学校模块 - 表白墙新增帖子、圈子、视频功能 + +* **重点:四月份将同时发布第一版学校模块,包括表白墙和在线课堂** + + +* **VUE 版 Skyeye 云** + + 已完成 **100+** 个基础组件的开发 + +* **源代码对星球用户开放** +* 解决若干问题。 + + +Skyeye 具备**低代码、快捷开发、可视化设计、微服务**等特点,方便客户二次开发,极大的提高了开发效率。 + +erp: https://gitee.com/doc\_wei01/skyeye + +OA: https://gitee.com/dromara/skyeye + +报表:https://gitee.com/doc\_wei01/skyeye-report  有问题可以联系作者,详情请看开发计划。 + +**PC 端效果图** + + +| 效果图 | 效果图 | +|--------|--------| +| ![](/assets/img/news/Skyeye-v3.15.14-0.png) | ![](/assets/img/news/Skyeye-v3.15.14-1.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-2.png) | ![](/assets/img/news/Skyeye-v3.15.14-3.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-4.png) | ![](/assets/img/news/Skyeye-v3.15.14-5.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-6.png) | ![](/assets/img/news/Skyeye-v3.15.14-7.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-8.png) | ![](/assets/img/news/Skyeye-v3.15.14-9.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-10.png) | ![](/assets/img/news/Skyeye-v3.15.14-11.png) | +| ![](/assets/img/news/Skyeye-v3.15.14-12.png) | ![](/assets/img/news/Skyeye-v3.15.14-13.png) | + + +**移动端效果图** + + + + + + + + + + + + + + + +
+ 效果图 + + 效果图 + + 效果图 + + 效果图 +
+
+ 图片 +
+
+
+ 图片 +
+
+
+ 图片 +
+
+
+ 图片 +
+
\ No newline at end of file diff --git a/src/zh/news/Stream-Query-0.md b/src/zh/news/Stream-Query-0.md new file mode 100644 index 0000000000..5e80d8564d --- /dev/null +++ b/src/zh/news/Stream-Query-0.md @@ -0,0 +1,112 @@ +--- +title: 告别繁琐 Mapper!Stream-Query 正式入驻 GitCode 平台 +author: 2025年08月04日 08:54 +date: 2025-08-04 +cover: /assets/img/news/Stream-Query-0-0.gif +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/Stream-Query-0-0.gif) + + + +在Java开发领域,MyBatis-Plus 作为 MyBatis 的增强工具,因其简化 CRUD 操作而广受欢迎。但你是否曾为这些问题感到困扰? + + + +🤕 Mapper 接口泛滥:每个实体类都要写一个 Mapper,项目里堆满了 XxxMapper.java 文件 + +😠 复杂查询臃肿:处理一对多查询时,要手动循环查多次数据库,代码像"意大利面条"一样难维护 + +😣 Stream 操作重复:同样的 stream().filter().map() 写了无数遍,却找不到优雅的封装方式 + + + +现在,开源项目 Stream-Query 已正式入驻 GitCode 平台成为 G-Star 优秀毕业项目,将彻底改变你对 MyBatis-Plus 的认知! + + + +![](/assets/img/news/Stream-Query-0-1.jpg) + + + +**🚀 项目亮点:像工具类一样操作数据库** + + + + + +Stream-Query 的核心目标是让开发者完全摆脱 Mapper 的束缚。通过该项目,你可以像调用工具类静态方法一样轻松完成数据库操作,大大提升开发效率。 + + + +![](/assets/img/news/Stream-Query-0-2.png) + + + +**✨ 三大核心功能** + + + + + +1. 动态 Mapper 黑科技:项目采用 Byte-Buddy 技术实现运行时动态生成 Mapper,无需再为每个实体类编写繁琐的 Mapper 接口。  + +2. Stream 式查询处理:针对复杂查询场景,Stream-Query 提供了一对一、一对多等关系的便捷处理方式,轻松完成多次单表查询的数据组装。  + +3. Stream 操作简化:独创的 Steam 类(Steam = Stream-r,r 代表 Repeat),专门用于减少 Stream API 的重复代码,让函数式编程更加优雅。 + + + + +**🎯 项目初衷与愿景** + + + + + +Stream-Query 的诞生源于开发者对现代 Java 开发中 ORM 使用痛点的深刻洞察。 + + + +在日常开发实践中,项目创始人发现大多数开发者花费了过多时间在编写重复的 Mapper 接口、处理复杂的关联查询以及编写冗长的 Stream 操作上,这些机械性工作严重分散了开发者对核心业务逻辑的专注度。正是基于这样的观察,团队决心打造一个能够真正解放开发者生产力的工具,让数据库操作变得像使用工具类一样简单直观。 + + + +项目从创立之初就确立了明确的技术哲学:通过智能封装降低复杂度,而非简单叠加功能。这种理念体现在动态 Mapper 的巧妙实现上,它既保留了 MyBatis-Plus 的强大功能,又消除了其最繁琐的部分。而在 Stream 处理方面,项目创造性地引入了 "Stream-r" 的概念,将开发者从重复的流式操作中解放出来。 + + + +团队相信,通过持续的技术创新和社区共建,Stream-Query 有望成为 Java 生态中数据库操作的事实标准。未来,Stream-Query 将持续优化动态 Mapper 的性能,并进一步丰富 Stream 操作的工具方法,在这个过程中,每个开发者的反馈和建议都将成为推动项目进步的重要力量,共同塑造下一代 Java 数据访问层的最佳实践。  + + + +* * * + + + +来GitCode平台:立刻体验 Stream-Query + +💻开源许可证:Apache License v2.0 + +📮项目地址:http://gitcode.com/dromara/stream-query + + + + + +![](/assets/img/news/Stream-Query-0-3.png) + +**推荐阅读** + +[![](/assets/img/news/Stream-Query-0-4.png)](https://mp.weixin.qq.com/s?__biz=MzkyNjY0MDY1Ng==&mid=2247491795&idx=1&sn=70e669c10491d17c0fda127ffc116a42&scene=21#wechat_redirect) + +GitCode首批百大开源项目入围名单出炉 + +[![](/assets/img/news/Stream-Query-0-5.png)](https://mp.weixin.qq.com/s?__biz=MzkyNjY0MDY1Ng==&mid=2247489603&idx=1&sn=66a5adee615b79d841558c0b54b5881f&scene=21#wechat_redirect) + +【投稿赢 iPhone 17】「我的第一个开源项目」 + +![](/assets/img/news/Stream-Query-0-6.png) \ No newline at end of file diff --git a/src/zh/news/TestHub-0.md b/src/zh/news/TestHub-0.md index 96d4295e33..ba06e33e82 100644 --- a/src/zh/news/TestHub-0.md +++ b/src/zh/news/TestHub-0.md @@ -1,125 +1,125 @@ ---- -title: TestHub 自动化测试工具加入 Dromara 社区 -author: 失败女神 -tag: - - TestHub -date: 2023-08-29 -cover: /assets/img/news/TestHub-0-cover.png -head: - - - meta - - name: 新闻 ---- - -## 🌟 TestHub 自动化测试工具加入 Dromara 社区!🌟 - -亲爱的开发者们, - -我们兴奋地宣布,我们的开源项目—TestHub,一款强大的自动化测试工具,已经正式加入了 dromara 开源社区!我们诚挚地邀请您成为我们社区的一员,一同分享、学习和贡献。 - -TestHub 是为了解决在软件开发旅程中测试流程管理和执行的复杂挑战而诞生的。传统测试工具可能局限于接口级自动化,无法满足多样化的需求,而我们在 TestHub 中引入了独特的流程编排功能,让您能够轻松定义、管理和执行测试流程。无论是自动化测试、流程调度还是其他自动化任务,TestHub 的插件式架构都能够满足您的无限扩展需求 - -![](/assets/img/news/TestHub-0-1.jpg) - -## TestHub 提供的内置能力 - -- ✅ **check 功能**:对数据进行校验,类似于断言。 -- ✅ **checkobj 功能**:对比对象、列表,确保数据的一致性。 -- ✅ **sql 功能**:执行 SQL 语句,满足数据库操作需求。 -- ✅ **sql_begin 和 sql_commit 功能**:支持事务管理,确保数据的完整性。 -- ✅ **sleep 功能**:让程序休息指定毫秒数,方便处理等待场景。 -- ✅ **convert 功能**:灵活的数据转换处理,满足多样化的数据格式需求。 -- ✅ **const 功能**:定义常量,便于在测试流程中重复使用。 -- ✅ **http 功能**:发送 Http 请求,方便进行接口测试等。 - -在 TestHub 中,我们将复杂的测试流程管理变得直观易用。通过简单的拖放和连接步骤,您可以构建完整的测试流程,包括执行测试用例、准备数据、配置环境等,一切都在统一的平台上高效完成。 - -## 喜欢我们的开源项目吗?请给我们点颗星! - -- **Gitee**:https://gitee.com/dromara/TestHub。 -- **Github**:https://github.com/dromara/TestHub。 - - 感谢您一直以来对我们开源项目的支持和关注!作为开发者,您深知开源社区的力量,点亮一颗星星,就是对我们最真挚的鼓励。不管您是一位新手还是资深开发者,您的每颗星星都对我们意义重大。让我们一起为开源社区的繁荣贡献一份力量! - -## 软件发布阶段 - -### V 1.0.1 - -📅 2023 年 8 月 31 日 - -🪲🪲 修复 BUG 🪲🪲 - -- 解决搜索用例后新打开用例名称出现乱码的情况 - -👍👍 新增功能 👍👍 - -- 集成 sa-token 实现鉴权和登录 -- 支持 pgsql 作为数据库 -- http 支持设置超时时间 -- 新增版本升级记录 -- 新增自动生成用例编码 - -### V 1.0.0 - -📅 2023 年 7 月 31 日 TestHub 正式开源 1.0.0 版本发布 - -同日加入 dromara 社区,跪谢 🙇 社区一众大佬。 - -感谢 🙇 SMS4J 作者 风如歌大佬的鼎力支持 - -### 内测 - -📅 2023 年 2 月 7 日 - -TestHub 在此刻发布了根网科技内测初版,迈出了它成长之路的第一步。TestHub 测试平台从 2022 年 10 月开始构建,经过多次改进和完善,采用插件式架构满足了根网科技的私有协议需求,并引入了更多功能。 - -在这个成长的过程中,我们想对所有为 TestHub 奉献时间和智慧的伙伴们表示衷心感谢。正是有了你们的支持,TestHub 才能不断进化、壮大。 - -首先,我们要感谢那些在 TestHub 起步时给予巨大支持的伙伴,特别是`项目组领导刘培琦`。正是因为您的支持,TestHub 得以诞生。 - -同时,我们衷心感谢`部门总监黎伟`的大力支持。正是有了您的支持,TestHub 从一个简陋的小工具逐步完善,发展成为一个功能完备的平台。您的努力推动,让 TestHub 在公司内落地为研发团队提供了强大支持,特别是在 CTS 阶段,TestHub 得以真正完善,成为一款出色的产品。 - -感谢根网 CTS 团队的每一位成员,你们的建议对 TestHub 的完善发挥了不可或缺的作用。 - -最后,我们感谢所有使用 TestHub 的同事,你们的反馈和使用经验使得平台不断优化。正是通过您们的反馈,TestHub 才能不断成长和完善,为大家提供更好的体验。 - -在这个充满感激之情的阶段,我们期待着您继续的支持和参与,让 TestHub 在未来继续发展壮大,成为自动化测试领域的佼佼者。 - -## 诚邀开发者加入 - -**您是否热衷于技术创新?是否对自动化测试领域有浓厚兴趣?如果是,我们邀请您加入 TestHub 开发团队!** - -目前,我们的团队正在积极推动 TestHub 项目的发展,但在前端方面仍存在一定的人手缺口。我们正在寻找热爱前端开发的开发者,与我们一同努力,为 TestHub 带来更多创新和功能。 - -**我们期待您:** - -- 具备前端开发经验,熟悉相关技术栈。 -- 对自动化测试领域有兴趣,愿意为项目的成功贡献一份力量。 -- 具备团队合作精神,积极沟通,愿意分享经验和知识。 - -无论您是资深前端开发者,还是刚入门的新手,我们都欢迎您的加入!让我们共同构建一个充满活力和创造力的开发团队,为 TestHub 的发展添砖加瓦。 - -**加入我们的团队,一起创造美好未来!** - -## 加入 Dromara-TestHub 技术支持群的好处 - -🤝 **协作与交流**:与来自各地的开发者们一起合作,分享您的知识和经验,一同解决问题,共同推动项目的发展。 - -🚀 **学习机会**:借助社区的力量,您将有机会深入了解自动化测试领域的最新动态和最佳实践,不断提升自己的技能。 - -🌈 **贡献机会**:您可以通过编写代码、提交问题、撰写文档等多种方式参与到项目中,为整个社区做出宝贵的贡献。 - -🏆 **成就与认可**:您的贡献将会被广泛认可,不仅仅体现在代码中,还有可能在社区内获得声望和认可。 - -我们欢迎各级开发者的加入,无论您是新手还是资深大牛,都能在这里找到归属。为了更好地了解 TestHub,您可以: - -- 访问我们的官方网站 http://nsrule.com/,深入了解项目的详情。 -- 查阅我们的文档,快速掌握 TestHub 的使用方法。 -- 加入我们的社区论坛或聊天群,与其他成员分享交流。 - -让我们一同构建一个繁荣、友好的开源社区,在自动化测试领域取得更加出色的成就!期待您的加入! - -感谢您的支持与参与! - -**\[失败女神\]-TestHub 开发团队** - - +--- +title: TestHub 自动化测试工具加入 Dromara 社区 +author: 失败女神 +tag: + - TestHub +date: 2023-08-29 +cover: /assets/img/news/TestHub-0-cover.png +head: + - - meta + - name: 新闻 +--- + +## 🌟 TestHub 自动化测试工具加入 Dromara 社区!🌟 + +亲爱的开发者们, + +我们兴奋地宣布,我们的开源项目—TestHub,一款强大的自动化测试工具,已经正式加入了 dromara 开源社区!我们诚挚地邀请您成为我们社区的一员,一同分享、学习和贡献。 + +TestHub 是为了解决在软件开发旅程中测试流程管理和执行的复杂挑战而诞生的。传统测试工具可能局限于接口级自动化,无法满足多样化的需求,而我们在 TestHub 中引入了独特的流程编排功能,让您能够轻松定义、管理和执行测试流程。无论是自动化测试、流程调度还是其他自动化任务,TestHub 的插件式架构都能够满足您的无限扩展需求 + +![](/assets/img/news/TestHub-0-1.jpg) + +## TestHub 提供的内置能力 + +- ✅ **check 功能**:对数据进行校验,类似于断言。 +- ✅ **checkobj 功能**:对比对象、列表,确保数据的一致性。 +- ✅ **sql 功能**:执行 SQL 语句,满足数据库操作需求。 +- ✅ **sql_begin 和 sql_commit 功能**:支持事务管理,确保数据的完整性。 +- ✅ **sleep 功能**:让程序休息指定毫秒数,方便处理等待场景。 +- ✅ **convert 功能**:灵活的数据转换处理,满足多样化的数据格式需求。 +- ✅ **const 功能**:定义常量,便于在测试流程中重复使用。 +- ✅ **http 功能**:发送 Http 请求,方便进行接口测试等。 + +在 TestHub 中,我们将复杂的测试流程管理变得直观易用。通过简单的拖放和连接步骤,您可以构建完整的测试流程,包括执行测试用例、准备数据、配置环境等,一切都在统一的平台上高效完成。 + +## 喜欢我们的开源项目吗?请给我们点颗星! + +- **Gitee**:https://gitee.com/dromara/TestHub。 +- **Github**:https://github.com/dromara/TestHub。 + + 感谢您一直以来对我们开源项目的支持和关注!作为开发者,您深知开源社区的力量,点亮一颗星星,就是对我们最真挚的鼓励。不管您是一位新手还是资深开发者,您的每颗星星都对我们意义重大。让我们一起为开源社区的繁荣贡献一份力量! + +## 软件发布阶段 + +### V 1.0.1 + +📅 2023 年 8 月 31 日 + +🪲🪲 修复 BUG 🪲🪲 + +- 解决搜索用例后新打开用例名称出现乱码的情况 + +👍👍 新增功能 👍👍 + +- 集成 sa-token 实现鉴权和登录 +- 支持 pgsql 作为数据库 +- http 支持设置超时时间 +- 新增版本升级记录 +- 新增自动生成用例编码 + +### V 1.0.0 + +📅 2023 年 7 月 31 日 TestHub 正式开源 1.0.0 版本发布 + +同日加入 dromara 社区,跪谢 🙇 社区一众大佬。 + +感谢 🙇 SMS4J 作者 风如歌大佬的鼎力支持 + +### 内测 + +📅 2023 年 2 月 7 日 + +TestHub 在此刻发布了根网科技内测初版,迈出了它成长之路的第一步。TestHub 测试平台从 2022 年 10 月开始构建,经过多次改进和完善,采用插件式架构满足了根网科技的私有协议需求,并引入了更多功能。 + +在这个成长的过程中,我们想对所有为 TestHub 奉献时间和智慧的伙伴们表示衷心感谢。正是有了你们的支持,TestHub 才能不断进化、壮大。 + +首先,我们要感谢那些在 TestHub 起步时给予巨大支持的伙伴,特别是`项目组领导刘培琦`。正是因为您的支持,TestHub 得以诞生。 + +同时,我们衷心感谢`部门总监黎伟`的大力支持。正是有了您的支持,TestHub 从一个简陋的小工具逐步完善,发展成为一个功能完备的平台。您的努力推动,让 TestHub 在公司内落地为研发团队提供了强大支持,特别是在 CTS 阶段,TestHub 得以真正完善,成为一款出色的产品。 + +感谢根网 CTS 团队的每一位成员,你们的建议对 TestHub 的完善发挥了不可或缺的作用。 + +最后,我们感谢所有使用 TestHub 的同事,你们的反馈和使用经验使得平台不断优化。正是通过您们的反馈,TestHub 才能不断成长和完善,为大家提供更好的体验。 + +在这个充满感激之情的阶段,我们期待着您继续的支持和参与,让 TestHub 在未来继续发展壮大,成为自动化测试领域的佼佼者。 + +## 诚邀开发者加入 + +**您是否热衷于技术创新?是否对自动化测试领域有浓厚兴趣?如果是,我们邀请您加入 TestHub 开发团队!** + +目前,我们的团队正在积极推动 TestHub 项目的发展,但在前端方面仍存在一定的人手缺口。我们正在寻找热爱前端开发的开发者,与我们一同努力,为 TestHub 带来更多创新和功能。 + +**我们期待您:** + +- 具备前端开发经验,熟悉相关技术栈。 +- 对自动化测试领域有兴趣,愿意为项目的成功贡献一份力量。 +- 具备团队合作精神,积极沟通,愿意分享经验和知识。 + +无论您是资深前端开发者,还是刚入门的新手,我们都欢迎您的加入!让我们共同构建一个充满活力和创造力的开发团队,为 TestHub 的发展添砖加瓦。 + +**加入我们的团队,一起创造美好未来!** + +## 加入 Dromara-TestHub 技术支持群的好处 + +🤝 **协作与交流**:与来自各地的开发者们一起合作,分享您的知识和经验,一同解决问题,共同推动项目的发展。 + +🚀 **学习机会**:借助社区的力量,您将有机会深入了解自动化测试领域的最新动态和最佳实践,不断提升自己的技能。 + +🌈 **贡献机会**:您可以通过编写代码、提交问题、撰写文档等多种方式参与到项目中,为整个社区做出宝贵的贡献。 + +🏆 **成就与认可**:您的贡献将会被广泛认可,不仅仅体现在代码中,还有可能在社区内获得声望和认可。 + +我们欢迎各级开发者的加入,无论您是新手还是资深大牛,都能在这里找到归属。为了更好地了解 TestHub,您可以: + +- 访问我们的官方网站 http://nsrule.com/,深入了解项目的详情。 +- 查阅我们的文档,快速掌握 TestHub 的使用方法。 +- 加入我们的社区论坛或聊天群,与其他成员分享交流。 + +让我们一同构建一个繁荣、友好的开源社区,在自动化测试领域取得更加出色的成就!期待您的加入! + +感谢您的支持与参与! + +**\[失败女神\]-TestHub 开发团队** + + diff --git a/src/zh/news/TestHub-1.0.2.md b/src/zh/news/TestHub-1.0.2.md index 044e322592..cb4918d16f 100644 --- a/src/zh/news/TestHub-1.0.2.md +++ b/src/zh/news/TestHub-1.0.2.md @@ -1,116 +1,116 @@ ---- -title: 自动化测试工具:TestHub V1.0.2版本发布 -author: TestHub -tag: - - TestHub -date: 2023-10-31 -cover: /assets/img/news/TestHub-1.0.2-1.jpg -head: - - - meta - - name: 新闻 ---- - -## 自动化测试工具:TestHub V1.0.2 版本发布 - -## 简介 - -TestHub 是一款基于流程编排的自动化测试工具。是为了解决在软件开发旅程中测试流程管理和执行的复杂挑战而诞生的。传统测试工具可能局限于接口级自动化,无法满足多样化的需求,而我们在 TestHub 中引入了独特的流程编排功能,让您能够轻松定义、管理和执行测试流程。无论是自动化测试、流程调度还是其他自动化任务,TestHub 的插件式架构都能够满足您的无限扩展需求。 - -使用手册:http://nsrule.com/ - -演示环境:http://testhub.nsrule.com:11018/#/ - -Gitee 开源地址:https://gitee.com/dromara/TestHub - -Gitub 开源地址:https://github.com/dromara/TestHub - -演示视频:https://www.bilibili.com/video/BV1X94y1v7ak/ - -安装包:https://url84.ctfile.com/d/49656084-58580094-6ad8ce?p=3738 (访问密码: 3738) - -![](/assets/img/news/TestHub-1.0.2-1.jpg) - -## 更新内容 - -- 🪲🪲 修复 BUG🪲🪲 - -- 解决用例类目树超出不显示滚动条的问题 -- 修复无法退出登陆的问题 -- 修复 HTTP 超时问题 - -- 👍👍 新增功能 👍👍 - -- sql 能力支持同时执行多条 sql -- 实现简单级权限控制 -- 后端部分插件化改造 -- 提供组件库的功能 -- 提供页面配置行为 -- 支持配置多环境 - -## 如何使用 - -### 环境配置 - -我们可以在环境设置中配置环境级参数,在测试用例执行的时候选择环境用户切换不同参数 只有管理员可以添加和编辑环境 - -![](/assets/img/news/TestHub-1.0.2-2.jpg) -![](/assets/img/news/TestHub-1.0.2-3.jpg) - -### 行为设置 - -我们可以在行为设置中看到系统级行为和项目级别行为,其中系统级行为不可以被编辑,项目级行为可由管理员和创建人编辑 -![](/assets/img/news/TestHub-1.0.2-4.jpg) - -### 如何退出登陆 - -![](/assets/img/news/TestHub-1.0.2-5.jpg) - -## HTTP 如何设置超时时间 - -timeout 可以设置 HTTP 的超时时间 单位为秒 超时设置默认 60 秒 - -``` - - - - - - - - { - "acctId": "960307", - "orderPrice": "1.8", - "orderQty": "100" - } - - - - -``` - -## SQL 如何设置自动提交事务 与 如何批量执行 - -commit="true" 表示自动提交事务 bound 中可以写多条 sql - -``` - - - - - - - - { - "acctId": "960307", - "orderPrice": "1.8", - "orderQty": "100" - } - - - - -``` - -**\[失败女神\]-TestHub 开发团队** - - +--- +title: 自动化测试工具:TestHub V1.0.2版本发布 +author: TestHub +tag: + - TestHub +date: 2023-10-31 +cover: /assets/img/news/TestHub-1.0.2-1.jpg +head: + - - meta + - name: 新闻 +--- + +## 自动化测试工具:TestHub V1.0.2 版本发布 + +## 简介 + +TestHub 是一款基于流程编排的自动化测试工具。是为了解决在软件开发旅程中测试流程管理和执行的复杂挑战而诞生的。传统测试工具可能局限于接口级自动化,无法满足多样化的需求,而我们在 TestHub 中引入了独特的流程编排功能,让您能够轻松定义、管理和执行测试流程。无论是自动化测试、流程调度还是其他自动化任务,TestHub 的插件式架构都能够满足您的无限扩展需求。 + +使用手册:http://nsrule.com/ + +演示环境:http://testhub.nsrule.com:11018/#/ + +Gitee 开源地址:https://gitee.com/dromara/TestHub + +Gitub 开源地址:https://github.com/dromara/TestHub + +演示视频:https://www.bilibili.com/video/BV1X94y1v7ak/ + +安装包:https://url84.ctfile.com/d/49656084-58580094-6ad8ce?p=3738 (访问密码: 3738) + +![](/assets/img/news/TestHub-1.0.2-1.jpg) + +## 更新内容 + +- 🪲🪲 修复 BUG🪲🪲 + +- 解决用例类目树超出不显示滚动条的问题 +- 修复无法退出登陆的问题 +- 修复 HTTP 超时问题 + +- 👍👍 新增功能 👍👍 + +- sql 能力支持同时执行多条 sql +- 实现简单级权限控制 +- 后端部分插件化改造 +- 提供组件库的功能 +- 提供页面配置行为 +- 支持配置多环境 + +## 如何使用 + +### 环境配置 + +我们可以在环境设置中配置环境级参数,在测试用例执行的时候选择环境用户切换不同参数 只有管理员可以添加和编辑环境 + +![](/assets/img/news/TestHub-1.0.2-2.jpg) +![](/assets/img/news/TestHub-1.0.2-3.jpg) + +### 行为设置 + +我们可以在行为设置中看到系统级行为和项目级别行为,其中系统级行为不可以被编辑,项目级行为可由管理员和创建人编辑 +![](/assets/img/news/TestHub-1.0.2-4.jpg) + +### 如何退出登陆 + +![](/assets/img/news/TestHub-1.0.2-5.jpg) + +## HTTP 如何设置超时时间 + +timeout 可以设置 HTTP 的超时时间 单位为秒 超时设置默认 60 秒 + +``` + + + + + + + + { + "acctId": "960307", + "orderPrice": "1.8", + "orderQty": "100" + } + + + + +``` + +## SQL 如何设置自动提交事务 与 如何批量执行 + +commit="true" 表示自动提交事务 bound 中可以写多条 sql + +``` + + + + + + + + { + "acctId": "960307", + "orderPrice": "1.8", + "orderQty": "100" + } + + + + +``` + +**\[失败女神\]-TestHub 开发团队** + + diff --git a/src/zh/news/TestHub-1.0.3.md b/src/zh/news/TestHub-1.0.3.md index 4f387ddef6..acdc0e1c56 100644 --- a/src/zh/news/TestHub-1.0.3.md +++ b/src/zh/news/TestHub-1.0.3.md @@ -1,272 +1,272 @@ ---- -title: 自动化测试工具:TestHub V1.0.3 版本发布 -author: TestHub -tag: - - TestHub -date: 2024-01-25 -cover: /assets/img/news/TestHub-1.0.2-1.jpg -head: - - - meta - - name: 新闻 ---- - -# 自动化测试工具:TestHub V1.0.3 版本发布 - -## 简介 - -TestHub 是一款基于流程编排的自动化测试工具。是为了解决在软件开发旅程中测试流程管理和执行的复杂挑战而诞生的。传统测试工具可能局限于接口级自动化,无法满足多样化的需求,而我们在 TestHub 中引入了独特的流程编排功能,让您能够轻松定义、管理和执行测试流程。无论是自动化测试、流程调度还是其他自动化任务,TestHub 的插件式架构都能够满足您的无限扩展需求。 - -使用手册:http://nsrule.com/ - -演示环境:http://testhub.nsrule.com:11018/#/ - -Gitee 开源地址:https://gitee.com/dromara/TestHub - -Gitub 开源地址:https://github.com/dromara/TestHub - -演示视频:https://www.bilibili.com/video/BV1X94y1v7ak/ - -安装包:https://url37.ctfile.com/d/42659137-59604925-26bdd9?p=3710 (访问密码: 3710) - -![](/assets/img/news/TestHub-1.0.2-1.jpg) - -## 1.0.3 更新内容 - -- 🪲🪲 修复 BUG🪲🪲 - -- 优化用例编辑器卡顿问题 -- 修复 HTTP 不支持异步接口能问题 -- 自动生成的 ID 和人工输入的 ID 可能会产生重复@magic(感谢大佬) #I8AZW1 - -- 👍👍 新增功能 👍👍 - -- 优化 Formula 表达式日志展示。使用说明 -- 基于 antlr4 重构 Formula 表达式 -- 支持操作 cookie #I8I89D -- 参数支持复制键入 #I8B27J -- 优化环境变量的使用 #I8CEPE - -## 如何使用 Formula 表达式 - -**❓ 为什么要基于 antlr4 重构 Formula 表达式 ❓** - -ANTLR (ANother Tool for Language Recognition) 是一个强大的语法分析器生成工具,用于生成词法分析器和语法分析器。ANTLR4 是其最新版本,具有许多先进的功能,它有一些优势使其在一些情况下比 TestHub 之前版本手动解析更有优势,而且支持的语法更多。 - -Formula 表达式做为操作数是 TestHub 数据的最小操作单元, - -我们用 FormulaNode 表示一个的操作数 - TestHub 中内置了 5 种基础操作数 - -- DataNode 表示固定值,是有配置人员手动指定的, 例如:`xxxx` -- PathNode 表示变量值,可以基于 jsonPath 从决策上下文中获取指定的变量值,标识为`${xxx.yyy}` -- FuncNode 表示方法型函数的调用,标识为`%{方法名(形参 1:操作数,......)}` -- ArithmeticNode 表示计算算数运算, 例如:`1+2+3*4/5%3` -- MixNode 表示混合表达式 - -### 操作对象 - -后续介绍均以上下文中存在如下数据为前提 - -``` -{ - "store": { - "book": [ - { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 - }, - { - "category": "fiction", - "author": "Evelyn Waugh", - "title": "Sword of Honour", - "price": 12.99 - }, - { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99 - }, - { - "category": "fiction", - "author": "J. R. R. Tolkien", - "title": "The Lord of the Rings", - "isbn": "0-395-19395-8", - "price": 22.99 - } - ], - "bicycle": { - "color": "red", - "price": 19.95 - } - }, - "expensive": 2 -} -``` - -### 语法 - -#### 固定值-DataNode - -data="root"中的 我们写死的 root 其实就是 DataNode 类型的操作数 - -``` - -``` - -| 写法 | 例子 | 值 | -| ---------------------- | -------------------------------------------------------- | -------------------------- | -| `-1.0086` | 数字 | \-1.0086 | -| `{attr:{a:1},attr1:1}` | json 对象 | {"attr":{"a":1},"attr1":1} | -| `[${expensive},1]` | 列表 操作对象的 expensive 做为第一个元素,1 是第二个元素 | `[2,1]` | - -#### 变量值-PathNode - -::: warning PathNode 几乎支持了所有 jsonPath 的语法,但是我们不支持 `$['store']['book'][0]['title']` 这种`[]`的写法 如果目前的语法满足不了你的测试过程,请于`gitee`仓库提 lssues,我们会优先支持。 jsonPath 语法学习 请参考 https://github.com/alibaba/fastjson/wiki/JSONPath -::: - -| 写法 | 例子 | 值 | -| ------------------------------------------ | ------------------------------------------------------------------- | ------------------------------------------ | -| `${expensive}` | 操作对象的 expensive 的值 | 2 | -| `${store.book.price}` | 操作对象的 store 的每一个 book 的 price 值 | `[8.95, 12.99, 8.99, 22.99]` | -| `${store.book[-1].author}` | 操作对象的 store 的最后一个 book 的 author | J. R. R. Tolkien | -| `${store.book[0:2].author}` | 操作对象的 store 的 0 到 2book 的 author | Nigel Rees,Sword of Honour,Herman Melville | -| `${store.book[?(isbn)]}` | 操作对象的 store 的 book 中 isbn 属性不等于空的 | Herman Melville,J. R. R. Tolkien | -| `${store.book[author='Nigel Rees'].title}` | 操作对象的 store 的 book 中 author 等于 Nigel Rees 的 book 的 title | Sayings of the Century | - -#### 内置方法-FuncNode - -| 写法 | 例子 | 值 | -| ------------------------- | ------------------------------ | ---- | -| `%{getNowTime()}` | 获取当前时间 | 看表 | -| `%{add(attr1:1,attr2:2)}` | 进行 1+2 ,key:val 的写法 | 3 | -| `%{add(1,2)}` | 进行 1+2 默认 key 为下标的写法 | 3 | - -内置函数是我们基于 SPI 机制提供的扩展点之一,目前我们实现了这些内置函数 - -如果有不满足你的可以按照一下方式扩展 - -1. 继承 FunctionHandler - -``` -package org.dromara.testhub.nsrule.core.executer.mode.base.function; - -import com.alibaba.fastjson.JSONObject; -import org.dromara.testhub.nsrule.core.executer.context.Context; - -public interface FunctionHandler { - String getName(); - - Object execute(Context context, JSONObject data); -} - -``` - -2. 在 META-INF.services 路径下的 org.dromara.testhub.nsrule.core.executer.mode.base.function.FunctionHandler 配置的你扩展方法 - -#### 算数运算-ArithmeticNode - -是的我们支持了手写算数运算的表达式 - -| 符号 | 作用 | -| ---- | ------ | -| + | 加 | -| \- | 减 | -| `*` | 乘 | -| / | 除 | -| % | 取余 | -| () | 优先级 | - -| 写法 | 例子 | 值 | -| --------------- | ------------------- | --- | -| `1+2+3*4/5%3` | 计算`1+2+3*4/5%3` | 5.4 | -| `1+(2+3)*4/5%3` | 计算`1+(2+3)*4/5%3` | 2 | - -#### 混合表达式-MixNode - -用户组合所有 FormulaNode 最终结果返回字符串 - -| 写法 | 例子 | 值 | -| ------------------------------------------------ | ------------------------------------------- | ----------------------------------- | -| `http:${expensive}:10010/server/workerHeartbeat` | 将操作对象的 expensive 的值替换后返回字符串 | http:2:10010/server/workerHeartbeat | - -## 实践 - -![](/assets/img/news/TestHub-1.0.3-1.jpg) - -``` - - - - - - - {"store":{"book":[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],"bicycle":{"color":"red","price":19.95}},"expensive":2} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` +--- +title: 自动化测试工具:TestHub V1.0.3 版本发布 +author: TestHub +tag: + - TestHub +date: 2024-01-25 +cover: /assets/img/news/TestHub-1.0.2-1.jpg +head: + - - meta + - name: 新闻 +--- + +# 自动化测试工具:TestHub V1.0.3 版本发布 + +## 简介 + +TestHub 是一款基于流程编排的自动化测试工具。是为了解决在软件开发旅程中测试流程管理和执行的复杂挑战而诞生的。传统测试工具可能局限于接口级自动化,无法满足多样化的需求,而我们在 TestHub 中引入了独特的流程编排功能,让您能够轻松定义、管理和执行测试流程。无论是自动化测试、流程调度还是其他自动化任务,TestHub 的插件式架构都能够满足您的无限扩展需求。 + +使用手册:http://nsrule.com/ + +演示环境:http://testhub.nsrule.com:11018/#/ + +Gitee 开源地址:https://gitee.com/dromara/TestHub + +Gitub 开源地址:https://github.com/dromara/TestHub + +演示视频:https://www.bilibili.com/video/BV1X94y1v7ak/ + +安装包:https://url37.ctfile.com/d/42659137-59604925-26bdd9?p=3710 (访问密码: 3710) + +![](/assets/img/news/TestHub-1.0.2-1.jpg) + +## 1.0.3 更新内容 + +- 🪲🪲 修复 BUG🪲🪲 + +- 优化用例编辑器卡顿问题 +- 修复 HTTP 不支持异步接口能问题 +- 自动生成的 ID 和人工输入的 ID 可能会产生重复@magic(感谢大佬) #I8AZW1 + +- 👍👍 新增功能 👍👍 + +- 优化 Formula 表达式日志展示。使用说明 +- 基于 antlr4 重构 Formula 表达式 +- 支持操作 cookie #I8I89D +- 参数支持复制键入 #I8B27J +- 优化环境变量的使用 #I8CEPE + +## 如何使用 Formula 表达式 + +**❓ 为什么要基于 antlr4 重构 Formula 表达式 ❓** + +ANTLR (ANother Tool for Language Recognition) 是一个强大的语法分析器生成工具,用于生成词法分析器和语法分析器。ANTLR4 是其最新版本,具有许多先进的功能,它有一些优势使其在一些情况下比 TestHub 之前版本手动解析更有优势,而且支持的语法更多。 + +Formula 表达式做为操作数是 TestHub 数据的最小操作单元, + +我们用 FormulaNode 表示一个的操作数 - TestHub 中内置了 5 种基础操作数 + +- DataNode 表示固定值,是有配置人员手动指定的, 例如:`xxxx` +- PathNode 表示变量值,可以基于 jsonPath 从决策上下文中获取指定的变量值,标识为`${xxx.yyy}` +- FuncNode 表示方法型函数的调用,标识为`%{方法名(形参 1:操作数,......)}` +- ArithmeticNode 表示计算算数运算, 例如:`1+2+3*4/5%3` +- MixNode 表示混合表达式 + +### 操作对象 + +后续介绍均以上下文中存在如下数据为前提 + +``` +{ + "store": { + "book": [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { + "color": "red", + "price": 19.95 + } + }, + "expensive": 2 +} +``` + +### 语法 + +#### 固定值-DataNode + +data="root"中的 我们写死的 root 其实就是 DataNode 类型的操作数 + +``` + +``` + +| 写法 | 例子 | 值 | +| ---------------------- | -------------------------------------------------------- | -------------------------- | +| `-1.0086` | 数字 | \-1.0086 | +| `{attr:{a:1},attr1:1}` | json 对象 | {"attr":{"a":1},"attr1":1} | +| `[${expensive},1]` | 列表 操作对象的 expensive 做为第一个元素,1 是第二个元素 | `[2,1]` | + +#### 变量值-PathNode + +::: warning PathNode 几乎支持了所有 jsonPath 的语法,但是我们不支持 `$['store']['book'][0]['title']` 这种`[]`的写法 如果目前的语法满足不了你的测试过程,请于`gitee`仓库提 lssues,我们会优先支持。 jsonPath 语法学习 请参考 https://github.com/alibaba/fastjson/wiki/JSONPath +::: + +| 写法 | 例子 | 值 | +| ------------------------------------------ | ------------------------------------------------------------------- | ------------------------------------------ | +| `${expensive}` | 操作对象的 expensive 的值 | 2 | +| `${store.book.price}` | 操作对象的 store 的每一个 book 的 price 值 | `[8.95, 12.99, 8.99, 22.99]` | +| `${store.book[-1].author}` | 操作对象的 store 的最后一个 book 的 author | J. R. R. Tolkien | +| `${store.book[0:2].author}` | 操作对象的 store 的 0 到 2book 的 author | Nigel Rees,Sword of Honour,Herman Melville | +| `${store.book[?(isbn)]}` | 操作对象的 store 的 book 中 isbn 属性不等于空的 | Herman Melville,J. R. R. Tolkien | +| `${store.book[author='Nigel Rees'].title}` | 操作对象的 store 的 book 中 author 等于 Nigel Rees 的 book 的 title | Sayings of the Century | + +#### 内置方法-FuncNode + +| 写法 | 例子 | 值 | +| ------------------------- | ------------------------------ | ---- | +| `%{getNowTime()}` | 获取当前时间 | 看表 | +| `%{add(attr1:1,attr2:2)}` | 进行 1+2 ,key:val 的写法 | 3 | +| `%{add(1,2)}` | 进行 1+2 默认 key 为下标的写法 | 3 | + +内置函数是我们基于 SPI 机制提供的扩展点之一,目前我们实现了这些内置函数 + +如果有不满足你的可以按照一下方式扩展 + +1. 继承 FunctionHandler + +``` +package org.dromara.testhub.nsrule.core.executer.mode.base.function; + +import com.alibaba.fastjson.JSONObject; +import org.dromara.testhub.nsrule.core.executer.context.Context; + +public interface FunctionHandler { + String getName(); + + Object execute(Context context, JSONObject data); +} + +``` + +2. 在 META-INF.services 路径下的 org.dromara.testhub.nsrule.core.executer.mode.base.function.FunctionHandler 配置的你扩展方法 + +#### 算数运算-ArithmeticNode + +是的我们支持了手写算数运算的表达式 + +| 符号 | 作用 | +| ---- | ------ | +| + | 加 | +| \- | 减 | +| `*` | 乘 | +| / | 除 | +| % | 取余 | +| () | 优先级 | + +| 写法 | 例子 | 值 | +| --------------- | ------------------- | --- | +| `1+2+3*4/5%3` | 计算`1+2+3*4/5%3` | 5.4 | +| `1+(2+3)*4/5%3` | 计算`1+(2+3)*4/5%3` | 2 | + +#### 混合表达式-MixNode + +用户组合所有 FormulaNode 最终结果返回字符串 + +| 写法 | 例子 | 值 | +| ------------------------------------------------ | ------------------------------------------- | ----------------------------------- | +| `http:${expensive}:10010/server/workerHeartbeat` | 将操作对象的 expensive 的值替换后返回字符串 | http:2:10010/server/workerHeartbeat | + +## 实践 + +![](/assets/img/news/TestHub-1.0.3-1.jpg) + +``` + + + + + + + {"store":{"book":[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],"bicycle":{"color":"red","price":19.95}},"expensive":2} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` diff --git a/src/zh/news/Tianai-Captcha-0.md b/src/zh/news/Tianai-Captcha-0.md index cf19adac03..460de964e7 100644 --- a/src/zh/news/Tianai-Captcha-0.md +++ b/src/zh/news/Tianai-Captcha-0.md @@ -1,59 +1,59 @@ ---- -title: 欢迎Tianai-Captcha加入Dromara开源社区, 可能是开源界最好用的行为验证码工具 -author: Tianai -date: 2024-01-08 -cover: /assets/img/news/Tianai-Captcha-0-0.jpg -head: - - - meta - - name: 新闻 ---- - -## 作者介绍 - -> 95 后大龄程序员,一名野生的民间技术爱好者,15 年学习编程技术,迫于生计于 17 年就职于某电商公司, 在从业生涯中,本项目 2020 年发布后,后续也是改改停停,自古闲人出金货,也许有一天笔者自由了,会好好的完善这套框架。 - -## 引言: - -> 譬如在今之网络世界,为保障资讯之安全,凡入网之人或事,多须经由验证之法以证实己身之真实性。是以,验证码乃必不可少之一环也。其重要性备矣,具诚信者无不体知。 -> -> 朕观网络之变幻,验证码多以随机字母构成者为众所周知。然而,此类验证码对于一般用户而言,尤其易于应用。盖因滑动、点选等高级验证码,虽能提供更加友好之体验,然然不易于普通用户之应用。 -> -> 吾观察于平民百姓,多未涉猎于技术深处,对于复杂之滑动、点选类验证码而言,或存不解其所在。且诸多普通用户或使用传统设备,或因技术限制而难以适用此等新颖验证码。是以,此类验证码对于普罗百姓而言,未免难以为继。 -> -> 嗟乎!有智者闻我国民之难,乃发明滑动及点选验证码以应民需,其善心可嘉。彼将此等验证码开源,使广大百姓得以轻松接纳,实属可喜可贺。 -> -> 滑动及点选验证码之开源,如一泓清泉,涤荡网络之隐忧。于此,一般百姓不复为验证码所困,得以轻松、便利之享用。其操作简便,贴近生活,解民忧而广受欢迎,实为普及网络安全之一良策。 -> -> 开源此等验证码者,其举措实乃有益于民众。不仅促进了网络安全,亦鼓舞了普罗百姓参与其中之热情。愿诸般良好之举措,皆能为社会大众所接纳,盛行于世。 - ---- - -## 关于 TIANAI-CAPTCHA - -`tianai-captcha`简称`tac`,是一款集成滑动类、点选类的一款行为验证码,以使用简单、安全性强、界面美观、接入方便而,是为集好看、功能多、安全性强的一款开源行为验证码工具。 - ---- - -![](/assets/img/news/Tianai-Captcha-0-0.jpg) - -![](/assets/img/news/Tianai-Captcha-0-1.jpg) - -## 在线体验 - -**http://captcha.tianai.cloud** - -## 使用方式 - -**http://doc.captcha.tianai.cloud** - -## 源码地址 - -**https://gitee.com/dromara/tianai-captcha** - -**https://github.com/dromara/tianai-captcha** - -关于 Dromara - -Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务 RPC,运维监控,Agent 监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 - -Dromara 开源社区目前拥有 10+GVP 项目,总 star 数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用 Dromara 社区的开源项目。 +--- +title: 欢迎Tianai-Captcha加入Dromara开源社区, 可能是开源界最好用的行为验证码工具 +author: Tianai +date: 2024-01-08 +cover: /assets/img/news/Tianai-Captcha-0-0.jpg +head: + - - meta + - name: 新闻 +--- + +## 作者介绍 + +> 95 后大龄程序员,一名野生的民间技术爱好者,15 年学习编程技术,迫于生计于 17 年就职于某电商公司, 在从业生涯中,本项目 2020 年发布后,后续也是改改停停,自古闲人出金货,也许有一天笔者自由了,会好好的完善这套框架。 + +## 引言: + +> 譬如在今之网络世界,为保障资讯之安全,凡入网之人或事,多须经由验证之法以证实己身之真实性。是以,验证码乃必不可少之一环也。其重要性备矣,具诚信者无不体知。 +> +> 朕观网络之变幻,验证码多以随机字母构成者为众所周知。然而,此类验证码对于一般用户而言,尤其易于应用。盖因滑动、点选等高级验证码,虽能提供更加友好之体验,然然不易于普通用户之应用。 +> +> 吾观察于平民百姓,多未涉猎于技术深处,对于复杂之滑动、点选类验证码而言,或存不解其所在。且诸多普通用户或使用传统设备,或因技术限制而难以适用此等新颖验证码。是以,此类验证码对于普罗百姓而言,未免难以为继。 +> +> 嗟乎!有智者闻我国民之难,乃发明滑动及点选验证码以应民需,其善心可嘉。彼将此等验证码开源,使广大百姓得以轻松接纳,实属可喜可贺。 +> +> 滑动及点选验证码之开源,如一泓清泉,涤荡网络之隐忧。于此,一般百姓不复为验证码所困,得以轻松、便利之享用。其操作简便,贴近生活,解民忧而广受欢迎,实为普及网络安全之一良策。 +> +> 开源此等验证码者,其举措实乃有益于民众。不仅促进了网络安全,亦鼓舞了普罗百姓参与其中之热情。愿诸般良好之举措,皆能为社会大众所接纳,盛行于世。 + +--- + +## 关于 TIANAI-CAPTCHA + +`tianai-captcha`简称`tac`,是一款集成滑动类、点选类的一款行为验证码,以使用简单、安全性强、界面美观、接入方便而,是为集好看、功能多、安全性强的一款开源行为验证码工具。 + +--- + +![](/assets/img/news/Tianai-Captcha-0-0.jpg) + +![](/assets/img/news/Tianai-Captcha-0-1.jpg) + +## 在线体验 + +**http://captcha.tianai.cloud** + +## 使用方式 + +**http://doc.captcha.tianai.cloud** + +## 源码地址 + +**https://gitee.com/dromara/tianai-captcha** + +**https://github.com/dromara/tianai-captcha** + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务 RPC,运维监控,Agent 监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + +Dromara 开源社区目前拥有 10+GVP 项目,总 star 数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用 Dromara 社区的开源项目。 diff --git a/src/zh/news/WGAI-0.md b/src/zh/news/WGAI-0.md new file mode 100644 index 0000000000..0e0f3da388 --- /dev/null +++ b/src/zh/news/WGAI-0.md @@ -0,0 +1,83 @@ +--- +title: 新晋 AI 开源项目 WGAI 加入 Dromara 社区,轻量级、模块化的AI助手框架 +author: WGAI +date: 2025-02-10 +cover: /assets/img/news/WGAI-0-0.jpg +head: + - - meta + - name: 新闻 +--- + +### 🚀 新的起点 + +我们非常高兴地宣布,WGAI 项目正式加入了 Dromara 开源社区!这是一个重要的里程碑,标志着 WGAI 将能够与更多的开发者和研究者分享其独特的功能,并且共同推动人工智能领域的发展。 + +关于 WGAI WGAI 是一个开放源码的人工智能项目,它基于 Apache License 2.0 许可证发布。这意味着您可以在满足一定条件下自由使用、复制和分发本作品。在 WGAI 中,用户不仅可以直接利用预训练的模型进行各种应用,还可以根据自己的需求对模型进行自定义训练,以适应特定场景下的要求。 + +![](/assets/img/news/WGAI-0-0.jpg) + +### 🚀  功能设计 + +* 不懂python也可直接执行再也不用担心技术瓶颈 + +* 在线训练、在线标注、模型列表 + +* 包含OCR、图文、音视频等识别 + +* 本地化部署语音识别、热词配置 + + +## 🚀 四大核心优势 + +1. **Java全栈友好** - 深度整合Spring生态,提供`wgai-spring-boot-starter` + +2. **自我训练模型** - WGAI 的一大特色是支持用户自行训练模型。通过这种方式,无论是学术研究还是工业应用,用户都能根据自身的数据集和任务目标来优化模型性能 + +3. **工业级部署** - 支持ONNX导出与TensorRT加速 + +4. **简单易用** - WGAI提供了简洁的API接口和详细的文档,开发者可以快速上手并集成到自己的项目中 + + +## 🛠️ 适用场景 + +* 安防监控-人脸识别(机场、车站的身份核验)、行为检测(识别异常行为(如跌倒、斗殴),实时触发警报)。 + +* 交通物流-自动驾驶(实时识别道路标志、行人、车辆,辅助导航决策)、车牌识别(高速公路ETC系统或停车场自动识别车牌号码)。 + +* 工业制造-缺陷检测(AI视觉检查产品表面划痕、尺寸偏差,提升质检效率)、预测性维护(通过识别设备振动或温度数据异常,预判故障)。 + +* 金融安全-欺诈检测(分析交易模式识别异常行为如异地大额转账)、证件核验(OCR技术自动提取身份证、银行卡信息,比对真伪)。 + + +# 💻 快速入门 + +### 一键训练 + +![](/assets/img/news/WGAI-0-1.png) + +### 一键识别 + +![](/assets/img/news/WGAI-0-2.jpg) + +![](/assets/img/news/WGAI-0-3.jpg) + + + +![](/assets/img/news/WGAI-0-4.jpg) + + + +## 🚀开源地址 + +WGAI作为一个轻量级、模块化的AI助手框架,旨在为开发者提供简单易用的工具,帮助快速构建智能应用。无论你是AI领域的初学者,还是经验丰富的开发者,WGAI都能为你提供强大的支持。欢迎大家试用并反馈意见,共同推动项目的进步! + +如果你觉得这个项目对你有帮助,别忘了给个Star哦!🌟 + +* **Gitee**: https://gitee.com/dromara/wgai + +* **GitHub**: https://github.com/dromara/wgai + + +感谢大家的支持! + +* * * \ No newline at end of file diff --git a/src/zh/news/WGAI-V2.0.md b/src/zh/news/WGAI-V2.0.md new file mode 100644 index 0000000000..ff54a442cf --- /dev/null +++ b/src/zh/news/WGAI-V2.0.md @@ -0,0 +1,117 @@ +--- +title: WGAI 训练平台 V2.0 重磅升级 +author: WGAI +date: 2025-03-05 +cover: /assets/img/news/WGAI-V2.0-0.png +head: + - - meta + - name: 新闻 +--- + +## **WGAI训练平台V2.0重磅发布:一键模型分发、跨平台共享、第三方无缝接入,开启智能协作新时代**!  + +**——Dromara开源社区再添企业级AI利器** + +## **引言**  + +在AI技术高速迭代的今天,如何实现模型训练与推理的高效协同,降低企业智能化转型门槛?WGAI训练平台作为Dromara开源生态中专注AI开发的核心工具,始终以“**简化流程、赋能协作**”为目标。本次V2.0版本升级,我们聚焦**模型分发效率**、**资源协作共享**与**生态开放兼容**三大方向,带来更贴合企业需求的解决方案! + +* * * + +## **一、核心升级亮点解析**  + +#### **1\. 模型一键下发:1台训练机,N台推理机,资源利用率提升300%** + +* **功能说明**:支持将训练完成的模型**一键下发至任意推理平台**,实现“训练-推理”链路自动化。无论是边缘设备还是云端集群,均可通过平台界面快速配置部署路径,告别手动导出与脚本上传的繁琐操作。 + +* **技术优势**: + + +* 基于动态资源调度算法,自动适配不同推理框架(如TensorFlow Serving、ONNX Runtime等)的接口需求。 + + +* **用户价值**:企业可灵活扩展推理算力,快速响应业务高峰,同时减少训练资源闲置成本。 + + +![](/assets/img/news/WGAI-V2.0-0.png) + + + +#### **2\. 模型共享协作:跨平台资源互通,构建企业AI资产池** + +* **功能说明**:允许平台间**按权限共享模型**,支持将模型下发给指定推理机或开放给协作团队,实现知识沉淀与复用。 + +* **技术亮点**: + + +* 提供模型版本追踪与依赖分析,确保共享模型的可靠性与一致性。 + + +* **场景案例**:某制造企业通过共享缺陷检测模型至多个产线推理平台,实现质检标准统一,故障排查效率提升40%。 + + +![](/assets/img/news/WGAI-V2.0-1.png) + + + +#### **3\. 第三方无缝接入:开放订阅机制,打通智能业务闭环** + +* **功能说明**:新增**订阅地址与视频流接口**,支持第三方系统通过API订阅模型报警信息,或直视频流至平台进行实时分析推送结果返回。 + +* **技术突破**: + + +* 内置多协议解析引擎,兼容主流视频流格式(RTSP/RTMP/HLS),降低接入成本。 + + +* **应用场景**:安防领域客户通过接入视频流,实时调用人脸识别模型,异常事件图片自动推送至指挥中心,响应速度达毫秒级。 + + +![](/assets/img/news/WGAI-V2.0-2.png) + +![](/assets/img/news/WGAI-V2.0-3.png) + + + +* * * + +## **二、升级背后的技术革新**  + +本次版本依托Dromara社区强大的技术底座,深度融合以下能力: + +* **低代码扩展**:提供可视化配置界面,用户无需编码即可完成第三方系统对接,契合国产化低代码趋势。 + + +* * * + +## **三、用户如何受益**?  + +* **企业管理者**:构建跨部门AI协作网络,避免重复开发,降低70%的模型管理成本。 + +* **生态伙伴**:开放API与插件机制,快速接入行业解决方案。 + + +* * * + +## **四、立即体验**  + +* **开源地址Gitee**:https://gitee.com/dromara/wgai + +* **开源地址GitHub**:https://github.com/dromara/wgai + +* **体验地址**:http://116.198.227.105:8888 + +* **演示视频**:https://www.bilibili.com/video/BV13C9BYiEFS?t=38.4 + +* **加入社群**: + + +![](/assets/img/news/WGAI-V2.0-4.jpg) + + + +* * * + +## **结语**  + +WGAI V2.0不仅是技术升级,更是对AI开发范式的一次重构。我们相信,**开放、协作、高效**将成为未来AI工程化的核心关键词。立即升级体验,与Dromara社区共同推动AI普惠化进程! \ No newline at end of file diff --git a/src/zh/news/WGAI-V3.0.md b/src/zh/news/WGAI-V3.0.md new file mode 100644 index 0000000000..9311359914 --- /dev/null +++ b/src/zh/news/WGAI-V3.0.md @@ -0,0 +1,111 @@ +--- +title: WGAI star 800+ 训练平台V3.0重磅发布:全栈式数字人训练引擎,赋能打造自己专属智能体! +author: WGAI +date: 2025-05-16 +cover: /assets/img/news/WGAI-V3.0-0.png +head: + - - meta + - name: 新闻 +--- + +### 🔥 🔥 🔥  **WGAI star 800+ 训练平台V3.0重磅发布:全栈式数字人训练引擎,赋能打造自己专属智能体**! + +> 🚫 郑重承诺:永久免费!不设商业版! + +> #### ❤ **更新展示** +> +> * ![](/assets/img/news/WGAI-V3.0-0.png) +> +> +> +> * 图片变成动态数字人视频/设置自己自己的文本与音色 +> +> * https://img.nj-kj.com/zhangwei\_1745562613859\_1745465917540\_1745567724504.mp4 +> +> +> * * * + +> #### ✨**主要更新** +> +> * ✅ 增加数字人训练体系-一张图片即可达到动态数字人 +> +> * ✅ 增加自定义音频,0-179音色供给选择 +> +> * ✅ 增加数字人管理/自定义语音TTS转换 +> +> * ✅ 一件训练妈妈再也不用担心我不看不懂代码了! +> +> +> * * * + +> #### 📦 **下期预告** +> +> * 🚀 增加实时流数字人交互,人机交互 +> +> * 🚀 数字人接轨本地大模型,知识库智能对话问答 +> +> +> * * * + +### 🖼️**效果展示** + +> #### **1\. 数字人形象训练:自定义形象数字人** +> +> ![](/assets/img/news/WGAI-V3.0-1.png) +> +> +> +> * **功能说明**:支持通过平台界面**自定义数字人形象**(发型、服饰、表情等),上传图片或支持命名与身份背景设定,打造自己专属数字人。 +> +> * **技术优势**: +> +> * 集成AI生成式对抗网络(GAN),一键生成高精度人脸与动态表情。 +> +> * 内置轻量化渲染引擎,适配Web、移动端、XR设备等多平台展示。 +> + +> #### **2\. 动作训练自由编排:让数字人“活”起来** +> +> ![](/assets/img/news/WGAI-V3.0-2.png) +> +> * **功能说明**:提供可视化动作编辑器,支持**自定义手势、肢体动作、口型同步**,通过关键帧设定或AI动作捕捉技术,实现流畅自然的交互表现。 +> +> * **技术亮点**: \*   融合骨骼绑定与物理引擎,动作过渡平滑无卡顿。 \*   支持与语音内容自动匹配口型(基于LSTM时序模型),提升真实感。 +> + +> #### **3\. 声音克隆与情感化播报:听见“独一无二”的AI** +> +> ![](/assets/img/news/WGAI-V3.0-3.png) +> +> ![](/assets/img/news/WGAI-V3.0-4.png) +> +> * **功能说明**:179种音色随意挑选,生成个性化数字人声音;提供情感参数调节(如欢快、严肃、温柔),适配不同交互场景。 +> +> * **技术突破**: \*   基于深度神经网络(Tacotron 2+WaveGlow),实现高保真语音合成。 \*   集成情感识别模型(BERT+Prosody分析),让语音输出更具感染力。 +> + +> #### **4\. 前瞻能力预告:智能问答+本地化部署,解锁商业闭环,妈妈再也不用担心我被卡脖子了** +> +> * **即将上线**: \*   **语音模型智能问答**:对接大语言模型(如ChatGLM、Llama 2),实现多轮对话与业务咨询(如产品查询、故障排查)。 \*   **本地化私有部署**:支持离线环境运行,数据完全私有化,满足金融、政务等高安全需求场景。 \*   **API生态扩展**:开放数字人驱动接口,无缝嵌入企业CRM、直播系统、智能硬件。 +> + +* * * + +### **四、立即体验** + +* **开源地址Gitee**:https://gitee.com/dromara/wgai + +* **开源地址GitHub**:https://github.com/dromara/wgai + +* **体验地址**:http://1.95.152.91:9999/   密码:wgai wgai@2024 + +* **演示视频**:https://www.bilibili.com/video/BV13C9BYiEFS?t=38.4 + +* **加入社群**: + + ![](/assets/img/news/WGAI-V3.0-5.jpg) + + + + +* * * \ No newline at end of file diff --git a/src/zh/news/WGAI-V4.0.md b/src/zh/news/WGAI-V4.0.md new file mode 100644 index 0000000000..975634e824 --- /dev/null +++ b/src/zh/news/WGAI-V4.0.md @@ -0,0 +1,95 @@ +--- +title: WGAI训练识别平台V4.0重磅发布:一站式智能训练增加GPU-OPENCL-CPU 处理训练识别! +author: WGAI +date: 2025-07-18 +cover: /assets/img/news/WGAI-V4.0-0.jpg +head: + - - meta + - name: 新闻 +--- + +### 🔥 🔥 🔥  **WGAI训练识别平台V4.0重磅发布:一站式智能训练增加GPU-OPENCL-CPU 处理训练识别**! + +> 🚫 郑重承诺:永久免费!不设商业版! 只有知识星球! + +> #### ✨**主要更新** +> +> * ✅ 增加解码方式GPU/NPU/CPU 支持流媒体/摄像头直接接入 +> +> * ✅ 增加报警推送录像-增加视频订阅配置 +> +> * ✅ 增加标注记忆-快捷标注 +> +> * ✅ 更新前端新UI体 +> +> +> * * * + +> #### 📦 **下期预告** +> +> * 🚀 减少消耗增加迭代内容 +> +> * 🚀 增加自动标注功能 +> +> +> * * * + +### 🖼️**效果展示** + +> #### **1\. 增加解码方式** + +![](/assets/img/news/WGAI-V4.0-0.jpg) + +> * **功能说明**:支持通过平台界面设置解码加速-解码方式GPU/NPU/CPU +> +> * **技术优势**: +> +> * 适配国产化、英特尔、英伟达GPU加速,解码加速,识别加速 +> + +> #### **2\. 增加一路视频多纬度模型识别** + +![](/assets/img/news/WGAI-V4.0-1.png) + +> * **功能说明**: 一条视频流支持多模型配置识别。 +> +> * **技术亮点**: \*   可多模型判断后增加准确度 并增加模型直接联系 \*   例如:区域入侵-入侵人行为检测-入侵人员检测 +> + +> #### **3\. 增加报警报警录像** + +![](/assets/img/news/WGAI-V4.0-2.jpg) + + + +> * **功能说明**:增加报警报警录像、录像推送、新ui内容 +> +> * **技术突破**:录像内也可存在分析结果 +> + +> #### **4\. 新ui配色** + +![](/assets/img/news/WGAI-V4.0-3.jpg) + +![](/assets/img/news/WGAI-V4.0-4.jpg) + +![](/assets/img/news/WGAI-V4.0-5.jpg) + +![](/assets/img/news/WGAI-V4.0-6.jpg) + +* * * + +### **四、立即体验** + +* **开源地址Gitee**:https://gitee.com/dromara/wgai + +* **开源地址GitHub**:https://github.com/dromara/wgai + +* **体验地址**:http://1.95.152.91:9999/   密码:wgai wgai@2024 + +* **演示视频**:https://www.bilibili.com/video/BV13C9BYiEFS?t=38.4 + +* **加入社群**: + + +![](/assets/img/news/WGAI-V4.0-7.jpg) \ No newline at end of file diff --git a/src/zh/news/Warm-Flow-1.3.4.md b/src/zh/news/Warm-Flow-1.3.4.md new file mode 100644 index 0000000000..a445c7272c --- /dev/null +++ b/src/zh/news/Warm-Flow-1.3.4.md @@ -0,0 +1,116 @@ +--- +title: Warm-Flow发布1.3.4, 支持solon和便捷性提升 +author: Warm-Flow +date: 2024-11-27 +cover: /assets/img/news/Warm-Flow-1.3.4-0.png +head: + - - meta + - name: 新闻 +--- + +# Warm-Flow发布1.3.4, 支持solon和便捷性提升 + +![](/assets/img/news/Warm-Flow-1.3.4-0.png) + +## 1、老群被干没了,新群: + +![](/assets/img/news/Warm-Flow-1.3.4-1.png) + +**本次的版本大大提升了工作流的使用便捷性,如下:** + +> 设计器引入,新增支持solon 新增监听器spel表达式,并且支持扩展 +> 增加全局监听器,针对整个系统,通过接口接入方式 流程变量表达式支持替换集合 + +## 2、详细更新内容: + +* 更新日志 + + +* \[feat\] 新增监听器spel表达式,并且支持扩展 + +* \[feat\] 增加全局监听器,针对整个系统,通过接口接入方式 + +* \[feat\] 新增审批前获取当前办理人接口,类似satoken方式 @huangjian + +* \[feat\] 流程变量表达式支持替换集合 @huangjian + +* \[feat\] 设计器引入,新增支持solon + +* \[feat\] 新增创建流程定义,默认初始化节点 + +* \[feat\] 新增根据流程定义id集合,查询流程实例集合api + +* \[update\] 监听器配置页面ui调整 + +* \[update\] 重新定义监听器名称,原全局监听器改名为流程监听器,局部监听器改名为节点监听器 + +* \[update\] 已经开启过审批任务的不可取消发布和删除 + +* \[update\] 转办、委派、加签和减签,增加参数合法性校验 + +* \[update\] 修改流程变量传递方式,可通过办理人变量表达式或者分派监听器,初始化后续所有办理人 + +* \[update\] 加载handler取消懒加载,重构test项目 + +* \[update\] 办理人变量表达式,删除策略前缀,通过$和#区分 + +* \[update\] 流程版本号默认改完自动递增,不接收外部设置 + +* \[refactor\] 重构条件表达式和办理人变量表达式 + +* \[remove\] 移除权限监听器 + + +## 3、项目介绍 + +> Warm-Flow国产工作流引擎🎉,其特点简洁轻量,五脏俱全,可扩展,是一个可通过jar引入设计器的工作流。 + +1. 简洁易用:只有7张表,代码量少,可快速上手和集成 + +2. 审批功能:支持通过、退回、任意跳转、转办、终止、会签、票签、委派和加减签、互斥和并行网关 + +3. 监听器与流程变量:支持四种监听器和不同颗粒的的作用范围,支持spel表达式,灵活可扩展,参数传递,动态权限 + +4. 流程图:流程引擎自带流程图,可在不集成流程设计器情况下使用 + +5. 流程设计器:可通过jar包形式快速集成到项目,减少繁琐代码搬运和适配,支持solon和springboot + +6. 条件表达式:内置常见的和spel条件表达式,并且支持自定义扩展 + +7. 办理人变量表达式:内置${handler}和spel格式的表达式,可满足不同场景,灵活可扩展 + +8. orm框架扩展:目前支持MyBatis、Mybatis-Plus、Mybatis-Flex和Jpa,后续会由社区提供其他支持,扩展方便 + +9. 数据库支持:目前支持MySQL 、Oracle 和PostgreSQL,后续会继续支持其他数据库或者国产数据库 + +10. 多租户与软删除:流程引擎自身维护多租户和软删除实现,也可使用对应orm框架的实现方式 + +11. 同时支持spring和solon + +12. 兼容java8和java17,理论11也可以 + +13. 官方提供基于ruoyi-vue封装实战项目,很实用 + + +## 4、演示地址 + +* admin/admin123 + + +演示地址:http://www.hhzai.top + +## 7、官网 + +http://warm-flow.cn + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/Warm-Flow-1.3.4-2.webp) \ No newline at end of file diff --git a/src/zh/news/Warm-Flow-1.6.6.md b/src/zh/news/Warm-Flow-1.6.6.md new file mode 100644 index 0000000000..76644078fc --- /dev/null +++ b/src/zh/news/Warm-Flow-1.6.6.md @@ -0,0 +1,169 @@ +--- +title: Warm-Flow 新春版 1.6.6 发布,国产工作流引擎 +author: Warm-Flow +date: 2025-01-24 +cover: /assets/img/news/Warm-Flow-1.6.6-0.png +head: + - - meta + - name: 新闻 +--- + +# 🧨Warm-Flow新春版1.6.6:网关直连和流程图重构, 新增Ruoyi-Vue-Plus优秀开源集成案例 + +* 本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。 + +* 新增Ruoyi-Vue-Plus优秀开源集成案例 + + +## 更新日志 + +* \[feat\] 导入、导出和保存等新增json格式支持DefService.importIs/importJson/importDef/saveDef/exportJson + +* \[feat\] 新增获取后置节点方法NodeService.suffixNodeList + +* \[feat\] 新增网关直连和测试案例 + +* \[feat\] 流程图右上角新增完成状态颜色示例 + +* \[feat\] 新增流程图查询接口和扩展接口ChartService + +* \[feat\] 新增历史表数据同步为新的流程图元数据 + +* \[feat\] 新增sqlserver全量脚本 + +* \[update\] 导入、导出和保存xml格式标识为即将删除,请参照hh-vue切换json的api + +* \[update\] FlowFactory修改为FlowEngine + +* \[update\] 历史表目标节点编码和目标节点名称字段长度改为200 + +* \[update\] 通过或者退回到并行网关,开启多个任务,改为只产生一条历史记录 + +* \[update\] 退回或者任务完成,其他需要被删除的任务不需要记录历史表,因为已经存在退回记录,不需要重复记录 + +* \[update\] 转办、委派、加签和减签,改为只产生一条历史记录 + +* \[update\] 批量保存改为默认1000条一批 + +* \[update\] 流程设计保存,增加遮罩层 + +* \[refactor\] 流程图绘制调整重构 + +* \[refactor\] 移除mybatis-flex,easy-query和jpa的扩展包,独立成项目,由专门人维护 + +* \[refactor\] 实体类和dao获取改为通过反射,解耦orm-core包 + +* \[refactor\] 重构获取前置节点方法NodeService.previousNodeList + +* \[fix\] 修复退回时存在其他代办任务,未删除的问题 + +* \[fix\] 修复流程退回目标节点前存在并行网关,导致不生成代办任务的问题 + +* \[fix\] 修复条件表达式中如果有`|`或导致错误分隔的问题 + +* \[fix\] 修复绘制流程图,错误判断同一条录像的key + +* \[fix\] 修复结束节点还执行创建监听器的问题 + +* \[remove\] 移除DefService获取流程图api,由ChartService中chartIns和chartDef代替 + +* \[remove\] 删除前端log打印 + +* \[remove\] 移除oracle和postgresql升级脚本,后续只提供mysql升级脚本,所有的全量脚本,其他升级脚本的自行转换 + + + + +## 项目介绍 + +**Dromara Warm-Flow国产工作流引擎,其特点简洁轻量,五脏俱全,可扩展,是一个可通过jar引入设计器的工作流**。 + +1. 支持常见审批功能、监听器与流程变量、条件表达式、办理人变量表达式 + +2. 自带流程图、流程设计器 + +3. 生态丰富可扩展 + +4. 文档全面 + + + + +![](/assets/img/news/Warm-Flow-1.6.6-0.png) + +## 演示地址 + +* admin/admin123 + + +演示地址:http://www.hhzai.top + +## 官网 + +https://warm-flow.dromara.org/ + +## 演示图 + + + + + + + + + + + + + + + + + + + + + + +
+ +
+

+ +

+

+ +
+ +
+ +
+ + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/Warm-Flow-1.6.6-6.webp) \ No newline at end of file diff --git a/src/zh/news/Warm-Flow-1.6.7.md b/src/zh/news/Warm-Flow-1.6.7.md new file mode 100644 index 0000000000..adc2ee2cff --- /dev/null +++ b/src/zh/news/Warm-Flow-1.6.7.md @@ -0,0 +1,103 @@ +--- +title: 三头六臂显神通:Warm-Flow引擎实现多维度灵活配置 +author: Warm-Flow +date: 2025-03-03 +cover: /assets/img/news/Warm-Flow-v1.6.7-0.png +head: + - - meta + - name: 新闻 +--- + +#          Warm-Flow 引擎实现多维度灵活配置 + +![](/assets/img/news/Warm-Flow-v1.6.7-0.png) + +# + +## 主要更新内容 + +* 设计器支持节点扩展属性设置 + +* 流程图扩展,新增接口,方便追加文字 + +* 流程状态支持颜色支持自定义 + + +## 详细更新日志 + +* 升级指南 + +* \[feat\] 设计器支持节点扩展属性设置 + +* \[feat\] 流程图扩展,新增接口,方便追加文字 + +* \[feat\] 流程状态支持颜色支持自定义 + +* \[update\] 节点表版本号字段标识下个版本删除 + +* \[update\] Jackson反序列化时忽略未知字段 + +* \[update\] 删除部分代码,调整注释 + +* \[update\] 修改当票签和会签节点时,注意事项描述 + +* \[fix\] 规范solon,api注解 防止某些情况获取不到方法参数名 + +* \[fix\] 删除流程实例的时候,办理用户不存在,导致删除失败 + +* \[fix\] #IBP397:修复当设计流程,开始节点出现再负坐标时,文字名称未显示 + +* \[fix\] #IBP3LK:修复开启流程,流程图第一个节点不是待办颜色 + +* \[fix\] 网关节点编辑文字报错处理 + +* \[remove\] 移除流程定义xml导入导出方式 + +* \[remove\] 移除多余的skip\_Any\_Node字段 + +* \[style\] 常量改成大写和下划线 + + +## 项目介绍 + +**Dromara Warm-Flow国产工作流引擎,其特点简洁轻量,五脏俱全,灵活扩展性强,是一个可通过jar引入设计器的工作流**。 + +1. 支持常见审批功能、监听器与流程变量、条件表达式、办理人变量表达式 + +2. 自带流程图、流程设计器、节点扩展属性 + +3. 生态丰富可扩展 + +4. 文档全面 + + +![](/assets/img/news/Warm-Flow-v1.6.7-1.png) + + + +## 演示地址 + +* admin/admin123 + + +演示地址:http://www.hhzai.top + +## 官网 + +https://warm-flow.dromara.org + +## 本次更新效果图 + +![](/assets/img/news/Warm-Flow-v1.6.7-2.png)![](/assets/img/news/Warm-Flow-v1.6.7-3.png)![](/assets/img/news/Warm-Flow-v1.6.7-4.png)![](/assets/img/news/Warm-Flow-v1.6.7-5.png) + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/Warm-Flow-v1.6.7-6.webp) \ No newline at end of file diff --git a/src/zh/news/Warm-Flow-1.6.8.md b/src/zh/news/Warm-Flow-1.6.8.md new file mode 100644 index 0000000000..eb97141cb3 --- /dev/null +++ b/src/zh/news/Warm-Flow-1.6.8.md @@ -0,0 +1,63 @@ +--- +title: Warm-Flow版本升级1.6.8:修复部分错误 +author: Warm-Flow +date: 2025-03-19 +cover: /assets/img/news/Warm-Flow-1.6.8-0.png +head: + - - meta + - name: 新闻 +--- + +# + +![](/assets/img/news/Warm-Flow-1.6.8-0.png) + +## **主要更新内容**  + +* 升级指南 + +* \[fix\] 流程复制后,丢失原有的,驳回到指定节点配置信息 + +* \[fix\] 流程图退回状态设置,缺少判空 + +* \[fix\] 给flow\_user表associated字段增加普通索引,提升查询和更新性能 + +* \[fix\] 修复目标节点有多个节点指向它,当有并行网关时,导致不生成待办任务问题 + +* \[fix\] 修复公共API排序升序无效 + + +## **项目介绍**  + +**Dromara Warm-Flow国产工作流引擎,其特点简洁轻量,五脏俱全,灵活扩展性强,是一个可通过jar引入设计器的工作流**。 + +1. 支持常见审批功能、监听器与流程变量、条件表达式、办理人变量表达式 + +2. 自带流程图、流程设计器、节点扩展属性 + +3. 生态丰富可扩展 + +4. 文档全面 + + +## **功能思维导图**  + +![](/assets/img/news/Warm-Flow-1.6.8-1.png) + +## **流程图**  + +![](/assets/img/news/Warm-Flow-1.6.8-2.png) + +## **演示地址**  + +http://www.hhzai.top + +账号密码:admin/admin123 + +## **官网**  + +https://warm-flow.dromara.org + +## **Warm-Flow视频**  + +Warm-Flow初体验 https://www.bilibili.com/video/BV1AWRGYEEVr \ No newline at end of file diff --git a/src/zh/news/Warm-Flow-1.6.9.md b/src/zh/news/Warm-Flow-1.6.9.md new file mode 100644 index 0000000000..8c60e8c193 --- /dev/null +++ b/src/zh/news/Warm-Flow-1.6.9.md @@ -0,0 +1,67 @@ +--- +title: Warm-Flow版本升级1.6.9:修复部分错误 +author: Warm-Flow +date: 2025-04-10 +cover: /assets/img/news/Warm-Flow-1.6.9-0.png +head: + - - meta + - name: 新闻 +--- + +# Warm-Flow版本升级1.6.9:修复部分错误 + +![](/assets/img/news/Warm-Flow-1.6.9-0.png) + +## **主要更新内容**  + +* \[update\] 处理扩展节点多选反显 + +* \[update\] 删除多余的注释 + +* \[fix\] Page未实现Serializable,导致Dubbo3.2+ 版本中默认的 STRICT严格检查报错  @yuegc + +* \[fix\] 修复双击中间节点后,再点击跳转线不打开右边抽屉的问题  @Vittery + +* \[fix\] 修复当图形出现在负坐标的时候,开始、结束、互斥网关节点文字没有显示在正确位置的问题 + +* \[remove\] 删除办理人表达式抽象类 + + +## **项目介绍**  + +**Dromara Warm-Flow国产工作流引擎,其特点简洁轻量,五脏俱全,灵活扩展性强,是一个可通过jar引入设计器的工作流**。 + +1. 支持常见审批功能、监听器与流程变量、条件表达式、办理人表达式 + +2. 自带流程图、流程设计器、节点扩展属性 + +3. 支持常见的orm框架 + +4. 支持不同的数据库 + +5. 生态丰富可扩展,文档全面 + + +## **功能思维导图**  + +![](/assets/img/news/Warm-Flow-1.6.9-1.png) + + + +## **流程图**  + +![](/assets/img/news/Warm-Flow-1.6.9-2.png) + +## **演示地址**  + +http://www.hhzai.top + +账号密码:admin/admin123 + +## **官网**  + +https://warm-flow.dromara.org + +## **Warm-Flow视频**  + +Warm-Flow初体验 https://www.bilibili.com/video/BV1AWRGYEEVr/ \ No newline at end of file diff --git a/src/zh/news/Warm-Flow-1.7.0.md b/src/zh/news/Warm-Flow-1.7.0.md new file mode 100644 index 0000000000..5b38575fb0 --- /dev/null +++ b/src/zh/news/Warm-Flow-1.7.0.md @@ -0,0 +1,91 @@ +--- +title: 国产免费工作流引擎star 5.9k,Warm-Flow版本升级1.7.0(新增大量好用功能) +author: 2025年04月27日 11:15 +date: 2025-04-27 +cover: /assets/img/news/Warm-Flow-1.7.0-0.png +head: + - - meta + - name: 新闻 +--- + +# 🔥 国产免费工作流引擎star 5.9k,Warm-Flow版本升级1.7.0(新增大量好用功能) + +📢 用户期待功能上线: + +新增撤销、驳回到上一任务和任务拿回功能 ^\_^ + +> 🎯 自2024年2月加入Dromara开源社区,Warm-Flow已吸引: +> +> * 👥 10+ 核心贡献者 +> +> * 🌟 5900+ Gitee Stars +> +> +> **🚫 郑重承诺:永久免费!不设商业版**! + +## ✨ 主要更新 + +\[新增功能\] + +* ✅ 待办任务表新增流程状态字段 + +* ✅ 设计器节点办理人支持中文反显 + +* ✅ 新增16个流程控制API(撤销/驳回/拿回等) + + +\[优化修复\] + +* 🚀 性能提升:优化节点查询重复代码 + +* 🔧 修复并行网关多任务执行问题 + +* 🧹 清理过期接口(标注删除/替代方案) + + +## 📦 项目亮点 + +🔨 国产轻量级工作流引擎,特点: + +1. 📌 支持流程变量/条件表达式 + +2. 🎨 内置可视化设计器 + +3. 🔄 多数据库/ORM框架适配 + +4. 📚 完整生态扩展能力 + + +## 🖼️ 效果展示 +
+ Warm-Flow-1.7.0-0 +
+ +
+ Warm-Flow-1.7.0-1 +
+ +![](/assets/img/news/Warm-Flow-1.7.0-2.png) + +![](/assets/img/news/Warm-Flow-1.7.0-3.png) + +## + + + +🚀 立即体验:http://www.hhzai.top +🌐 访问官网:https://warm-flow.dromara.org + + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/Warm-Flow-1.7.0-4.webp) \ No newline at end of file diff --git a/src/zh/news/Warm-Flow-1.7.3.md b/src/zh/news/Warm-Flow-1.7.3.md new file mode 100644 index 0000000000..134fb50af2 --- /dev/null +++ b/src/zh/news/Warm-Flow-1.7.3.md @@ -0,0 +1,69 @@ +--- +title: Warm-Flow发布1.7.3 端午节(设计器流和流程图大升级) +author: Warm-Flow +date: 2025-05-30 +cover: /assets/img/news/Warm-Flow-1.7.3-0.png +head: + - - meta + - name: 新闻 +--- + +# + +## 更新内容 + +* \[feat\] 新版流程图通过前端渲染 + +* \[perf\] 美化流程设计器ui + +* \[feat\] 办理人权限处理器,新增办理人转换接口,比如角色转用户 + +* \[update\] 优化报错提醒 + +* \[update\] 添加Version适配当前设计器的行为 + +* \[update\] 组件内置访问请求/warm-flow-ui/token-name改成/warm-flow-ui/config + + +**新版设计器** + +![](/assets/img/news/Warm-Flow-1.7.3-0.png) + +**新版流程图** + +![](/assets/img/news/Warm-Flow-1.7.3-1.png) + +## 项目介绍 + +**Dromara Warm-Flow国产工作流引擎,其特点简洁轻量,五脏俱全,灵活扩展性强,是一个可通过jar引入设计器的工作流**。 + +1. 支持常见审批功能、监听器与流程变量、条件表达式、办理人表达式 + +2. 自带流程图、流程设计器、节点扩展属性 + +3. 支持常见的orm框架 + +4. 支持不同的数据库 + +5. 生态丰富可扩展,文档全面 + + +## 功能思维导图 + +
+ Warm-Flow-1.7.3-2 +
+ +## 演示地址 + +http://www.hhzai.top + +账号密码:admin/admin123 + +## 官网 + +https://warm-flow.dromara.org + +## Warm-Flow视频 + +Warm-Flow初体验 https://www.bilibili.com/video/BV1AWRGYEEVr \ No newline at end of file diff --git a/src/zh/news/Warm-Flow-1.8.0.md b/src/zh/news/Warm-Flow-1.8.0.md new file mode 100644 index 0000000000..695830acd3 --- /dev/null +++ b/src/zh/news/Warm-Flow-1.8.0.md @@ -0,0 +1,93 @@ +--- +title: Warm-Flow 1.8.0 重大更新 +author: Warm-Flow +date: 2025-08-14 +cover: /assets/img/news/Warm-Flow-1.8.0-0.png +head: + - - meta + - name: 新闻 +--- + +# + +## 🚀 Warm-Flow迎来重大突破,自研仿钉钉设计器震撼发布! + +亲爱的开发者朋友们,我们很高兴地宣布Warm-Flow工作流引擎迎来了1.8.0版本的重大更新!这次更新不仅带来了全新的功能特性,更在用户体验上实现了质的飞跃。 + +## 🔥 核心亮点 + +### 自主研发仿钉钉设计器 + +Warm-Flow现在同时支持**经典模式**和**仿钉钉模式**双设计器!我们基于logic-flow自主研发了仿钉钉设计器,避免了维护两套代码的复杂性,实现了更好的统一性和可维护性。 + +### 智能交互体验升级 + +* 跳转线自动识别:当您绘制回退线条时,系统会自动识别并设置为退回跳转类型 + +* 节点自由拖动:经典模式下节点和连线文字支持自由拖动调整 + +* 智能编辑控制:设计器现在根据流程发布状态自动判断是否可编辑 + + +### 功能增强与优化 + +* 新增\[getFirstBetweenNode\](file://D:\\IdeaProjects\\min\\RuoYi-Vue-Warm-Flow\\warm-flow-test\\warm-flow-mybatis-sb-test\\src\\test\\java\\org\\dromara\\warm\\flow\\FlowTest.java#L217-L220)接口,快速获取第一个中间节点 + +* 流程图渲染支持顶部名称显示/隐藏控制 + +* 中间节点新增用户图标,界面更加友好 + +* 优化代码格式和注释,提升开发体验 + + +## 📊 可视化展示 + +新版流程设计器界面更加美观直观: + +![](/assets/img/news/Warm-Flow-1.8.0-0.png) + +* * * + + + + + +![](/assets/img/news/Warm-Flow-1.8.0-1.png) + + + +## 🌟 为什么选择Warm-Flow? + +作为国产工作流引擎,Warm-Flow具有以下优势: + +1. **简洁轻量** - 五脏俱全,灵活扩展性强 + +2. **双模式支持** - 原生支持经典和仿钉钉双模式 + +3. **快速集成** - 可通过jar包快速集成设计器 + +4. **广泛兼容** - 支持多种ORM框架和数据库 + + +## 🎯 功能全景 + + + +
+ Warm-Flow-1.8.0-2 +
+ + +功能思维导图 + +## 🚀 快速体验 + +**演示地址**:http://www.hhzai.top**账号密码**:admin/admin123 + +**官方网站**:https://warm-flow.dromara.org + +想要深入了解?观看我们的视频教程:从零精通: 全流程开发与源码解读 + +* * * + +**立即升级到Warm-Flow 1.8.0,体验全新的工作流设计之旅**! \ No newline at end of file diff --git a/src/zh/news/dbswitch-0.md b/src/zh/news/dbswitch-0.md index 5663dc22af..6158b35737 100644 --- a/src/zh/news/dbswitch-0.md +++ b/src/zh/news/dbswitch-0.md @@ -1,112 +1,112 @@ ---- -title: 欢迎Dromara新晋开源项目-dbswitch,异构数据库迁移同步工具! -author: 三胖 -date: 2023-11-27 -cover: /assets/img/news/dbswitch-0-0.png -head: - - - meta - - name: 新闻 ---- - -# 一个适用于异构数据库迁移同步的简单工具 dbswitch - -## 作者介绍 - -- 网名:三胖(inrgihc) -- dromara 开源组织成员,项目 dromara/dbswitch 作者 -- 项目地址:https://gitee.com/dromara/dbswitch - -![](/assets/img/news/dbswitch-0-0.png) - -## dbswitch 的诞生 - -你需要**将 Oracle 等老牌数据库中的数据一键搞到 MySQL 或 PostgreSQL 中**么?你需要**将 MySQL 等关系型数据库中的数据一键搞到 Greenplum/ClickHouse 等 OLAP 数据库中进行分析**么? - -如果你在工作中遇到与我同样的需求,那么不妨体验下 dbswitch 工具。 - -dbswitch 是在**数据库间数据搬迁**和**数据入仓入湖**这两大背景环境下诞生的,虽然目标路程还很长,但是**dbswitch 作为一款开源工具**会一直再努力坚持着(也许各个数据库厂商都有自己的专业迁移工具)。 - -## dbswitch 的功能 - -简言之,dbswitch 提供源端数据库向目的端数据库的批量迁移同步功能: - -- 结构迁移: - -(1)支持字段类型、主键信息、建表语句等的转换,并生成建表 SQL 语句。 - -(2)支持基于正则表达式转换的表名与字段名映射转换。 - -- 数据同步: - -(1)基于 JDBC 的分批次读取源端数据库数据,并基于 jdbc(insert/copy 方式)将数据分批次写入目的数据库。 - -(2)支持有主键表(基于数据比对变化计算原理的)增量变更(insert/update/delete)同步功能 - -## dbswitch 支持的数据库 - -当前基于**驱动隔离**已经集成支持**多版本的数据库**产品如下: - -- 甲骨文的 Oracle -- 微软的 Microsoft SQLServer -- MySQL/MariaDB -- PostgreSQL -- Greenplum(需使用 PostgreSQL 类型) -- IBM 的 DB2 -- Sybase 数据库 -- 国产达梦数据库 DMDB -- 国产人大金仓数据库 Kingbase8 -- 国产翰高数据库 HighGo -- 国产神通数据库 Oscar -- 国产南大通用数据库 GBase8a -- Apache Hive(基于 JdbcStorageHandler) -- SQLite3 -- OpenGuass -- ClickHouse -- MongoDB - -## dbswitch 的部署体验 - -### 1.一键部署 - -- 基于 docker-compose 的一键安装(或升级): - -``` -curl -sSL https://gitee.com/inrgihc/dbswitch/attach_files/1551962/download | sh -``` - -- 基于 docker 的一键安装: - -假设已经部署好的 MySQL 数据库地址为 192.168.31.57,端口为 3306,账号为 test,密码为 123456 - -``` -docker run -d --name dbswitch \ - -e MYSQLDB_HOST=192.168.31.57 \ - -e MYSQLDB_PORT=3306 \ - -e MYSQLDB_USERNAME=test \ - -e MYSQLDB_PASSWORD='123456' \ - -v /tmp:/tmp \ - -p 9088:9088 \ - registry.cn-hangzhou.aliyuncs.com/inrgihc/dbswitch:1.8.2 -``` - -## 2.部分截图 - -![](/assets/img/news/dbswitch-0-1.png)![](/assets/img/news/dbswitch-0-2.png) - -- 二次集成开发 - -dbswitch 也支持 java 下二次集成开发,具体可查看 dbswitch 项目中的文档说明。 - -## 关注 dbswitch - -欢迎体验使用 dbswitch 工具,同时项目中也提供了 dbswitch 相关的实现原理。对项目有什么想法、建议或 BUG,可以加微信进群深度交流(加好友请注明:"程序交流"),也可创建 issues 进行反馈: - -![](/assets/img/news/dbswitch-0-3.png) - -## 友情项目 - -\[1\] Greenplum 一键安装 - -\[2\] 新闻文章正文抽取正文抽取组件 - -- **项目地址:https://gitee.com/dromara/dbswitch** +--- +title: 欢迎Dromara新晋开源项目-dbswitch,异构数据库迁移同步工具! +author: 三胖 +date: 2023-11-27 +cover: /assets/img/news/dbswitch-0-0.png +head: + - - meta + - name: 新闻 +--- + +# 一个适用于异构数据库迁移同步的简单工具 dbswitch + +## 作者介绍 + +- 网名:三胖(inrgihc) +- dromara 开源组织成员,项目 dromara/dbswitch 作者 +- 项目地址:https://gitee.com/dromara/dbswitch + +![](/assets/img/news/dbswitch-0-0.png) + +## dbswitch 的诞生 + +你需要**将 Oracle 等老牌数据库中的数据一键搞到 MySQL 或 PostgreSQL 中**么?你需要**将 MySQL 等关系型数据库中的数据一键搞到 Greenplum/ClickHouse 等 OLAP 数据库中进行分析**么? + +如果你在工作中遇到与我同样的需求,那么不妨体验下 dbswitch 工具。 + +dbswitch 是在**数据库间数据搬迁**和**数据入仓入湖**这两大背景环境下诞生的,虽然目标路程还很长,但是**dbswitch 作为一款开源工具**会一直再努力坚持着(也许各个数据库厂商都有自己的专业迁移工具)。 + +## dbswitch 的功能 + +简言之,dbswitch 提供源端数据库向目的端数据库的批量迁移同步功能: + +- 结构迁移: + +(1)支持字段类型、主键信息、建表语句等的转换,并生成建表 SQL 语句。 + +(2)支持基于正则表达式转换的表名与字段名映射转换。 + +- 数据同步: + +(1)基于 JDBC 的分批次读取源端数据库数据,并基于 jdbc(insert/copy 方式)将数据分批次写入目的数据库。 + +(2)支持有主键表(基于数据比对变化计算原理的)增量变更(insert/update/delete)同步功能 + +## dbswitch 支持的数据库 + +当前基于**驱动隔离**已经集成支持**多版本的数据库**产品如下: + +- 甲骨文的 Oracle +- 微软的 Microsoft SQLServer +- MySQL/MariaDB +- PostgreSQL +- Greenplum(需使用 PostgreSQL 类型) +- IBM 的 DB2 +- Sybase 数据库 +- 国产达梦数据库 DMDB +- 国产人大金仓数据库 Kingbase8 +- 国产翰高数据库 HighGo +- 国产神通数据库 Oscar +- 国产南大通用数据库 GBase8a +- Apache Hive(基于 JdbcStorageHandler) +- SQLite3 +- OpenGuass +- ClickHouse +- MongoDB + +## dbswitch 的部署体验 + +### 1.一键部署 + +- 基于 docker-compose 的一键安装(或升级): + +``` +curl -sSL https://gitee.com/inrgihc/dbswitch/attach_files/1551962/download | sh +``` + +- 基于 docker 的一键安装: + +假设已经部署好的 MySQL 数据库地址为 192.168.31.57,端口为 3306,账号为 test,密码为 123456 + +``` +docker run -d --name dbswitch \ + -e MYSQLDB_HOST=192.168.31.57 \ + -e MYSQLDB_PORT=3306 \ + -e MYSQLDB_USERNAME=test \ + -e MYSQLDB_PASSWORD='123456' \ + -v /tmp:/tmp \ + -p 9088:9088 \ + registry.cn-hangzhou.aliyuncs.com/inrgihc/dbswitch:1.8.2 +``` + +## 2.部分截图 + +![](/assets/img/news/dbswitch-0-1.png)![](/assets/img/news/dbswitch-0-2.png) + +- 二次集成开发 + +dbswitch 也支持 java 下二次集成开发,具体可查看 dbswitch 项目中的文档说明。 + +## 关注 dbswitch + +欢迎体验使用 dbswitch 工具,同时项目中也提供了 dbswitch 相关的实现原理。对项目有什么想法、建议或 BUG,可以加微信进群深度交流(加好友请注明:"程序交流"),也可创建 issues 进行反馈: + +![](/assets/img/news/dbswitch-0-3.png) + +## 友情项目 + +\[1\] Greenplum 一键安装 + +\[2\] 新闻文章正文抽取正文抽取组件 + +- **项目地址:https://gitee.com/dromara/dbswitch** diff --git a/src/zh/news/easyAI-v1.2.6.md b/src/zh/news/easyAI-v1.2.6.md new file mode 100644 index 0000000000..3983b63d92 --- /dev/null +++ b/src/zh/news/easyAI-v1.2.6.md @@ -0,0 +1,249 @@ +--- +title: 原生Java人工智能算法框架 easyAI v1.2.6版本更新 +author: EasyAi +tag: + - EasyAI +date: 2024-10-08 +cover: /assets/img/news/easyAI-v1.2.6-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/easyAI-v1.2.6-0.png) + +## 前言 + +EasyAi的出现对于Java的意义,等同于在JavaWeb领域spring出现的意义一样——做一个开箱即用,让每一个开发者都可以使用EasyAi,来开发符合自己人工智能业务需求的小微模型,这就是它的使命! + +## EasyAi介绍 + +EasyAi无任何依赖,它是一个原生Java人工智能算法框架。首先,**它可以Maven一键丝滑引入我们的Java项目,无需任何额外的环境配置与依赖,做到开箱即用。**再者,它既有一些我们已经封装好的图像目标检测及人工智能客服的模块,也提供各种深度学习,机器学习,强化学习,启发式学习,矩阵运算,等底层算法工具。开发者可以通过简单的学习,就能完成根据自身业务,深度开发符合自己业务的小微模型 + +* EasyAI码云下载链接:https://gitee.com/dromara/easyAi + +* EasyAI技术文档地址:https://www.myeasyai.cn/ + +* EasyAI详细视频教程:https://www.bilibili.com/video/av89134035 + +* EasyAI框架0基础深度开发及人工智能完整体系教程:https://www.bilibili.com/cheese/play/ss17600 + + +# v1.2.6 更新内容 + +## 词向量嵌入器与TransFormer已经能支持自定义词粒度训练 + +> **小技巧:**开发者在进行词向量训练样本时,需要考虑“词粒度”,如果训练样本相对较少,则设置更粗的词粒度,会产生更好的效果。例如训练:`select * from student` 这么一句sql语句,如果不考虑词粒度完全用默认最低词粒度,即单个字符作为词粒度,那么我们生成的时候,是以 **`s`,`e`,`l`,`e`,`c`,`t`......** 这种基本单位形式进行训练与生成的。 +> +> > 但如果我们考虑词粒度,可以以 **`select`,`*`,`from`,`student`** 更粗的粒度作为训练的基本单位,如果若干固定字符总以一个整体进行展示,那么设置合适的词粒度进行训练,我们的效率和稳定性必然要高很多。 + +### 语句模板实体类增加新 构造参数和方法来配合自定义词粒度 + +* `SentenceModel`为词向量嵌入提供的,装载所有语句的模板类 + + +> `public SentenceModel(String splitWord)`可以带间隔符构造参数的构造方法,若开发者要自定义词粒度粗细,使用设置间隔符,使用间隔符间隔语句之间的词,自定义控制词粒度粗细。 +> +> > 例如:`select * from student` 该语句中的词语之间使用隔符"空格",那么可以使用带间隔符的构造方法`SentenceModel s = new SentenceModel(" ")`,这样训练时词向量嵌入器会将`select`,`*`,`from`,`student`作为一个整体进行训练,间隔符可自定义。如果开发者使用无构造参数的构造方法,则词向量嵌入器会默认将每个单个字符作为一个整体进行训练。**注意:小训练样本往往设置更粗的粒度有助于稳定性,目前自定义粒度粗细,只对依赖TransFormer的方法有效** +> > +> > > 参数`String splitWord`为自定义词粒度间隔符 +> +> `public SentenceModel()`无参构造,无参构造会默认以最小词粒度,即每个字符作为一个整体进行词向量训练。 +> +> `public void setSentence(String sentence)`向语句模板实体类里面输入一句话(训练样本),该样本语句为**无间隔符样本语句**。该方法无论是使用无参构造,还是带间隔符参数构造本类都可调用。 +> +> `public void setSentenceBySplitWord(String sentence)`向语句模板实体类里面输入一句话(训练样本),该样本语句为**携带间隔符的样本语句**。若使用该方法,必须以`public SentenceModel(String splitWord)`带间隔符参数,来构造本类,才可调用。 + +## TF参数配置类 增加了两个新参数来增强业务稳定性 + +> `private String splitWord`设置词粒度间隔符,**注意:该间隔符的设置必须与语句模板实体类中的构造参数设置的间隔符一致!**。若无需间隔符,该值可设置为`null`,但同时语句模板实体类也必须使用对应的无参构造。 +> +> `private boolean selfTimeCode`是否使用自增时间序列位置编码,若为`true`则tf位置编码使用按时间序列,均匀自增。若为`false`则tf位置编码使用对称三角函数位置编码 +> +> > 使用对称三角函数位置编码与自增编码在不同的业务环境下,表现不同。通常来说开发生成式语句,使用对称三角函数位置编码效果表现会更好,而做语句语义分类使用自增位置编码效果会更好,但这只是个参考并不绝对,通常建议该参数作为调参的尝试,都试一试,则优取之。 + +## 词向量嵌入器配合自定义词粒度更新,获取词向量矩阵的api + +> `public MyWordFeature getEmbedding(String word, long eventId, boolean once)`获取一段话(或词)的词向量矩阵实体类。 +> +> > 参数`String word`要获取词向量的一段话或者词 +> +> > 参数`boolean once`是否进行一次性获取,若该参数为`false`则本次调用是对一段话参数`String word`按每个字符单位获取词向量,根据该段话的字符顺序从0开始向下行排列构成一句话的一个词向量矩阵,每一个字的词向量占据词向量矩阵实体类中的词向量矩阵的一行。若该参数为`true`,则是对词参数`String word`看作一个整体,直接获取该词对应的向量,当它为`true`时,通常是跟有间隔符设置词粒度时,同时相关使用。 +> +> > 参数`long eventId`为执行的生命周期内,线程安全唯一ID + +### 以下是该方法演示的一个简单的小DEMO(中文自动生成 sql语句),注意这只是演示用法的demo,只有三条样本数据,只能作为演示api用法用途: + +``` +public class Test { +    public static TfConfig config = new TfConfig();//最外层配置文件 +    public static WordEmbedding wordEmbedding = new WordEmbedding();//词向量嵌入器 +    public static int wordVectorDimension = 32;//设置词向量维度 +    public static TalkToTalk talk; + +    public static void main(String[] args) throws Exception { +        init();//初始化操作 +        //creat();//生成 +        study();//训练 +    } + +    public static void creat() throws Exception { +        wordEmbedding.insertModel(readWordModel(), wordVectorDimension);//将模型文件反序列化为模型实体类,加载词向量模型 +        talk = new TalkToTalk(wordEmbedding, config);//创建长语句对话类 +        talk.init();//初始化长语句对话类 +        talk.insertModel(readTalkModel());//长语句对话类加载tf模型 +        //注意以上部分为初始化且加载模型部分,在服务启动时执行且只执行一次。不需要每次识别都加载模型! +        //TalkToTalk在实际使用中必须单例!实际调用getAnswer进行识别的TalkToTalk不能是new出来的! +        //而是直接获取服务启动时,初始化加载好的单例类。如果每次识别都要加载一次模型的话,整个过程会非常耗时! +        String a = talk.getAnswer("查询#的学生信息", 1);//输入问题,返回答案 +        System.out.println(a);//最终输出:select * from student where id = # +    } + +    public static void init() { +        wordEmbedding.setConfig(new SentenceConfig());//词向量设置配置类 +        wordEmbedding.setStudyTimes(100);//词向量训练循环100次 +        config.setMaxLength(40);//最大长度设置为40 +        config.setMultiNumber(2);//每层编解码器设置2个多头 +        config.setFeatureDimension(wordVectorDimension);//设置词向量维度 +        config.setAllDepth(1);//设置tf编解码器深度 +        config.setSplitWord(" ");//设置空格为词隔断符 +        config.setSelfTimeCode(false);//设置为对称三角函数位置编码 +        config.setTimes(500);//循环训练五百次 +        config.setStudyPoint(0.01);//设置tf学习率 +        config.setShowLog(true);//对学习中的数据打印 +        config.setRegularModel(RZ.NOT_RZ);//无正则模式 +        config.setRegular(0.0);//无正则系数 +    } + +    public static void study() throws Exception { +        SentenceModel sentenceModel = new SentenceModel(config.getSplitWord());//设置隔断符训练样本类 +        List talkBodies = data();//读取测试数据 +        for (TalkBody value : talkBodies) {//塞入测试数据 +            sentenceModel.setSentence(value.getQuestion());//将问题塞入无隔断符样本类 +            sentenceModel.setSentenceBySplitWord(value.getAnswer());//将回答塞入有隔断符号样本类 +        } +        wordEmbedding.init(sentenceModel, wordVectorDimension);//初始化词向量嵌入 +        WordTwoVectorModel wordTwoVectorModel = wordEmbedding.start();//词向量训练完毕,并返回词向量模型 +        talk = new TalkToTalk(wordEmbedding, config);//创建长语句对话类 +        talk.init();//初始化长语句对话类 +        TransFormerModel transFormerModel = talk.study(talkBodies);//训练完毕并返回tf模型。 +        Tools.writeModel(JSONObject.toJSONString(wordTwoVectorModel), "/Users/lidapeng/job/testDocument/model/testWord.json");//写出模型 +        Tools.writeModel(JSONObject.toJSONString(transFormerModel), "/Users/lidapeng/job/testDocument/model/testTalk.json");//写出模型 +    } + +    private static WordTwoVectorModel readWordModel() {//读模型 +        File file = new File("/Users/lidapeng/job/testDocument/model/testWord.json"); +        String a = Tools.readPaper(file); +        return JSONObject.parseObject(a, WordTwoVectorModel.class); +    } + +    private static TransFormerModel readTalkModel() {//读模型 +        File file = new File("/Users/lidapeng/job/testDocument/model/testTalk.json"); +        String a = Tools.readPaper(file); +        return JSONObject.parseObject(a, TransFormerModel.class); +    } + +    public static List data() {//配置测试用三条训练数据 +        List talkBodies = new ArrayList<>(); +        TalkBody talkBody1 = new TalkBody(); +        TalkBody talkBody2 = new TalkBody(); +        TalkBody talkBody3 = new TalkBody(); +        talkBody1.setQuestion("查询#的学生信息");//查询名字叫做#的学生信息 +        talkBody1.setAnswer("select * from student where id = #"); +        talkBody2.setQuestion("添加id为#,年龄17的学生信息"); +        talkBody2.setAnswer("insert into student ( id , age ) values ( # , 17 )"); +        talkBody3.setQuestion("将#的年龄改为18"); +        talkBody3.setAnswer("update student set age = 18 where id = #"); +        talkBodies.add(talkBody1); +        talkBodies.add(talkBody2); +        talkBodies.add(talkBody3); +        return talkBodies; +    } +} +``` + +## 人脸检测效果演示 + +![](/assets/img/news/easyAI-v1.2.6-1.png) + +### 图像识别FastYolo效果展示 + +* 使用EasyAi实现图像结算自动贩卖机视觉内核 + + +![](/assets/img/news/easyAI-v1.2.6-2.jpg) + +### sayOrder人工智能客服 + +* sayOrder是依赖EasyAi进行封装的人工智能客服系统。 + +* 它可以分析用户输入的语义,来识别用户的行为,并通过typeID来区分用户意图ID。并通过捕捉其后台设置的关键词类别,来抓出系统关心的用户在语句中包含的内容,比如语句中的时间,地点等。 + +* 它还可以与用户自主进行问答交互,进行自主解答疑问或者进行其余意图的交流等。 + +* 项目链接地址: https://gitee.com/dromara/sayOrder + + +### sayOrder交互基本业务流程演示 + +* 用户第一次进行输入表达自己的想法![](/assets/img/news/easyAI-v1.2.6-3.png) + +* SayOrder发现用户的描述缺少订单必要信息,则进行反问。用户接收到SayOrder的反问,进一步补充的自己的想法![](/assets/img/news/easyAI-v1.2.6-4.png) + +* 用户第二次输入信息依然不满足后台14分类法律咨询的订单关键信息要求,继续补充信息,最终完成订单信息补充生成订单。![](/assets/img/news/easyAI-v1.2.6-5.png) + +* 用户输入想要咨询的问题,SayOrder对用户咨询的问题进行自主解答![](/assets/img/news/easyAI-v1.2.6-6.png) + + +### 架构设计 + +**常用底层算法模块** + +* 基础矩阵及线代计算模块: + + 1.内置矩阵类,矩阵计算类,可以完成常用矩阵四则运算,奇偶性,多元线性回归,逻辑斯蒂回归,欧式距离,余弦相似性,im2col,逆im2col,求代数余子式,求逆,求伴随矩阵,内积等,微分等一系列api。 + + 2.RGB三通道矩阵,可进行图像转化,剪切,分块,生成图像矩阵等操作方便后续计算。 + +* 机器学习-聚类: + + k聚类,混合高斯聚类,密度聚类,学习向量量化聚类等 + +* 机器学习-分类及拟合:多层前馈神经网络,多层循环神经网络,残差网络,多层残差循环神经网络,卷积神经网络,决策树,随机森林,k最近邻等 + +* 启发式算法:粒子群,蚁群,模拟退火 + +* 强化学习 动态规划,蒙特卡洛分析,马尔可夫,时序差分 + + +**常用上层算法模块** + +* 视觉图像:图像识别,图片摘要,目标检测 + +* 自然语言:语义理解,拆词分词,推理敏感及关键词,语句补全,语言交流 + +* 游戏机器人:自主策略,自主行动 + + +### 使用 + +1.将项目下载后打包进本地maven库 + +2.将easyAi pom文件引入地址引入项目 + +### 关注项目 + +* 对项目有什么想法或者建议与学习,都可以加我工作微信![](/assets/img/news/easyAI-v1.2.6-7.jpg) + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/easyAI-v1.2.6-8.webp) \ No newline at end of file diff --git a/src/zh/news/easyAI-v1.2.7.md b/src/zh/news/easyAI-v1.2.7.md new file mode 100644 index 0000000000..32a871b222 --- /dev/null +++ b/src/zh/news/easyAI-v1.2.7.md @@ -0,0 +1,151 @@ +--- +title: 原生Java人工智能算法框架 easyAI v1.2.7版本更新 +author: easyAI +tag: + - EasyAI +date: 2024-10-14 +cover: /assets/img/news/easyAI-v1.2.7-0.png +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/easyAI-v1.2.7-0.png) + +## 前言 + +EasyAi的出现对于Java的意义,等同于在JavaWeb领域spring出现的意义一样——做一个开箱即用,让每一个开发者都可以使用EasyAi,来开发符合自己人工智能业务需求的小微模型,这就是它的使命! + +## EasyAi介绍 + +EasyAi无任何依赖,它是一个原生Java人工智能算法框架。首先,**它可以Maven一键丝滑引入我们的Java项目,无需任何额外的环境配置与依赖,做到开箱即用。**再者,它既有一些我们已经封装好的图像目标检测及人工智能客服的模块,也提供各种深度学习,机器学习,强化学习,启发式学习,矩阵运算,等底层算法工具。开发者可以通过简单的学习,就能完成根据自身业务,深度开发符合自己业务的小微模型 + +* EasyAI码云下载链接:https://gitee.com/dromara/easyAi + +* EasyAI技术文档地址:https://www.myeasyai.cn/ + +* EasyAI详细视频教程:https://www.bilibili.com/video/av89134035 + +* EasyAI框架0基础深度开发及人工智能完整体系教程:https://www.bilibili.com/cheese/play/ss17600 + + +# + +* + + +# v1.2.7 更新内容 + +## 1.2.7当训练特征纬度足够大时,已经支持多核并行计算对训练计算进行加速了! + +### 目标检测配置类增加多核加速参数 + +> `private int coreNumber`设置多线程并行计算加速线程的数量,该数量的理论极限值为你当前设备核心数\*2,该值设置小于等于1时,则依然为单进程运算 +> +> > **注意:只有当移动检测窗口宽高`private int windowWidth`,`private int windowHeight`中至少一个超过300时,才值得开启并行运算进行加速。若小于该数值,则并行运算效率反而会低于单进程运算!** + +### TF参数配置类增加多核加速参数 + +> `private int coreNumber`设置多线程并行计算加速线程的数量,该数量的理论极限值为你当前设备核心数\*2,该值设置小于等于1时,则依然为单进程运算 +> +> > **注意:只有当特征维度`private int featureDimension`超过280时,才值得开启并行运算进行加速。若小于该数值,则并行运算效率反而会低于单进程运算!** + +### easyAI包已上传至Maven中央库 + +> 以后在pom文件引入以下JAR包地址即可,无需进行下载手动打包。 + +``` +     +    cn.myeasyai.easyai +    easyAi +    1.2.7 +     +``` + + + +## 人脸检测效果演示 + +![](/assets/img/news/easyAI-v1.2.7-1.png) + +### 图像识别FastYolo效果展示 + +* 使用EasyAi实现图像结算自动贩卖机视觉内核 + + +![](/assets/img/news/easyAI-v1.2.7-2.jpg) + +### sayOrder人工智能客服 + +* sayOrder是依赖EasyAi进行封装的人工智能客服系统。 + +* 它可以分析用户输入的语义,来识别用户的行为,并通过typeID来区分用户意图ID。并通过捕捉其后台设置的关键词类别,来抓出系统关心的用户在语句中包含的内容,比如语句中的时间,地点等。 + +* 它还可以与用户自主进行问答交互,进行自主解答疑问或者进行其余意图的交流等。 + +* 项目链接地址: https://gitee.com/dromara/sayOrder + + +### sayOrder交互基本业务流程演示 + +* 用户第一次进行输入表达自己的想法![](/assets/img/news/easyAI-v1.2.7-3.png) + +* SayOrder发现用户的描述缺少订单必要信息,则进行反问。用户接收到SayOrder的反问,进一步补充的自己的想法![](/assets/img/news/easyAI-v1.2.7-4.png) + +* 用户第二次输入信息依然不满足后台14分类法律咨询的订单关键信息要求,继续补充信息,最终完成订单信息补充生成订单。![](/assets/img/news/easyAI-v1.2.7-5.png) + +* 用户输入想要咨询的问题,SayOrder对用户咨询的问题进行自主解答![](/assets/img/news/easyAI-v1.2.7-6.png) + + +### 架构设计 + +**常用底层算法模块** + +* 基础矩阵及线代计算模块: + + 1.内置矩阵类,矩阵计算类,可以完成常用矩阵四则运算,奇偶性,多元线性回归,逻辑斯蒂回归,欧式距离,余弦相似性,im2col,逆im2col,求代数余子式,求逆,求伴随矩阵,内积等,微分等一系列api。 + + 2.RGB三通道矩阵,可进行图像转化,剪切,分块,生成图像矩阵等操作方便后续计算。 + +* 机器学习-聚类: + + k聚类,混合高斯聚类,密度聚类,学习向量量化聚类等 + +* 机器学习-分类及拟合:多层前馈神经网络,多层循环神经网络,残差网络,多层残差循环神经网络,卷积神经网络,决策树,随机森林,k最近邻等 + +* 启发式算法:粒子群,蚁群,模拟退火 + +* 强化学习 动态规划,蒙特卡洛分析,马尔可夫,时序差分 + + +**常用上层算法模块** + +* 视觉图像:图像识别,图片摘要,目标检测 + +* 自然语言:语义理解,拆词分词,推理敏感及关键词,语句补全,语言交流 + +* 游戏机器人:自主策略,自主行动 + + +### 使用 + +1.将项目下载后打包进本地maven库 + +2.将easyAi pom文件引入地址引入项目 + +### 关注项目 + +* 对项目有什么想法或者建议与学习,都可以加我工作微信![](/assets/img/news/easyAI-v1.2.7-7.jpg) + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/easyAI-v1.2.7-8.webp) \ No newline at end of file diff --git a/src/zh/news/easyAI-v1.2.9.md b/src/zh/news/easyAI-v1.2.9.md new file mode 100644 index 0000000000..080b4192d5 --- /dev/null +++ b/src/zh/news/easyAI-v1.2.9.md @@ -0,0 +1,168 @@ +--- +title: 原生Java人工智能算法框架 easyAI-v1.2.9版本更新 +author: EasyAi +date: 2024-11-25 +cover: /assets/img/news/easyAI-v1.2.9-0.webp +head: + - - meta + - name: 新闻 +--- + +![](/assets/img/news/easyAI-v1.2.9-0.webp) + +## 前言 + +EasyAi的出现对于Java的意义,等同于在JavaWeb领域spring出现的意义一样——做一个开箱即用,让每一个开发者都可以使用EasyAi,来开发符合自己人工智能业务需求的小微模型,这就是它的使命! + +## EasyAi介绍 + +EasyAi无任何依赖,它是一个原生Java人工智能算法框架。首先,**它可以Maven一键丝滑引入我们的Java项目,无需任何额外的环境配置与依赖,做到开箱即用。**再者,它既有一些我们已经封装好的图像目标检测及人工智能客服的模块,也提供各种深度学习,机器学习,强化学习,启发式学习,矩阵运算,等底层算法工具。开发者可以通过简单的学习,就能完成根据自身业务,深度开发符合自己业务的小微模型 + +* EasyAI码云下载链接:https://gitee.com/dromara/easyAi + +* EasyAI技术文档地址:https://www.myeasyai.cn/ + +* EasyAI详细视频教程:https://www.bilibili.com/video/av89134035 + +* EasyAI框架0基础深度开发及人工智能完整体系教程:https://www.bilibili.com/cheese/play/ss17600 + + + + +* + + + + +# v1.2.9 + +## 人脸识别 + +* 基于EasyAi新创建了一个人脸识别的算法内核项目seeFace + +* 内核算法链接:https://gitee.com/ldp\_dpsmax/see-face + +* 视频教程地址:https://www.bilibili.com/video/BV1A8UeYwEsX + + +### 快速API + +> `public ErrorMessage look(ThreeChannelMatrix face, long eventID, int secondExplore)`获取该图像的特征向量 +> +> > 参数`ThreeChannelMatrix face`为解析图像的三通道矩阵 +> +> > 参数`long eventID`线程安全唯一ID +> +> > 参数`int secondExplore`迭代强度,识别时建议使用30,入库时提高到60,该数值越大对人脸抓取就越稳,但速度越慢。所以人脸入库获取向量时 提高该数值,求最稳。识别获取特征时降低到30,可以在兼顾一定稳定的同时求快。 +> +> 返回实体`ErrorMessage` +> +> > `private int errorCode = 0`为错误码,当错误码为0时才是正常获取了结果 +> +> > `private String errorMessage`错误原因 +> +> > `FaceMessage faceMessage`人脸信息结果,当错误码不为0时,该返回实体为`null` +> > +> > > 参数`private Matrix feature`为特征矩阵,内置二维数组保存特征值,可通过`getMatrix()`方法将二维数组提取出来用来特征值保存入库,以方便使用向量数据库对比。 +> +> > > 参数`private ThreeChannelMatrix channel`定位到的人脸特征的图像三通道矩阵,可以使用`ImageTools`中的`imageTools.writeImage(faceMessage.getChannel(), "/Users/lidapeng/job/faceData/test/b1.jpg");`方法将人脸输出出来,观察人脸定位结果,如果抓取位置异常,说明本次识别结果不理想,可进行重新拍摄再次获取。 + +### easyAI包已上传至Maven中央库 + +> 以后在pom文件引入以下JAR包地址即可,无需进行下载手动打包。 + +``` +     +    org.dromara.easyai +    easyAi +    1.2.9 +     +``` + + + +## 人脸检测效果演示 + +![](/assets/img/news/easyAI-v1.2.9-1.png) + +### 图像识别FastYolo效果展示 + +* 使用EasyAi实现图像结算自动贩卖机视觉内核 + + +![](/assets/img/news/easyAI-v1.2.9-2.jpg) + +### sayOrder人工智能客服 + +* sayOrder是依赖EasyAi进行封装的人工智能客服系统。 + +* 它可以分析用户输入的语义,来识别用户的行为,并通过typeID来区分用户意图ID。并通过捕捉其后台设置的关键词类别,来抓出系统关心的用户在语句中包含的内容,比如语句中的时间,地点等。 + +* 它还可以与用户自主进行问答交互,进行自主解答疑问或者进行其余意图的交流等。 + +* 项目链接地址: https://gitee.com/dromara/sayOrder + + +### sayOrder交互基本业务流程演示 + +* 用户第一次进行输入表达自己的想法![](/assets/img/news/easyAI-v1.2.9-3.png) + +* SayOrder发现用户的描述缺少订单必要信息,则进行反问。用户接收到SayOrder的反问,进一步补充的自己的想法![](/assets/img/news/easyAI-v1.2.9-4.png) + +* 用户第二次输入信息依然不满足后台14分类法律咨询的订单关键信息要求,继续补充信息,最终完成订单信息补充生成订单。![](/assets/img/news/easyAI-v1.2.9-5.png) + +* 用户输入想要咨询的问题,SayOrder对用户咨询的问题进行自主解答![](/assets/img/news/easyAI-v1.2.9-6.png) + + +### 架构设计 + +**常用底层算法模块** + +* 基础矩阵及线代计算模块: + + 1.内置矩阵类,矩阵计算类,可以完成常用矩阵四则运算,奇偶性,多元线性回归,逻辑斯蒂回归,欧式距离,余弦相似性,im2col,逆im2col,求代数余子式,求逆,求伴随矩阵,内积等,微分等一系列api。 + + 2.RGB三通道矩阵,可进行图像转化,剪切,分块,生成图像矩阵等操作方便后续计算。 + +* 机器学习-聚类: + + k聚类,混合高斯聚类,密度聚类,学习向量量化聚类等 + +* 机器学习-分类及拟合:多层前馈神经网络,多层循环神经网络,残差网络,多层残差循环神经网络,卷积神经网络,决策树,随机森林,k最近邻等 + +* 启发式算法:粒子群,蚁群,模拟退火 + +* 强化学习 动态规划,蒙特卡洛分析,马尔可夫,时序差分 + + +**常用上层算法模块** + +* 视觉图像:图像识别,图片摘要,目标检测 + +* 自然语言:语义理解,拆词分词,推理敏感及关键词,语句补全,语言交流 + +* 游戏机器人:自主策略,自主行动 + + +### 使用 + +1.将项目下载后打包进本地maven库 + +2.将easyAi pom文件引入地址引入项目 + +### 关注项目 + +* 对项目有什么想法或者建议与学习,都可以加我工作微信![](/assets/img/news/easyAI-v1.2.9-7.jpg) + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/easyAI-v1.2.9-8.webp) \ No newline at end of file diff --git a/src/zh/news/electron-egg-3.1.0.md b/src/zh/news/electron-egg-3.1.0.md index c86c435381..964a76310d 100644 --- a/src/zh/news/electron-egg-3.1.0.md +++ b/src/zh/news/electron-egg-3.1.0.md @@ -1,79 +1,79 @@ ---- -title: ElectronEgg v3.1.0 正式发布,跨平台桌面软件开发 -author: ElectronEgg -date: 2023-04-28 -cover: /assets/img/news/electron-egg-cover.png -head: - - - meta - - name: 新闻 ---- - -**值得信赖** -![](/assets/img/news/electron-egg-3.1.0-1.png) -**为什么使用** - -桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 - -electron 技术是流行趋势,QQ、百度翻译、阿里网盘、迅雷、有道云笔记 ...... - -**简单** - -只需懂 JavaScript - -**开源** - -gitee:https://gitee.com/dromara/electron-egg 3200+ - -github:https://github.com/dromara/electron-egg 800+ - -## 本次更新 - -**3.1.0** - -1. 【增加】Utils 模块 mac 功能:getMAC /isMAC -2. 【增加】Utils 模块 IP 功能:publicIpv4 /publicIpv6 -3. 【增加】Job 模块 childJob 功能:createProcess /getPids/execPromise -4. 【增加】Job 模块 childJobPool 功能:create /run/runPromise /getChildByPid / getChild / getPids / killAll -5. 【增加】exception 模块:main/child/renderer 进程捕获异常后是否退出 -6. 【增加】ps 模块:getEncryptDir /isEncrypted/exitChildJob /isChildJob/isChildPoolJob -7. 【增加】Utils 模块:co /deprecate/extend /get-port/time -8. 【增加】tools 模块:加密文件过滤及匹配 -9. 【增加】bin 模块:clean 清理加密文件 - -## 使用场景 - -### 1\. 常规桌面软件 - -- windows 平台 - demo - ![](/assets/img/news/electron-egg-3.7.0-2.png) - -- macOS 平台 - demo - ![](/assets/img/news/electron-egg-3.7.0-3.png) - -- linux 平台 - 国产 UOS、Deepin - demo - ![](/assets/img/news/electron-egg-3.7.0-4.png) - -- linux 平台 (ubuntu) - demo - ![](/assets/img/news/electron-egg-3.7.0-5.png) - -### 2\. vue、react、web 转换成桌面软件 - -- vue-ant-design(本地) - ![](/assets/img/news/electron-egg-3.7.0-6.png) - -- 禅道项目管理(web 项目地址) - ![](/assets/img/news/electron-egg-3.7.0-7.png) - -### 3\. 用户案例 - -![](/assets/img/news/electron-egg-3.7.0-8.png) - -![](/assets/img/news/electron-egg-3.7.0-9.png) - -![](/assets/img/news/electron-egg-3.7.0-10.png) - -## 更多案例 - -访问官网:electron-egg: 一个入门简单、跨平台、企业级桌面软件开发框架 - -**https://www.kaka996.com/** +--- +title: ElectronEgg v3.1.0 正式发布,跨平台桌面软件开发 +author: ElectronEgg +date: 2023-04-28 +cover: /assets/img/news/electron-egg-cover.png +head: + - - meta + - name: 新闻 +--- + +**值得信赖** +![](/assets/img/news/electron-egg-3.1.0-1.png) +**为什么使用** + +桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 + +electron 技术是流行趋势,QQ、百度翻译、阿里网盘、迅雷、有道云笔记 ...... + +**简单** + +只需懂 JavaScript + +**开源** + +gitee:https://gitee.com/dromara/electron-egg 3200+ + +github:https://github.com/dromara/electron-egg 800+ + +## 本次更新 + +**3.1.0** + +1. 【增加】Utils 模块 mac 功能:getMAC /isMAC +2. 【增加】Utils 模块 IP 功能:publicIpv4 /publicIpv6 +3. 【增加】Job 模块 childJob 功能:createProcess /getPids/execPromise +4. 【增加】Job 模块 childJobPool 功能:create /run/runPromise /getChildByPid / getChild / getPids / killAll +5. 【增加】exception 模块:main/child/renderer 进程捕获异常后是否退出 +6. 【增加】ps 模块:getEncryptDir /isEncrypted/exitChildJob /isChildJob/isChildPoolJob +7. 【增加】Utils 模块:co /deprecate/extend /get-port/time +8. 【增加】tools 模块:加密文件过滤及匹配 +9. 【增加】bin 模块:clean 清理加密文件 + +## 使用场景 + +### 1\. 常规桌面软件 + +- windows 平台 - demo + ![](/assets/img/news/electron-egg-3.7.0-2.png) + +- macOS 平台 - demo + ![](/assets/img/news/electron-egg-3.7.0-3.png) + +- linux 平台 - 国产 UOS、Deepin - demo + ![](/assets/img/news/electron-egg-3.7.0-4.png) + +- linux 平台 (ubuntu) - demo + ![](/assets/img/news/electron-egg-3.7.0-5.png) + +### 2\. vue、react、web 转换成桌面软件 + +- vue-ant-design(本地) + ![](/assets/img/news/electron-egg-3.7.0-6.png) + +- 禅道项目管理(web 项目地址) + ![](/assets/img/news/electron-egg-3.7.0-7.png) + +### 3\. 用户案例 + +![](/assets/img/news/electron-egg-3.7.0-8.png) + +![](/assets/img/news/electron-egg-3.7.0-9.png) + +![](/assets/img/news/electron-egg-3.7.0-10.png) + +## 更多案例 + +访问官网:electron-egg: 一个入门简单、跨平台、企业级桌面软件开发框架 + +**https://www.kaka996.com/** diff --git a/src/zh/news/electron-egg-3.10.0.md b/src/zh/news/electron-egg-3.10.0.md index 3ec050b1e4..3bf96df379 100644 --- a/src/zh/news/electron-egg-3.10.0.md +++ b/src/zh/news/electron-egg-3.10.0.md @@ -1,115 +1,115 @@ ---- -title: 企业级,跨平台桌面软件开发框架 electron-egg 3.10.0版本发布! -author: egg -date: 2024-02-02 -cover: /assets/img/news/electron-egg-3.10.0-0.png -head: - - - meta - - name: 新闻 ---- - -大家好,`electron-egg 3.10.0` 发布。在这个版本里我们优化了跨语言支持。 - -简单来说,就是可以用`java`、`go`等语言来写业务,然后通过 electron-egg 调用;支持 windows、macOS、Linux。其原理是通过 child_process 创建子进程,并返回一个包含各种属性和方法的对象,cross 模块也提供了一些实用的 api。 - -文章结尾,我们提供了 `electron-egg for java`和 `electron-egg for go` 的案例效果。 - -目前,框架已经广泛应用于记账、政务、企业、医疗、学校、股票交易、ERP、娱乐、视频等领域客户端,请放心使用! - -![](/assets/img/news/electron-egg-3.10.0-0.png) - -### 为什么使用 - -桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 - -electron 技术是流行趋势,QQ、百度翻译、阿里网盘、迅雷、有道云笔记 ...... - -### 开源 - -gitee:https://gitee.com/dromara/electron-egg 4300+ - -github:https://github.com/dromara/electron-egg 1400+ - -### 本次更新 - -### 3.10.0 - -1. 【优化】优化 ee-core 模块,支持 go、java 等。 -2. 【增加】新增 ee-core cross 模块 API:run()\\killAll()\\kill(pid)\\killByName(name)\\getUrl(name)\\getProcByName(name)\\getProc(pid)getPids()。 -3. 【增加】新增 ee-core cross 模块进程对象,包含 API:pid\\name\\port\\config\\child\\emitter\\kill()\\getUrl()\\getArgsObj()。 -4. 【增加】新增 go、java 功能使用 demo。 -5. 【修复】修复 electron/index 缓存问题。 -6. 【修复】修复 jobs 日志写多了会卡死的问题。 -7. 【修复】修复 第三方使用 model 错乱问题。 -8. 【修改】修改 配置 默认开启 gpu,修改默认协议为 file://。 -9. 【修改】修改 go 开发环境配置。 -10. 【升级】升级 ee-core v2.8.2 - -### 下载 - -``` -# gitee -git clone https://gitee.com/dromara/electron-egg.git - -# github -git clone https://github.com/dromara/electron-egg.git -``` - -### 安装 - -``` -# 设置国内镜像源(加速) -npm config set registry=https://registry.npmmirror.com - -#如果下载electron慢,配置如下 -npm config set electron_mirror=https://registry.npmmirror.com/-/binary/electron/ - -# 根目录,安装 electron 依赖 -npm i - -# 进入【前端目录】安装 frontend 依赖 -cd frontend -npm i -``` - -### 运行项目 - -``` -npm run start -``` - -![](/assets/img/news/electron-egg-3.10.0-1.png) - -### electron-egg for go 案例 - -#### mayfly-go - -下载地址:https://url21.ctfile.com/f/10443521-1017255719-990745?p=9598 (访问密码: 9598) - -开源地址:https://github.com/dromara/mayfly-go (web) - -![](/assets/img/news/electron-egg-3.10.0-2.png) - -![](/assets/img/news/electron-egg-3.10.0-3.png) - -![](/assets/img/news/electron-egg-3.10.0-4.png) - -![](/assets/img/news/electron-egg-3.10.0-5.png) - -### electron-egg for java 案例 - -#### northstar - -开源地址:https://gitee.com/dromara/northstar - -![](/assets/img/news/electron-egg-3.10.0-6.png) - -![](/assets/img/news/electron-egg-3.10.0-7.png) - -#### sms - -![](/assets/img/news/electron-egg-3.10.0-8.png) - -### 更多 - -访问官网:https://www.kaka996.com/ +--- +title: 企业级,跨平台桌面软件开发框架 electron-egg 3.10.0版本发布! +author: egg +date: 2024-02-02 +cover: /assets/img/news/electron-egg-3.10.0-0.png +head: + - - meta + - name: 新闻 +--- + +大家好,`electron-egg 3.10.0` 发布。在这个版本里我们优化了跨语言支持。 + +简单来说,就是可以用`java`、`go`等语言来写业务,然后通过 electron-egg 调用;支持 windows、macOS、Linux。其原理是通过 child_process 创建子进程,并返回一个包含各种属性和方法的对象,cross 模块也提供了一些实用的 api。 + +文章结尾,我们提供了 `electron-egg for java`和 `electron-egg for go` 的案例效果。 + +目前,框架已经广泛应用于记账、政务、企业、医疗、学校、股票交易、ERP、娱乐、视频等领域客户端,请放心使用! + +![](/assets/img/news/electron-egg-3.10.0-0.png) + +### 为什么使用 + +桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 + +electron 技术是流行趋势,QQ、百度翻译、阿里网盘、迅雷、有道云笔记 ...... + +### 开源 + +gitee:https://gitee.com/dromara/electron-egg 4300+ + +github:https://github.com/dromara/electron-egg 1400+ + +### 本次更新 + +### 3.10.0 + +1. 【优化】优化 ee-core 模块,支持 go、java 等。 +2. 【增加】新增 ee-core cross 模块 API:run()\\killAll()\\kill(pid)\\killByName(name)\\getUrl(name)\\getProcByName(name)\\getProc(pid)getPids()。 +3. 【增加】新增 ee-core cross 模块进程对象,包含 API:pid\\name\\port\\config\\child\\emitter\\kill()\\getUrl()\\getArgsObj()。 +4. 【增加】新增 go、java 功能使用 demo。 +5. 【修复】修复 electron/index 缓存问题。 +6. 【修复】修复 jobs 日志写多了会卡死的问题。 +7. 【修复】修复 第三方使用 model 错乱问题。 +8. 【修改】修改 配置 默认开启 gpu,修改默认协议为 file://。 +9. 【修改】修改 go 开发环境配置。 +10. 【升级】升级 ee-core v2.8.2 + +### 下载 + +``` +# gitee +git clone https://gitee.com/dromara/electron-egg.git + +# github +git clone https://github.com/dromara/electron-egg.git +``` + +### 安装 + +``` +# 设置国内镜像源(加速) +npm config set registry=https://registry.npmmirror.com + +#如果下载electron慢,配置如下 +npm config set electron_mirror=https://registry.npmmirror.com/-/binary/electron/ + +# 根目录,安装 electron 依赖 +npm i + +# 进入【前端目录】安装 frontend 依赖 +cd frontend +npm i +``` + +### 运行项目 + +``` +npm run start +``` + +![](/assets/img/news/electron-egg-3.10.0-1.png) + +### electron-egg for go 案例 + +#### mayfly-go + +下载地址:https://url21.ctfile.com/f/10443521-1017255719-990745?p=9598 (访问密码: 9598) + +开源地址:https://github.com/dromara/mayfly-go (web) + +![](/assets/img/news/electron-egg-3.10.0-2.png) + +![](/assets/img/news/electron-egg-3.10.0-3.png) + +![](/assets/img/news/electron-egg-3.10.0-4.png) + +![](/assets/img/news/electron-egg-3.10.0-5.png) + +### electron-egg for java 案例 + +#### northstar + +开源地址:https://gitee.com/dromara/northstar + +![](/assets/img/news/electron-egg-3.10.0-6.png) + +![](/assets/img/news/electron-egg-3.10.0-7.png) + +#### sms + +![](/assets/img/news/electron-egg-3.10.0-8.png) + +### 更多 + +访问官网:https://www.kaka996.com/ diff --git a/src/zh/news/electron-egg-3.7.0.md b/src/zh/news/electron-egg-3.7.0.md index a966a9564c..655317a9bb 100644 --- a/src/zh/news/electron-egg-3.7.0.md +++ b/src/zh/news/electron-egg-3.7.0.md @@ -1,92 +1,92 @@ ---- -title: ElectronEgg v3.7.0 正式发布,跨平台桌面软件开发 -author: ElectronEgg -date: 2023-09-20 -cover: /assets/img/news/electron-egg-cover.png -head: - - - meta - - name: 新闻 ---- - -**为什么使用** - -桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 - -electron 技术是流行趋势,QQ、百度翻译、阿里网盘、迅雷、有道云笔记 ...... - -**简单** - -只需懂 JavaScript - -**开源** - -gitee:https://gitee.com/dromara/electron-egg 3700+ - -github:https://github.com/dromara/electron-egg 1000+ - -## 本次更新 - -**3.7.0** - -1. 【增加】新增 config/bin.js 配置文件,统一处理 ee-bin 功能配置项。 -2. 【增加】新增 ee-bin dev 命令,同时启动 frontend electron 服务。 -3. 【增加】新增 ee-bin start 命令,使用 node spawn 启动 electron。 -4. 【增加】新增 ee-bin build 命令,构建出包含 renderer 进程数据的 process。 -5. 【增加】新增 ee-core boot、failure 页面,优化开发体验。 -6. 【增加】新增 ee-core jsondb 支持修改数据存储目录。 -7. 【增加】新增 ee-bin rd 参数,支持 dist、target 参数。 -8. 【增加】新增 demo 分支,(frontend)loading 动画、登录窗口效果、加载本机图片效果。 -9. 【增加】新增 demo 分支,(electron)jsondb 目录切换功能、java 插件状态检查功能。 -10. 【优化】优化 ee-bin 命令的 log 提示,增加颜色效果。 - -11. 【优化】优化 ee-core config.openDevTools 支持传参。 - -12. 【优化】优化 http 服务 listen 参数。 - -13. 【优化】优化 开发体验。 - -14. 【修复】修复 getPort 端口获取 bug。 - -15. 【删除】删除 app.on ('activate')、 app.on ('second-instance')。 - -16. 【升级】升级 ee-core -> v2.5.0、升级 ee-bin -> 1.2.0 - -![](/assets/img/news/electron-egg-3.7.0-1.png) - -## 使用场景 - -### 1\. 常规桌面软件 - -- windows 平台 - demo - ![](/assets/img/news/electron-egg-3.7.0-2.png) - -- macOS 平台 - demo - ![](/assets/img/news/electron-egg-3.7.0-3.png) - -- linux 平台 - 国产 UOS、Deepin - demo - ![](/assets/img/news/electron-egg-3.7.0-4.png) - -- linux 平台 (ubuntu) - demo - ![](/assets/img/news/electron-egg-3.7.0-5.png) - -### 2\. vue、react、web 转换成桌面软件 - -- vue-ant-design(本地) - ![](/assets/img/news/electron-egg-3.7.0-6.png) - -- 禅道项目管理(web 项目地址) - ![](/assets/img/news/electron-egg-3.7.0-7.png) - -### 3\. 用户案例 - -![](/assets/img/news/electron-egg-3.7.0-8.png) - -![](/assets/img/news/electron-egg-3.7.0-9.png) - -![](/assets/img/news/electron-egg-3.7.0-10.png) - -## 更多案例 - -访问官网:electron-egg: 一个入门简单、跨平台、企业级桌面软件开发框架 - -**https://www.kaka996.com/** +--- +title: ElectronEgg v3.7.0 正式发布,跨平台桌面软件开发 +author: ElectronEgg +date: 2023-09-20 +cover: /assets/img/news/electron-egg-cover.png +head: + - - meta + - name: 新闻 +--- + +**为什么使用** + +桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 + +electron 技术是流行趋势,QQ、百度翻译、阿里网盘、迅雷、有道云笔记 ...... + +**简单** + +只需懂 JavaScript + +**开源** + +gitee:https://gitee.com/dromara/electron-egg 3700+ + +github:https://github.com/dromara/electron-egg 1000+ + +## 本次更新 + +**3.7.0** + +1. 【增加】新增 config/bin.js 配置文件,统一处理 ee-bin 功能配置项。 +2. 【增加】新增 ee-bin dev 命令,同时启动 frontend electron 服务。 +3. 【增加】新增 ee-bin start 命令,使用 node spawn 启动 electron。 +4. 【增加】新增 ee-bin build 命令,构建出包含 renderer 进程数据的 process。 +5. 【增加】新增 ee-core boot、failure 页面,优化开发体验。 +6. 【增加】新增 ee-core jsondb 支持修改数据存储目录。 +7. 【增加】新增 ee-bin rd 参数,支持 dist、target 参数。 +8. 【增加】新增 demo 分支,(frontend)loading 动画、登录窗口效果、加载本机图片效果。 +9. 【增加】新增 demo 分支,(electron)jsondb 目录切换功能、java 插件状态检查功能。 +10. 【优化】优化 ee-bin 命令的 log 提示,增加颜色效果。 + +11. 【优化】优化 ee-core config.openDevTools 支持传参。 + +12. 【优化】优化 http 服务 listen 参数。 + +13. 【优化】优化 开发体验。 + +14. 【修复】修复 getPort 端口获取 bug。 + +15. 【删除】删除 app.on ('activate')、 app.on ('second-instance')。 + +16. 【升级】升级 ee-core -> v2.5.0、升级 ee-bin -> 1.2.0 + +![](/assets/img/news/electron-egg-3.7.0-1.png) + +## 使用场景 + +### 1\. 常规桌面软件 + +- windows 平台 - demo + ![](/assets/img/news/electron-egg-3.7.0-2.png) + +- macOS 平台 - demo + ![](/assets/img/news/electron-egg-3.7.0-3.png) + +- linux 平台 - 国产 UOS、Deepin - demo + ![](/assets/img/news/electron-egg-3.7.0-4.png) + +- linux 平台 (ubuntu) - demo + ![](/assets/img/news/electron-egg-3.7.0-5.png) + +### 2\. vue、react、web 转换成桌面软件 + +- vue-ant-design(本地) + ![](/assets/img/news/electron-egg-3.7.0-6.png) + +- 禅道项目管理(web 项目地址) + ![](/assets/img/news/electron-egg-3.7.0-7.png) + +### 3\. 用户案例 + +![](/assets/img/news/electron-egg-3.7.0-8.png) + +![](/assets/img/news/electron-egg-3.7.0-9.png) + +![](/assets/img/news/electron-egg-3.7.0-10.png) + +## 更多案例 + +访问官网:electron-egg: 一个入门简单、跨平台、企业级桌面软件开发框架 + +**https://www.kaka996.com/** diff --git a/src/zh/news/electron-egg-3.9.0.md b/src/zh/news/electron-egg-3.9.0.md index 13d319b875..dd79d15bf4 100644 --- a/src/zh/news/electron-egg-3.9.0.md +++ b/src/zh/news/electron-egg-3.9.0.md @@ -1,90 +1,90 @@ ---- -title: ElectronEgg v3.9.0 发布,快速开发一个桌面应用 -author: electron-egg -date: 2023-12-26 -cover: /assets/img/news/electron-egg-3.10.0-0.png -head: - - - meta - - name: 新闻 ---- - -大家好,`electron-egg 3.9.0` 重大更新。 - -在这个版本里我们做了重大更新,支持了 `go` 语言,天生的跨平台、高性能,可以把核心业务用 go 来写,能极大提升了软件体验。 - -目前,框架已经广泛应用于记账、政务、企业、医疗、学校、股票交易、ERP、娱乐、视频等领域客户端,请放心使用! - -![](/assets/img/news/electron-egg-3.10.0-0.png) - -### 为什么使用 - -桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 - -electron 技术是流行趋势,QQ、百度翻译、阿里网盘、迅雷、有道云笔记 ...... - -### 开源 - -gitee:https://gitee.com/dromara/electron-egg 4200+ - -github:https://github.com/dromara/electron-egg 1300+ - -### 本次更新 - -### 3.9.0 - -**【重大更新】** - -1. 【增加】新增 ee-go 模块,支持 go 语言,以及众多功能。 -2. 【增加】新增 ee-bin dev for go。 -3. 【增加】新增 ee-bin dev 配置 electron.loadingPage。 -4. 【增加】新增 ee-core cross 模块,支持跨语言服务。 -5. 【增加】新增 ee-core 生产环境配置 loadingPage。 -6. 【优化】优化 ee-bin dev --serve 支持自定义命令。 -7. 【优化】优化 ee-bin exec --cmds 支持自定义命令。 -8. 【优化】优化 ee-bin build --cmds 支持自定义命令。 -9. 【升级】升级 ee-core v2.7.1,升级 ee-bin v1.4.0 - -### 下载 - -``` -# gitee -git clone https://gitee.com/dromara/electron-egg.git - -# github -git clone https://github.com/dromara/electron-egg.git -``` - -### 安装 - -``` -# 设置国内镜像源(加速) -npm config set registry=https://registry.npmmirror.com - -#如果下载electron慢,配置如下 -npm config set electron_mirror=https://registry.npmmirror.com/-/binary/electron/ - -# 根目录,安装 electron 依赖 -npm i - -# 进入【前端目录】安装 frontend 依赖 -cd frontend -npm i -``` - -### 运行项目 - -``` -npm run start -``` - -## ![](/assets/img/news/electron-egg-3.10.0-1.png) - -### 用户案例 - -![](/assets/img/news/electron-egg-3.9.0-2.jpg) - -![](/assets/img/news/electron-egg-3.9.0-3.png) - -### 更多 - -访问官网:https://www.kaka996.com/ +--- +title: ElectronEgg v3.9.0 发布,快速开发一个桌面应用 +author: electron-egg +date: 2023-12-26 +cover: /assets/img/news/electron-egg-3.10.0-0.png +head: + - - meta + - name: 新闻 +--- + +大家好,`electron-egg 3.9.0` 重大更新。 + +在这个版本里我们做了重大更新,支持了 `go` 语言,天生的跨平台、高性能,可以把核心业务用 go 来写,能极大提升了软件体验。 + +目前,框架已经广泛应用于记账、政务、企业、医疗、学校、股票交易、ERP、娱乐、视频等领域客户端,请放心使用! + +![](/assets/img/news/electron-egg-3.10.0-0.png) + +### 为什么使用 + +桌面软件(办公方向、 个人工具),仍然是未来十几年 PC 端需求之一,提高工作效率 + +electron 技术是流行趋势,QQ、百度翻译、阿里网盘、迅雷、有道云笔记 ...... + +### 开源 + +gitee:https://gitee.com/dromara/electron-egg 4200+ + +github:https://github.com/dromara/electron-egg 1300+ + +### 本次更新 + +### 3.9.0 + +**【重大更新】** + +1. 【增加】新增 ee-go 模块,支持 go 语言,以及众多功能。 +2. 【增加】新增 ee-bin dev for go。 +3. 【增加】新增 ee-bin dev 配置 electron.loadingPage。 +4. 【增加】新增 ee-core cross 模块,支持跨语言服务。 +5. 【增加】新增 ee-core 生产环境配置 loadingPage。 +6. 【优化】优化 ee-bin dev --serve 支持自定义命令。 +7. 【优化】优化 ee-bin exec --cmds 支持自定义命令。 +8. 【优化】优化 ee-bin build --cmds 支持自定义命令。 +9. 【升级】升级 ee-core v2.7.1,升级 ee-bin v1.4.0 + +### 下载 + +``` +# gitee +git clone https://gitee.com/dromara/electron-egg.git + +# github +git clone https://github.com/dromara/electron-egg.git +``` + +### 安装 + +``` +# 设置国内镜像源(加速) +npm config set registry=https://registry.npmmirror.com + +#如果下载electron慢,配置如下 +npm config set electron_mirror=https://registry.npmmirror.com/-/binary/electron/ + +# 根目录,安装 electron 依赖 +npm i + +# 进入【前端目录】安装 frontend 依赖 +cd frontend +npm i +``` + +### 运行项目 + +``` +npm run start +``` + +## ![](/assets/img/news/electron-egg-3.10.0-1.png) + +### 用户案例 + +![](/assets/img/news/electron-egg-3.9.0-2.jpg) + +![](/assets/img/news/electron-egg-3.9.0-3.png) + +### 更多 + +访问官网:https://www.kaka996.com/ diff --git a/src/zh/news/fastRequest-2023.2.3.md b/src/zh/news/fastRequest-2023.2.3.md index 76af8a8e4d..ee393cbedf 100644 --- a/src/zh/news/fastRequest-2023.2.3.md +++ b/src/zh/news/fastRequest-2023.2.3.md @@ -1,104 +1,104 @@ ---- -title: 支持 Postman 同步,IDEA 插件 Fast Request 2023.2.3 发布 -author: Fast Request -tag: - - fastRequest -date: 2023-12-20 -cover: /assets/img/news/fastRequest-2023.2.3-0.png -head: - - - meta - - name: 新闻 ---- - -# 简介 - -**Restful Fast Request** 是一个类似于 Postman 的 IDEA 插件。它是一个强大的 restful api 工具包插件,可以根据已有的方法帮助您快速、自动生成 url 和 params。Restful Fast Request = API 调试工具 + API 管理工具 + API 搜索工具。它有一个漂亮的界面来完成请求、检查服务器响应、存储你的 api 请求和导出 api 请求。插件帮助你在 IDEA 界面内更快更高效得调试你的 API。 - -**最新域名**:**api-buddy.cn** - -**Restful Fast Request 为简化 API 调试而生,3 秒调完 Spring 接口不是梦**,所以少年,赶紧**上号**吧 - -**倾听用户的声音,不断提升自我**,本次**Restful Fast Request**更新主要内容如下: - -**重要功能、新功能、优化项、修复项** - -- **feat:API 同步至 Postman** -- **feat:自动域名切换项目名下拉框自动切换** -- **feat:Markdown 文档模板配置** -- **feat:JSON5 支持** -- **perf:Environment 重构** -- **perf:项目域名添加优化** -- **perf:api 文档同步触发在 api 保存的时候** -- **perf:忽略字段使用 @fastRequestParseIgnore** -- **fix:SearchEveryWhere 兼容 idea2023.3** -- **fix:body 中传非 json 报错** -- **fix:历史请求显示错误** - -## 1\. API 同步至 Postman - -仅需配置 Postman 的 token 和对应 workspace 的 ID,再点击保存或同步按钮。即可将 API 上传到 Postman.无需额外操作。 - -同时支持了 Environment 变量的上传和域名的变量上传。 - -![](/assets/img/news/fastRequest-2023.2.3-0.png) - -![](/assets/img/news/fastRequest-2023.2.3-1.png) - -![](/assets/img/news/fastRequest-2023.2.3-2.png) - -## 2. **Markdown 文档模板配置** - -利用 Velocity 和 Markdown 技术,实现导出文档和 \*\*Api 在线文档 \*\*自定义格式实现,满足用户各种样式类型文档的输出。 - -\*\*点击\*\*查看文档 - -![](/assets/img/news/fastRequest-2023.2.3-3.png) - -![](/assets/img/news/fastRequest-2023.2.3-4.png) - -![](/assets/img/news/fastRequest-2023.2.3-5.png) - -### 3\. JSON5 支持 - -请求体 JSON5 格式支持,JSON 字段注释支持等 - -![](/assets/img/news/fastRequest-2023.2.3-6.png) - -json5 - -## 4\. Environment 重构 - -Environment 区分为本地值(Current value)和共享值(Initial value) - -Initial value 可以通过提交 .fastRequest/config/fastRequestCurrentProjectEnvironment.json 实现共享 - -![](/assets/img/news/fastRequest-2023.2.3-7.png) - -## 5\. 项目域名添加优化 - -添加项目名的时候,自动识别 module 名称作为下拉框选项,也可以自定义输入. - -![](/assets/img/news/fastRequest-2023.2.3-8.png) - -projectName - -## 6\. 忽略字段使用@fastRequestParseIgnore - -针对实体类需要忽略解析的字段,可以在注释中添加@fastRequestParseIgnore 来实现,原来的注解@parseIgnore 依然适用,减少代码入侵 - -``` -/** - * xxx description - * @fastRequestParseIgnore - */ -private String someIgnoreField ; - -``` - -还有非常多的细节优化不再一一描述,赶紧升级吧! - -# 更多详情 - -请点击 ------------->**这里 https://api-buddy.cn/guide/history.html** - -**看完还不赶紧上号?** +--- +title: 支持 Postman 同步,IDEA 插件 Fast Request 2023.2.3 发布 +author: Fast Request +tag: + - fastRequest +date: 2023-12-20 +cover: /assets/img/news/fastRequest-2023.2.3-0.png +head: + - - meta + - name: 新闻 +--- + +# 简介 + +**Restful Fast Request** 是一个类似于 Postman 的 IDEA 插件。它是一个强大的 restful api 工具包插件,可以根据已有的方法帮助您快速、自动生成 url 和 params。Restful Fast Request = API 调试工具 + API 管理工具 + API 搜索工具。它有一个漂亮的界面来完成请求、检查服务器响应、存储你的 api 请求和导出 api 请求。插件帮助你在 IDEA 界面内更快更高效得调试你的 API。 + +**最新域名**:**api-buddy.cn** + +**Restful Fast Request 为简化 API 调试而生,3 秒调完 Spring 接口不是梦**,所以少年,赶紧**上号**吧 + +**倾听用户的声音,不断提升自我**,本次**Restful Fast Request**更新主要内容如下: + +**重要功能、新功能、优化项、修复项** + +- **feat:API 同步至 Postman** +- **feat:自动域名切换项目名下拉框自动切换** +- **feat:Markdown 文档模板配置** +- **feat:JSON5 支持** +- **perf:Environment 重构** +- **perf:项目域名添加优化** +- **perf:api 文档同步触发在 api 保存的时候** +- **perf:忽略字段使用 @fastRequestParseIgnore** +- **fix:SearchEveryWhere 兼容 idea2023.3** +- **fix:body 中传非 json 报错** +- **fix:历史请求显示错误** + +## 1\. API 同步至 Postman + +仅需配置 Postman 的 token 和对应 workspace 的 ID,再点击保存或同步按钮。即可将 API 上传到 Postman.无需额外操作。 + +同时支持了 Environment 变量的上传和域名的变量上传。 + +![](/assets/img/news/fastRequest-2023.2.3-0.png) + +![](/assets/img/news/fastRequest-2023.2.3-1.png) + +![](/assets/img/news/fastRequest-2023.2.3-2.png) + +## 2. **Markdown 文档模板配置** + +利用 Velocity 和 Markdown 技术,实现导出文档和 \*\*Api 在线文档 \*\*自定义格式实现,满足用户各种样式类型文档的输出。 + +\*\*点击\*\*查看文档 + +![](/assets/img/news/fastRequest-2023.2.3-3.png) + +![](/assets/img/news/fastRequest-2023.2.3-4.png) + +![](/assets/img/news/fastRequest-2023.2.3-5.png) + +### 3\. JSON5 支持 + +请求体 JSON5 格式支持,JSON 字段注释支持等 + +![](/assets/img/news/fastRequest-2023.2.3-6.png) + +json5 + +## 4\. Environment 重构 + +Environment 区分为本地值(Current value)和共享值(Initial value) + +Initial value 可以通过提交 .fastRequest/config/fastRequestCurrentProjectEnvironment.json 实现共享 + +![](/assets/img/news/fastRequest-2023.2.3-7.png) + +## 5\. 项目域名添加优化 + +添加项目名的时候,自动识别 module 名称作为下拉框选项,也可以自定义输入. + +![](/assets/img/news/fastRequest-2023.2.3-8.png) + +projectName + +## 6\. 忽略字段使用@fastRequestParseIgnore + +针对实体类需要忽略解析的字段,可以在注释中添加@fastRequestParseIgnore 来实现,原来的注解@parseIgnore 依然适用,减少代码入侵 + +``` +/** + * xxx description + * @fastRequestParseIgnore + */ +private String someIgnoreField ; + +``` + +还有非常多的细节优化不再一一描述,赶紧升级吧! + +# 更多详情 + +请点击 ------------->**这里 https://api-buddy.cn/guide/history.html** + +**看完还不赶紧上号?** diff --git a/src/zh/news/fastRequest-2024.1.1.md b/src/zh/news/fastRequest-2024.1.1.md index b03067ddfa..3fa8d54a7c 100644 --- a/src/zh/news/fastRequest-2024.1.1.md +++ b/src/zh/news/fastRequest-2024.1.1.md @@ -1,84 +1,84 @@ ---- -title: IDEA 插件 Fast Request 2024.1.1 强势来袭 -author: Fast Request -tag: - - fastRequest -date: 2024-01-16 -cover: /assets/img/news/fastRequest-2024.1.1-0.png -head: - - - meta - - name: 新闻 ---- - -# 简介 - -**Restful Fast Request** 是一个类似于 Postman 的 IDEA 插件。它是一个强大的 restful api 工具包插件,可以根据已有的方法帮助您快速、自动生成 url 和 params。Restful Fast Request = API 调试工具 + API 管理工具 + API 搜索工具。它有一个漂亮的界面来完成请求、检查服务器响应、存储你的 api 请求和导出 api 请求。插件帮助你在 IDEA 界面内更快更高效得调试你的 API。 - -**最新域名**:**api-buddy.cn** - -**Restful Fast Request 为简化 API 调试而生,3 秒调完 Spring 接口不是梦**,所以少年,赶紧**上号**吧 - -**倾听用户的声音,不断提升自我**,本次**Restful Fast Request**更新主要内容如下: - -**重要功能、新功能、优化项、修复项** - -- **SearchEveryWhere 支持类名搜索** -- **.fastRequest 目录生成策略** -- **忽略字段名解析支持** -- **Apis,Navigate 弹框打开支持** -- **Content-Disposition 支持优化** -- **SearchEveryWhere 性能优化** -- **Navigate 中的 api 加载性能优化** -- **Markdown 文档批量 api 导出增加返回值文档** -- **集合泛型解析错误** - -## 1.SearchEveryWhere 支持类名搜索 - -![](/assets/img/news/fastRequest-2024.1.1-0.png) - -searchEveryWhere - -输入 Url 所在的类名,即可搜索该类下面的所有 API - -## 2.fastRequest 目录生成策略 - -默认打开 Project 不再生成.fastRequest 目录,只有操作插件才生成 - -### 3.忽略字段名解析支持 - -![](/assets/img/news/fastRequest-2024.1.1-1.png) - -ignoreFiled - -只需要在配置中增加字段名,即可将实体类中的特定字段忽略生成 - -## 4.Apis,Navigate 弹框打开支持 - -![](/assets/img/news/fastRequest-2024.1.1-2.png) - -![](/assets/img/news/fastRequest-2024.1.1-3.png) - -可以通过快捷键快速打开 Apis 和 Navigate 窗口查看信息, 窗口打开的情况下再按 ESC 可以关闭窗口 - -## 5.SearchEveryWhere 性能优化 - -优化了 SearchEveryWhere 搜索逻辑,大大提生大项目的搜索性能。我们测试了拥有 7000 个 API 的项目,搜索响应非常快 - -## 6.Content-Disposition 支持优化 - -文件下载支持针对一下 2 种格式的支持,同时支持文件名转码 - -``` -Content-Disposition:attachment; filename*=UTF-8''fastRequest%20.txt -Content-Disposition:attachment; filename=fastRequest.txt - - -``` - -还有非常多的细节优化不再一一描述,赶紧升级吧! - -# 更多详情 - -请点击 ------------->**这里 https://api-buddy.cn/guide/history.html** - -**看完还不赶紧上号?** +--- +title: IDEA 插件 Fast Request 2024.1.1 强势来袭 +author: Fast Request +tag: + - fastRequest +date: 2024-01-16 +cover: /assets/img/news/fastRequest-2024.1.1-0.png +head: + - - meta + - name: 新闻 +--- + +# 简介 + +**Restful Fast Request** 是一个类似于 Postman 的 IDEA 插件。它是一个强大的 restful api 工具包插件,可以根据已有的方法帮助您快速、自动生成 url 和 params。Restful Fast Request = API 调试工具 + API 管理工具 + API 搜索工具。它有一个漂亮的界面来完成请求、检查服务器响应、存储你的 api 请求和导出 api 请求。插件帮助你在 IDEA 界面内更快更高效得调试你的 API。 + +**最新域名**:**api-buddy.cn** + +**Restful Fast Request 为简化 API 调试而生,3 秒调完 Spring 接口不是梦**,所以少年,赶紧**上号**吧 + +**倾听用户的声音,不断提升自我**,本次**Restful Fast Request**更新主要内容如下: + +**重要功能、新功能、优化项、修复项** + +- **SearchEveryWhere 支持类名搜索** +- **.fastRequest 目录生成策略** +- **忽略字段名解析支持** +- **Apis,Navigate 弹框打开支持** +- **Content-Disposition 支持优化** +- **SearchEveryWhere 性能优化** +- **Navigate 中的 api 加载性能优化** +- **Markdown 文档批量 api 导出增加返回值文档** +- **集合泛型解析错误** + +## 1.SearchEveryWhere 支持类名搜索 + +![](/assets/img/news/fastRequest-2024.1.1-0.png) + +searchEveryWhere + +输入 Url 所在的类名,即可搜索该类下面的所有 API + +## 2.fastRequest 目录生成策略 + +默认打开 Project 不再生成.fastRequest 目录,只有操作插件才生成 + +### 3.忽略字段名解析支持 + +![](/assets/img/news/fastRequest-2024.1.1-1.png) + +ignoreFiled + +只需要在配置中增加字段名,即可将实体类中的特定字段忽略生成 + +## 4.Apis,Navigate 弹框打开支持 + +![](/assets/img/news/fastRequest-2024.1.1-2.png) + +![](/assets/img/news/fastRequest-2024.1.1-3.png) + +可以通过快捷键快速打开 Apis 和 Navigate 窗口查看信息, 窗口打开的情况下再按 ESC 可以关闭窗口 + +## 5.SearchEveryWhere 性能优化 + +优化了 SearchEveryWhere 搜索逻辑,大大提生大项目的搜索性能。我们测试了拥有 7000 个 API 的项目,搜索响应非常快 + +## 6.Content-Disposition 支持优化 + +文件下载支持针对一下 2 种格式的支持,同时支持文件名转码 + +``` +Content-Disposition:attachment; filename*=UTF-8''fastRequest%20.txt +Content-Disposition:attachment; filename=fastRequest.txt + + +``` + +还有非常多的细节优化不再一一描述,赶紧升级吧! + +# 更多详情 + +请点击 ------------->**这里 https://api-buddy.cn/guide/history.html** + +**看完还不赶紧上号?** diff --git a/src/zh/news/hmily-2.0.2.md b/src/zh/news/hmily-2.0.2.md index e8cd50bacd..b8219790e6 100644 --- a/src/zh/news/hmily-2.0.2.md +++ b/src/zh/news/hmily-2.0.2.md @@ -1,67 +1,67 @@ ---- -title: Hmily发布2.0.2-RELEASE版本 -author: xiaoyu -tag: - - hmily -date: 2019-04-05 -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: 新闻 ---- - -## Hmily 发布 2.0.2-RELEASE 版本 - -- 解决 SpringCloud 使用 hystrix 配置线程池策略的问题。 - -- 新增对 springcloud 内嵌事务调用的问题。 - -- 新增 Hmily 负载均衡策略。 - -- 其他 bug 的修护,与代码的优化。 - -- 去除不必须的第三方 jar 包。 - -- 零侵入方式的引入。 - -## Hmily 对现在流行 RPC 框架以及 Spring 的支持情况。 - -- dubbo 2.7.0 以下所有版本。 - -- Springcloud Dalston 以上版本,包括支持现在的 Finchley 与 Greenwich - -- Motan 所有版本。 - -- 3.0 以上所有 Spring 版本。 - -## Hmily 在 2.0.2 版本对使用者 RPC 集群时候负载均衡策略。 - -- hmily 提供了自己实现的负载均衡策略,只是针对加了@Hmily 的接口 - -dubbo 集群配置,配置负载方式为:loadbalance="hmily" - -```xml - -``` - -Springcloud 在调用方的 yml 配置文件中新增: - -```yml -hmily : - ribbon: - rule - enabled : true -``` - -## Hmily 的具体使用文档: - -- 官网文档 :https://dromara.org/website/zh-cn/docs/hmily/index.html - -- github 地址: https://github.com/yu199195/hmily - -- gitee 地址: https://gitee.com/dromara/hmily - -- 欢迎大家 star fork ,提供优秀的代码与建议。 +--- +title: Hmily发布2.0.2-RELEASE版本 +author: xiaoyu +tag: + - hmily +date: 2019-04-05 +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: 新闻 +--- + +## Hmily 发布 2.0.2-RELEASE 版本 + +- 解决 SpringCloud 使用 hystrix 配置线程池策略的问题。 + +- 新增对 springcloud 内嵌事务调用的问题。 + +- 新增 Hmily 负载均衡策略。 + +- 其他 bug 的修护,与代码的优化。 + +- 去除不必须的第三方 jar 包。 + +- 零侵入方式的引入。 + +## Hmily 对现在流行 RPC 框架以及 Spring 的支持情况。 + +- dubbo 2.7.0 以下所有版本。 + +- Springcloud Dalston 以上版本,包括支持现在的 Finchley 与 Greenwich + +- Motan 所有版本。 + +- 3.0 以上所有 Spring 版本。 + +## Hmily 在 2.0.2 版本对使用者 RPC 集群时候负载均衡策略。 + +- hmily 提供了自己实现的负载均衡策略,只是针对加了@Hmily 的接口 + +dubbo 集群配置,配置负载方式为:loadbalance="hmily" + +```xml + +``` + +Springcloud 在调用方的 yml 配置文件中新增: + +```yml +hmily : + ribbon: + rule + enabled : true +``` + +## Hmily 的具体使用文档: + +- 官网文档 :https://dromara.org/website/zh-cn/docs/hmily/index.html + +- github 地址: https://github.com/yu199195/hmily + +- gitee 地址: https://gitee.com/dromara/hmily + +- 欢迎大家 star fork ,提供优秀的代码与建议。 diff --git a/src/zh/news/hmily-2.1.1.md b/src/zh/news/hmily-2.1.1.md index 6495b8ef9a..55b8e4019a 100644 --- a/src/zh/news/hmily-2.1.1.md +++ b/src/zh/news/hmily-2.1.1.md @@ -1,302 +1,302 @@ ---- -title: 时隔一年,dromara团队发布全新架构Hmily分布式事务的2.1.1版本 -author: xiaoyu -date: 2020-09-28 -tag: - - hmily -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: 新闻 ---- - -感谢朋友们一路以来的支持,让大家久等了。在这一个版本中,我们团队重构了整个项目,合理的划分功能模块,新增配置中心,调整底层存储结构,解决疑难 bug,以及其他新功能的支持,也吸收了更多开源社区的优秀人才的加入。 - -## 架构全景图 - -![架构全景图](/assets/img/architecture/hmily-framework.png) - -### 功能 - -- 高可靠性 :支持分布式场景下,事务异常回滚,超时异常恢复,防止事务悬挂。 -- 易用性 :提供零侵入性式的 Spring-Boot, Spring-Namespace 快速与业务系统集成。 -- 高性能 :去中心化设计,与业务系统完全融合,天然支持集群部署。 -- 可观测性 :Metrics 多项指标性能监控,以及 admin 管理后台 UI 展示。 -- 多种 RPC :支持 Dubbo, SpringCloud,Montan ,sofa-rpc 等知名 RPC 框架。 -- 日志存储 :支持 mysql, oracle, mongodb, redis, zookeeper 等方式。 -- 复杂场景 :支持 RPC 嵌套调用事务。 - -### 重构部分 - -- **在模块划分上:** - - - 抽离出开箱即用的 SPI 自定义模块。 - - 定义事务日志多种存储方式的 SPI 模块。 - - 定义事务日志多种序列化方式的 SPI 模块。 - - 新增配置中心,支持各种主流的配置中心(nacos,apollo,zookeeper 等),并支持配置的动态刷新。 - - 新增 metrics 模块,用来监控运行时候的各种信息。 - - 抽离出核心的事务执行模块。 - - 抽离出多种 RPC 支持模块。 - - 抽离出 spring 与 spring boot 支持模块。 - -- **在依赖包版本上:** - - - guava 升级到 29.0 - - curator 升级到 5.1.0 - -- **在代码质量上:** - - - 严格的 check-style 代码检查,秉承优雅,简单易懂原则(talk is cheap ,show you code)。 - -- **在开放性上:** - - - 社区奉行简单,快乐,和谐基本原则 - -- **在目标上:** - - 打造一款高可用,高性能,简单易用金融级的分布式事务解决方案。 - -### 解决疑难 bug: - -- `dubbo`框架不支持注解方式的使用(spring-boot-starter-dubbo)。 -- `motan`框架不支持注解方式的使用。 -- `spring-cloud`用户如果使用 feign 与 hystrix 整合 hmily 时候的线程切换问题。 -- 极端情况下事务日志序列化异常。 -- try 阶段超时异常,导致事务悬挂 bug。 -- confirm 与 cancel 阶段异常时候,事务未能正确恢复 bug。 -- 在事务日志存储上,支持同步与异步 2 种模式,供用户选择。 - -### 用户使用与升级指南 - -对于 hmily 用户来说,只需三个步骤,即可解决 RPC 服务调用之间的柔性事务 - -- 引用 hmily 对各种 rpc 支持的 jar 包。 -- 添加 hmily 配置。 -- 在 rpc 接口方法上添加 @Hmily 注解。 - -**依赖的变更** - -用户依赖的方式没有更改,只需要将版本升级到 2.1.0。下面举 dubbo 微服务列子 - -**dubbo rpc 微服务** - -- dubbo 接口服务依赖 - -``` - - org.dromara - hmily-annotation - 2.1.0 - -``` - -- dubbo 服务提供者依赖(<2.7) - -``` - - org.dromara - hmily-dubbo - 2.1.0 - - - or - - - org.dromara - hmily-spring-boot-starter-dubbo - 2.1.0 - -``` - -**hmily 配置的变更** - -在新版 2.1.0 中,新增了 hmily-config 模块,支持本地与注册中心模式。用户首先需要在项目`resouce`文件下新建一个名称为`hmily.yml`的文件。默认路径为项目的 `resource`目录下,也可以使用 `-Dhmily.conf` 指定,也可以把配置放在 `user.dir` 目录下。优先级别 `-Dhmily.conf` > `user.dir` >`resource`。文件格式如下(一部分,以下是配置成本地模式): - -``` - server: - configMode: local - appName: account-dubbo - # 如果server.configMode eq local 的时候才会读取到这里的配置信息. - config: - appName: account-dubbo - serializer: kryo - contextTransmittalMode: threadLocal - scheduledThreadMax: 16 - scheduledRecoveryDelay: 60 - scheduledCleanDelay: 60 - scheduledPhyDeletedDelay: 600 - scheduledInitDelay: 30 - recoverDelayTime: 60 - cleanDelayTime: 180 - limit: 200 - retryMax: 10 - bufferSize: 8192 - consumerThreads: 16 - asyncRepository: true - autoSql: true - phyDeleted: true - storeDays: 3 - repository: mysql - -repository: - database: - driverClassName: com.mysql.jdbc.Driver - url : jdbc:mysql://127.0.0.1:3306/hmily?useUnicode=true&characterEncoding=utf8 - username: root - password: - maxActive: 20 - minIdle: 10 - connectionTimeout: 30000 - idleTimeout: 600000 - maxLifetime: 1800000 -``` - -**如果你想将配置文件放在`Nacos`配置中心:** - -- 第一步: - -``` -hmily: - server: - configMode: nacos - appName: xxxxx - # 如果server.configMode eq local 的时候才会读取到这里的配置信息. - -remote: - nacos: - server: 192.168.3.22:8848 - dataId: hmily.properties - group: DEFAULT_GROUP - timeoutMs: 6000 - fileExtension: yml - passive: true -``` - -- 第二步:将 hmily 的配置,放在 nacos 配置中心上 - -**如果你想将配置文件放在`Apollo`配置中心:** - -- 第一步: - -``` -hmily: - server: - configMode: apollo - appName: xxxx - # 如果server.configMode eq local 的时候才会读取到这里的配置信息. - -remote: - apollo: - appId: hmily-xxxxx - configService: http://192.168.3.22:8080 - namespace: byin_hmily - secret: - fileExtension: yml - passive: true - env: dev - meta: http://192.168.3.22:8080 -``` - -- 第二步:将 hmily 的配置,放在 apollo 配置中心上 - -还有其他的配置方式以及配置内容的详解,请参考:https://dromara.org/zh-cn/docs/hmily/config.html - -**注解方式的使用的变更** - -在之前的版本中,rpc 接口与实现都只需要添加 `@Hmily` 注解, 现在需要进行变更,在 rpc 接口方法上是添加 `@Hmily`,用来标识这是一个 hmily 分布式事务的接口方法, 在接口的方法实现上则需要添加 `@HmilyTCC`,然后指定 `confirm` 与 `cancel`方法名称. - -**举例(dubbo 中 say 方法需要参与分布式事务):** - -``` -public interface HelloService { - - @Hmily - void say(String hello); -} - -public class HelloServiceImpl implements HelloService { - - @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel") - public void say(String hello) { - System.out.println("hello world"); - } - - public void sayConfrim(String hello) { - System.out.println(" confirm hello world"); - } - - public void sayCancel(String hello) { - System.out.println(" cancel hello world"); - } -} -``` - -**举例(springcloud 中 say 方法需要参与分布式事务):** - -- spring-cloud 服务调用方 FeignClient 中 - -``` -@FeignClient(value = "helle-service") -public interface HelloService { - - @Hmily - @RequestMapping("/helle-service/sayHello") - void say(String hello); -} -``` - -- spring-cloud 服务提供方 - -``` -@RestController -public class HelloController { - - private final HelloService helloService ; - - @Autowired - public AccountController(HelloService helloService) { - this.helloService= helloService; - } - - @RequestMapping("/sayHello") - public void payment(String hello) { - return helloService.say(hello); - } -} -public interface HelloService { - - void say(String hello); -} -public class HelloServiceImpl implements HelloService { - - @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel") - public void say(String hello) { - System.out.println("hello world"); - } - - public void sayConfrim(String hello) { - System.out.println(" confirm hello world"); - } - - public void sayCancel(String hello) { - System.out.println(" cancel hello world"); - } -} -``` - -**事务日志存储结构的更改** - -在使用上,用户使用或者升级不用关心,框架会默认初始化好。 - -## 下一个版本 - -- 因为调整了架构,在其他模式的支持上将会变得更加容易,在下一个版本,将会发布 TAC 模式(try-auto-cancel)使用此模式,将大大简化框架的使用程度,开发者不需要关心 confirm 以及 cancel 方法的开发,对老系统的改造提供了更好的兼容性,不用担心额外的开发任务,一切就交给 hmily 吧。 -- 将对 brpc 用户进行支持。 -- 将对 tars-rpc 用户进行支持。 - -## 社区共建 - -我们秉承`和谐快乐`,`代码至上` 的原则,如果你有想法,愿意和我们一起成长,一起贡献,快来加入我们吧! - -- github:https://github.com/dromara/hmily -- gitee:https://gitee.com/dromara/hmily -- qq 群: 162614487 +--- +title: 时隔一年,dromara团队发布全新架构Hmily分布式事务的2.1.1版本 +author: xiaoyu +date: 2020-09-28 +tag: + - hmily +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: 新闻 +--- + +感谢朋友们一路以来的支持,让大家久等了。在这一个版本中,我们团队重构了整个项目,合理的划分功能模块,新增配置中心,调整底层存储结构,解决疑难 bug,以及其他新功能的支持,也吸收了更多开源社区的优秀人才的加入。 + +## 架构全景图 + +![架构全景图](/assets/img/architecture/hmily-framework.png) + +### 功能 + +- 高可靠性 :支持分布式场景下,事务异常回滚,超时异常恢复,防止事务悬挂。 +- 易用性 :提供零侵入性式的 Spring-Boot, Spring-Namespace 快速与业务系统集成。 +- 高性能 :去中心化设计,与业务系统完全融合,天然支持集群部署。 +- 可观测性 :Metrics 多项指标性能监控,以及 admin 管理后台 UI 展示。 +- 多种 RPC :支持 Dubbo, SpringCloud,Montan ,sofa-rpc 等知名 RPC 框架。 +- 日志存储 :支持 mysql, oracle, mongodb, redis, zookeeper 等方式。 +- 复杂场景 :支持 RPC 嵌套调用事务。 + +### 重构部分 + +- **在模块划分上:** + + - 抽离出开箱即用的 SPI 自定义模块。 + - 定义事务日志多种存储方式的 SPI 模块。 + - 定义事务日志多种序列化方式的 SPI 模块。 + - 新增配置中心,支持各种主流的配置中心(nacos,apollo,zookeeper 等),并支持配置的动态刷新。 + - 新增 metrics 模块,用来监控运行时候的各种信息。 + - 抽离出核心的事务执行模块。 + - 抽离出多种 RPC 支持模块。 + - 抽离出 spring 与 spring boot 支持模块。 + +- **在依赖包版本上:** + + - guava 升级到 29.0 + - curator 升级到 5.1.0 + +- **在代码质量上:** + + - 严格的 check-style 代码检查,秉承优雅,简单易懂原则(talk is cheap ,show you code)。 + +- **在开放性上:** + + - 社区奉行简单,快乐,和谐基本原则 + +- **在目标上:** + - 打造一款高可用,高性能,简单易用金融级的分布式事务解决方案。 + +### 解决疑难 bug: + +- `dubbo`框架不支持注解方式的使用(spring-boot-starter-dubbo)。 +- `motan`框架不支持注解方式的使用。 +- `spring-cloud`用户如果使用 feign 与 hystrix 整合 hmily 时候的线程切换问题。 +- 极端情况下事务日志序列化异常。 +- try 阶段超时异常,导致事务悬挂 bug。 +- confirm 与 cancel 阶段异常时候,事务未能正确恢复 bug。 +- 在事务日志存储上,支持同步与异步 2 种模式,供用户选择。 + +### 用户使用与升级指南 + +对于 hmily 用户来说,只需三个步骤,即可解决 RPC 服务调用之间的柔性事务 + +- 引用 hmily 对各种 rpc 支持的 jar 包。 +- 添加 hmily 配置。 +- 在 rpc 接口方法上添加 @Hmily 注解。 + +**依赖的变更** + +用户依赖的方式没有更改,只需要将版本升级到 2.1.0。下面举 dubbo 微服务列子 + +**dubbo rpc 微服务** + +- dubbo 接口服务依赖 + +``` + + org.dromara + hmily-annotation + 2.1.0 + +``` + +- dubbo 服务提供者依赖(<2.7) + +``` + + org.dromara + hmily-dubbo + 2.1.0 + + + or + + + org.dromara + hmily-spring-boot-starter-dubbo + 2.1.0 + +``` + +**hmily 配置的变更** + +在新版 2.1.0 中,新增了 hmily-config 模块,支持本地与注册中心模式。用户首先需要在项目`resouce`文件下新建一个名称为`hmily.yml`的文件。默认路径为项目的 `resource`目录下,也可以使用 `-Dhmily.conf` 指定,也可以把配置放在 `user.dir` 目录下。优先级别 `-Dhmily.conf` > `user.dir` >`resource`。文件格式如下(一部分,以下是配置成本地模式): + +``` + server: + configMode: local + appName: account-dubbo + # 如果server.configMode eq local 的时候才会读取到这里的配置信息. + config: + appName: account-dubbo + serializer: kryo + contextTransmittalMode: threadLocal + scheduledThreadMax: 16 + scheduledRecoveryDelay: 60 + scheduledCleanDelay: 60 + scheduledPhyDeletedDelay: 600 + scheduledInitDelay: 30 + recoverDelayTime: 60 + cleanDelayTime: 180 + limit: 200 + retryMax: 10 + bufferSize: 8192 + consumerThreads: 16 + asyncRepository: true + autoSql: true + phyDeleted: true + storeDays: 3 + repository: mysql + +repository: + database: + driverClassName: com.mysql.jdbc.Driver + url : jdbc:mysql://127.0.0.1:3306/hmily?useUnicode=true&characterEncoding=utf8 + username: root + password: + maxActive: 20 + minIdle: 10 + connectionTimeout: 30000 + idleTimeout: 600000 + maxLifetime: 1800000 +``` + +**如果你想将配置文件放在`Nacos`配置中心:** + +- 第一步: + +``` +hmily: + server: + configMode: nacos + appName: xxxxx + # 如果server.configMode eq local 的时候才会读取到这里的配置信息. + +remote: + nacos: + server: 192.168.3.22:8848 + dataId: hmily.properties + group: DEFAULT_GROUP + timeoutMs: 6000 + fileExtension: yml + passive: true +``` + +- 第二步:将 hmily 的配置,放在 nacos 配置中心上 + +**如果你想将配置文件放在`Apollo`配置中心:** + +- 第一步: + +``` +hmily: + server: + configMode: apollo + appName: xxxx + # 如果server.configMode eq local 的时候才会读取到这里的配置信息. + +remote: + apollo: + appId: hmily-xxxxx + configService: http://192.168.3.22:8080 + namespace: byin_hmily + secret: + fileExtension: yml + passive: true + env: dev + meta: http://192.168.3.22:8080 +``` + +- 第二步:将 hmily 的配置,放在 apollo 配置中心上 + +还有其他的配置方式以及配置内容的详解,请参考:https://dromara.org/zh-cn/docs/hmily/config.html + +**注解方式的使用的变更** + +在之前的版本中,rpc 接口与实现都只需要添加 `@Hmily` 注解, 现在需要进行变更,在 rpc 接口方法上是添加 `@Hmily`,用来标识这是一个 hmily 分布式事务的接口方法, 在接口的方法实现上则需要添加 `@HmilyTCC`,然后指定 `confirm` 与 `cancel`方法名称. + +**举例(dubbo 中 say 方法需要参与分布式事务):** + +``` +public interface HelloService { + + @Hmily + void say(String hello); +} + +public class HelloServiceImpl implements HelloService { + + @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel") + public void say(String hello) { + System.out.println("hello world"); + } + + public void sayConfrim(String hello) { + System.out.println(" confirm hello world"); + } + + public void sayCancel(String hello) { + System.out.println(" cancel hello world"); + } +} +``` + +**举例(springcloud 中 say 方法需要参与分布式事务):** + +- spring-cloud 服务调用方 FeignClient 中 + +``` +@FeignClient(value = "helle-service") +public interface HelloService { + + @Hmily + @RequestMapping("/helle-service/sayHello") + void say(String hello); +} +``` + +- spring-cloud 服务提供方 + +``` +@RestController +public class HelloController { + + private final HelloService helloService ; + + @Autowired + public AccountController(HelloService helloService) { + this.helloService= helloService; + } + + @RequestMapping("/sayHello") + public void payment(String hello) { + return helloService.say(hello); + } +} +public interface HelloService { + + void say(String hello); +} +public class HelloServiceImpl implements HelloService { + + @HmilyTCC(confirmMethod = "sayConfrim", cancelMethod = "sayCancel") + public void say(String hello) { + System.out.println("hello world"); + } + + public void sayConfrim(String hello) { + System.out.println(" confirm hello world"); + } + + public void sayCancel(String hello) { + System.out.println(" cancel hello world"); + } +} +``` + +**事务日志存储结构的更改** + +在使用上,用户使用或者升级不用关心,框架会默认初始化好。 + +## 下一个版本 + +- 因为调整了架构,在其他模式的支持上将会变得更加容易,在下一个版本,将会发布 TAC 模式(try-auto-cancel)使用此模式,将大大简化框架的使用程度,开发者不需要关心 confirm 以及 cancel 方法的开发,对老系统的改造提供了更好的兼容性,不用担心额外的开发任务,一切就交给 hmily 吧。 +- 将对 brpc 用户进行支持。 +- 将对 tars-rpc 用户进行支持。 + +## 社区共建 + +我们秉承`和谐快乐`,`代码至上` 的原则,如果你有想法,愿意和我们一起成长,一起贡献,快来加入我们吧! + +- github:https://github.com/dromara/hmily +- gitee:https://gitee.com/dromara/hmily +- qq 群: 162614487 diff --git a/src/zh/news/hmily-restart.md b/src/zh/news/hmily-restart.md index c2d8b8b725..e900403308 100644 --- a/src/zh/news/hmily-restart.md +++ b/src/zh/news/hmily-restart.md @@ -1,97 +1,97 @@ ---- -title: Hmily分布式事务重启月度报告 -author: xiaoyu -date: 2020-09-08 -tag: - - hmily -cover: /assets/img/architecture/hmily-framework.png -head: - - - meta - - name: 新闻 ---- - -Hmily 是一款高性能,高可靠,易使用的柔性分布式事务解决方案,目前提供了对 dubbo,spring-cloud,motan,grpc 等 rpc 框架的支持,在易用性上提供零侵入性式的 Spring-Boot, Spring-Namespace 快速集成,目标是打造金融级的一体系分布式事务解决方案。 - -## 调整 Hmily 架构,更合理的模块划分 - -**全景图:** - -![全景图](/assets/img/architecture/hmily-framework.png) - -**架构调整:** - -- 抽离核心执行模块,支持多种事务模式以及混合使用(TCC 模式,TAC 模式) -- 核心模块去除对 spring 的依赖 -- 定义多种 SPI 接口的实现 -- 新增 `hmily-rpc` : 聚合多种 rpc 框架的支持 -- 新增 `hmily-spi` : hmily 框架自定义 spi 机制实现 -- 新增 `hmily-bom` : 解决版本依赖管理冲突的问题 -- 新增 `hmily-metrics` : 监控 JVM,线程,事务运行状态,耗时等信息 -- 新增 `hmily-tcc` : tcc 模式的核心实现 -- 新增 `hmily-tac` : tac 模式的核心实现 - -**SPI 模块划分:** - -- 新增 `hmily-repository`: 事务日志存储模块,支持(mysql,oracle,postgresql,sqlserver,zookeeper,redis,mongodb,file) -- 新增 `hmily-serializer`: 事务日志序列化模块, 支持 (hessian,jdk,kryo,protobuf) -- 新增 `hmily-config`:配置模块,支持(本地模式,zookeeper,nacos,apollo,etcd) -- 新增 `hmily-tac-sqlparser` :tac 模式下,sql 解析模块 - -### 梳理 Hmily 社区 issue,解决 bug。 - -![hmily-bug](/assets/img/architecture/hmily-bug.png) - -如上图:在社区中,主要是梳理和解决之前社区反馈的问题,以及社区合作进行新的开发。 - -**解决 bug(列举几个):** - -- `dubbo`框架不支持注解方式的使用(spring-boot-starter-dubbo) -- `motan`框架不支持注解方式的使用 -- `spring-cloud`用户如果使用 feign 与 hystrix 整合 hmily 时候的异常问题 -- 事务日志序列化异常 -- 超时异常事务悬挂 bug -- 事务定时恢复 bug - -**社区完成功能(列举几个):** - -- `build`:新增 travis-ci 功能 -- 事务日志支持:`oracle`, `postgresql`,`sqlsever`,`mongo`,`zookeeper`,`file`,`redis` -- 配置模块:新增 apollo,etcd,nacos 配置中心支持 -- demo:新增 motan-rpc 方式使用 hmily 分布式事务 demo - -### 社区共建 - -社区奉行`简单`,`快乐`,`优雅`,和`谐基`本原则。 - -- 代码准则:代码遵循 hmily-checkstyle 标准,也有很多灵活自由的空间。(talk is cheap ,show you code) -- 开放准则:希望在这里每个人都有好的思想和观点,大家一起讨论,反复 review 代码,思考解决 bug,快乐成长,绝不搞一言堂。 - -### 最近 - -会发布最新架构的 hmily-2.1.0 版本(只会支持 TCC 模式) - -**配置模块** - -- 配置动态刷新功能,支持所有的配置中心 - -**TAC 模式:** - -- `SQL-parser`: 正在接入`apache-shardingsphere`,`apache-calcite` -- `SQL-revert`:正在开发 - -### 大约在冬季 - -很高兴来了这里季节,在这个时间点,会发布`hmily-2.2.0`版本,这个版本将完全支持`TAC`,`TCC`模式。 -`TAC(transaction auto rollback)` :有了这个模式,用户再也不用担心像 TCC 那样去写反向的 cancel 方法了。大大减少了使用成本以及学习成本。 -`TCC`: 稳定性,可靠性得到大大加强,彻底解决事务悬挂问题。 - -### 以后的以后 - -- 更多 RPC 框架的支持:brpc 等等。 -- 支持 XA 模式。 - -......这里空起来,很多多的规划,希望你来参与建设。 - -- github:https://github.com/dromara/hmily -- gitee:https://github.com/shuaiqiyu/hmily -- qq 群: 162614487 +--- +title: Hmily分布式事务重启月度报告 +author: xiaoyu +date: 2020-09-08 +tag: + - hmily +cover: /assets/img/architecture/hmily-framework.png +head: + - - meta + - name: 新闻 +--- + +Hmily 是一款高性能,高可靠,易使用的柔性分布式事务解决方案,目前提供了对 dubbo,spring-cloud,motan,grpc 等 rpc 框架的支持,在易用性上提供零侵入性式的 Spring-Boot, Spring-Namespace 快速集成,目标是打造金融级的一体系分布式事务解决方案。 + +## 调整 Hmily 架构,更合理的模块划分 + +**全景图:** + +![全景图](/assets/img/architecture/hmily-framework.png) + +**架构调整:** + +- 抽离核心执行模块,支持多种事务模式以及混合使用(TCC 模式,TAC 模式) +- 核心模块去除对 spring 的依赖 +- 定义多种 SPI 接口的实现 +- 新增 `hmily-rpc` : 聚合多种 rpc 框架的支持 +- 新增 `hmily-spi` : hmily 框架自定义 spi 机制实现 +- 新增 `hmily-bom` : 解决版本依赖管理冲突的问题 +- 新增 `hmily-metrics` : 监控 JVM,线程,事务运行状态,耗时等信息 +- 新增 `hmily-tcc` : tcc 模式的核心实现 +- 新增 `hmily-tac` : tac 模式的核心实现 + +**SPI 模块划分:** + +- 新增 `hmily-repository`: 事务日志存储模块,支持(mysql,oracle,postgresql,sqlserver,zookeeper,redis,mongodb,file) +- 新增 `hmily-serializer`: 事务日志序列化模块, 支持 (hessian,jdk,kryo,protobuf) +- 新增 `hmily-config`:配置模块,支持(本地模式,zookeeper,nacos,apollo,etcd) +- 新增 `hmily-tac-sqlparser` :tac 模式下,sql 解析模块 + +### 梳理 Hmily 社区 issue,解决 bug。 + +![hmily-bug](/assets/img/architecture/hmily-bug.png) + +如上图:在社区中,主要是梳理和解决之前社区反馈的问题,以及社区合作进行新的开发。 + +**解决 bug(列举几个):** + +- `dubbo`框架不支持注解方式的使用(spring-boot-starter-dubbo) +- `motan`框架不支持注解方式的使用 +- `spring-cloud`用户如果使用 feign 与 hystrix 整合 hmily 时候的异常问题 +- 事务日志序列化异常 +- 超时异常事务悬挂 bug +- 事务定时恢复 bug + +**社区完成功能(列举几个):** + +- `build`:新增 travis-ci 功能 +- 事务日志支持:`oracle`, `postgresql`,`sqlsever`,`mongo`,`zookeeper`,`file`,`redis` +- 配置模块:新增 apollo,etcd,nacos 配置中心支持 +- demo:新增 motan-rpc 方式使用 hmily 分布式事务 demo + +### 社区共建 + +社区奉行`简单`,`快乐`,`优雅`,和`谐基`本原则。 + +- 代码准则:代码遵循 hmily-checkstyle 标准,也有很多灵活自由的空间。(talk is cheap ,show you code) +- 开放准则:希望在这里每个人都有好的思想和观点,大家一起讨论,反复 review 代码,思考解决 bug,快乐成长,绝不搞一言堂。 + +### 最近 + +会发布最新架构的 hmily-2.1.0 版本(只会支持 TCC 模式) + +**配置模块** + +- 配置动态刷新功能,支持所有的配置中心 + +**TAC 模式:** + +- `SQL-parser`: 正在接入`apache-shardingsphere`,`apache-calcite` +- `SQL-revert`:正在开发 + +### 大约在冬季 + +很高兴来了这里季节,在这个时间点,会发布`hmily-2.2.0`版本,这个版本将完全支持`TAC`,`TCC`模式。 +`TAC(transaction auto rollback)` :有了这个模式,用户再也不用担心像 TCC 那样去写反向的 cancel 方法了。大大减少了使用成本以及学习成本。 +`TCC`: 稳定性,可靠性得到大大加强,彻底解决事务悬挂问题。 + +### 以后的以后 + +- 更多 RPC 框架的支持:brpc 等等。 +- 支持 XA 模式。 + +......这里空起来,很多多的规划,希望你来参与建设。 + +- github:https://github.com/dromara/hmily +- gitee:https://github.com/shuaiqiyu/hmily +- qq 群: 162614487 diff --git a/src/zh/news/hutool-5.8.0.md b/src/zh/news/hutool-5.8.0.md index 3cefb69d97..27a965df68 100644 --- a/src/zh/news/hutool-5.8.0.md +++ b/src/zh/news/hutool-5.8.0.md @@ -1,107 +1,107 @@ ---- -title: Hutool-5.8.0.M1 发布,尝试里程碑发布 -author: hutool -tag: - - hutool -date: 2022-03-30 -cover: /assets/img/architecture/hutool-framework.png -head: - - - meta - - name: 新闻 ---- - -> Hutool 是一个小而全的 Java 工具类库,提供优雅、高效和便捷的工具方法。 - -## Hutool 是什么 - -![架构全景图](/assets/img/architecture/hutool-framework.png) - -本来这个版本应该是 5.7.23 的,可惜用户提了一些 issue,这些问题的解决必须修改原有代码结构: - -1. 如 MongoDB 客户端封装,由于其驱动本身做了不兼容修改,包装的工具类不得不进行修改。 -2. 涉及到 Bean 拷贝的代码部分(BeanCopier),由于一个参数失效,以为只是简单的一个 bug,后来发现是整个设计有问题……崩溃程度可想而知,肝了两个晚上重构了这部分代码。 -3. 修改代码的同时才发现还有很多部分的设计有问题,顺便做了小重构。 -4. 为了解决每次大版本升级的可能带来的对老用户的影响,此次版本采用里程碑方式发布,版本为 M1(感觉给用户送了颗 CPU),也是解决 Hutool 每次“激进”升级的问题(毕竟年龄大了,要稳重) -5. 希望购买代替捐赠,如果你希望支持下 Hutool,可以去 Hutool 主页点->击进入周边商店购买 Hutool 周边来支持 Hutool 哦,这比捐赠实惠的多(毕竟捐赠者我不知道如何道谢,很有道德负担……) - -鸣谢一下此次版本一起讨论和一起解决大量 issue 的 Hutool 几位成员: -@阿超 @Cherryrum @Husky - ---- - -## 5.8.0.M1 - -### ❌ 不兼容特性 - -• 【db 】 【不向下兼容 】增加 MongoDB4.x 支持返回 MongoClient 变更(pr#568@Gitee) -• 【json 】 【可能兼容问题】修改 JSONObject 结构,继承自 MapWrapper -• 【core 】 【可能兼容问题】BeanCopier 重构,新建 XXXCopier,删除 XXXValueProvider -• 【core 】 【可能兼容问题】URLEncoder 废弃,URLEncoderUtil 使用 RFC3986 -• 【core 】 【可能兼容问题】Base32 分离编码和解码,以便减少数据加载,支持 Hex 模式 -• 【core 】 【可能兼容问题】Base58 分离编码和解码 -• 【core 】 【可能兼容问题】Base62 分离编码和解码,增加 inverted 模式支持 -• 【core 】 【兼容问题 】PunyCode 参数由 String 改为 Charsequence -• 【cron 】 【可能兼容问题】SimpleValueParser 改名为 AbsValueParser,改为 abstract -• 【poi 】 【可能兼容问题】ExcelUtil.getBigWriter 返回值改为 BigExcelWriter -• 【core 】 【可能兼容问题】Opt.ofEmptyAble 参数由 List 改为 Collection 子类(pr#580@Gitee) -• 【json 】 【可能兼容问题】JSON 转 Bean 时,使用 JSON 本身的相关设置,而非默认(issue#2212@Github) -• 【json 】 【可能兼容问题】JSONConfig 中 isOrder 废弃,默认全部有序 - -### 🐣 新特性 - -• 【http 】 HttpRequest.form 采用 TableMap 方式(issue#I4W427@Gitee) -• 【core 】 AnnotationUtil 增加 getAnnotationAlias 方法(pr#554@Gitee) -• 【core 】 FileUtil.extName 增加对 tar.gz 特殊处理(issue#I4W5FS@Gitee) -• 【crypto 】 增加 XXTEA 实现(issue#I4WH2X@Gitee) -• 【core 】 增加 Table 实现(issue#2179@Github) -• 【core 】 增加 UniqueKeySet(issue#I4WUWR@Gitee) -• 【core 】 阿拉伯数字转换成中文对发票票面金额转换的扩展(pr#570@Gitee) -• 【core 】 ArrayUtil 增加 replace 方法(pr#570@Gitee) -• 【core 】 CsvReadConfig 增加自定义标题行行号(issue#2180@Github) -• 【core 】 FileAppender 优化初始 List 大小(pr#2197@Github) -• 【core 】 Base32 增加 pad 支持(pr#2195@Github) -• 【core 】 Dict 增加 setFields 方法(pr#578@Gitee) -• 【db 】 新加 db.meta 的索引相关接口(pr#563@Gitee) -• 【db 】 Oracle 中 Column#typeName 后的长度去掉(pr#563@Gitee) -• 【poi 】 优化 ExcelReader,采用只读模式(pr#2204@Gitee) -• 【poi 】 优化 ExcelBase,将 alias 放入 -• 【poi 】 优化 ExcelBase,将 alias 放入 -• 【core 】 改进 StrUtil#startWith、endWith 性能 -• 【cron 】 增加 CronPatternParser、MatcherTable -• 【http 】 GlobalHeaders 增加系统属性 allowUnsafeServerCertChange、allowUnsafeRenegotiation -• 【http 】 UserAgentUtil 解析,增加 MiUI/XiaoMi 浏览器判断逻辑(pr#581@Gitee) -• 【core 】 FileAppender 添加锁构造(pr#2211@Github) -• 【poi 】 ExcelReader 增加构造(pr#2213@Github) -• 【core 】 MapUtil 提供 change 函数,EnumUtil 提供 getBy 函数,通过 lambda 进行枚举字段映射(pr#583@Gitee) -• 【core 】 CompareUtil 增加 comparingIndexed(pr#585@Gitee) -• 【db 】 DruidDataSource 构建时支持自定义参数(issue#I4ZKCW@Gitee) -• 【poi 】 ExcelWriter 增加 addImg 重载(issue#2218@Github) -• 【bloomFilter】 增加 FuncFilter -• 【http 】 增加 GlobalInterceptor(issue#2217) - -### 🐞Bug 修复 - -• 【core 】 修复 ObjectUtil.hasNull 传入 null 返回 true 的问题(pr#555@Gitee) -• 【core 】 修复 NumberConverter 对数字转换的问题(issue#I4WPF4@Gitee) -• 【core 】 修复 ReflectUtil.getMethods 获取接口方法问题(issue#I4WUWR@Gitee) -• 【core 】 修复 NamingCase 中大写转换问题(pr#572@Gitee) -• 【http 】 修复 GET 重定向时,携带参数问题(issue#2189@Github) -• 【core 】 修复 FileUtil、FileCopier 相对路径获取父路径错误问题(pr#2188@Github) -• 【core 】 修复 CopyOptions 中 fieldNameEditor 无效问题(issue#2202@Github) -• 【json 】 修复 JSON 对 Map.Entry 的解析问题 -• 【core 】 修复 MapConverter 中 map 与 map 转换兼容问题 -• 【poi 】 解决 sax 读取时,POI-5.2.x 兼容性问题 -• 【core 】 修复判断两段时间区间交集问题(pr#2210@Github) -• 【http 】 修复标签误删问题(issue#I4Z7BV@Gitee) -• 【core 】 修复 Win 下文件名带\*问题(pr#584@Gitee) -• 【core 】 FileUtil.getMimeType 增加 rar、7z 支持(issue#I4ZBN0@Gitee) -• 【json 】 JSON 修复 transient 设置无效问题(issue#2212@Github) -• 【core 】 修复 IterUtil.getElementType 获取结果为 null 的问题(issue#2222@Github) -• 【core 】 修复农历转公历在闰月时错误(issue#I4ZSGJ@Gitee) - -## 社区共建 - -我们秉承`和谐快乐`,`代码至上` 的原则,如果你有想法,愿意和我们一起成长,一起贡献,快来加入我们吧! - -- github:https://github.com/dromara/hutool -- gitee:https://gitee.com/dromara/hutool +--- +title: Hutool-5.8.0.M1 发布,尝试里程碑发布 +author: hutool +tag: + - hutool +date: 2022-03-30 +cover: /assets/img/architecture/hutool-framework.png +head: + - - meta + - name: 新闻 +--- + +> Hutool 是一个小而全的 Java 工具类库,提供优雅、高效和便捷的工具方法。 + +## Hutool 是什么 + +![架构全景图](/assets/img/architecture/hutool-framework.png) + +本来这个版本应该是 5.7.23 的,可惜用户提了一些 issue,这些问题的解决必须修改原有代码结构: + +1. 如 MongoDB 客户端封装,由于其驱动本身做了不兼容修改,包装的工具类不得不进行修改。 +2. 涉及到 Bean 拷贝的代码部分(BeanCopier),由于一个参数失效,以为只是简单的一个 bug,后来发现是整个设计有问题……崩溃程度可想而知,肝了两个晚上重构了这部分代码。 +3. 修改代码的同时才发现还有很多部分的设计有问题,顺便做了小重构。 +4. 为了解决每次大版本升级的可能带来的对老用户的影响,此次版本采用里程碑方式发布,版本为 M1(感觉给用户送了颗 CPU),也是解决 Hutool 每次“激进”升级的问题(毕竟年龄大了,要稳重) +5. 希望购买代替捐赠,如果你希望支持下 Hutool,可以去 Hutool 主页点->击进入周边商店购买 Hutool 周边来支持 Hutool 哦,这比捐赠实惠的多(毕竟捐赠者我不知道如何道谢,很有道德负担……) + +鸣谢一下此次版本一起讨论和一起解决大量 issue 的 Hutool 几位成员: +@阿超 @Cherryrum @Husky + +--- + +## 5.8.0.M1 + +### ❌ 不兼容特性 + +• 【db 】 【不向下兼容 】增加 MongoDB4.x 支持返回 MongoClient 变更(pr#568@Gitee) +• 【json 】 【可能兼容问题】修改 JSONObject 结构,继承自 MapWrapper +• 【core 】 【可能兼容问题】BeanCopier 重构,新建 XXXCopier,删除 XXXValueProvider +• 【core 】 【可能兼容问题】URLEncoder 废弃,URLEncoderUtil 使用 RFC3986 +• 【core 】 【可能兼容问题】Base32 分离编码和解码,以便减少数据加载,支持 Hex 模式 +• 【core 】 【可能兼容问题】Base58 分离编码和解码 +• 【core 】 【可能兼容问题】Base62 分离编码和解码,增加 inverted 模式支持 +• 【core 】 【兼容问题 】PunyCode 参数由 String 改为 Charsequence +• 【cron 】 【可能兼容问题】SimpleValueParser 改名为 AbsValueParser,改为 abstract +• 【poi 】 【可能兼容问题】ExcelUtil.getBigWriter 返回值改为 BigExcelWriter +• 【core 】 【可能兼容问题】Opt.ofEmptyAble 参数由 List 改为 Collection 子类(pr#580@Gitee) +• 【json 】 【可能兼容问题】JSON 转 Bean 时,使用 JSON 本身的相关设置,而非默认(issue#2212@Github) +• 【json 】 【可能兼容问题】JSONConfig 中 isOrder 废弃,默认全部有序 + +### 🐣 新特性 + +• 【http 】 HttpRequest.form 采用 TableMap 方式(issue#I4W427@Gitee) +• 【core 】 AnnotationUtil 增加 getAnnotationAlias 方法(pr#554@Gitee) +• 【core 】 FileUtil.extName 增加对 tar.gz 特殊处理(issue#I4W5FS@Gitee) +• 【crypto 】 增加 XXTEA 实现(issue#I4WH2X@Gitee) +• 【core 】 增加 Table 实现(issue#2179@Github) +• 【core 】 增加 UniqueKeySet(issue#I4WUWR@Gitee) +• 【core 】 阿拉伯数字转换成中文对发票票面金额转换的扩展(pr#570@Gitee) +• 【core 】 ArrayUtil 增加 replace 方法(pr#570@Gitee) +• 【core 】 CsvReadConfig 增加自定义标题行行号(issue#2180@Github) +• 【core 】 FileAppender 优化初始 List 大小(pr#2197@Github) +• 【core 】 Base32 增加 pad 支持(pr#2195@Github) +• 【core 】 Dict 增加 setFields 方法(pr#578@Gitee) +• 【db 】 新加 db.meta 的索引相关接口(pr#563@Gitee) +• 【db 】 Oracle 中 Column#typeName 后的长度去掉(pr#563@Gitee) +• 【poi 】 优化 ExcelReader,采用只读模式(pr#2204@Gitee) +• 【poi 】 优化 ExcelBase,将 alias 放入 +• 【poi 】 优化 ExcelBase,将 alias 放入 +• 【core 】 改进 StrUtil#startWith、endWith 性能 +• 【cron 】 增加 CronPatternParser、MatcherTable +• 【http 】 GlobalHeaders 增加系统属性 allowUnsafeServerCertChange、allowUnsafeRenegotiation +• 【http 】 UserAgentUtil 解析,增加 MiUI/XiaoMi 浏览器判断逻辑(pr#581@Gitee) +• 【core 】 FileAppender 添加锁构造(pr#2211@Github) +• 【poi 】 ExcelReader 增加构造(pr#2213@Github) +• 【core 】 MapUtil 提供 change 函数,EnumUtil 提供 getBy 函数,通过 lambda 进行枚举字段映射(pr#583@Gitee) +• 【core 】 CompareUtil 增加 comparingIndexed(pr#585@Gitee) +• 【db 】 DruidDataSource 构建时支持自定义参数(issue#I4ZKCW@Gitee) +• 【poi 】 ExcelWriter 增加 addImg 重载(issue#2218@Github) +• 【bloomFilter】 增加 FuncFilter +• 【http 】 增加 GlobalInterceptor(issue#2217) + +### 🐞Bug 修复 + +• 【core 】 修复 ObjectUtil.hasNull 传入 null 返回 true 的问题(pr#555@Gitee) +• 【core 】 修复 NumberConverter 对数字转换的问题(issue#I4WPF4@Gitee) +• 【core 】 修复 ReflectUtil.getMethods 获取接口方法问题(issue#I4WUWR@Gitee) +• 【core 】 修复 NamingCase 中大写转换问题(pr#572@Gitee) +• 【http 】 修复 GET 重定向时,携带参数问题(issue#2189@Github) +• 【core 】 修复 FileUtil、FileCopier 相对路径获取父路径错误问题(pr#2188@Github) +• 【core 】 修复 CopyOptions 中 fieldNameEditor 无效问题(issue#2202@Github) +• 【json 】 修复 JSON 对 Map.Entry 的解析问题 +• 【core 】 修复 MapConverter 中 map 与 map 转换兼容问题 +• 【poi 】 解决 sax 读取时,POI-5.2.x 兼容性问题 +• 【core 】 修复判断两段时间区间交集问题(pr#2210@Github) +• 【http 】 修复标签误删问题(issue#I4Z7BV@Gitee) +• 【core 】 修复 Win 下文件名带\*问题(pr#584@Gitee) +• 【core 】 FileUtil.getMimeType 增加 rar、7z 支持(issue#I4ZBN0@Gitee) +• 【json 】 JSON 修复 transient 设置无效问题(issue#2212@Github) +• 【core 】 修复 IterUtil.getElementType 获取结果为 null 的问题(issue#2222@Github) +• 【core 】 修复农历转公历在闰月时错误(issue#I4ZSGJ@Gitee) + +## 社区共建 + +我们秉承`和谐快乐`,`代码至上` 的原则,如果你有想法,愿意和我们一起成长,一起贡献,快来加入我们吧! + +- github:https://github.com/dromara/hutool +- gitee:https://gitee.com/dromara/hutool diff --git a/src/zh/news/mica-mqtt-2.4.0.md b/src/zh/news/mica-mqtt-2.4.0.md new file mode 100644 index 0000000000..91538ae70e --- /dev/null +++ b/src/zh/news/mica-mqtt-2.4.0.md @@ -0,0 +1,207 @@ +--- +title: Dromara mica-mqtt 2.4.0 发布 +author: mica-mqtt +date: 2024-12-19 +cover: /assets/img/news/mica-mqtt-2.4.0-0.png +head: + - - meta + - name: 新闻 +--- + +# Dromara mica-mqtt 2.4.0 发布,完善服务端接口,推荐升级 + +![](/assets/img/news/mica-mqtt-2.4.0-0.png) + +## 一、前言 + +Dromara mica-mqtt **2.4.0** 正式版已经发布,请注意从 `2.4.x` 版本开始将 maven groupId 迁移到了 `org.dromara.mica-mqtt`,包名切换到了 `org.dromara`,并且切换到 central sonatype(不支持快照版)其它使用上均和老版本保持一致。`2.4.x` 将会持续完善和丰富服务端功能,后续版本也会做一些 **大** 的调整和优化,当然我们会尽量 **减少接口变动** 方便大家升级。**强烈推荐** 大家尽快升级到新的 `2.4.x`。 + +另外由于老的文档散落在各个模块,不方便查看,我们新增了文档站:https://mica-mqtt.dromara.org + +## 二、功能 + +* 支持 MQTT v3.1、v3.1.1 以及 v5.0 协议。 + +* 支持 websocket mqtt 子协议(支持 mqtt.js)。 + +* 支持 http rest api,`http api 文档详见:` https://gitee.com/dromara/mica-mqtt/blob/master/docs/http-api.md + +* 支持 MQTT client 客户端。 + +* 支持 MQTT server 服务端。 + +* 支持 MQTT 遗嘱消息。 + +* 支持 MQTT 保留消息。 + +* 支持自定义消息(mq)处理转发实现集群。 + +* MQTT 客户端 阿里云 mqtt 连接 demo。 + +* 支持 GraalVM 编译成本机可执行程序。 + +* 支持 Spring boot、Solon 和 JFinal 项目快速接入。 + +* mica-mqtt-spring-boot-starter 支持对接 Prometheus + Grafana。 + +* 基于 redis Stream 实现集群,详见 `mica-mqtt-broker 模块:` https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-broker)。 + + +## 三、使用场景 + +* 物联网(云端 mqtt broker) + +* 物联网(边缘端消息通信) + +* 群组类 IM + +* 消息推送 + +* 简单、易用的 mqtt client 客户端 + + +## 四、更新记录 + +* ✨ http api 添加 `stats`、`clients` 列表和 `client详情` 接口。 + +* ✨ MqttServer 和 MqttServerTemplate 添加 `getClientInfo` `getClients` 系列客户端信息接口。 + +* ✨ MqttServer 和 MqttServerTemplate 添加 `getSubscriptions` 获取客户端订阅列表接口。 + +* ✨ MqttServer 和 MqttServerTemplate 添加 `getStat` 统计接口。 + +* 🚚 调整 maven groupId `net.dreamlu` 到新的 `org.dromara.mica-mqtt`。 + +* 🚚 调整包名 `net.dreamlu.iot.mqtt` 到新的 `org.dromara.mica.mqtt`,其他均保持不变。 + +* 🚚 切换到 central sonatype,central sonatype 不支持快照版,mica-mqtt 不再发布快照版。 + +* 🐛 修复订阅发送时机问题 gitee #IB72L6 感谢 `@江上烽` 反馈 + + +## 五、快速上手 + +### 5.1 Spring boot 项目 + +**客户端**: + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-client-spring-boot-starter +  ${mica-mqtt.version} + +``` + +**mica-mqtt-client-spring-boot-starter 使用文档**: https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-spring-boot-starter/README.md + +**服务端:** + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-server-spring-boot-starter +  ${mica-mqtt.version} + +``` + +**mica-mqtt-server-spring-boot-starter使用文档**: https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-spring-boot-starter/README.md + +### 5.2 solon 项目 + +**客户端**: + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-client-solon-plugin +  ${mica-mqtt.version} + +``` + +**mica-mqtt-client-solon-plugin 使用文档**: https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-solon-plugin/README.md + +**服务端**: + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-server-solon-plugin +  ${mica-mqtt.version} + +``` + +**mica-mqtt-server-solon-plugin 使用文档**: https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-solon-plugin/README.md + +### 5.3 JFinal 项目 + +**客户端**: + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-client-jfinal-plugin +  ${mica-mqtt.version} + +``` + +**mica-mqtt-client-jfinal-plugin 使用文档**: https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-client-jfinal-plugin/README.md + +**服务端**: + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-server-jfinal-plugin +  ${mica-mqtt.version} + +``` + +**mica-mqtt-server-jfinal-plugin 使用文档** https://gitee.com/dromara/mica-mqtt/tree/master/starter/mica-mqtt-server-jfinal-plugin/README.md + +### 5.4 其他 Java 项目 + +**客户端**: + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-client +  ${mica-mqtt.version} + +``` + +**mica-mqtt-client 使用文档**:https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-client/README.md + +**服务端**: + +``` + +  org.dromara.mica-mqtt +  mica-mqtt-server +  ${mica-mqtt.version} + +``` + +**mica-mqtt-server 使用文档**: https://gitee.com/dromara/mica-mqtt/tree/master/mica-mqtt-server/README.md + +## 六、开源地址 + +* Gitee:https://gitee.com/dromara/mica-mqtt + +* GitHub:https://github.com/dromara/mica-mqtt + +* GitCode:https://gitcode.com/dromara/mica-mqtt + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/mica-mqtt-2.4.0-1.png) \ No newline at end of file diff --git a/src/zh/news/sms4j-2.0.1.md b/src/zh/news/sms4j-2.0.1.md index 7f4e737652..930ca5cfa3 100644 --- a/src/zh/news/sms4j-2.0.1.md +++ b/src/zh/news/sms4j-2.0.1.md @@ -1,75 +1,75 @@ ---- -title: sms4j V2.0.1版本正式发布 -author: SMS-风 -tag: - - sms4j -date: 2023-04-26 -cover: /assets/img/news/sms4j-cover.png -head: - - - meta - - name: 新闻 ---- - -## sms4j V2.0.1 版本正式发布 - -该版本中包含了一些重要的更新和改进,以提高框架的性能、可靠性和用户体验。在这里,我们将详细介绍这些更新内容。 - -1. **数据库刷新和读取的方法进行了优化** - -为了提高系统的性能,我们对数据库刷新和读取的方法进行了优化。我们优化了查询和更新操作的算法和实现方式,从而提高了系统的响应速度和处理效率。现在,您可以更快速地读取和更新数据库中的数据,使您的业务处理更加高效。 - -2. **添加了一个用于刷新数据库配置的方法** - -我们添加了一个新的方法,用于刷新数据库配置。这个新方法可以让您更灵活地管理数据库配置,以满足您的不同业务需求。现在,您可以通过这个新方法来刷新和更新数据库配置,从而使您的业务运行更加稳定和高效。 - -3. **重构容联云国内短信实现,使用 Open Api 替代 SDK** - -我们对容联云国内短信实现进行了重构,使用了 Open API 替代了 SDK。这样做的好处是,您可以更快速地发送短信,从而提高系统的响应速度和处理效率。此外,Open API 还提供了更加稳定和可靠的短信发送服务,使您的业务处理更加可靠。 - -4. **重构阿里云国内短信实现,使用 Open Api 替代 SDK** - -我们对阿里云国内短信实现进行了重构,使用了 Open API 替代了 SDK。这样做的好处是,您可以更加稳定地发送短信,从而提高了系统的稳定性和用户体验。同时,Open API 还提供了更加灵活和丰富的功能,满足不同业务的需求。 - -5. **重构腾讯云国内短信实现,使用 Open Api 替代 SDK** - -我们对腾讯云国内短信实现进行了重构,使用了 Open API 替代了 SDK。这样做的好处是,您可以更快速地发送短信,提高了系统的响应速度和处理效率。同时,Open API 还提供了更多的功能和服务,使您的业务处理更加高效和便捷。 - -6. **抽取公共配置信息,支持 Builder 模式** - -我们抽取了公共配置信息,并支持 Builder 模式,以便您更灵活地管理系统配置。现在,您可以更加方便地管理和配置系统,以满足您不同业务的需求。同时,Builder 模式还提供了更加直观和灵活的配置方式,使您可以更快速地配置和管理系统。 - -7. **优化部分细节处理** - -在本次更新中,我们还对系统的部分细节进行了优化处理。我们修复了一些小 bug,以提高系统的稳定性和可靠性。同时,我们还改进了一些界面和操作方式,使系统更加易用和便捷。 - -8. **数据库名称不再强制要求分类配置** - -在之前的版本中,数据库的名称必须按照一定的规则进行分类配置。现在,我们放宽了这个要求,使您可以更加灵活地管理和配置数据库。这样做的好处是,您可以更加方便地进行数据库的管理和维护,从而提高系统的可靠性和稳定性。 - -9. **添加了自定义的配置形式,配置更加灵活** - -在本次更新中,我们还添加了一种新的配置方式,使您可以更加灵活地管理和配置系统。您可以根据自己的需求和业务场景,自定义配置内容和形式,以便更好地满足您的业务需求。 - -以下我们看一个简单的新的配置方式的示例: - -``` -//unisms短信差异化配置 - public void setConfig(){ - //以下空字符串仅为演示使用,实际项目可以通过各种途径获取相应的数据 - UniConfig uni = UniConfig.builder() - .accessKeyId("") - .templateId("") - .templateName("") - .signature("") - .build(); - SupplierFactory.setUniConfig(uni); - } -``` - -更多的功能和详细的文档,请查看官方文档! - -如果我们的项目对您产生了帮助,请为我们点上一颗 star - -官方网站 https://wind.kim - -gitee 仓库 https://gitee.com/dromara/sms4j +--- +title: sms4j V2.0.1版本正式发布 +author: SMS-风 +tag: + - sms4j +date: 2023-04-26 +cover: /assets/img/news/sms4j-cover.png +head: + - - meta + - name: 新闻 +--- + +## sms4j V2.0.1 版本正式发布 + +该版本中包含了一些重要的更新和改进,以提高框架的性能、可靠性和用户体验。在这里,我们将详细介绍这些更新内容。 + +1. **数据库刷新和读取的方法进行了优化** + +为了提高系统的性能,我们对数据库刷新和读取的方法进行了优化。我们优化了查询和更新操作的算法和实现方式,从而提高了系统的响应速度和处理效率。现在,您可以更快速地读取和更新数据库中的数据,使您的业务处理更加高效。 + +2. **添加了一个用于刷新数据库配置的方法** + +我们添加了一个新的方法,用于刷新数据库配置。这个新方法可以让您更灵活地管理数据库配置,以满足您的不同业务需求。现在,您可以通过这个新方法来刷新和更新数据库配置,从而使您的业务运行更加稳定和高效。 + +3. **重构容联云国内短信实现,使用 Open Api 替代 SDK** + +我们对容联云国内短信实现进行了重构,使用了 Open API 替代了 SDK。这样做的好处是,您可以更快速地发送短信,从而提高系统的响应速度和处理效率。此外,Open API 还提供了更加稳定和可靠的短信发送服务,使您的业务处理更加可靠。 + +4. **重构阿里云国内短信实现,使用 Open Api 替代 SDK** + +我们对阿里云国内短信实现进行了重构,使用了 Open API 替代了 SDK。这样做的好处是,您可以更加稳定地发送短信,从而提高了系统的稳定性和用户体验。同时,Open API 还提供了更加灵活和丰富的功能,满足不同业务的需求。 + +5. **重构腾讯云国内短信实现,使用 Open Api 替代 SDK** + +我们对腾讯云国内短信实现进行了重构,使用了 Open API 替代了 SDK。这样做的好处是,您可以更快速地发送短信,提高了系统的响应速度和处理效率。同时,Open API 还提供了更多的功能和服务,使您的业务处理更加高效和便捷。 + +6. **抽取公共配置信息,支持 Builder 模式** + +我们抽取了公共配置信息,并支持 Builder 模式,以便您更灵活地管理系统配置。现在,您可以更加方便地管理和配置系统,以满足您不同业务的需求。同时,Builder 模式还提供了更加直观和灵活的配置方式,使您可以更快速地配置和管理系统。 + +7. **优化部分细节处理** + +在本次更新中,我们还对系统的部分细节进行了优化处理。我们修复了一些小 bug,以提高系统的稳定性和可靠性。同时,我们还改进了一些界面和操作方式,使系统更加易用和便捷。 + +8. **数据库名称不再强制要求分类配置** + +在之前的版本中,数据库的名称必须按照一定的规则进行分类配置。现在,我们放宽了这个要求,使您可以更加灵活地管理和配置数据库。这样做的好处是,您可以更加方便地进行数据库的管理和维护,从而提高系统的可靠性和稳定性。 + +9. **添加了自定义的配置形式,配置更加灵活** + +在本次更新中,我们还添加了一种新的配置方式,使您可以更加灵活地管理和配置系统。您可以根据自己的需求和业务场景,自定义配置内容和形式,以便更好地满足您的业务需求。 + +以下我们看一个简单的新的配置方式的示例: + +``` +//unisms短信差异化配置 + public void setConfig(){ + //以下空字符串仅为演示使用,实际项目可以通过各种途径获取相应的数据 + UniConfig uni = UniConfig.builder() + .accessKeyId("") + .templateId("") + .templateName("") + .signature("") + .build(); + SupplierFactory.setUniConfig(uni); + } +``` + +更多的功能和详细的文档,请查看官方文档! + +如果我们的项目对您产生了帮助,请为我们点上一颗 star + +官方网站 https://wind.kim + +gitee 仓库 https://gitee.com/dromara/sms4j diff --git a/src/zh/news/sms4j-2.0.2.md b/src/zh/news/sms4j-2.0.2.md index 2ccbdcd07c..f5b488746c 100644 --- a/src/zh/news/sms4j-2.0.2.md +++ b/src/zh/news/sms4j-2.0.2.md @@ -1,53 +1,53 @@ ---- -title: sms4j 2.0.2版本发布 -author: SMS-风 -tag: - - sms4j -date: 2023-05-11 -cover: /assets/img/news/sms4j-cover.png -head: - - - meta - - name: 新闻 ---- - -## sms4j 2.0.2 版本发布 - -最新版本的 sms4j 仓库在 Gitee 上的 star 突破了 500,我们非常感谢社区用户对该项目的支持和认可。 - -之前有人给我们提出引入的 SDK 和依赖过多,导致项目有些重量化了,我们现在正在逐步的进行优化 - -以下是 sms4j 项目的一些重要更新: - -## 项目结构优化: - -我们对项目结构进行了优化,让它更加轻量级,易于使用和维护。 - -## 去除腾讯 SDK 依赖: - -我们已经去除了腾讯 SDK 的依赖,这使得项目更加轻量级,并提高了整体性能。 - -## 去除 uni-sms 的 SDK 依赖: - -我们还去除了 uni-sms 的 SDK 依赖,这将使得 sms4j 更加灵活和可扩展。 - -## 修复自定义配置抛出异常问题: - -我们已经修复了自定义配置抛出异常的问题,确保 sms4j 的可靠性。 - -## 优化腾讯云短信实现: - -我们对腾讯云短信实现进行了优化,提高了其稳定性和性能。 - -## 修复 SQL 配置问题: - -我们修复了 SQL 配置下概率性无法获取数据库链接问题,同时不再强制要求数据库名分开配置。 - -## 已知问题: - -我们注意到阿里云短信因时序与阿里云服务器时序无法完全一致,在调用时会偶发性出现签名校验失败的问题,我们正在解决这个问题。 - -以上是我们最新版本 sms4j 的更新日志,希望这些改进能够提高 sms4j 的稳定性和性能,并为您的开发工作带来便利。 - -如果您觉得我们的项目为您带来了帮助,请为我们点上一颗 star,您的支持是我们前进的最大动力! - -如果您有时间并且对开源感兴趣,也欢迎您加入到我们之中! +--- +title: sms4j 2.0.2版本发布 +author: SMS-风 +tag: + - sms4j +date: 2023-05-11 +cover: /assets/img/news/sms4j-cover.png +head: + - - meta + - name: 新闻 +--- + +## sms4j 2.0.2 版本发布 + +最新版本的 sms4j 仓库在 Gitee 上的 star 突破了 500,我们非常感谢社区用户对该项目的支持和认可。 + +之前有人给我们提出引入的 SDK 和依赖过多,导致项目有些重量化了,我们现在正在逐步的进行优化 + +以下是 sms4j 项目的一些重要更新: + +## 项目结构优化: + +我们对项目结构进行了优化,让它更加轻量级,易于使用和维护。 + +## 去除腾讯 SDK 依赖: + +我们已经去除了腾讯 SDK 的依赖,这使得项目更加轻量级,并提高了整体性能。 + +## 去除 uni-sms 的 SDK 依赖: + +我们还去除了 uni-sms 的 SDK 依赖,这将使得 sms4j 更加灵活和可扩展。 + +## 修复自定义配置抛出异常问题: + +我们已经修复了自定义配置抛出异常的问题,确保 sms4j 的可靠性。 + +## 优化腾讯云短信实现: + +我们对腾讯云短信实现进行了优化,提高了其稳定性和性能。 + +## 修复 SQL 配置问题: + +我们修复了 SQL 配置下概率性无法获取数据库链接问题,同时不再强制要求数据库名分开配置。 + +## 已知问题: + +我们注意到阿里云短信因时序与阿里云服务器时序无法完全一致,在调用时会偶发性出现签名校验失败的问题,我们正在解决这个问题。 + +以上是我们最新版本 sms4j 的更新日志,希望这些改进能够提高 sms4j 的稳定性和性能,并为您的开发工作带来便利。 + +如果您觉得我们的项目为您带来了帮助,请为我们点上一颗 star,您的支持是我们前进的最大动力! + +如果您有时间并且对开源感兴趣,也欢迎您加入到我们之中! diff --git a/src/zh/news/sms4j-2.0.md b/src/zh/news/sms4j-2.0.md index fc8369dfa1..1a7e5f6cbf 100644 --- a/src/zh/news/sms4j-2.0.md +++ b/src/zh/news/sms4j-2.0.md @@ -1,122 +1,122 @@ ---- -title: sms4j 2.0 全新来袭 -author: SMS-风 -tag: - - sms4j -date: 2023-04-17 -cover: /assets/img/news/sms4j-cover.png -head: - - - meta - - name: 新闻 ---- - -## sms4j 2.0 全新来袭 - -即 sms-aggregation 成功加入 dromara 之后,很多人向我们反应了项目名称太长不好记,也太绕口, 在经过了有奖名称征集之后,我们定名为 sms4j 并伴随着大版本更新一同发布。这次更新不仅仅改变了项目名称,启用了新的 logo,还调整了项目结构,加入了更多新特性,下面我们一起来简单的看一下吧! - -![](/assets/img/news/sms4j-cover.png) - -## 首先是 maven 的变化 - -老版本中,groupId 为作者的个人网站,同时也是项目的官网,在新的版本中统一改为了 Dromara 的地址 - -``` - - org.dromara.sms4j - sms4j-spring-boot-starter - 最新版本请查看gitee或官网 - -``` - -## 其次是功能的调整 - -在 1.X 版本中,(前名称 sms-aggregatio)只支持单家运营商的使用,不能同时使用多家厂商,对于厂商的切换也只能依靠配置文件的改变,可以说是功能及其单一,但是在新版本中,添加了多厂商支持和并用,同时配置方式也发生了变化 - -``` -sms: - alibaba: - #阿里云的accessKey - accessKeyId: 您的accessKey - #阿里云的accessKeySecret - accessKeySecret: 您的accessKeySecret - #短信签名 - signature: 测试签名 - #模板ID 用于发送固定模板短信使用 - templateId: SMS_215125134 - #模板变量 上述模板的变量 - templateName: code - #请求地址 默认为dysmsapi.aliyuncs.com 如无特殊改变可以不用设置 - requestUrl: dysmsapi.aliyuncs.com - huawei: - #华为短信appKey - appKey: 5N6fvXXXX920HaWhVXXXXXX7fYa - #华为短信appSecret - app-secret: Wujt7EYzZTBXXXXXXEhSP6XXXX - #短信签名 - signature: 华为短信测试 - #通道号 - sender: 8823040504797 - #模板ID 如果使用自定义模板发送方法可不设定 - template-id: acXXXXXXXXc274b2a8263479b954c1ab5 - #华为回调地址,如不需要可不设置或为空 - statusCallBack: - #华为分配的app请求地址 - url: https://XXXXX.cn-north-4.XXXXXXXX.com:443 -``` - -``` -@RestController -@RequestMapping("/test/") -public class DemoController { - - // 测试发送固定模板短信 - @RequestMapping("/") - public void doLogin(String username, String password) { - //阿里云向此手机号发送短信 - SmsFactory.createSmsBlend(SupplierType.ALIBABA).sendMessage("18888888888","123456"); - //华为短信向此手机号发送短信 - SmsFactory.createSmsBlend(SupplierType.HUAWEI).sendMessage("16666666666","000000"); - } -} -``` - -在以上仅仅只是示例,在实际的实用中可以同时支持更多的厂商。 - -## 其他的方面 - -sms4j 还添加了数据库配置的支持,开发者可以通过在数据库添加配置来替代配置文件,做到动态切换厂商 - -``` -sms: - # 告诉框架要读取的厂商配置来源,此处为枚举形式 - config-type: sql_config - sql: - # JDBC驱动 - driver-class-name: com.mysql.cj.jdbc.Driver - # 要链接的数据库名称 - database-name: dev - # 连接字符串 - url: jdbc:mysql://localhost:3306 - # 数据库账号 - username: root - # 数据库密码 - password: 123456 - # 配置所在表名 - table-name: config_info - # 厂商名称存储字段 - supplier-field-name: user_name - # 配置所在字段 - config-name: pay_psw - # 配置启用标识字段 - start-name: state - # 配置启用值 此处意思为 当字段state值为1时,则启用这个配置 - is-start: 1 -``` - -更多的新特性和功能不在这里一 一介绍了,具体的可以查看官方文档或者 gitee 仓库 - -**官方文档** **https://wind.kim/** - -**gitee 仓库** **https://gitee.com/dromara/sms4j** - -**您的支持是我们前进的动力,如果我们的项目对您产生了帮助或者您觉得还不错,请为我们点上一颗 star** +--- +title: sms4j 2.0 全新来袭 +author: SMS-风 +tag: + - sms4j +date: 2023-04-17 +cover: /assets/img/news/sms4j-cover.png +head: + - - meta + - name: 新闻 +--- + +## sms4j 2.0 全新来袭 + +即 sms-aggregation 成功加入 dromara 之后,很多人向我们反应了项目名称太长不好记,也太绕口, 在经过了有奖名称征集之后,我们定名为 sms4j 并伴随着大版本更新一同发布。这次更新不仅仅改变了项目名称,启用了新的 logo,还调整了项目结构,加入了更多新特性,下面我们一起来简单的看一下吧! + +![](/assets/img/news/sms4j-cover.png) + +## 首先是 maven 的变化 + +老版本中,groupId 为作者的个人网站,同时也是项目的官网,在新的版本中统一改为了 Dromara 的地址 + +``` + + org.dromara.sms4j + sms4j-spring-boot-starter + 最新版本请查看gitee或官网 + +``` + +## 其次是功能的调整 + +在 1.X 版本中,(前名称 sms-aggregatio)只支持单家运营商的使用,不能同时使用多家厂商,对于厂商的切换也只能依靠配置文件的改变,可以说是功能及其单一,但是在新版本中,添加了多厂商支持和并用,同时配置方式也发生了变化 + +``` +sms: + alibaba: + #阿里云的accessKey + accessKeyId: 您的accessKey + #阿里云的accessKeySecret + accessKeySecret: 您的accessKeySecret + #短信签名 + signature: 测试签名 + #模板ID 用于发送固定模板短信使用 + templateId: SMS_215125134 + #模板变量 上述模板的变量 + templateName: code + #请求地址 默认为dysmsapi.aliyuncs.com 如无特殊改变可以不用设置 + requestUrl: dysmsapi.aliyuncs.com + huawei: + #华为短信appKey + appKey: 5N6fvXXXX920HaWhVXXXXXX7fYa + #华为短信appSecret + app-secret: Wujt7EYzZTBXXXXXXEhSP6XXXX + #短信签名 + signature: 华为短信测试 + #通道号 + sender: 8823040504797 + #模板ID 如果使用自定义模板发送方法可不设定 + template-id: acXXXXXXXXc274b2a8263479b954c1ab5 + #华为回调地址,如不需要可不设置或为空 + statusCallBack: + #华为分配的app请求地址 + url: https://XXXXX.cn-north-4.XXXXXXXX.com:443 +``` + +``` +@RestController +@RequestMapping("/test/") +public class DemoController { + + // 测试发送固定模板短信 + @RequestMapping("/") + public void doLogin(String username, String password) { + //阿里云向此手机号发送短信 + SmsFactory.createSmsBlend(SupplierType.ALIBABA).sendMessage("18888888888","123456"); + //华为短信向此手机号发送短信 + SmsFactory.createSmsBlend(SupplierType.HUAWEI).sendMessage("16666666666","000000"); + } +} +``` + +在以上仅仅只是示例,在实际的实用中可以同时支持更多的厂商。 + +## 其他的方面 + +sms4j 还添加了数据库配置的支持,开发者可以通过在数据库添加配置来替代配置文件,做到动态切换厂商 + +``` +sms: + # 告诉框架要读取的厂商配置来源,此处为枚举形式 + config-type: sql_config + sql: + # JDBC驱动 + driver-class-name: com.mysql.cj.jdbc.Driver + # 要链接的数据库名称 + database-name: dev + # 连接字符串 + url: jdbc:mysql://localhost:3306 + # 数据库账号 + username: root + # 数据库密码 + password: 123456 + # 配置所在表名 + table-name: config_info + # 厂商名称存储字段 + supplier-field-name: user_name + # 配置所在字段 + config-name: pay_psw + # 配置启用标识字段 + start-name: state + # 配置启用值 此处意思为 当字段state值为1时,则启用这个配置 + is-start: 1 +``` + +更多的新特性和功能不在这里一 一介绍了,具体的可以查看官方文档或者 gitee 仓库 + +**官方文档** **https://wind.kim/** + +**gitee 仓库** **https://gitee.com/dromara/sms4j** + +**您的支持是我们前进的动力,如果我们的项目对您产生了帮助或者您觉得还不错,请为我们点上一颗 star** diff --git a/src/zh/news/sms4j-2.1.0.md b/src/zh/news/sms4j-2.1.0.md index a4326e5403..6e1fa9fdd8 100644 --- a/src/zh/news/sms4j-2.1.0.md +++ b/src/zh/news/sms4j-2.1.0.md @@ -1,60 +1,60 @@ ---- -title: SMS4J 2.1.0版本正式发布! -author: SMS-风 -tag: - - sms4j -date: 2023-05-29 -cover: /assets/img/news/sms4j-cover.png -head: - - - meta - - name: 新闻 ---- - -📣 **爷爷!你关注的短信框架终于更新啦!** - -## 🎊SMS4J  2.1.0 版本正式发布!🎊 - -本次更新带来了诸多的新特性,也修复了先前版本中的诸多问题,接下来让我们一起来看一下本次更新的内容吧! - -## 🎉 新特性 🎉 - -1. #### 新增天翼云短信支持 - - V 2.1.0 版本再添新支持厂商,天翼云短信,在新版本中,小伙伴们可以使用天翼云短信啦 - -2. #### 添加 solon 框架支持 - - solon 是一个非常优秀的国产框架,作者也是一个很强大的男人(单挑 spring 的男人),目前 solon 框架已经得到了多家军工企业的青睐,没准未来会与 spring 平分天下哦!在这里也感谢 solon 的作者对于我们的支持。 - -3. #### SupplierFactory 添加一个通用的 set 方法 - - 对于想要传值来构建不同实现类型的小伙伴来说,每个厂商一个 set 方法用起来不太方便,这里我们整合了一个完整的 set 方法,可以实现所有厂商的 set - -4. #### 添加负载均衡工具 - - 在本次的版本中添加了一个实验性的新特性,短信负载均衡,当你有多个厂商同时存在,并且不关注本次短信使用哪个厂商发送时,可以使用该工具,工具采用平滑加权负载均衡算法实现,可以根据设置的权重,自动选择托管中的短信服务 - -5. #### 添加了一个静态方法用于获取 LinkedHashMap 实例 - - 很多人并不常用 LinkedHashMap,为了方便使用和记忆,我们添加了用于获取它的实例,位于 - `org.dromara.sms4j.comm.utils.SmsUtil`类中的`getNewMap()` - -## 🔧 修复 🔧 - -1. 修复云片短信未能如期创建实现对象的问题 - 在先前的版本中我们发现,云片短信并未能如期的创建一个完整的实现,我们在这个版本中对其进行了修复 -2. 修复亿美短信返回值异常 - 在 2.0.2 版本中,亿美短信的返回值在某些情况下存在异常(比如请求的 url 错误时),在这个版本中我们对此进行了修复 -3. 修复阿里云短信返回值异常 - 在先前的版本中,阿里云短信的返回值未能如期的通过 SmsResponse 对象返回,而是打印在了 log 中 -4. 先前版本中短信拦截未生效 - -短信限制功能的拦截一直过度依赖于 Spring 的 AOP 实现,一旦开启就是全局的拦截,无法做到精准的厂商级别拦截,在 2.1.0 版本中我们对于短信限制功能进行了重构,使其能够精准的分厂商进行短信拦截 - -## 📀 优化 📀 - -- 优化了厂商短信的实现 -- 优化 javax 加密包使用 -- 部分短信发送的返回值添加了非空判断 -- 优化阿里云实现的部分代码 -- 优化 SmsFactory 内部实现,获取更好的性能和拓展性 +--- +title: SMS4J 2.1.0版本正式发布! +author: SMS-风 +tag: + - sms4j +date: 2023-05-29 +cover: /assets/img/news/sms4j-cover.png +head: + - - meta + - name: 新闻 +--- + +📣 **爷爷!你关注的短信框架终于更新啦!** + +## 🎊SMS4J  2.1.0 版本正式发布!🎊 + +本次更新带来了诸多的新特性,也修复了先前版本中的诸多问题,接下来让我们一起来看一下本次更新的内容吧! + +## 🎉 新特性 🎉 + +1. #### 新增天翼云短信支持 + + V 2.1.0 版本再添新支持厂商,天翼云短信,在新版本中,小伙伴们可以使用天翼云短信啦 + +2. #### 添加 solon 框架支持 + + solon 是一个非常优秀的国产框架,作者也是一个很强大的男人(单挑 spring 的男人),目前 solon 框架已经得到了多家军工企业的青睐,没准未来会与 spring 平分天下哦!在这里也感谢 solon 的作者对于我们的支持。 + +3. #### SupplierFactory 添加一个通用的 set 方法 + + 对于想要传值来构建不同实现类型的小伙伴来说,每个厂商一个 set 方法用起来不太方便,这里我们整合了一个完整的 set 方法,可以实现所有厂商的 set + +4. #### 添加负载均衡工具 + + 在本次的版本中添加了一个实验性的新特性,短信负载均衡,当你有多个厂商同时存在,并且不关注本次短信使用哪个厂商发送时,可以使用该工具,工具采用平滑加权负载均衡算法实现,可以根据设置的权重,自动选择托管中的短信服务 + +5. #### 添加了一个静态方法用于获取 LinkedHashMap 实例 + + 很多人并不常用 LinkedHashMap,为了方便使用和记忆,我们添加了用于获取它的实例,位于 + `org.dromara.sms4j.comm.utils.SmsUtil`类中的`getNewMap()` + +## 🔧 修复 🔧 + +1. 修复云片短信未能如期创建实现对象的问题 + 在先前的版本中我们发现,云片短信并未能如期的创建一个完整的实现,我们在这个版本中对其进行了修复 +2. 修复亿美短信返回值异常 + 在 2.0.2 版本中,亿美短信的返回值在某些情况下存在异常(比如请求的 url 错误时),在这个版本中我们对此进行了修复 +3. 修复阿里云短信返回值异常 + 在先前的版本中,阿里云短信的返回值未能如期的通过 SmsResponse 对象返回,而是打印在了 log 中 +4. 先前版本中短信拦截未生效 + +短信限制功能的拦截一直过度依赖于 Spring 的 AOP 实现,一旦开启就是全局的拦截,无法做到精准的厂商级别拦截,在 2.1.0 版本中我们对于短信限制功能进行了重构,使其能够精准的分厂商进行短信拦截 + +## 📀 优化 📀 + +- 优化了厂商短信的实现 +- 优化 javax 加密包使用 +- 部分短信发送的返回值添加了非空判断 +- 优化阿里云实现的部分代码 +- 优化 SmsFactory 内部实现,获取更好的性能和拓展性 diff --git a/src/zh/news/sms4j-2.2.0.md b/src/zh/news/sms4j-2.2.0.md index 51cf8fa4c6..f78ae94ec5 100644 --- a/src/zh/news/sms4j-2.2.0.md +++ b/src/zh/news/sms4j-2.2.0.md @@ -1,132 +1,132 @@ ---- -title: 这年头,坐上火箭的不光神州,还有sms4j的版本 -author: sms4j -tag: - - sms4j -date: 2023-06-21 -cover: /assets/img/news/sms4j-cover.png -head: - - - meta - - name: 新闻 ---- - -## sms4j 2.2.0 版本正式发布 - -在拖延了 NNNNNN 多久之后,sms4j 的 2.2.0 版本终于发布了!不过嘛,作为一个有良心的作者,怎么能让大家白等呢! - -这次又是给大家带来了诸多的干货! - -### 新特性 - -1. JAVA SE 适配正式支持 -2. 网易云短信接入 -3. redis 支持接口化处理 -4. 插件能力发布 - - ### BUG 修复 - -5. 阿里云短信发送会不定期报错 -6. 数据库配置下不填写数据库名称会报错的问题 -7. 去除多余的注解,该注解曾导致项目的 spring 线程池失效 -8. 修复腾讯云 json 解析问题 -9. 修复 sql 配置方式 json 解析问题 - -### 优化 - -返回信息优化 - -### JAVA SE 适配 - -在发部了这么多版本之后,承诺过的 java se 适配终于添加了,现在 java se 项目可以直接进行使用了 - -##### maven 依赖 - -``` - - org.dromara.sms4j - sms4j-javase-plugin - version - -``` - -##### 配置文件 - -``` -sms: - alibaba: - access-key-id: 您的accessKey - access-key-secret: 您的accessKeySecret - template-id: 您的templateId - template-name: 您的templateName - signature: 您的短信签名 -# 其他配置…… -``` - -##### 读取配置 - -``` -SEInitializer.initializer().fromYaml(); -``` - -##### 发送短信 - -``` -SmsFactory.createSmsBlend(SupplierType.ALIBABA).sendMessage("手机号码", "短信"); -``` - -原生 SE 使用支持多种的配置,其他的使用方法请参考 官方文档 - -### 网易云短信接入 - -在仓库的 issues 中,我们收到了很多的厂商接入建议,我们也在逐步的从中选取厂商进行接入(会优先接入新用户量相对较大的),本次网易云短信来自贡献者 \*\*阿丢丢 \*\* ,在这里感谢大家对于我们的支持,也欢迎大家参与到我们之中 - -### redis 支持接口化处理 - -sms4j 自带的短信拦截功能中,依赖于缓存,虽说内部实现了一个缓存,但是没有持久化的能力,略显不足,故而用户可以自己选择使用 redis 作为缓存,在先前的版本中默认的适用了 springboot 集成的 redis 进行连接,但是部分用户反馈他们并没有使用这个。所以,我们拓展了 redis 的能力,你可以自己实现一个接口,然后替代内部的 redis 实现,从而使用自己的方式进行缓存。 - -使用方式: - -实现位于`org.dromara.sms4j.api.universal`的接口`RedisUtil`,并实现两个方法 - -设置带有缓存时间的 key,三个参数分别为  redis key   redis value   过期时间 time - -过期时间的单位为秒 - -`boolean setOrTime(String key, Object value, Long time)` - -获取 key - -`Object getByKey(String key)` - -当用户实现了该接口,并启用了 redis 作为 sms4j 拦截缓存后,框架内部的缓存实现将被替代,从而使用用户自己的 redis - -### 插件能力发布 - -曾经有一吨人(体重加起来不少于 1 吨)问过我,sms4j 是否会支持其他的通知,比如企业微信,钉钉,飞书,邮件等等。其实刚开始并没有这方面的规划………… - -但是既然这么多人都问了,肯定是需求不少了 -本着 为天地立心,为生民立命,为往圣继绝学,为万世开太平 的伟大理想!(就是为了 star 和赞助) -现在开始逐步接入,sms4j 项目本体主旨不变,其他只作为额外的能力,通过额外 maven 依赖进行引入使用。 -当然,贡献组成员的时间精力都很有限的,如果你有时间或者有兴趣的话可以联系我或者直接提交 pr ,参与 到我们之中,原则上我们欢迎任何贡献者参与到我们! -至于我为啥不单独立仓库,别问,问就是懒! -重新立仓库我需要重新搞个子域名,重新建立个仓库,重新攒 star,重新起名,重新设计 logo……………… -拉个摊子太烦了,所以干脆都放这了,就当是 sms4j 的插件吧 - -#### 现有插件 - -- sms4j-mail 邮件发送插件 -- 敬请期待………… - -通知类是一个大类别,不是一朝一夕可完善完成的事情,我们需要做的还有很多。路漫漫其修远兮 - -我们真心的邀请大家参与到我们之中,跟我们一起成长,一起为往圣继绝学! - -## 结语 - -最后还请大家给个 star 支持一下,无论是 gitee 或者是 github,我们都将十分感谢 - -gitee  https://gitee.com/dromara/sms4j - -github  https://github.com/dromara/sms4j - -官方文档   https://wind.kim +--- +title: 这年头,坐上火箭的不光神州,还有sms4j的版本 +author: sms4j +tag: + - sms4j +date: 2023-06-21 +cover: /assets/img/news/sms4j-cover.png +head: + - - meta + - name: 新闻 +--- + +## sms4j 2.2.0 版本正式发布 + +在拖延了 NNNNNN 多久之后,sms4j 的 2.2.0 版本终于发布了!不过嘛,作为一个有良心的作者,怎么能让大家白等呢! + +这次又是给大家带来了诸多的干货! + +### 新特性 + +1. JAVA SE 适配正式支持 +2. 网易云短信接入 +3. redis 支持接口化处理 +4. 插件能力发布 + + ### BUG 修复 + +5. 阿里云短信发送会不定期报错 +6. 数据库配置下不填写数据库名称会报错的问题 +7. 去除多余的注解,该注解曾导致项目的 spring 线程池失效 +8. 修复腾讯云 json 解析问题 +9. 修复 sql 配置方式 json 解析问题 + +### 优化 + +返回信息优化 + +### JAVA SE 适配 + +在发部了这么多版本之后,承诺过的 java se 适配终于添加了,现在 java se 项目可以直接进行使用了 + +##### maven 依赖 + +``` + + org.dromara.sms4j + sms4j-javase-plugin + version + +``` + +##### 配置文件 + +``` +sms: + alibaba: + access-key-id: 您的accessKey + access-key-secret: 您的accessKeySecret + template-id: 您的templateId + template-name: 您的templateName + signature: 您的短信签名 +# 其他配置…… +``` + +##### 读取配置 + +``` +SEInitializer.initializer().fromYaml(); +``` + +##### 发送短信 + +``` +SmsFactory.createSmsBlend(SupplierType.ALIBABA).sendMessage("手机号码", "短信"); +``` + +原生 SE 使用支持多种的配置,其他的使用方法请参考 官方文档 + +### 网易云短信接入 + +在仓库的 issues 中,我们收到了很多的厂商接入建议,我们也在逐步的从中选取厂商进行接入(会优先接入新用户量相对较大的),本次网易云短信来自贡献者 \*\*阿丢丢 \*\* ,在这里感谢大家对于我们的支持,也欢迎大家参与到我们之中 + +### redis 支持接口化处理 + +sms4j 自带的短信拦截功能中,依赖于缓存,虽说内部实现了一个缓存,但是没有持久化的能力,略显不足,故而用户可以自己选择使用 redis 作为缓存,在先前的版本中默认的适用了 springboot 集成的 redis 进行连接,但是部分用户反馈他们并没有使用这个。所以,我们拓展了 redis 的能力,你可以自己实现一个接口,然后替代内部的 redis 实现,从而使用自己的方式进行缓存。 + +使用方式: + +实现位于`org.dromara.sms4j.api.universal`的接口`RedisUtil`,并实现两个方法 + +设置带有缓存时间的 key,三个参数分别为  redis key   redis value   过期时间 time + +过期时间的单位为秒 + +`boolean setOrTime(String key, Object value, Long time)` + +获取 key + +`Object getByKey(String key)` + +当用户实现了该接口,并启用了 redis 作为 sms4j 拦截缓存后,框架内部的缓存实现将被替代,从而使用用户自己的 redis + +### 插件能力发布 + +曾经有一吨人(体重加起来不少于 1 吨)问过我,sms4j 是否会支持其他的通知,比如企业微信,钉钉,飞书,邮件等等。其实刚开始并没有这方面的规划………… + +但是既然这么多人都问了,肯定是需求不少了 +本着 为天地立心,为生民立命,为往圣继绝学,为万世开太平 的伟大理想!(就是为了 star 和赞助) +现在开始逐步接入,sms4j 项目本体主旨不变,其他只作为额外的能力,通过额外 maven 依赖进行引入使用。 +当然,贡献组成员的时间精力都很有限的,如果你有时间或者有兴趣的话可以联系我或者直接提交 pr ,参与 到我们之中,原则上我们欢迎任何贡献者参与到我们! +至于我为啥不单独立仓库,别问,问就是懒! +重新立仓库我需要重新搞个子域名,重新建立个仓库,重新攒 star,重新起名,重新设计 logo……………… +拉个摊子太烦了,所以干脆都放这了,就当是 sms4j 的插件吧 + +#### 现有插件 + +- sms4j-mail 邮件发送插件 +- 敬请期待………… + +通知类是一个大类别,不是一朝一夕可完善完成的事情,我们需要做的还有很多。路漫漫其修远兮 + +我们真心的邀请大家参与到我们之中,跟我们一起成长,一起为往圣继绝学! + +## 结语 + +最后还请大家给个 star 支持一下,无论是 gitee 或者是 github,我们都将十分感谢 + +gitee  https://gitee.com/dromara/sms4j + +github  https://github.com/dromara/sms4j + +官方文档   https://wind.kim diff --git a/src/zh/news/sms4j-3.0.0.md b/src/zh/news/sms4j-3.0.0.md index aefca840ce..ada0c61bd9 100644 --- a/src/zh/news/sms4j-3.0.0.md +++ b/src/zh/news/sms4j-3.0.0.md @@ -1,147 +1,147 @@ ---- -title: sms4j 3.0.0版本震撼发布 短信重试,多方共用,负载均衡正式来袭 -author: sms4j -tag: - - sms4j -date: 2023-09-18 -cover: /assets/img/news/sms4j-cover.png -head: - - - meta - - name: 新闻 ---- - -## 🎉sms4j 3.0.0 版本震撼发布 短信重试,多方共用,负载均衡正式来袭 🎉 - -在历经了数月的等待之后(主要作者在摸鱼,写得慢。。。。)sms4j 的 3.0 版本终于正式发布,本次版本为大版本更新,与 2.x 不兼容,但是本次更新是一个长期支持版本,并且带来了诸多的新特性,接下来我们一起来看看吧 - -## 🚀 新特性 - -- 支持短信失败重试,可以自定义重试次数 -- 支持单厂商多配置使用 -- 支持接口类配置 -- 支持自定义缓存来源 -- 支持默认负载均衡形式,权重可自定义配置 -- 支持邮件发送 -- 支持邮箱监听 -- 支持邮件发送失败自动重试 -- 支持短信拦截 - -## 🏇 使用方式变更 - -在 sms4j3.0 版本中,使用方式与先前的 2.X 有所不同,使用上不再依托 2.X 中的枚举,而是改为更加自由的自定义 key 标识 - -``` - SmsBlend smsBlend = SmsFactory.getSmsBlend("在配置中定义的configId"); - SmsResponse smsResponse = smsBlend.sendMessage("18888888888","123"); -``` - -## 🔧 配置方式变更 - -在 3.0 版本中,去除了先前中的 JDBC 配置等形式,改为更加自由的接口类配置,同时保留了 yml 配置,项目更加轻量,使用更加自由方便 - -#### yml 配置 - -``` -sms: - # 标注从yml读取配置 - config-type: yaml - blends: - # 自定义的标识,也就是configId这里可以是任意值(最好不要是中文) - tx1: - #厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 - supplier: tencent - #您的accessKey - access-key-id: 您的accessKey - #您的accessKeySecret - access-key-secret: 您的accessKeySecret - #您的短信签名 - signature: 您的短信签名 - #模板ID 非必须配置,如果使用sendMessage的快速发送需此配置 - template-id: xxxxxxxx - #您的sdkAppId - sdk-app-id: 您的sdkAppId - # 自定义的标识,也就是configId这里可以是任意值(最好不要是中文) -``` - -#### 接口类配置 - -``` -@Component -public class ReadConfig implements SmsReadConfig { - @Override - public BaseConfig getSupplierConfig(String configId) { - UniConfig uniConfig = new UniConfig(); - //此处仅为示例,实际环境中,数据可以来自任意位置, - return uniConfig; - } - - @Override - public List getSupplierConfigList() { - //此处仅为示例,实际环境中,数据可以来自任意位置, - return null; - } -} - -@Component -public class Demo { - @Autowired - ReadConfig config; - - @PostConstruct - public void init(){ - // 创建SmsBlend 短信实例 - SmsFactory.createSmsBlend(config,"在配置中定义的configId"); - } -} -``` - -## 📧 邮件插件 - -邮件插件在这次的更新中也得到了很大的优化,现在的邮件插件使用起来更加简单方便,同时可以覆盖多种场景,并且支持自定义 html 模板发送邮件 - -支持包括: - -- html 模板邮件 -- 携带多个附件的邮件 -- 自动打包成 zip 的邮件 -- 标准的文字邮件 -- 抄送人 -- 密送人 -- 失败自动重试 - -``` -//以下仅做演示,实际使用需要填入数据 -MailSmtpConfig config = MailSmtpConfig.builder.build; - -//这里的key可以是任何可对比类型,用于后续从工厂取出邮件实现类用 -MailFactory.put("qq",config) - -MailClient mail = MailFactory.createMailClient("qq"); - -mail.send(message); -``` - -邮件插件去除了先前版本中过多的重载方法,统一改为 send 方法进行发送,通过`MailMessage`构建不同的邮件 - -## 📩 邮箱监听 - -邮箱监听可以监听到某个 IMAP 协议邮箱中收到的邮件,并对其进行一定的处理。 - -``` -MonitorFactory.put("自定义的标识",MailImapConfig配置,Monitor回调对象) -MonitorFactory.start("put中自定义的标识") -``` - -邮箱监听开启后可以监听指定配置下的邮箱,并将收到的邮件进行自定义的处理 - -## 🔨 问题修复 - -本次版本中,还修复了 2.X 中存在的一些问题 - -- 华为云短信在群发时签名错误 -- 腾讯云短信在某些情况下导致的发送失败 -- unisms 的返回值存在异常 -- 阿里云短信在某些极端情况下会出现签名错误 -- springUtil 与其他框架冲突 -- 多租户某些状态下配置存在问题 -- 邮件发送时 ssl 配置状态错误 +--- +title: sms4j 3.0.0版本震撼发布 短信重试,多方共用,负载均衡正式来袭 +author: sms4j +tag: + - sms4j +date: 2023-09-18 +cover: /assets/img/news/sms4j-cover.png +head: + - - meta + - name: 新闻 +--- + +## 🎉sms4j 3.0.0 版本震撼发布 短信重试,多方共用,负载均衡正式来袭 🎉 + +在历经了数月的等待之后(主要作者在摸鱼,写得慢。。。。)sms4j 的 3.0 版本终于正式发布,本次版本为大版本更新,与 2.x 不兼容,但是本次更新是一个长期支持版本,并且带来了诸多的新特性,接下来我们一起来看看吧 + +## 🚀 新特性 + +- 支持短信失败重试,可以自定义重试次数 +- 支持单厂商多配置使用 +- 支持接口类配置 +- 支持自定义缓存来源 +- 支持默认负载均衡形式,权重可自定义配置 +- 支持邮件发送 +- 支持邮箱监听 +- 支持邮件发送失败自动重试 +- 支持短信拦截 + +## 🏇 使用方式变更 + +在 sms4j3.0 版本中,使用方式与先前的 2.X 有所不同,使用上不再依托 2.X 中的枚举,而是改为更加自由的自定义 key 标识 + +``` + SmsBlend smsBlend = SmsFactory.getSmsBlend("在配置中定义的configId"); + SmsResponse smsResponse = smsBlend.sendMessage("18888888888","123"); +``` + +## 🔧 配置方式变更 + +在 3.0 版本中,去除了先前中的 JDBC 配置等形式,改为更加自由的接口类配置,同时保留了 yml 配置,项目更加轻量,使用更加自由方便 + +#### yml 配置 + +``` +sms: + # 标注从yml读取配置 + config-type: yaml + blends: + # 自定义的标识,也就是configId这里可以是任意值(最好不要是中文) + tx1: + #厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: tencent + #您的accessKey + access-key-id: 您的accessKey + #您的accessKeySecret + access-key-secret: 您的accessKeySecret + #您的短信签名 + signature: 您的短信签名 + #模板ID 非必须配置,如果使用sendMessage的快速发送需此配置 + template-id: xxxxxxxx + #您的sdkAppId + sdk-app-id: 您的sdkAppId + # 自定义的标识,也就是configId这里可以是任意值(最好不要是中文) +``` + +#### 接口类配置 + +``` +@Component +public class ReadConfig implements SmsReadConfig { + @Override + public BaseConfig getSupplierConfig(String configId) { + UniConfig uniConfig = new UniConfig(); + //此处仅为示例,实际环境中,数据可以来自任意位置, + return uniConfig; + } + + @Override + public List getSupplierConfigList() { + //此处仅为示例,实际环境中,数据可以来自任意位置, + return null; + } +} + +@Component +public class Demo { + @Autowired + ReadConfig config; + + @PostConstruct + public void init(){ + // 创建SmsBlend 短信实例 + SmsFactory.createSmsBlend(config,"在配置中定义的configId"); + } +} +``` + +## 📧 邮件插件 + +邮件插件在这次的更新中也得到了很大的优化,现在的邮件插件使用起来更加简单方便,同时可以覆盖多种场景,并且支持自定义 html 模板发送邮件 + +支持包括: + +- html 模板邮件 +- 携带多个附件的邮件 +- 自动打包成 zip 的邮件 +- 标准的文字邮件 +- 抄送人 +- 密送人 +- 失败自动重试 + +``` +//以下仅做演示,实际使用需要填入数据 +MailSmtpConfig config = MailSmtpConfig.builder.build; + +//这里的key可以是任何可对比类型,用于后续从工厂取出邮件实现类用 +MailFactory.put("qq",config) + +MailClient mail = MailFactory.createMailClient("qq"); + +mail.send(message); +``` + +邮件插件去除了先前版本中过多的重载方法,统一改为 send 方法进行发送,通过`MailMessage`构建不同的邮件 + +## 📩 邮箱监听 + +邮箱监听可以监听到某个 IMAP 协议邮箱中收到的邮件,并对其进行一定的处理。 + +``` +MonitorFactory.put("自定义的标识",MailImapConfig配置,Monitor回调对象) +MonitorFactory.start("put中自定义的标识") +``` + +邮箱监听开启后可以监听指定配置下的邮箱,并将收到的邮件进行自定义的处理 + +## 🔨 问题修复 + +本次版本中,还修复了 2.X 中存在的一些问题 + +- 华为云短信在群发时签名错误 +- 腾讯云短信在某些情况下导致的发送失败 +- unisms 的返回值存在异常 +- 阿里云短信在某些极端情况下会出现签名错误 +- springUtil 与其他框架冲突 +- 多租户某些状态下配置存在问题 +- 邮件发送时 ssl 配置状态错误 diff --git a/src/zh/news/sms4j-3.1.0.md b/src/zh/news/sms4j-3.1.0.md index 1eb8198130..0179866819 100644 --- a/src/zh/news/sms4j-3.1.0.md +++ b/src/zh/news/sms4j-3.1.0.md @@ -1,48 +1,48 @@ ---- -title: sms4j 3.1.0终于发布啦! -author: sms4j -tag: - - sms4j -date: 2024-01-10 -cover: /assets/img/news/sms4j-3.1.0-0.png -head: - - - meta - - name: 新闻 ---- - -# 📣📣sms4j 3.1.0 终于发布啦!📣📣 - -2023 年转瞬即逝,sms4j 项目在这段时间里取得了令人瞩目的成就。截至目前,我们已经获得了超过 1300 个 star,这离不开大家对 sms4j 项目的支持和关注,我代表整个开发团队向大家表示衷心的感谢! - -同时,我很高兴地告诉大家,我们的开发团队正在不断壮大,并迎来了一批新的成员加入。他们的加入为项目注入了新的活力和创造力。此外,我们还收到了许多建设性的意见和建议,这些宝贵的反馈将有助于我们进一步提升 sms4j 的功能和性能。 - -接下来,让我们一起来了解一下本次 3.1.0 版本的更新内容,以及认识一下团队的新成员吧!在这个版本中,我们增加了一些新的特性和功能,以满足用户不断增长的需求。同时,我们也修复了一些已知的问题和 bug,提升了系统的稳定性和可靠性。 - -如果我们的项目对你产生了帮助,或者你觉得还算值得鼓励,请用你发财的小手帮助点上一个 start - -**官网地址 https://sms4j.com** - -**gitee 仓库 https://gitee.com/dromara/sms4j** - -**github 仓库 https://github.com/dromara/sms4j** - -## 新增功能: - -- 联麓短信:我们新增了对联麓短信的支持,现在可以像使用其他厂商一样方便地进行使用。 -- 鼎众短信:我们还新增了对鼎众短信的支持,鼎众短信可以发送无模板短信,有需要的小伙伴可以尝试一下。 -- OA 通知:在 3.1 版本中,我们正式加入了对 OA 通知的支持,兼容钉钉、飞书和企业微信。 - -## 优化功能: - -- 邮件功能优化:现在邮件发送可以直接发送 HTML 字符串,并且可以存在模板变量。 -- 标准短信方法优化:我们添加了一个方法,该方法用于发送固定模板下的多参数模板短信。 -- 全局黑名单功能优化:在这次的更新中,我们重构了底层对于短信失败重试和短信黑名单的功能,现在它变得更加轻量、更加稳定。 -- 新增方法 reload 和 reloadAll:我们在核心工厂类(SmsFactory)中新增了方法 reload 和 reloadAll,用于重新从接口实现中读取并重新实例化短信对象。这样可以极大地减少对于重新读取配置的工作量。 - -## 修复问题: - -- 合一短信 HMAC 模式下签名无效的问题:我们修复了合一短信在 HMAC 模式下签名无效的问题。 -- 邮件插件 JDK17 的适配:我们修复了邮件插件在 JDK17 上的适配问题。 -- 容联云短信发送失败问题:我们修复了容联云短信发送失败的问题。 -- 阿里云短信在个别情况下报签名错误问题:我们修复了阿里云短信在个别情况下报签名错误的问题。 -- 云片短信在无模板下报错的问题:我们修复了云片短信在无模板下报错的问题。 +--- +title: sms4j 3.1.0终于发布啦! +author: sms4j +tag: + - sms4j +date: 2024-01-10 +cover: /assets/img/news/sms4j-3.1.0-0.png +head: + - - meta + - name: 新闻 +--- + +# 📣📣sms4j 3.1.0 终于发布啦!📣📣 + +2023 年转瞬即逝,sms4j 项目在这段时间里取得了令人瞩目的成就。截至目前,我们已经获得了超过 1300 个 star,这离不开大家对 sms4j 项目的支持和关注,我代表整个开发团队向大家表示衷心的感谢! + +同时,我很高兴地告诉大家,我们的开发团队正在不断壮大,并迎来了一批新的成员加入。他们的加入为项目注入了新的活力和创造力。此外,我们还收到了许多建设性的意见和建议,这些宝贵的反馈将有助于我们进一步提升 sms4j 的功能和性能。 + +接下来,让我们一起来了解一下本次 3.1.0 版本的更新内容,以及认识一下团队的新成员吧!在这个版本中,我们增加了一些新的特性和功能,以满足用户不断增长的需求。同时,我们也修复了一些已知的问题和 bug,提升了系统的稳定性和可靠性。 + +如果我们的项目对你产生了帮助,或者你觉得还算值得鼓励,请用你发财的小手帮助点上一个 start + +**官网地址 https://sms4j.com** + +**gitee 仓库 https://gitee.com/dromara/sms4j** + +**github 仓库 https://github.com/dromara/sms4j** + +## 新增功能: + +- 联麓短信:我们新增了对联麓短信的支持,现在可以像使用其他厂商一样方便地进行使用。 +- 鼎众短信:我们还新增了对鼎众短信的支持,鼎众短信可以发送无模板短信,有需要的小伙伴可以尝试一下。 +- OA 通知:在 3.1 版本中,我们正式加入了对 OA 通知的支持,兼容钉钉、飞书和企业微信。 + +## 优化功能: + +- 邮件功能优化:现在邮件发送可以直接发送 HTML 字符串,并且可以存在模板变量。 +- 标准短信方法优化:我们添加了一个方法,该方法用于发送固定模板下的多参数模板短信。 +- 全局黑名单功能优化:在这次的更新中,我们重构了底层对于短信失败重试和短信黑名单的功能,现在它变得更加轻量、更加稳定。 +- 新增方法 reload 和 reloadAll:我们在核心工厂类(SmsFactory)中新增了方法 reload 和 reloadAll,用于重新从接口实现中读取并重新实例化短信对象。这样可以极大地减少对于重新读取配置的工作量。 + +## 修复问题: + +- 合一短信 HMAC 模式下签名无效的问题:我们修复了合一短信在 HMAC 模式下签名无效的问题。 +- 邮件插件 JDK17 的适配:我们修复了邮件插件在 JDK17 上的适配问题。 +- 容联云短信发送失败问题:我们修复了容联云短信发送失败的问题。 +- 阿里云短信在个别情况下报签名错误问题:我们修复了阿里云短信在个别情况下报签名错误的问题。 +- 云片短信在无模板下报错的问题:我们修复了云片短信在无模板下报错的问题。 diff --git a/src/zh/news/soul-1.0.4.md b/src/zh/news/soul-1.0.4.md index d1e7d01846..2d33bc73ce 100644 --- a/src/zh/news/soul-1.0.4.md +++ b/src/zh/news/soul-1.0.4.md @@ -1,49 +1,49 @@ ---- -title: Soul网关发布1.0.4-RELEASE版本 -author: xiaoyu -date: 2019-04-09 -tag: - - Soul -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 新闻 ---- - -### Soul 网关发布 1.0.4-RELEASE 版本 - -- 修复在 1.0.3 版本的后台管理中,出现的 bug。 -- 配置信息序列化方式支持自定义扩展。默认的序列化方式由 kroy 改为了 java 序列化方式。 -- dubbo 框架支持的更改。 - -### 对 dubbo 用户使用的更改。 - -- 在以前的版本中(1.0.2 or 1.0.3),dubbo 的参数是通过 header 头上传递,在 1.0.4 版本中是通过 body 传递 - -- 更新了相关的文档信息。 - -### 关于使用 1.0.4 版本的建议。 - -- 1.0.4 版本支持用户自定义插件开发,支持正则表达式的匹配。 - -- dubbo 参数传递的更改,我觉得这样会更加友好。 - -### 如果您之前使用的 1.0.2 版本,想要更新到 1.0.4 版本。 - -- 在插件表新增 role 字段。 - -- 重新启动 1.0.4 版本的管理后台。 - -- 执行同步所有插件(因为序列化方式的更改) - -- 启动 1.0.4 版本的 soul-web 服务。 - -### 遇到问题? - -- 添加 qq 群(429951241) - -- 官网文档:https://dromara.org/website/zh-cn/docs/soul/soul.html - -- github 地址: https://github.com/Dromara/soul - -- gitee 地址: https://gitee.com/dromara/soul +--- +title: Soul网关发布1.0.4-RELEASE版本 +author: xiaoyu +date: 2019-04-09 +tag: + - Soul +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 新闻 +--- + +### Soul 网关发布 1.0.4-RELEASE 版本 + +- 修复在 1.0.3 版本的后台管理中,出现的 bug。 +- 配置信息序列化方式支持自定义扩展。默认的序列化方式由 kroy 改为了 java 序列化方式。 +- dubbo 框架支持的更改。 + +### 对 dubbo 用户使用的更改。 + +- 在以前的版本中(1.0.2 or 1.0.3),dubbo 的参数是通过 header 头上传递,在 1.0.4 版本中是通过 body 传递 + +- 更新了相关的文档信息。 + +### 关于使用 1.0.4 版本的建议。 + +- 1.0.4 版本支持用户自定义插件开发,支持正则表达式的匹配。 + +- dubbo 参数传递的更改,我觉得这样会更加友好。 + +### 如果您之前使用的 1.0.2 版本,想要更新到 1.0.4 版本。 + +- 在插件表新增 role 字段。 + +- 重新启动 1.0.4 版本的管理后台。 + +- 执行同步所有插件(因为序列化方式的更改) + +- 启动 1.0.4 版本的 soul-web 服务。 + +### 遇到问题? + +- 添加 qq 群(429951241) + +- 官网文档:https://dromara.org/website/zh-cn/docs/soul/soul.html + +- github 地址: https://github.com/Dromara/soul + +- gitee 地址: https://gitee.com/dromara/soul diff --git a/src/zh/news/soul-2.1.x.md b/src/zh/news/soul-2.1.x.md index f3544f6d01..ef1e811e49 100644 --- a/src/zh/news/soul-2.1.x.md +++ b/src/zh/news/soul-2.1.x.md @@ -1,111 +1,111 @@ ---- -title: Soul网关发布的2.1.X版本,到底有多方便? -author: xiaoyu -tag: - - Soul -date: 2019-12-12 -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 新闻 ---- - -**Soul** 网关自从去年 10 月我开源以来,经历了一年的事情,接受到了来自社区很多朋友的建议,并进行持续不断的优化,已经提供了非常丰富的功能,很多功能都是高度自定义,可视化,高度可扩展的,现在做一个归纳总结。 - -**插件** - -- 提供了系统自带的各种插件,比如签名,监控,限流,熔断,http 代理,dubbo 代理,websocket 等等。 - -- 支持用户快速的进行插件的自定义开发。 - -- 插件的所有数据,开关状态支持动态变更。 - -**数据同步** - -- 提供了 `http长轮询`,`zookeeper`,`websocket` 三种不同的数据同步策略,让用户自由选择。 -- 推荐用户使用 websocket 方式,最轻量,在集群环境下,效率更高。 - -**对于用户** - -- 首先我们提供了便于用户接入的 client 包,用户可以把快速的把自己的项目接入到 soul 网关。 -- 默认情况下,用户完全不用关心 soul 网关的选择器,规则等配置。 -- 用户之前的接口完全是零侵入,不需要任何更改,只是需要把访问域名改成网关的域名即可。 -- 比如 dubbo 用户,几乎就是 http 的方式完成了 http 协议到 dubbo 协议的互相转换。 -- `soul` 使用的是 http 协议,那么注定它就是跨语言的,net 程序员,php 程序员等等,要和 java 进行数据交互,那么就大大的可行了。 - -举个列子 ,比如你有一个 dubbo 接口 参数定义是一个 java bean, - -``` -public void insert(final DubboTest dubboTest) { -} -public class DubboTest implements Serializable { - private String id; - private String name; -} -``` - -如果你使用 soul 网关要发起对它的调用,你的 http 传参数 就是在 body 里面 传一个 json 字符串 ,和普通的 http 调用无差别。 - -``` -{"id":"123","name":"xiaoyu"} -``` - -**对于开发者(程序员)** - -- 随着使用者越来越多,每个公司使用情况又不一样,soul 网关在 2.1.X 版本,处处留出来更多的高度自定义扩展性,让开发者,更加方便或者有信心融入进来。 -- 比如,自定义插件,过滤器,dubbo 参数解析器,iphost 解析器,返回结果等等。。这里我着重说一下自定义返回结果。 - 我们知道,soul 网关默认的返回结果是: - -``` -{"code":200, "message ": "成功!","data" :"helloWorld!"} -``` - -但是,在运用 soul 网关对你的业务系统进行调用的时候,你的业务系统可能定义的结果并不是上述结构,可能你的 字段叫 msg,这样就会造成结构不一样,给前端处理带来了困扰。我们注意到了这个事情 :https://github.com/Dromara/soul/issues/109 , 现在已经优化,用户可以定制化的来定义返回结果,具体的要看 soul 文档。 - -**说了这么多,吹了这么多牛逼,那么我们来看看 soul 网关到底可以在什么场景下能发挥大作用。** - -**后台管理 web** - -- 首先随便微服务的流行,我们的后台都划分成很多的微服务,我相信你们每个公司都有一个后台管理系统吧,如果我没猜错的话,他们大体上是如下架构。 - -![soul-rpc](/assets/img/architecture/soul-rpc.png) - -- 很简单对吧,就是有个运营管理平台的 web 项目,去调用每个微服务,来进行后台的查看等等。随着你们业务需要的加大,可能这里需要调用的微服务越来越多,你的 `controller`越来越多,现在比如你修改了 商品模块的 接口,你要发版会造成所有其他的模块也操作不了(就是你发版影响了其他模块的使用,别杠这里只是比方,不要整蓝绿发版啥的,明白意思吧)。 - 如果有运营人员在操作其他模块,会不会吐槽你? - -假如你是公司架构师,我说的是假如,那么你要怎么解决这个问题呢?当然,我们把这样一个大的 web 系统,拆分成很多小 web 系统,单独的进行发布,但是这样会引入一个问题,怎么统一登陆,鉴权?(很多后台管理系统还有权限的划分) ,这个时候,`soul` 网关 就能发挥重要的作用了,下面我只是列举了一下简单的调用图。 - -![soul-admin](/assets/img/architecture/soul-admin-1.png) - -这样多方便,集成了网关,每个微服务注册到网关,网关根据路由规则来进行调用。自动发现服务,连运维配置 nginx 的工作都省了,把运维的工资给你,美滋滋。 - -### 公司入口网关(开放平台) - -- 如果一个公司要做开放平台或者入口网关,鉴权,限流,监控,熔断肯定少不了。 - -- 如果贵公司是 dubbo 体系,开发人员写了 dubbo 服务后,还要傻乎乎的新增一个 web 项目,来提供接口给别人调用吗? - -- 如果一个接口被攻击,你怎么处理呢?如果被大流量攻击,你怎么处理呢? - -- 不巧,soul 在设计之初就是来干这种事情的,我们来看一下整体的架构图。 - -![soul-framework](/assets/img/architecture/soul-framework.png) - -## 零零总总还有很多其他功能 - -- 比如支持 websocket 代理。 -- 比如支持文件上传下载。 - -- 比如你可以自定义的开发你的插件啊。 - -## 最后最后 - -- github 地址 :https://github.com/Dromara/soul - -- gitee 地址 :https://gitee.com/dromara/soul - -- 文档:https://dromara.org/zh-cn/docs/soul/soul.html - -- 欢迎大家关注,如果贵公司有使用到,或者需要学习交流,或者提供代码参与开发的朋友也可以加群来进行讨论 ,qq 群(429951241) - -- 最后 3.0 已经在进行开源了,3.0 是经历过 2 年双 11 大并发场景验证过的,现在一步一步将它开源出来,希望给大家带来帮助. +--- +title: Soul网关发布的2.1.X版本,到底有多方便? +author: xiaoyu +tag: + - Soul +date: 2019-12-12 +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 新闻 +--- + +**Soul** 网关自从去年 10 月我开源以来,经历了一年的事情,接受到了来自社区很多朋友的建议,并进行持续不断的优化,已经提供了非常丰富的功能,很多功能都是高度自定义,可视化,高度可扩展的,现在做一个归纳总结。 + +**插件** + +- 提供了系统自带的各种插件,比如签名,监控,限流,熔断,http 代理,dubbo 代理,websocket 等等。 + +- 支持用户快速的进行插件的自定义开发。 + +- 插件的所有数据,开关状态支持动态变更。 + +**数据同步** + +- 提供了 `http长轮询`,`zookeeper`,`websocket` 三种不同的数据同步策略,让用户自由选择。 +- 推荐用户使用 websocket 方式,最轻量,在集群环境下,效率更高。 + +**对于用户** + +- 首先我们提供了便于用户接入的 client 包,用户可以把快速的把自己的项目接入到 soul 网关。 +- 默认情况下,用户完全不用关心 soul 网关的选择器,规则等配置。 +- 用户之前的接口完全是零侵入,不需要任何更改,只是需要把访问域名改成网关的域名即可。 +- 比如 dubbo 用户,几乎就是 http 的方式完成了 http 协议到 dubbo 协议的互相转换。 +- `soul` 使用的是 http 协议,那么注定它就是跨语言的,net 程序员,php 程序员等等,要和 java 进行数据交互,那么就大大的可行了。 + +举个列子 ,比如你有一个 dubbo 接口 参数定义是一个 java bean, + +``` +public void insert(final DubboTest dubboTest) { +} +public class DubboTest implements Serializable { + private String id; + private String name; +} +``` + +如果你使用 soul 网关要发起对它的调用,你的 http 传参数 就是在 body 里面 传一个 json 字符串 ,和普通的 http 调用无差别。 + +``` +{"id":"123","name":"xiaoyu"} +``` + +**对于开发者(程序员)** + +- 随着使用者越来越多,每个公司使用情况又不一样,soul 网关在 2.1.X 版本,处处留出来更多的高度自定义扩展性,让开发者,更加方便或者有信心融入进来。 +- 比如,自定义插件,过滤器,dubbo 参数解析器,iphost 解析器,返回结果等等。。这里我着重说一下自定义返回结果。 + 我们知道,soul 网关默认的返回结果是: + +``` +{"code":200, "message ": "成功!","data" :"helloWorld!"} +``` + +但是,在运用 soul 网关对你的业务系统进行调用的时候,你的业务系统可能定义的结果并不是上述结构,可能你的 字段叫 msg,这样就会造成结构不一样,给前端处理带来了困扰。我们注意到了这个事情 :https://github.com/Dromara/soul/issues/109 , 现在已经优化,用户可以定制化的来定义返回结果,具体的要看 soul 文档。 + +**说了这么多,吹了这么多牛逼,那么我们来看看 soul 网关到底可以在什么场景下能发挥大作用。** + +**后台管理 web** + +- 首先随便微服务的流行,我们的后台都划分成很多的微服务,我相信你们每个公司都有一个后台管理系统吧,如果我没猜错的话,他们大体上是如下架构。 + +![soul-rpc](/assets/img/architecture/soul-rpc.png) + +- 很简单对吧,就是有个运营管理平台的 web 项目,去调用每个微服务,来进行后台的查看等等。随着你们业务需要的加大,可能这里需要调用的微服务越来越多,你的 `controller`越来越多,现在比如你修改了 商品模块的 接口,你要发版会造成所有其他的模块也操作不了(就是你发版影响了其他模块的使用,别杠这里只是比方,不要整蓝绿发版啥的,明白意思吧)。 + 如果有运营人员在操作其他模块,会不会吐槽你? + +假如你是公司架构师,我说的是假如,那么你要怎么解决这个问题呢?当然,我们把这样一个大的 web 系统,拆分成很多小 web 系统,单独的进行发布,但是这样会引入一个问题,怎么统一登陆,鉴权?(很多后台管理系统还有权限的划分) ,这个时候,`soul` 网关 就能发挥重要的作用了,下面我只是列举了一下简单的调用图。 + +![soul-admin](/assets/img/architecture/soul-admin-1.png) + +这样多方便,集成了网关,每个微服务注册到网关,网关根据路由规则来进行调用。自动发现服务,连运维配置 nginx 的工作都省了,把运维的工资给你,美滋滋。 + +### 公司入口网关(开放平台) + +- 如果一个公司要做开放平台或者入口网关,鉴权,限流,监控,熔断肯定少不了。 + +- 如果贵公司是 dubbo 体系,开发人员写了 dubbo 服务后,还要傻乎乎的新增一个 web 项目,来提供接口给别人调用吗? + +- 如果一个接口被攻击,你怎么处理呢?如果被大流量攻击,你怎么处理呢? + +- 不巧,soul 在设计之初就是来干这种事情的,我们来看一下整体的架构图。 + +![soul-framework](/assets/img/architecture/soul-framework.png) + +## 零零总总还有很多其他功能 + +- 比如支持 websocket 代理。 +- 比如支持文件上传下载。 + +- 比如你可以自定义的开发你的插件啊。 + +## 最后最后 + +- github 地址 :https://github.com/Dromara/soul + +- gitee 地址 :https://gitee.com/dromara/soul + +- 文档:https://dromara.org/zh-cn/docs/soul/soul.html + +- 欢迎大家关注,如果贵公司有使用到,或者需要学习交流,或者提供代码参与开发的朋友也可以加群来进行讨论 ,qq 群(429951241) + +- 最后 3.0 已经在进行开源了,3.0 是经历过 2 年双 11 大并发场景验证过的,现在一步一步将它开源出来,希望给大家带来帮助. diff --git a/src/zh/news/soul-2.2.0.md b/src/zh/news/soul-2.2.0.md index 85ddc82672..2aa1453a4c 100644 --- a/src/zh/news/soul-2.2.0.md +++ b/src/zh/news/soul-2.2.0.md @@ -1,222 +1,222 @@ ---- -title: 【Soul网关发布2.2.0】让高性能网关变得如此简单! -author: xiaoyu -tag: - - Soul -date: 2020-06-17 -cover: /assets/img/architecture/soul-framework.png -head: - - - meta - - name: 新闻 ---- - -我们还是先来看看新增功能,然后再讲故事。 - -- 完全的插件化架构设计,插件热插拔。 -- 完整支持 dubbo 所有版本,alibaba-dubbo ,apache-dubbo。 -- 支持 dubbo 泛化调用,多参数,复杂参数接口。 -- 增强 monitor 插件,移除 influxdb 支持,新增内存,CPU,QPS,TPS,响应迟延等 metrics,支持接入 Prometheus。 -- springCloud 插件支持 eureka 与 nacos 二种注册中心。 -- waf 插件增强,支持黑白名单,以及混合模式。 -- 抽离 Hystrix 熔断功能,独立成插件支持。 -- 修护 Zookeeper 数据同步方式 bug,新增 nacos 同步数据方式。 -- 多种 soul-client 支持,提供传统 spring,以及 springboot 等方式接入。 -- 优化 soul-admin 后台控制界面。 -- 负载均衡算法 bug 修护。 -- 修护大文件上传时候的 bug。 -- …….太多了不一一列举了。 - -## 体验新架构,10 分钟搞定一个高可用高性能网关。 - -**启动 soul-admin** - -- 下载 soul-admin.jar 包,并启动. - -``` -> wget https://yu199195.github.io/jar/soul-admin.jar -> java -jar soul-admin.jar --spring.datasource.url="jdbc:mysql://你的url:3306/soul?useUnicode=true&characterEncoding=utf-8&useSSL=false" - --spring.datasource.username='you username' --spring.datasource.password='you password' -``` - -- 访问 http://localhost:9095/index.html 默认的用户名:admin 密码:123456。 - -**搭建属于你的网关** - -- 首先你新建一个空的 springboot 项目,可以参考 soul-bootstrap. 也可以在 spring 官网:[https://spring.io/quickstart] -- 引入如下 jar 包: - -``` - - org.springframework.boot - spring-boot-starter-webflux - 2.2.2-RELEASE - - - - org.springframework.boot - spring-boot-starter-actuator - 2.2.2-RELEASE - - - - - org.dromara - soul-spring-boot-starter-gateway - 2.2.0 - - - - - org.dromara - soul-spring-boot-starter-sync-data-websocket - 2.2.0 - -``` - -- 在你的 `application.yaml` 文件中加上如下配置: - -``` -spring: - main: - allow-bean-definition-overriding: true - -management: - health: - defaults: - enabled: false -soul : - sync: - websocket : - urls: ws://localhost:9095/websocket //设置成你的soul-admin地址 -``` - -- 这样网关的环境就已经搭建完成。 - -## 体验新架构下的插件热插拔 - -- 问:我想使用熔断功能,应该如何做呢? - -- 答:你可以在 pom.xml 文件 引入以下依赖,更多的还请看:https://dromara.org/zh-cn/docs/soul/soul.html - -``` - - - org.dromara - soul-spring-boot-starter-plugin-hystrix - 2.2.0 - - -``` - -- 问:我怎么接入 dubbo 服务呢? - -- 答: - 1)如果你使用的是 alibaba-dubbo,那么你应该引入如下: - -``` - - - org.dromara - soul-spring-boot-starter-plugin-alibaba-dubbo - 2.2.0 - - -``` - -2) 如果你使用 apache-dubbo,那么你应该引入如下: - -``` - - - org.dromara - soul-spring-boot-starter-plugin-apache-dubbo - 2.2.0 - - -``` - -3. 更多的使用请你参考:https://dromara.org/zh-cn/docs/soul/user-dubbo.html - -- 问:如果我想使用限流功能呢? - -- 答:你可以引入以下依赖,具体的参考:https://dromara.org/zh-cn/docs/soul/plugin-rateLimiter.html - -``` - - - org.dromara - soul-spring-boot-starter-plugin-ratelimiter - 2.2.0 - - -``` - -- 总而言之,你想要使用什么插件,你就新增该插件的依赖。就这?是热插拔么。。 -- 问:那有些插件我不想用了怎么办? -- 答:在 soul-admin 后台禁用该插件即可,想用就开启。 - -## Soul 网关的特性 - -- 我觉得最大的特色是在流量筛选和管控方面。无论多复杂的请求,可以根据各种条件,规则,匹配方式,来进行流量过滤,筛选,处理。这个过程完全是可视化,自定义,即时生效的,程序无需任何更改。 -- 每个配置都在 soul-admin 控制台配置,会同步到每个 Soul 网关节点的 JVM 内存,这也是 Soul 集群高性能的关键之一,在 soul 网关内部,使用了 http 长轮询,websocket,zookeeper 等方式,独立实现了分布式配置中心的功能。 -- Soul 网关使用 Reactor 编程方式来实现,独立了线程调度,低消耗,经过网关的流量,我们在开启 10 个插件都处理的情况下,延迟是 1~2ms。 -- 插件机制,默认提供了限流,熔断,黑白名单,认证等等插件。 -- 支持 A/B test,蓝绿发布(因为掌控了所有流量这个很容易做)。 - -## Soul 网关有哪些使用场景,又有哪些值得你关注或者学习的? - -首先我觉得还是实用主义,需要用到,才会去了解。那么在什么场景下,你需要用到呢? - -**后台管理 web** - -- 首先随便微服务的流行,我们的后台都划分成很多的微服务,我相信你们每个公司都有一个后台管理系统吧,如果我没猜错的话,他们大体上是如下架构。 - ![soul-admin](/assets/img/architecture/soul-admin.png) - -- 它会有什么问题呢?大家思考一下。 - - - 每个微服务项目的开发人员都在这上面进行开发,会越来越笨重。 - - - 如何不停机发布的问题?你要发布商品模块的接口,会造成所有其他的模块使用不了。 - - - 假如某一个模块接口的请求量很大(需要部署多个),另一个模块不需要,你又怎么拆分呢? - -- 有人又会说,那我把他们拆处理,拆成一个一个 web 不就行了么?但是这样又会带来一个新的问题,负载均衡在哪里做?统一的认证在哪里做? -- Soul 网关就很好了解决了以上所有问题,只需要把你的微服务注册到 Soul 网关。你想怎么玩都可以,不重样的.. 比如 order 模块有 2 个应用,你要发布新的版本,你可以在网关里面,把流量先打到其中一个,另一个进行更新,更新完了以后,再把流量放过去。改变了以前运维掌控一切的观念,java 程序员,也可以玩的更好,运维都省了,向老板申请加薪指日可待。 -- 需要统一鉴权?你只需要在网关新增一个适合自己业务的鉴权插件就 OK。 - **公司入口网关(开放平台)** -- 如果一个公司要做开放平台或者入口网关,鉴权,限流,监控,熔断肯定少不了。 -- 如果贵公司是 dubbo 体系,开发人员写了 dubbo 服务后,还要傻乎乎的新增一个 web 项目,来提供接口给别人调用吗? -- 如果一个接口被攻击,你怎么处理呢?如果被大流量攻击,你怎么处理呢? -- 不巧,soul 在设计之初就是来干这种事情的,我们来看一下整体的架构图。 - -![soul-framework](/assets/img/architecture/soul-framework.png) - -- Soul 网关是使用响应式编程实现的,响应式编程绝对是未来 java 邻域的重要方向,看风向标 Spring 体系就好了。我在 14 年的时候,天天写 for 循环操作集合,溜的一笔。领导对我说,要使用 lambda 表达式,这是未来的重点,今天来看,如果你是 java 程序员,你不会 lambda 函数式编程,你好意思么。 - -## 从发布 2.2.0,谈谈近几年的开源体会。 - -我是 17 年左右的时候,开始写开源项目的,最开始我和王亮一起讨论设计了 LCN 分布式事务,后面自己又陆续写了 Hmily,Raincat,Myth 等分布式事务中间件,再后来写了 Soul 网关,这一路走来,遇到很多很有意思的事,也遭受很多小白用户的摧残。总的感觉,一个好的开源项目,高扩展,可插拔的设计实在太重要了。 - -- 案例一:Soul 网关刚开始数据同步只支持 Zookeeper 方式,有些用户反馈,我们没有 zk,那怎么办? -- 案例二 :Soul 网关是支持 Dubbo 的,但是有些用户是 alibaba-dubbo,有些用户 apache-dubbo,你又怎么说? -- 案例三 :soul 刚开始提供的客户端都是基于 Springboot 的,有些用户是传统的 Spring,你又怎么说? - -所以插件化设计,SPI 可插拔设计势在必行。 - -**SPI VS 可插拔** - -诚然 SPI 扩展方式,是可插拔的基石,但是他们又不完全等同。举个列子:假如我们先存储一条数据,你定好了 SPI 接口,也有 Mysql,mongodb,elasticseach,zookeeper 等等多种方式实现,现在你要考虑的是把它组合在一起项目里面,还是放在不同的项目,按需打包和加载呢?这些都是要考虑的,所以不能一股脑的 SPI 方式。 - -**checkStyle** - -- 严格的代码规范,是对源码学习中,框架使用者的尊重,更是一种开源的态度。 - -- 严格的代码规范,让人看起来舒服,也更容易让人理解整个代码。 - -- 也希望各位小伙伴提交 PR 的时候,至少本地要 Install 成功,之前有些 PR,为了不打击他们的积极性,合并之后流着泪修改。 - -**参与开源** - -- 目前我主要专注于 Apache ShardingSphere,这是中国人在 apache 组织的第一个关于数据库分库分表的顶级项目,欢迎大家参与进来。https://github.com/apache/shardingsphere。 - -- 国人在开源方面,技术方面大到芯片,小到 MATLAB ,都落后挺多的,也希望大家拥有开源的心态,多参与开源,学习技术,宣传思想,为往圣继绝学! +--- +title: 【Soul网关发布2.2.0】让高性能网关变得如此简单! +author: xiaoyu +tag: + - Soul +date: 2020-06-17 +cover: /assets/img/architecture/soul-framework.png +head: + - - meta + - name: 新闻 +--- + +我们还是先来看看新增功能,然后再讲故事。 + +- 完全的插件化架构设计,插件热插拔。 +- 完整支持 dubbo 所有版本,alibaba-dubbo ,apache-dubbo。 +- 支持 dubbo 泛化调用,多参数,复杂参数接口。 +- 增强 monitor 插件,移除 influxdb 支持,新增内存,CPU,QPS,TPS,响应迟延等 metrics,支持接入 Prometheus。 +- springCloud 插件支持 eureka 与 nacos 二种注册中心。 +- waf 插件增强,支持黑白名单,以及混合模式。 +- 抽离 Hystrix 熔断功能,独立成插件支持。 +- 修护 Zookeeper 数据同步方式 bug,新增 nacos 同步数据方式。 +- 多种 soul-client 支持,提供传统 spring,以及 springboot 等方式接入。 +- 优化 soul-admin 后台控制界面。 +- 负载均衡算法 bug 修护。 +- 修护大文件上传时候的 bug。 +- …….太多了不一一列举了。 + +## 体验新架构,10 分钟搞定一个高可用高性能网关。 + +**启动 soul-admin** + +- 下载 soul-admin.jar 包,并启动. + +``` +> wget https://yu199195.github.io/jar/soul-admin.jar +> java -jar soul-admin.jar --spring.datasource.url="jdbc:mysql://你的url:3306/soul?useUnicode=true&characterEncoding=utf-8&useSSL=false" + --spring.datasource.username='you username' --spring.datasource.password='you password' +``` + +- 访问 http://localhost:9095/index.html 默认的用户名:admin 密码:123456。 + +**搭建属于你的网关** + +- 首先你新建一个空的 springboot 项目,可以参考 soul-bootstrap. 也可以在 spring 官网:[https://spring.io/quickstart] +- 引入如下 jar 包: + +``` + + org.springframework.boot + spring-boot-starter-webflux + 2.2.2-RELEASE + + + + org.springframework.boot + spring-boot-starter-actuator + 2.2.2-RELEASE + + + + + org.dromara + soul-spring-boot-starter-gateway + 2.2.0 + + + + + org.dromara + soul-spring-boot-starter-sync-data-websocket + 2.2.0 + +``` + +- 在你的 `application.yaml` 文件中加上如下配置: + +``` +spring: + main: + allow-bean-definition-overriding: true + +management: + health: + defaults: + enabled: false +soul : + sync: + websocket : + urls: ws://localhost:9095/websocket //设置成你的soul-admin地址 +``` + +- 这样网关的环境就已经搭建完成。 + +## 体验新架构下的插件热插拔 + +- 问:我想使用熔断功能,应该如何做呢? + +- 答:你可以在 pom.xml 文件 引入以下依赖,更多的还请看:https://dromara.org/zh-cn/docs/soul/soul.html + +``` + + + org.dromara + soul-spring-boot-starter-plugin-hystrix + 2.2.0 + + +``` + +- 问:我怎么接入 dubbo 服务呢? + +- 答: + 1)如果你使用的是 alibaba-dubbo,那么你应该引入如下: + +``` + + + org.dromara + soul-spring-boot-starter-plugin-alibaba-dubbo + 2.2.0 + + +``` + +2) 如果你使用 apache-dubbo,那么你应该引入如下: + +``` + + + org.dromara + soul-spring-boot-starter-plugin-apache-dubbo + 2.2.0 + + +``` + +3. 更多的使用请你参考:https://dromara.org/zh-cn/docs/soul/user-dubbo.html + +- 问:如果我想使用限流功能呢? + +- 答:你可以引入以下依赖,具体的参考:https://dromara.org/zh-cn/docs/soul/plugin-rateLimiter.html + +``` + + + org.dromara + soul-spring-boot-starter-plugin-ratelimiter + 2.2.0 + + +``` + +- 总而言之,你想要使用什么插件,你就新增该插件的依赖。就这?是热插拔么。。 +- 问:那有些插件我不想用了怎么办? +- 答:在 soul-admin 后台禁用该插件即可,想用就开启。 + +## Soul 网关的特性 + +- 我觉得最大的特色是在流量筛选和管控方面。无论多复杂的请求,可以根据各种条件,规则,匹配方式,来进行流量过滤,筛选,处理。这个过程完全是可视化,自定义,即时生效的,程序无需任何更改。 +- 每个配置都在 soul-admin 控制台配置,会同步到每个 Soul 网关节点的 JVM 内存,这也是 Soul 集群高性能的关键之一,在 soul 网关内部,使用了 http 长轮询,websocket,zookeeper 等方式,独立实现了分布式配置中心的功能。 +- Soul 网关使用 Reactor 编程方式来实现,独立了线程调度,低消耗,经过网关的流量,我们在开启 10 个插件都处理的情况下,延迟是 1~2ms。 +- 插件机制,默认提供了限流,熔断,黑白名单,认证等等插件。 +- 支持 A/B test,蓝绿发布(因为掌控了所有流量这个很容易做)。 + +## Soul 网关有哪些使用场景,又有哪些值得你关注或者学习的? + +首先我觉得还是实用主义,需要用到,才会去了解。那么在什么场景下,你需要用到呢? + +**后台管理 web** + +- 首先随便微服务的流行,我们的后台都划分成很多的微服务,我相信你们每个公司都有一个后台管理系统吧,如果我没猜错的话,他们大体上是如下架构。 + ![soul-admin](/assets/img/architecture/soul-admin.png) + +- 它会有什么问题呢?大家思考一下。 + + - 每个微服务项目的开发人员都在这上面进行开发,会越来越笨重。 + + - 如何不停机发布的问题?你要发布商品模块的接口,会造成所有其他的模块使用不了。 + + - 假如某一个模块接口的请求量很大(需要部署多个),另一个模块不需要,你又怎么拆分呢? + +- 有人又会说,那我把他们拆处理,拆成一个一个 web 不就行了么?但是这样又会带来一个新的问题,负载均衡在哪里做?统一的认证在哪里做? +- Soul 网关就很好了解决了以上所有问题,只需要把你的微服务注册到 Soul 网关。你想怎么玩都可以,不重样的.. 比如 order 模块有 2 个应用,你要发布新的版本,你可以在网关里面,把流量先打到其中一个,另一个进行更新,更新完了以后,再把流量放过去。改变了以前运维掌控一切的观念,java 程序员,也可以玩的更好,运维都省了,向老板申请加薪指日可待。 +- 需要统一鉴权?你只需要在网关新增一个适合自己业务的鉴权插件就 OK。 + **公司入口网关(开放平台)** +- 如果一个公司要做开放平台或者入口网关,鉴权,限流,监控,熔断肯定少不了。 +- 如果贵公司是 dubbo 体系,开发人员写了 dubbo 服务后,还要傻乎乎的新增一个 web 项目,来提供接口给别人调用吗? +- 如果一个接口被攻击,你怎么处理呢?如果被大流量攻击,你怎么处理呢? +- 不巧,soul 在设计之初就是来干这种事情的,我们来看一下整体的架构图。 + +![soul-framework](/assets/img/architecture/soul-framework.png) + +- Soul 网关是使用响应式编程实现的,响应式编程绝对是未来 java 邻域的重要方向,看风向标 Spring 体系就好了。我在 14 年的时候,天天写 for 循环操作集合,溜的一笔。领导对我说,要使用 lambda 表达式,这是未来的重点,今天来看,如果你是 java 程序员,你不会 lambda 函数式编程,你好意思么。 + +## 从发布 2.2.0,谈谈近几年的开源体会。 + +我是 17 年左右的时候,开始写开源项目的,最开始我和王亮一起讨论设计了 LCN 分布式事务,后面自己又陆续写了 Hmily,Raincat,Myth 等分布式事务中间件,再后来写了 Soul 网关,这一路走来,遇到很多很有意思的事,也遭受很多小白用户的摧残。总的感觉,一个好的开源项目,高扩展,可插拔的设计实在太重要了。 + +- 案例一:Soul 网关刚开始数据同步只支持 Zookeeper 方式,有些用户反馈,我们没有 zk,那怎么办? +- 案例二 :Soul 网关是支持 Dubbo 的,但是有些用户是 alibaba-dubbo,有些用户 apache-dubbo,你又怎么说? +- 案例三 :soul 刚开始提供的客户端都是基于 Springboot 的,有些用户是传统的 Spring,你又怎么说? + +所以插件化设计,SPI 可插拔设计势在必行。 + +**SPI VS 可插拔** + +诚然 SPI 扩展方式,是可插拔的基石,但是他们又不完全等同。举个列子:假如我们先存储一条数据,你定好了 SPI 接口,也有 Mysql,mongodb,elasticseach,zookeeper 等等多种方式实现,现在你要考虑的是把它组合在一起项目里面,还是放在不同的项目,按需打包和加载呢?这些都是要考虑的,所以不能一股脑的 SPI 方式。 + +**checkStyle** + +- 严格的代码规范,是对源码学习中,框架使用者的尊重,更是一种开源的态度。 + +- 严格的代码规范,让人看起来舒服,也更容易让人理解整个代码。 + +- 也希望各位小伙伴提交 PR 的时候,至少本地要 Install 成功,之前有些 PR,为了不打击他们的积极性,合并之后流着泪修改。 + +**参与开源** + +- 目前我主要专注于 Apache ShardingSphere,这是中国人在 apache 组织的第一个关于数据库分库分表的顶级项目,欢迎大家参与进来。https://github.com/apache/shardingsphere。 + +- 国人在开源方面,技术方面大到芯片,小到 MATLAB ,都落后挺多的,也希望大家拥有开源的心态,多参与开源,学习技术,宣传思想,为往圣继绝学! diff --git a/src/zh/news/warm-flow-1.2.8.md b/src/zh/news/warm-flow-1.2.8.md new file mode 100644 index 0000000000..a850781d50 --- /dev/null +++ b/src/zh/news/warm-flow-1.2.8.md @@ -0,0 +1,263 @@ +--- +title: warm-flow v1.2.8 更新 - 新增办理人变量表达式和条件表达式支持spel +author: warm-flow +date: 2024-09-25 +cover: /assets/img/news/warm-flow-v1.2.8-0.png +head: + - - meta + - name: 新闻 +--- + +# warm-flow 1.2.8版本更新,新增办理人变量表达式和条件表达式支持spel + +![](/assets/img/news/warm-flow-v1.2.8-0.png) + +* 【升级注意事项】 + +* 本次升级,内置json库snack3方式,改为spi方式加载,业务项目中存在哪种json就会使用哪种的实现, 支持顺序按顺序加载一种:snack3、jackson、fastjson、gson,并且目前只实现了这四种,可扩展 + +* 如在未集成snack3库的环境下,还需要使用snack3库,需要单独使用(原组件使用snack3库) + + ``` + + org.noear + snack3 + 3.2.88 + + ``` + +* 更新日志 + +* \[feat\] json库支持snack3、jackson、fastjson和gson,并且支持扩展 + +* \[feat\] 增加办理人变量表达式,支持${xxx}替换和spel,并支持扩展 + +* \[feat\] ListenerVariable监听器变量新增FlowParams字段,方便开始监听器全局传递参数 + +* \[feat\] 终止新增对开始和完成监听器的支持 + +* \[update\] springboot项目的条件表达式默认支持spel + +* \[update\] 历史记录改为单条保存,删除重复代码 + +* \[update\] 修改FlowUserDao的bean名称 + +* \[update\] 中间节点拆分为或签,会签,票签 + +* \[fix\] 修复历史记录创建时间相等,导致流程图渲染异常 + +* \[fix\]修复Mybatis逻辑删除变成真实删除的缺陷                               @xiarigang + +* \[refactor\] 重构id生成器,支持orm默认策略,删除数据填充默认实现类,改为匿名类 + + +# 部分更新内容介绍 + +## 1、增加办理人变量表达式 + +### 1.1、默认办理人变量策略 + +#### 前端页面设置变量 + +* 比如:`@@default@@|${handler1},role:1,1` + +* `@@default@@|${handler1}`中@@default@@表示默认办理人变量策略,handler1是需要被流程变量中替换的标识 + +* `role:1,1`表示办理人角色和具体办理人 + + +![](/assets/img/news/warm-flow-v1.2.8-1.png) + +#### 后端代码设置变量 + +``` + +// 流程变量 +Map variable = new HashMap<>(); +variable.put("handler1", "100"); +flowParams.variable(variable); + +Instance instance = insService.skipByInsId(testLeave.getInstanceId(), flowParams); +``` + +### 1.2、spel办理人变量策略 + +#### 前端页面设置变量 + +* 比如:`@@spel@@|#{@user.evalVar(#handler2)}` + +* `#{@user.evalVar(#handler2)}`是spel表达式,`#handler2`是方法入参变量,可以不设置 + + +![](/assets/img/news/warm-flow-v1.2.8-2.png) + +#### 后端代码设置变量 + +``` +/** + * 用户类 + */ +@Component("user") +public class User { + +    /** +     * spel办理人变量表达式 +     * @param handler2 办理人 +     * @return String +     */ +    public String evalVar(String handler2) { +        return handler2; +    } +} + +// 流程变量 +Map variable = new HashMap<>(); +variable.put("handler2", "101"); +flowParams.variable(variable); + +Instance instance = insService.skipByInsId(testLeave.getInstanceId(), flowParams); +``` + +## 2、监听器变量新增FlowParams字段 + +> ListenerVariable监听器变量新增FlowParams字段,方便开始监听器全局传递参数 + +``` +@Component +public class GlobalStartListener implements Listener { + + +  private static final Logger log = LoggerFactory.getLogger(GlobalStartListener.class); + +  /** +   * 设置办理人id、所拥有的权限等操作,也可以放到业务代码中办理前设置,或者局部监听器 +   * @param listenerVariable 监听器变量 +   */ +  @Override +  public void notify(ListenerVariable listenerVariable) { +    log.info("全局开始监听器"); + +    FlowParams flowParams = listenerVariable.getFlowParams(); +    LoginUser user = SecurityUtils.getLoginUser(); +    // 设置当前办理人id +    flowParams.setHandler(user.getUser().getUserId().toString()); + +    // 设置办理人所拥有的权限,比如角色、部门、用户等 +    List permissionList = flowParams.getPermissionFlag(); +    if (StringUtils.isEmpty(permissionList)) { +      permissionList = new ArrayList<>(); +    } + +    List roles = user.getUser().getRoles(); +    if (Objects.nonNull(roles)) { +      permissionList.addAll(roles.stream().map(role -> "role:" + role.getRoleId()).collect(Collectors.toList())); +    } +    permissionList.add("dept:" + SecurityUtils.getLoginUser().getUser().getDeptId()); +    permissionList.add(user.getUser().getUserId().toString()); +    flowParams.setPermissionFlag(permissionList); + +    log.info("全局开始监听器结束;{}", "开启流程完成"); +  } +} +``` + +## 3、条件表达式默认支持spel + +> springboot项目的条件表达式默认支持spel + +* 前端配置如`#{@user.eval(#flag)}`表达式,入库前要拼接前缀,方便区分表达式类型,最终为`@@spel@@|#{@user.eval(#flag)}` + +* `#flag`为变量和以下方法入参命名一致,可不设置入参 + + +![](/assets/img/news/warm-flow-v1.2.8-3.png) + +``` +@Component("user") +public class User { + +  /** +   * spel条件表达:判断大于等4 +   * @param flag 待判断的字符串 +   * @return boolean +   */ +  public boolean eval(String flag) { +    BigDecimal a = new BigDecimal(flag); +    BigDecimal b = new BigDecimal("4"); +    return a.compareTo(b) > 0; +  } +} + +/** + * 新增OA 请假申请 + * + * @param testLeave OA 请假申请 + * @return 结果 + */ +@Override +public int insertTestLeave(TestLeave testLeave, String flowStatus) +{ +  FlowParams flowParams = FlowParams.build().flowCode(getFlowType(testLeave)); +  // 流程变量 +  Map variable = new HashMap<>(); +  variable.put("flag", String.valueOf(testLeave.getDay())); +  flowParams.variable(variable); + +  Instance instance = insService.start(id, flowParams); +  return instance != null? 1 : 0; +} +``` + +# warm-flow介绍 + +> \[!IMPORTANT\] Warm-Flow国产工作流引擎🎉,其特点简洁轻量但又不简单,五脏俱全,组件独立,可扩展,可满足中小项目的组件。 + +1. 简洁易用:只有7张表,代码量少,可快速上手和集成 + +2. 审批功能:支持通过、退回、任意跳转、转办、终止、会签、票签、委派和加减签、互斥和并行网关 + +3. 监听器与流程变量:支持五种监听器,可应对不同场景,灵活可扩展,参数传递,动态权限 + +4. 流程图:流程引擎自带流程图,可在不集成流程设计器情况下使用 + +5. 条件表达式:内置常见的和spel条件表达式,并且支持自定义扩展 + +6. 办理人变量表达式:内置${handler}和spel格式的表达式,可满足不同场景,灵活可扩展 + +7. orm框架扩展:目前支持MyBatis、Mybatis-Plus、Mybatis-Flex和Jpa,后续会由社区提供其他支持,扩展方便 + +8. 数据库支持:目前支持MySQL 、Oracle 和PostgreSQL,后续会继续支持其他数据库或者国产数据库 + +9. 多租户与软删除:流程引擎自身维护多租户和软删除实现,也可使用对应orm框架的实现方式 + +10. 支持角色、部门和用户等权限配置 + +11. 同时支持spring和solon + +12. 兼容java8和java17,理论11也可以 + +13. 官方提供基于ruoyi-vue封装实战项目,很实用 + + +## 演示地址 + +* admin/admin123 + + +演示地址:http://www.hhzai.top + +## 官网 + +http://warm-flow.cn + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/qrcode_zsxq.webp) \ No newline at end of file diff --git a/src/zh/news/wemq.md b/src/zh/news/wemq.md index c5560671d0..bb621b7c17 100644 --- a/src/zh/news/wemq.md +++ b/src/zh/news/wemq.md @@ -1,133 +1,133 @@ ---- -title: 欢迎开源项目 WEMQ 加入 Dromara 社区,物联网设备调试系统 -author: wemq -date: 2023-09-25 -cover: /assets/img/news/wemq-cover.png -head: - - - meta - - name: 新闻 ---- - -## 项目背景 - -随着物联网技术的迅猛发展,越来越多的物联网设备被应用于各个行业和领域。物联网设备的调试和管理是确保设备正常运行和提供稳定服务的关键环节。然而,传统的物联网设备调试方案往往存在一些问题,如复杂的配置流程、安全性不足、功能不完善等。 - -为了解决这些问题,**WeMQ**应运而生。WeMQ 是一款面向物联网设备运营商的开源物联网设备调试系统,旨在提供完整的物联网设备调试方案,并集成了设备管理、MQTT 服务器管理、客户管理等功能。该系统采用自研的 Nmqs 通信层组件,实现了连接信息的加密,保证了数据的安全性。 - -WeMQ 的项目背景源于对物联网设备调试过程中存在问题的深入研究和理解。通过提供开源的解决方案,WeMQ 希望为物联网设备运营商提供一个高性能、安全可靠、功能强大的调试和管理平台,帮助他们简化设备调试流程、提高调试效率,并保证设备的稳定运行。 - -**项目的主要特性:** - -1. 提供一套完整的物联网设备调试方案,包括设备管理、MQTT 服务器管理、客户管理等功能,帮助设备运营商简化调试流程。 -2. 自研 Nmqs 通信层组件,实现连接信息的加密,确保数据传输的安全性。 -3. 提供高性能、稳定可靠的服务,满足大规模物联网设备的调试和管理需求。 -4. 开源项目,吸引更多的开发者参与贡献,推动物联网设备调试领域的发展。 - -通过 WeMQ,物联网设备运营商可以更轻松地进行设备调试和管理,提高工作效率,降低运营成本,并为最终用户提供更好的物联网设备体验。 - - - - - -## 技术选型 - -#### 1\. 系统环境 - -- Java 8 -- Servlet 3.0 -- Apache Maven 3 - -#### 2\. 主框架 - -- Spring Boot 2.7.x -- Spring Framework 5.3.x -- Spring MVC 5.3.x - -#### 3\. 持久层 - -- Mybatis 3.5.x -- Alibaba Druid 1.2.x -- Hibernate Validation 6.0.x -- Java MySQL Connector 8.0.x - -#### 4\. 视图层 - -- Thymeleaf 3.x -- Bootstrap 5.x -- Layui 2.x - -#### 5\. 工具类 - -- Apache Commons -- Hutool 5.x - -#### 6\. 通信层 - -- Nmqs 实现连接信息加密、消息转发,支持 WS/TCP 协议连接 -- Eclipse Paho - -## 项目结构 - -```` -cn.mmanager -├── mm-common // 工具类 -│ └── annotation // 自定义注解 -│ └── constant // 通用常量 -│ └── core // 核心控制 -│ └── enums // 通用枚举 -│ └── exception // 通用异常 -├── mm-framework // 框架核心 -│ └── aspectj // 注解实现 -│ └── interceptor // 拦截器 -│ └── manager // 异步处理 -│ └── web // 前端控制 -├── mm-web // Web服务 -├── mm-dao // 数据访问层 -├── mm-service // 业务层 -├── mm-model // 模型``` -```` - -## 通信层 - -``` - _ _ __ __ ____ _____ - | \ | | \/ |/ __ \ / ____| - | \| | \ / | | | | (___ - | . ` | |\/| | | | |\___ \ - | |\ | | | | |__| |____) | - |_| \_|_| |_|\___\_\_____/ -``` - -通信层组件在物联网设备调试系统中扮演着重要的角色,它负责设备与服务器之间的通信和数据传输。在 WeMQ 中,采用了自研的 Nmqs 通信层组件,它实现了连接信息的加密,确保数据传输的安全性。 - -**Nmqs 通信层组件具有以下特点和功能:** - -1. **连接信息加密**:Nmqs 采用先进的加密算法,对设备与服务器之间的连接信息进行加密处理。这样可以确保通信过程中的数据安全,防止信息被窃取或篡改。 -2. **安全认证**:Nmqs 提供了安全认证机制,确保只有经过授权的设备和服务器才能建立连接。通过身份验证和密钥交换等方式,确保通信双方的身份合法性和通信的安全性。 -3. **数据压缩和优化**:Nmqs 支持数据压缩和优化技术,可以在传输过程中对数据进行压缩,减少数据传输的带宽占用和传输延迟,提高通信效率。 -4. **可靠性保证**:Nmqs 具备可靠性保证机制,能够处理通信过程中的数据丢失、重传和错误处理。它采用可靠的传输协议,确保数据的完整性和可靠性。 -5. **适应性和灵活性**:Nmqs 通信层组件具有良好的适应性和灵活性,可以适应不同的网络环境和设备类型。它支持多种通信协议和网络传输方式,能够适应不同的物联网设备调试场景。 - -通过自研的 Nmqs 通信层组件,WeMQ 能够提供安全可靠的设备与服务器之间的通信服务。它保护设备数据的安全性,确保通信过程的可靠性和稳定性,为物联网设备调试和管理提供了强大的基础支持。 - -## 开源地址 - -**Gitee:https://gitee.com/dromara/WeMQ** - -**Github:https://github.com/dromara/WeMQ** - -## Issues & Pull Requests - -欢迎提交 Issues 和 Pull Requests,开源大门永远向所有人敞开。 - -## 作者介绍 - -- 名称:NicholasLD -- 全栈开发工程师,物联网爱好者 -- Dromara 开源组织成员,dromara/WeMQ 作者 - -**联系方式:** - -- Email: 878639947@qq.com -- QQ: 878639947 -- WeChat: NicholasLD505 +--- +title: 欢迎开源项目 WEMQ 加入 Dromara 社区,物联网设备调试系统 +author: wemq +date: 2023-09-25 +cover: /assets/img/news/wemq-cover.png +head: + - - meta + - name: 新闻 +--- + +## 项目背景 + +随着物联网技术的迅猛发展,越来越多的物联网设备被应用于各个行业和领域。物联网设备的调试和管理是确保设备正常运行和提供稳定服务的关键环节。然而,传统的物联网设备调试方案往往存在一些问题,如复杂的配置流程、安全性不足、功能不完善等。 + +为了解决这些问题,**WeMQ**应运而生。WeMQ 是一款面向物联网设备运营商的开源物联网设备调试系统,旨在提供完整的物联网设备调试方案,并集成了设备管理、MQTT 服务器管理、客户管理等功能。该系统采用自研的 Nmqs 通信层组件,实现了连接信息的加密,保证了数据的安全性。 + +WeMQ 的项目背景源于对物联网设备调试过程中存在问题的深入研究和理解。通过提供开源的解决方案,WeMQ 希望为物联网设备运营商提供一个高性能、安全可靠、功能强大的调试和管理平台,帮助他们简化设备调试流程、提高调试效率,并保证设备的稳定运行。 + +**项目的主要特性:** + +1. 提供一套完整的物联网设备调试方案,包括设备管理、MQTT 服务器管理、客户管理等功能,帮助设备运营商简化调试流程。 +2. 自研 Nmqs 通信层组件,实现连接信息的加密,确保数据传输的安全性。 +3. 提供高性能、稳定可靠的服务,满足大规模物联网设备的调试和管理需求。 +4. 开源项目,吸引更多的开发者参与贡献,推动物联网设备调试领域的发展。 + +通过 WeMQ,物联网设备运营商可以更轻松地进行设备调试和管理,提高工作效率,降低运营成本,并为最终用户提供更好的物联网设备体验。 + + + + + +## 技术选型 + +#### 1\. 系统环境 + +- Java 8 +- Servlet 3.0 +- Apache Maven 3 + +#### 2\. 主框架 + +- Spring Boot 2.7.x +- Spring Framework 5.3.x +- Spring MVC 5.3.x + +#### 3\. 持久层 + +- Mybatis 3.5.x +- Alibaba Druid 1.2.x +- Hibernate Validation 6.0.x +- Java MySQL Connector 8.0.x + +#### 4\. 视图层 + +- Thymeleaf 3.x +- Bootstrap 5.x +- Layui 2.x + +#### 5\. 工具类 + +- Apache Commons +- Hutool 5.x + +#### 6\. 通信层 + +- Nmqs 实现连接信息加密、消息转发,支持 WS/TCP 协议连接 +- Eclipse Paho + +## 项目结构 + +```` +cn.mmanager +├── mm-common // 工具类 +│ └── annotation // 自定义注解 +│ └── constant // 通用常量 +│ └── core // 核心控制 +│ └── enums // 通用枚举 +│ └── exception // 通用异常 +├── mm-framework // 框架核心 +│ └── aspectj // 注解实现 +│ └── interceptor // 拦截器 +│ └── manager // 异步处理 +│ └── web // 前端控制 +├── mm-web // Web服务 +├── mm-dao // 数据访问层 +├── mm-service // 业务层 +├── mm-model // 模型``` +```` + +## 通信层 + +``` + _ _ __ __ ____ _____ + | \ | | \/ |/ __ \ / ____| + | \| | \ / | | | | (___ + | . ` | |\/| | | | |\___ \ + | |\ | | | | |__| |____) | + |_| \_|_| |_|\___\_\_____/ +``` + +通信层组件在物联网设备调试系统中扮演着重要的角色,它负责设备与服务器之间的通信和数据传输。在 WeMQ 中,采用了自研的 Nmqs 通信层组件,它实现了连接信息的加密,确保数据传输的安全性。 + +**Nmqs 通信层组件具有以下特点和功能:** + +1. **连接信息加密**:Nmqs 采用先进的加密算法,对设备与服务器之间的连接信息进行加密处理。这样可以确保通信过程中的数据安全,防止信息被窃取或篡改。 +2. **安全认证**:Nmqs 提供了安全认证机制,确保只有经过授权的设备和服务器才能建立连接。通过身份验证和密钥交换等方式,确保通信双方的身份合法性和通信的安全性。 +3. **数据压缩和优化**:Nmqs 支持数据压缩和优化技术,可以在传输过程中对数据进行压缩,减少数据传输的带宽占用和传输延迟,提高通信效率。 +4. **可靠性保证**:Nmqs 具备可靠性保证机制,能够处理通信过程中的数据丢失、重传和错误处理。它采用可靠的传输协议,确保数据的完整性和可靠性。 +5. **适应性和灵活性**:Nmqs 通信层组件具有良好的适应性和灵活性,可以适应不同的网络环境和设备类型。它支持多种通信协议和网络传输方式,能够适应不同的物联网设备调试场景。 + +通过自研的 Nmqs 通信层组件,WeMQ 能够提供安全可靠的设备与服务器之间的通信服务。它保护设备数据的安全性,确保通信过程的可靠性和稳定性,为物联网设备调试和管理提供了强大的基础支持。 + +## 开源地址 + +**Gitee:https://gitee.com/dromara/WeMQ** + +**Github:https://github.com/dromara/WeMQ** + +## Issues & Pull Requests + +欢迎提交 Issues 和 Pull Requests,开源大门永远向所有人敞开。 + +## 作者介绍 + +- 名称:NicholasLD +- 全栈开发工程师,物联网爱好者 +- Dromara 开源组织成员,dromara/WeMQ 作者 + +**联系方式:** + +- Email: 878639947@qq.com +- QQ: 878639947 +- WeChat: NicholasLD505 diff --git a/src/zh/news/x-easypdf-v3.3.0.md b/src/zh/news/x-easypdf-v3.3.0.md new file mode 100644 index 0000000000..2308589bc9 --- /dev/null +++ b/src/zh/news/x-easypdf-v3.3.0.md @@ -0,0 +1,107 @@ +--- +title: x-easypdf v3.3.0发布,拥有AI加持的pdf框架 +author: x-easypdf +date: 2025-02-17 +cover: /assets/img/news/x-easypdf-v3.3.0-0.png +head: + - - meta + - name: 新闻 +--- + +##### + +![](/assets/img/news/x-easypdf-v3.3.0-0.png) + + + +x-easypdf是一个java语言简化处理pdf的框架,包含fop模块与pdfbox模块,fop模块以创建功能为主,基于xsl-fo模板生成pdf文档,以数据源的方式进行模板渲染;pdfbox模块以编辑功能为主,对标准的pdfbox进行扩展,添加了成吨的功能。 + +本次更新内容如下: + +###### 新特性: + +* 【pdfbox】新增jpeg2000格式图像支持 + +* 【pdfbox】新增大模型解析文档的支持 + +* 【pdfbox】新增开源中国(gitee)AI解析器 + +* 【pdfbox】新增智谱(glm)AI解析器 + +* 【pdfbox】新增腾讯(hunyuan)AI解析器 + +* 【pdfbox】新增阿里(qwen)AI解析器 + +* 【pdfbox】新增深度求索(deepseek)AI解析器 + +* 【pdfbox】新增字节跳动(doubao)AI解析器 + +* 【pdfbox】新增昆仑万维(tiangong)AI解析器 + +* 【pdfbox】新增月之暗面(kimi)AI解析器 + +* 【pdfbox】新增讯飞(spark)AI解析器 + +* 【pdfbox】新增线性化支持 + +* 【pdfbox】新增office文件转换pdf的支持(依赖office服务) + +* 【pdfbox】新增word转换器 + +* 【pdfbox】新增excel转换器 + +* 【pdfbox】新增ppt转换器 + +* 【pdfbox】新增html转换器 + +* 【pdfbox】新增rtf转换器 + +* 【pdfbox】新增附件处理器 + +* 【pdfbox】新增加载awt字体的支持 + +* 【fop】新增条形码无白边配置 + +* 【fop】新增设置条形码缓存的方法 + +* 【fop】新增权限配置 + +* 【fop】新增从资源路径加载awt字体的支持 + + +###### 原有变更: + +maven坐标变更,原 `groupId “org.dromara.x-easypdf”` 变更为 `org.dromara` + +###### 问题修复: + +* 【pdfbox模块】修复表格组件单元格添加多组件换行错误问题 + +* 【pdfbox模块】修复表格重叠问题 + +* 【pdfbox模块】修复空文本错误问题 + + + + +Gitee: https://gitee.com/dromara/x-easypdf  + +AI解析功能演示:  + +![](/assets/img/news/x-easypdf-v3.3.0-1.jpg) + + + + + +关于 Dromara + +Dromara 是由国内顶尖的开源项目作者共同组成的开源社区。提供包括分布式事务,流行工具,企业级认证,微服务RPC,运维监控,Agent监控,分布式日志,调度编排等一系列开源产品、解决方案与咨询、技术支持与培训认证服务。技术栈全面开源共建、 保持社区中立,致力于为全球用户提供微服务云原生解决方案。让参与的每一位开源爱好者,体会到开源的快乐。 + + + +Dromara开源社区目前拥有10+GVP项目,总star数量超过十万,构建了上万人的开源社区,有成千上万的个人及团队在使用Dromara社区的开源项目。 + +**欢迎大家来到知识星球和我互动** + +![](/assets/img/news/x-easypdf-v3.3.0-2.webp) \ No newline at end of file diff --git a/src/zh/news/x-file-storage-2.0.0.md b/src/zh/news/x-file-storage-2.0.0.md index 03b270730d..3cb6fc2bb3 100644 --- a/src/zh/news/x-file-storage-2.0.0.md +++ b/src/zh/news/x-file-storage-2.0.0.md @@ -1,266 +1,266 @@ ---- -title: 欢迎 X File Storage 加入 Dromara 开源社区,一站式文件存储 -author: XuYanwu -tag: - - X-File-Storage -date: 2023-10-19 -cover: /assets/img/news/X-File-Storage-Cover.svg -head: - - - meta - - name: 新闻 ---- - -

- logo
- 原名 X Spring File Storage 现已捐赠至 dromara 开源组织 -

- -## 📚 简介 - -一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS、七牛云 Kodo、腾讯云 COS、百度云 BOS、又拍云 USS、MinIO、 -Amazon S3、GoogleCloud Storage、金山云 KS3、美团云 MSS、京东云 OSS、天翼云 OOS、移动 云 EOS、沃云 OSS、 -网易数帆 NOS、Ucloud US3、青云 QingStor、平安云 OBS、首云 OSS、IBM COS、其它兼容 S3 协议的存储平台。查看 [所有支持的存储平台](https://x-file-storage.xuyanwu.cn/#/存储平台) - -💡 通过 WebDAV 连接到 Alist 后,可以使用百度网盘、天翼云盘、阿里云盘、迅雷网盘等常见存储服务,查看 [Alist 支持的存储平台](https://alist-doc.nn.ci/docs/webdav) - -GitHub:https://github.com/dromara/x-file-storage -Gitee:https://gitee.com/dromara/x-file-storage - -文档 1:https://x-file-storage.dromara.org -文档 2:https://x-file-storage.xuyanwu.cn -文档 3:https://spring-file-storage.xuyanwu.cn - ---- - -## 📜 更新内容 - -- 更改项目名、更改包名、优化项目结构 -- 新增直接读取 HttpServletRequest 的流进行上传,文件不落盘,速度更快 -- 新增支持 Metadata 元数据 -- 优化 ACL 异常处理 -- 优化文件删除逻辑 -- 修复 Amazon S3 上传文件偶现 ResetException 问题 -- 捐赠至 [dromara](https://dromara.org/zh) 开源社区 - -#### 项目依赖的变化 - -2.0.0 之前的版本 - -```xml - - cn.xuyanwu - spring-file-storage - 1.0.3 - -``` - -2.0.0 及以后的版本 - -```xml - - org.dromara.x-file-storage - x-file-storage-spring - 2.0.0 - -``` - -#### 配置参数的变化 - -2.0.0 之前的版本 - -```yaml -spring: - file-storage: #文件存储配置 - default-platform: huawei-obs-1 #默认使用的存储平台 - thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】 - #对应平台的配置写在这里,注意缩进要对齐 -``` - -2.0.0 及以后的版本 - -```yaml -dromara: - x-file-storage: #文件存储配置 - default-platform: huawei-obs-1 #默认使用的存储平台 - thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】 - #对应平台的配置写在这里,注意缩进要对齐 -``` - -#### 包名的变化 - -2.0.0 之前的版本 - -```java -cn.xuyanwu.spring.file.storage -cn.xuyanwu.spring.file.storage.spring -``` - -2.0.0 及以后的版本 - -```java -org.dromara.x.file.storage.core -org.dromara.x.file.storage.spring -``` - ---- - -## 📦 使用 - -点击 [快速入门](https://x-file-storage.xuyanwu.cn/#/快速入门) 查看全部存储平台的使用方法! - -#### 🔧 配置 - -这里以阿里云 OSS 为例,`pom.xml` 引入本项目,这里默认是 `SpringBoot` 环境,其它环境参考 [脱离 SpringBoot 单独使用](https://x-file-storage.xuyanwu.cn/#/脱离SpringBoot单独使用) - -```xml - - - org.dromara.x-file-storage - x-file-storage-spring - 2.0.0 - - - - com.aliyun.oss - aliyun-sdk-oss - 3.16.1 - -``` - -`application.yml` 配置文件中添加以下基础配置 - -```yaml -dromara: - x-file-storage: #文件存储配置 - default-platform: aliyun-oss-1 #默认使用的存储平台 - aliyun-oss: - - platform: aliyun-oss-1 # 存储平台标识 - enable-storage: true # 启用存储 - access-key: ?? - secret-key: ?? - end-point: ?? - bucket-name: ?? - domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ - base-path: test/ # 基础路径 -``` - -#### 🔨 编码 - -在启动类上加上`@EnableFileStorage`注解 - -```java -@EnableFileStorage -@SpringBootApplication -public class SpringFileStorageTestApplication { - - public static void main(String[] args) { - SpringApplication.run(SpringFileStorageTestApplication.class,args); - } - -} -``` - -#### ✨ 开始上传 - -支持 File、MultipartFile、byte[]、InputStream、URL、URI、String、HttpServletRequest,大文件会自动分片上传。如果想支持更多方式,请阅读 [文件适配器](https://x-file-storage.xuyanwu.cn/#/文件适配器) 章节 - -```java -@RestController -public class FileDetailController { - - @Autowired - private FileStorageService fileStorageService;//注入实列 - - /** - * 上传文件 - */ - @PostMapping("/upload") - public FileInfo upload(MultipartFile file) { - //只需要这一行代码即可上传成功 - return fileStorageService.of(file).upload(); - } - - /** - * 上传文件,成功返回文件 url - */ - @PostMapping("/upload2") - public String upload2(MultipartFile file) { - FileInfo fileInfo = fileStorageService.of(file) - .setPath("upload/") //保存到相对路径下,为了方便管理,不需要可以不写 - .setObjectId("0") //关联对象id,为了方便管理,不需要可以不写 - .setObjectType("0") //关联对象类型,为了方便管理,不需要可以不写 - .putAttr("role","admin") //保存一些属性,可以在切面、保存上传记录、自定义存储平台等地方获取使用,不需要可以不写 - .upload(); //将文件上传到对应地方 - return fileInfo == null ? "上传失败!" : fileInfo.getUrl(); - } - - /** - * 上传图片,成功返回文件信息 - * 图片处理使用的是 https://github.com/coobird/thumbnailator - */ - @PostMapping("/upload-image") - public FileInfo uploadImage(MultipartFile file) { - return fileStorageService.of(file) - .image(img -> img.size(1000,1000)) //将图片大小调整到 1000*1000 - .thumbnail(th -> th.size(200,200)) //再生成一张 200*200 的缩略图 - .upload(); - } - - /** - * 上传文件到指定存储平台,成功返回文件信息 - */ - @PostMapping("/upload-platform") - public FileInfo uploadPlatform(MultipartFile file) { - return fileStorageService.of(file) - .setPlatform("aliyun-oss-1") //使用指定的存储平台 - .upload(); - } - - /** - * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 - * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 - */ - @PostMapping("/upload-request") - public FileInfo uploadPlatform(HttpServletRequest request) { - return fileStorageService.of(request).upload(); - } -} -``` - -#### 🎨 其它操作 - -```java -//手动构造文件信息,可用于其它操作 -FileInfo fileInfo = new FileInfo() - .setPlatform("huawei-obs-1") - .setBasePath("test/") - .setPath("aa/") - .setFilename("image.png") - .setThFilename("image.png.min.jpg"); - -//文件是否存在 -boolean exists = fileStorageService.exists(fileInfo); -//下载 -byte[] bytes = fileStorageService.download(fileInfo).bytes(); -//删除 -fileStorageService.delete(fileInfo); -//其它更多操作 - -``` - -如果将文件记录保存到数据库中,还可以更方便的根据 URL 进行操作了,详情请阅读 [保存上传记录](https://x-file-storage.xuyanwu.cn/#/基础功能?id=保存上传记录) 章节 - -```java -//直接从数据库中获取 FileInfo 对象,更加方便执行其它操作 -FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://abc.def.com/test/aa/image.png"); - -//文件是否存在 -boolean exists = fileStorageService.exists("https://abc.def.com/test/aa/image.png"); -//下载 -byte[] bytes = fileStorageService.download("https://abc.def.com/test/aa/image.png").bytes(); -//删除 -fileStorageService.delete("https://abc.def.com/test/aa/image.png"); -//其它更多操作 -``` - -点击 [快速入门](https://x-file-storage.xuyanwu.cn/#/快速入门) 查看全部存储平台的使用方法! +--- +title: 欢迎 X File Storage 加入 Dromara 开源社区,一站式文件存储 +author: XuYanwu +tag: + - X-File-Storage +date: 2023-10-19 +cover: /assets/img/news/X-File-Storage-Cover.svg +head: + - - meta + - name: 新闻 +--- + +

+ logo
+ 原名 X Spring File Storage 现已捐赠至 dromara 开源组织 +

+ +## 📚 简介 + +一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS、七牛云 Kodo、腾讯云 COS、百度云 BOS、又拍云 USS、MinIO、 +Amazon S3、GoogleCloud Storage、金山云 KS3、美团云 MSS、京东云 OSS、天翼云 OOS、移动 云 EOS、沃云 OSS、 +网易数帆 NOS、Ucloud US3、青云 QingStor、平安云 OBS、首云 OSS、IBM COS、其它兼容 S3 协议的存储平台。查看 [所有支持的存储平台](https://x-file-storage.xuyanwu.cn/#/存储平台) + +💡 通过 WebDAV 连接到 Alist 后,可以使用百度网盘、天翼云盘、阿里云盘、迅雷网盘等常见存储服务,查看 [Alist 支持的存储平台](https://alist-doc.nn.ci/docs/webdav) + +GitHub:https://github.com/dromara/x-file-storage +Gitee:https://gitee.com/dromara/x-file-storage + +文档 1:https://x-file-storage.dromara.org +文档 2:https://x-file-storage.xuyanwu.cn +文档 3:https://spring-file-storage.xuyanwu.cn + +--- + +## 📜 更新内容 + +- 更改项目名、更改包名、优化项目结构 +- 新增直接读取 HttpServletRequest 的流进行上传,文件不落盘,速度更快 +- 新增支持 Metadata 元数据 +- 优化 ACL 异常处理 +- 优化文件删除逻辑 +- 修复 Amazon S3 上传文件偶现 ResetException 问题 +- 捐赠至 [dromara](https://dromara.org/zh) 开源社区 + +#### 项目依赖的变化 + +2.0.0 之前的版本 + +```xml + + cn.xuyanwu + spring-file-storage + 1.0.3 + +``` + +2.0.0 及以后的版本 + +```xml + + org.dromara.x-file-storage + x-file-storage-spring + 2.0.0 + +``` + +#### 配置参数的变化 + +2.0.0 之前的版本 + +```yaml +spring: + file-storage: #文件存储配置 + default-platform: huawei-obs-1 #默认使用的存储平台 + thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】 + #对应平台的配置写在这里,注意缩进要对齐 +``` + +2.0.0 及以后的版本 + +```yaml +dromara: + x-file-storage: #文件存储配置 + default-platform: huawei-obs-1 #默认使用的存储平台 + thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】 + #对应平台的配置写在这里,注意缩进要对齐 +``` + +#### 包名的变化 + +2.0.0 之前的版本 + +```java +cn.xuyanwu.spring.file.storage +cn.xuyanwu.spring.file.storage.spring +``` + +2.0.0 及以后的版本 + +```java +org.dromara.x.file.storage.core +org.dromara.x.file.storage.spring +``` + +--- + +## 📦 使用 + +点击 [快速入门](https://x-file-storage.xuyanwu.cn/#/快速入门) 查看全部存储平台的使用方法! + +#### 🔧 配置 + +这里以阿里云 OSS 为例,`pom.xml` 引入本项目,这里默认是 `SpringBoot` 环境,其它环境参考 [脱离 SpringBoot 单独使用](https://x-file-storage.xuyanwu.cn/#/脱离SpringBoot单独使用) + +```xml + + + org.dromara.x-file-storage + x-file-storage-spring + 2.0.0 + + + + com.aliyun.oss + aliyun-sdk-oss + 3.16.1 + +``` + +`application.yml` 配置文件中添加以下基础配置 + +```yaml +dromara: + x-file-storage: #文件存储配置 + default-platform: aliyun-oss-1 #默认使用的存储平台 + aliyun-oss: + - platform: aliyun-oss-1 # 存储平台标识 + enable-storage: true # 启用存储 + access-key: ?? + secret-key: ?? + end-point: ?? + bucket-name: ?? + domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ + base-path: test/ # 基础路径 +``` + +#### 🔨 编码 + +在启动类上加上`@EnableFileStorage`注解 + +```java +@EnableFileStorage +@SpringBootApplication +public class SpringFileStorageTestApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringFileStorageTestApplication.class,args); + } + +} +``` + +#### ✨ 开始上传 + +支持 File、MultipartFile、byte[]、InputStream、URL、URI、String、HttpServletRequest,大文件会自动分片上传。如果想支持更多方式,请阅读 [文件适配器](https://x-file-storage.xuyanwu.cn/#/文件适配器) 章节 + +```java +@RestController +public class FileDetailController { + + @Autowired + private FileStorageService fileStorageService;//注入实列 + + /** + * 上传文件 + */ + @PostMapping("/upload") + public FileInfo upload(MultipartFile file) { + //只需要这一行代码即可上传成功 + return fileStorageService.of(file).upload(); + } + + /** + * 上传文件,成功返回文件 url + */ + @PostMapping("/upload2") + public String upload2(MultipartFile file) { + FileInfo fileInfo = fileStorageService.of(file) + .setPath("upload/") //保存到相对路径下,为了方便管理,不需要可以不写 + .setObjectId("0") //关联对象id,为了方便管理,不需要可以不写 + .setObjectType("0") //关联对象类型,为了方便管理,不需要可以不写 + .putAttr("role","admin") //保存一些属性,可以在切面、保存上传记录、自定义存储平台等地方获取使用,不需要可以不写 + .upload(); //将文件上传到对应地方 + return fileInfo == null ? "上传失败!" : fileInfo.getUrl(); + } + + /** + * 上传图片,成功返回文件信息 + * 图片处理使用的是 https://github.com/coobird/thumbnailator + */ + @PostMapping("/upload-image") + public FileInfo uploadImage(MultipartFile file) { + return fileStorageService.of(file) + .image(img -> img.size(1000,1000)) //将图片大小调整到 1000*1000 + .thumbnail(th -> th.size(200,200)) //再生成一张 200*200 的缩略图 + .upload(); + } + + /** + * 上传文件到指定存储平台,成功返回文件信息 + */ + @PostMapping("/upload-platform") + public FileInfo uploadPlatform(MultipartFile file) { + return fileStorageService.of(file) + .setPlatform("aliyun-oss-1") //使用指定的存储平台 + .upload(); + } + + /** + * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 + * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 + */ + @PostMapping("/upload-request") + public FileInfo uploadPlatform(HttpServletRequest request) { + return fileStorageService.of(request).upload(); + } +} +``` + +#### 🎨 其它操作 + +```java +//手动构造文件信息,可用于其它操作 +FileInfo fileInfo = new FileInfo() + .setPlatform("huawei-obs-1") + .setBasePath("test/") + .setPath("aa/") + .setFilename("image.png") + .setThFilename("image.png.min.jpg"); + +//文件是否存在 +boolean exists = fileStorageService.exists(fileInfo); +//下载 +byte[] bytes = fileStorageService.download(fileInfo).bytes(); +//删除 +fileStorageService.delete(fileInfo); +//其它更多操作 + +``` + +如果将文件记录保存到数据库中,还可以更方便的根据 URL 进行操作了,详情请阅读 [保存上传记录](https://x-file-storage.xuyanwu.cn/#/基础功能?id=保存上传记录) 章节 + +```java +//直接从数据库中获取 FileInfo 对象,更加方便执行其它操作 +FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://abc.def.com/test/aa/image.png"); + +//文件是否存在 +boolean exists = fileStorageService.exists("https://abc.def.com/test/aa/image.png"); +//下载 +byte[] bytes = fileStorageService.download("https://abc.def.com/test/aa/image.png").bytes(); +//删除 +fileStorageService.delete("https://abc.def.com/test/aa/image.png"); +//其它更多操作 +``` + +点击 [快速入门](https://x-file-storage.xuyanwu.cn/#/快速入门) 查看全部存储平台的使用方法! diff --git a/src/zh/news/x-file-storage-2.1.0.md b/src/zh/news/x-file-storage-2.1.0.md index 8c69b7a923..b3fa5ed3a4 100644 --- a/src/zh/news/x-file-storage-2.1.0.md +++ b/src/zh/news/x-file-storage-2.1.0.md @@ -1,218 +1,218 @@ ---- -title: 一站式文件存储 X File Storage 发布 2.1.0 版本 -author: x-file-storage -date: 2024-01-22 -cover: /assets/img/news/X-File-Storage-Cover.svg -head: - - - meta - - name: 新闻 ---- - -原名 X Spring File Storage 现已捐赠至 dromara 开源组织 - -x-file-storage.dromara.org | x-file-storage.xuyanwu.cn | spring-file-storage.xuyanwu.cn - -![](/assets/img/news/x-file-storage-2.1.0-1.svg) ![](/assets/img/news/x-file-storage-2.1.0-2.svg) ![](/assets/img/news/x-file-storage-2.1.0-3.svg) - -# 📚 简介 - -一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS、七牛云 Kodo、腾讯云 COS、百度云 BOS、又拍云 USS、MinIO、 Amazon S3、GoogleCloud Storage、FastDFS、 Azure Blob Storage、Cloudflare R2、金山云 KS3、美团云 MSS、京东云 OSS、天翼云 OOS、移动 云 EOS、沃云 OSS、网易数帆 NOS、Ucloud US3、青云 QingStor、平安云 OBS、首云 OSS、IBM COS、其它兼容 S3 协议的存储平台。查看 所有支持的存储平台 - -💡 通过 WebDAV 连接到 Alist 后,可以使用百度网盘、天翼云盘、阿里云盘、迅雷网盘等常见存储服务,查看 Alist 支持的存储平台 - -GitHub:https://github.com/dromara/x-file-storage - -Gitee:https://gitee.com/dromara/x-file-storage - -文档 1:https://x-file-storage.dromara.org - -文档 2:https://x-file-storage.xuyanwu.cn - -文档 3:https://spring-file-storage.xuyanwu.cn - ---- - -# 📜 更新内容 - -- 新增 FastDFS 存储平台 -- 新增 Azure Blob Storage 存储平台 -- 新增复制文件,支持跨存储平台复制 -- 新增移动(重命名)文件,支持跨存储平台移动(重命名) -- 新增大文件手动分片上传(断点续传),1.0.0 版本早已支持大文件自动分片上传 -- 新增计算哈希功能,上传下载时可以边处理边计算 -- 上传无需强制获取文件大小,上传未知大小的文件更友好 -- 优化 SpringBoot 自动配置兼容非 SpringWeb 环境 -- 优化 FileKey 获取方式,避免空指针异常 -- 优化上传代码结构 -- 优化异常处理 -- 优化进度监听器 -- 修复上传时设置缩略图保存名称错误的 BUG -- 兼容低版本 SpringBoot(2.0.x)的依赖注入 -- 修复华为云 OBS 上传进度问题 -- 修复 MultipartFile 存储到本地时,在某些情况下输入流未关闭的问题 -- 修复 又拍云 USS 上传缩略图文件时 Response 未关闭的问题 - ---- - -# 📦 使用 - -点击 快速入门 查看全部存储平台的使用方法! - -#### 🔧 配置 - -这里以阿里云 OSS 为例,`pom.xml` 引入本项目,这里默认是 `SpringBoot` 环境,其它环境参考 脱离 SpringBoot 单独使用 - -``` - - - org.dromara.x-file-storage - x-file-storage-spring - 2.1.0 - - - - com.aliyun.oss - aliyun-sdk-oss - 3.16.1 - -``` - -`application.yml` 配置文件中添加以下基础配置 - -``` -dromara: - x-file-storage: #文件存储配置 - default-platform: aliyun-oss-1 #默认使用的存储平台 - aliyun-oss: - - platform: aliyun-oss-1 # 存储平台标识 - enable-storage: true # 启用存储 - access-key: ?? - secret-key: ?? - end-point: ?? - bucket-name: ?? - domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ - base-path: test/ # 基础路径 -``` - -#### 🔨 编码 - -在启动类上加上`@EnableFileStorage`注解 - -``` -@EnableFileStorage -@SpringBootApplication -public class SpringFileStorageTestApplication { - - public static void main(String[] args) { - SpringApplication.run(SpringFileStorageTestApplication.class,args); - } - -} -``` - -#### ✨ 开始上传 - -支持 File、MultipartFile、byte\[\]、InputStream、URL、URI、String、HttpServletRequest,大文件会自动分片上传。如果想支持更多方式,请阅读 文件适配器 章节 - -``` -@RestController -public class FileDetailController { - - @Autowired - private FileStorageService fileStorageService;//注入实列 - - /** - * 上传文件 - */ - @PostMapping("/upload") - public FileInfo upload(MultipartFile file) { - //只需要这一行代码即可上传成功 - return fileStorageService.of(file).upload(); - } - - /** - * 上传文件,成功返回文件 url - */ - @PostMapping("/upload2") - public String upload2(MultipartFile file) { - FileInfo fileInfo = fileStorageService.of(file) - .setPath("upload/") //保存到相对路径下,为了方便管理,不需要可以不写 - .setObjectId("0") //关联对象id,为了方便管理,不需要可以不写 - .setObjectType("0") //关联对象类型,为了方便管理,不需要可以不写 - .putAttr("role","admin") //保存一些属性,可以在切面、保存上传记录、自定义存储平台等地方获取使用,不需要可以不写 - .upload(); //将文件上传到对应地方 - return fileInfo == null ? "上传失败!" : fileInfo.getUrl(); - } - - /** - * 上传图片,成功返回文件信息 - * 图片处理使用的是 https://github.com/coobird/thumbnailator - */ - @PostMapping("/upload-image") - public FileInfo uploadImage(MultipartFile file) { - return fileStorageService.of(file) - .image(img -> img.size(1000,1000)) //将图片大小调整到 1000*1000 - .thumbnail(th -> th.size(200,200)) //再生成一张 200*200 的缩略图 - .upload(); - } - - /** - * 上传文件到指定存储平台,成功返回文件信息 - */ - @PostMapping("/upload-platform") - public FileInfo uploadPlatform(MultipartFile file) { - return fileStorageService.of(file) - .setPlatform("aliyun-oss-1") //使用指定的存储平台 - .upload(); - } - - /** - * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 - * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 - */ - @PostMapping("/upload-request") - public FileInfo uploadPlatform(HttpServletRequest request) { - return fileStorageService.of(request).upload(); - } -} -``` - -#### 🎨 其它操作 - -``` -//手动构造文件信息,可用于其它操作 -FileInfo fileInfo = new FileInfo() - .setPlatform("huawei-obs-1") - .setBasePath("test/") - .setPath("aa/") - .setFilename("image.png") - .setThFilename("image.png.min.jpg"); - -//文件是否存在 -boolean exists = fileStorageService.exists(fileInfo); -//下载 -byte[] bytes = fileStorageService.download(fileInfo).bytes(); -//删除 -fileStorageService.delete(fileInfo); -//其它更多操作 - -``` - -如果将文件记录保存到数据库中,还可以更方便的根据 URL 进行操作了,详情请阅读 保存上传记录 章节 - -``` -//直接从数据库中获取 FileInfo 对象,更加方便执行其它操作 -FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://abc.def.com/test/aa/image.png"); - -//文件是否存在 -boolean exists = fileStorageService.exists("https://abc.def.com/test/aa/image.png"); -//下载 -byte[] bytes = fileStorageService.download("https://abc.def.com/test/aa/image.png").bytes(); -//删除 -fileStorageService.delete("https://abc.def.com/test/aa/image.png"); -//其它更多操作 -``` - -点击 快速入门 https://x-file-storage.xuyanwu.cn/ 查看全部存储平台的使用方法! - ---- +--- +title: 一站式文件存储 X File Storage 发布 2.1.0 版本 +author: x-file-storage +date: 2024-01-22 +cover: /assets/img/news/X-File-Storage-Cover.svg +head: + - - meta + - name: 新闻 +--- + +原名 X Spring File Storage 现已捐赠至 dromara 开源组织 + +x-file-storage.dromara.org | x-file-storage.xuyanwu.cn | spring-file-storage.xuyanwu.cn + +![](/assets/img/news/x-file-storage-2.1.0-1.svg) ![](/assets/img/news/x-file-storage-2.1.0-2.svg) ![](/assets/img/news/x-file-storage-2.1.0-3.svg) + +# 📚 简介 + +一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS、七牛云 Kodo、腾讯云 COS、百度云 BOS、又拍云 USS、MinIO、 Amazon S3、GoogleCloud Storage、FastDFS、 Azure Blob Storage、Cloudflare R2、金山云 KS3、美团云 MSS、京东云 OSS、天翼云 OOS、移动 云 EOS、沃云 OSS、网易数帆 NOS、Ucloud US3、青云 QingStor、平安云 OBS、首云 OSS、IBM COS、其它兼容 S3 协议的存储平台。查看 所有支持的存储平台 + +💡 通过 WebDAV 连接到 Alist 后,可以使用百度网盘、天翼云盘、阿里云盘、迅雷网盘等常见存储服务,查看 Alist 支持的存储平台 + +GitHub:https://github.com/dromara/x-file-storage + +Gitee:https://gitee.com/dromara/x-file-storage + +文档 1:https://x-file-storage.dromara.org + +文档 2:https://x-file-storage.xuyanwu.cn + +文档 3:https://spring-file-storage.xuyanwu.cn + +--- + +# 📜 更新内容 + +- 新增 FastDFS 存储平台 +- 新增 Azure Blob Storage 存储平台 +- 新增复制文件,支持跨存储平台复制 +- 新增移动(重命名)文件,支持跨存储平台移动(重命名) +- 新增大文件手动分片上传(断点续传),1.0.0 版本早已支持大文件自动分片上传 +- 新增计算哈希功能,上传下载时可以边处理边计算 +- 上传无需强制获取文件大小,上传未知大小的文件更友好 +- 优化 SpringBoot 自动配置兼容非 SpringWeb 环境 +- 优化 FileKey 获取方式,避免空指针异常 +- 优化上传代码结构 +- 优化异常处理 +- 优化进度监听器 +- 修复上传时设置缩略图保存名称错误的 BUG +- 兼容低版本 SpringBoot(2.0.x)的依赖注入 +- 修复华为云 OBS 上传进度问题 +- 修复 MultipartFile 存储到本地时,在某些情况下输入流未关闭的问题 +- 修复 又拍云 USS 上传缩略图文件时 Response 未关闭的问题 + +--- + +# 📦 使用 + +点击 快速入门 查看全部存储平台的使用方法! + +#### 🔧 配置 + +这里以阿里云 OSS 为例,`pom.xml` 引入本项目,这里默认是 `SpringBoot` 环境,其它环境参考 脱离 SpringBoot 单独使用 + +``` + + + org.dromara.x-file-storage + x-file-storage-spring + 2.1.0 + + + + com.aliyun.oss + aliyun-sdk-oss + 3.16.1 + +``` + +`application.yml` 配置文件中添加以下基础配置 + +``` +dromara: + x-file-storage: #文件存储配置 + default-platform: aliyun-oss-1 #默认使用的存储平台 + aliyun-oss: + - platform: aliyun-oss-1 # 存储平台标识 + enable-storage: true # 启用存储 + access-key: ?? + secret-key: ?? + end-point: ?? + bucket-name: ?? + domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ + base-path: test/ # 基础路径 +``` + +#### 🔨 编码 + +在启动类上加上`@EnableFileStorage`注解 + +``` +@EnableFileStorage +@SpringBootApplication +public class SpringFileStorageTestApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringFileStorageTestApplication.class,args); + } + +} +``` + +#### ✨ 开始上传 + +支持 File、MultipartFile、byte\[\]、InputStream、URL、URI、String、HttpServletRequest,大文件会自动分片上传。如果想支持更多方式,请阅读 文件适配器 章节 + +``` +@RestController +public class FileDetailController { + + @Autowired + private FileStorageService fileStorageService;//注入实列 + + /** + * 上传文件 + */ + @PostMapping("/upload") + public FileInfo upload(MultipartFile file) { + //只需要这一行代码即可上传成功 + return fileStorageService.of(file).upload(); + } + + /** + * 上传文件,成功返回文件 url + */ + @PostMapping("/upload2") + public String upload2(MultipartFile file) { + FileInfo fileInfo = fileStorageService.of(file) + .setPath("upload/") //保存到相对路径下,为了方便管理,不需要可以不写 + .setObjectId("0") //关联对象id,为了方便管理,不需要可以不写 + .setObjectType("0") //关联对象类型,为了方便管理,不需要可以不写 + .putAttr("role","admin") //保存一些属性,可以在切面、保存上传记录、自定义存储平台等地方获取使用,不需要可以不写 + .upload(); //将文件上传到对应地方 + return fileInfo == null ? "上传失败!" : fileInfo.getUrl(); + } + + /** + * 上传图片,成功返回文件信息 + * 图片处理使用的是 https://github.com/coobird/thumbnailator + */ + @PostMapping("/upload-image") + public FileInfo uploadImage(MultipartFile file) { + return fileStorageService.of(file) + .image(img -> img.size(1000,1000)) //将图片大小调整到 1000*1000 + .thumbnail(th -> th.size(200,200)) //再生成一张 200*200 的缩略图 + .upload(); + } + + /** + * 上传文件到指定存储平台,成功返回文件信息 + */ + @PostMapping("/upload-platform") + public FileInfo uploadPlatform(MultipartFile file) { + return fileStorageService.of(file) + .setPlatform("aliyun-oss-1") //使用指定的存储平台 + .upload(); + } + + /** + * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 + * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 + */ + @PostMapping("/upload-request") + public FileInfo uploadPlatform(HttpServletRequest request) { + return fileStorageService.of(request).upload(); + } +} +``` + +#### 🎨 其它操作 + +``` +//手动构造文件信息,可用于其它操作 +FileInfo fileInfo = new FileInfo() + .setPlatform("huawei-obs-1") + .setBasePath("test/") + .setPath("aa/") + .setFilename("image.png") + .setThFilename("image.png.min.jpg"); + +//文件是否存在 +boolean exists = fileStorageService.exists(fileInfo); +//下载 +byte[] bytes = fileStorageService.download(fileInfo).bytes(); +//删除 +fileStorageService.delete(fileInfo); +//其它更多操作 + +``` + +如果将文件记录保存到数据库中,还可以更方便的根据 URL 进行操作了,详情请阅读 保存上传记录 章节 + +``` +//直接从数据库中获取 FileInfo 对象,更加方便执行其它操作 +FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://abc.def.com/test/aa/image.png"); + +//文件是否存在 +boolean exists = fileStorageService.exists("https://abc.def.com/test/aa/image.png"); +//下载 +byte[] bytes = fileStorageService.download("https://abc.def.com/test/aa/image.png").bytes(); +//删除 +fileStorageService.delete("https://abc.def.com/test/aa/image.png"); +//其它更多操作 +``` + +点击 快速入门 https://x-file-storage.xuyanwu.cn/ 查看全部存储平台的使用方法! + +--- diff --git a/src/zh/news/yft-design-0.md b/src/zh/news/yft-design-0.md index 98fccc72fa..0d258d32b8 100644 --- a/src/zh/news/yft-design-0.md +++ b/src/zh/news/yft-design-0.md @@ -1,103 +1,103 @@ ---- -title: 新晋开源项目 yft-design 加入 Dromara 社区,基于Canvas的开源版"创客贴" -author: yft -date: 2023-09-08 -cover: /assets/img/news/yft-design-0-1.png -head: - - - meta - - name: 新闻 ---- - -## yft-design 基于 Canvas 的开源版"创客贴" - -## 项目介绍 - -- dromara 开源组织成员,dromara/yft-design 作者。 -- 使用 Vue3 + TypeScript + Fabric.js + Pinia + ElementPlus 等。 -- 支持文字、图片、形状、线条、二维码 、条形码等几种常用的元素类型。 -- 每一种元素都拥有高度可编辑能力,缩略图显示,模板,支持导出 json,svg,img 等。 -- 在线设计、编辑名片,LOGO,图片,海报等。 - -## 项目运行 - -``` -npm install -npm run dev -npm run build -``` - -## 目录结构 - -``` -├── assets // 静态资源 -│ ├── fonts // 在线字体文件 -│ └── styles // 样式 -├── components // 与业务逻辑无关的通用组件 -├── configs // 配置文件 -├── extension // 扩展元素 -├── hooks // 供多个组件(模块)使用的 hooks 方法 -├── mocks // mocks 数据 -├── plugins // 自定义的 Vue 插件 -├── types // 类型定义文件 -├── store // Pinia store,参考:https://pinia.vuejs.org/ -├── utils // 通用的工具方法 -└── views // 业务组件目录 - ├── Canvas // 编辑模块 - ├── Editor // 页面模块 - └── Event // 事件模块 -``` - -## 页面编辑 - -1. 支持缩略图(右键操作)复制,粘贴,新增,删除,选择,拖动页面顺序 - -![](/assets/img/news/yft-design-0-1.png) - -## 画布编辑 - -1. 支持自定义选择画布尺寸(名片,单页,海报),印刷出血及安全线,印刷拼版,尺寸单位(mm)与(px)自由切换,快捷键滚轮缩放画布 - ![](/assets/img/news/yft-design-0-2.png) - -2. 支持渐变背景多种样式填充,支持线性渐变及经向渐变,可自定义修改角度,位置 - ![](/assets/img/news/yft-design-0-3.png) - -3. 支持网格背景多种样式填充,支持修改参数生成及随机生成和自定义生成模式 - ![](/assets/img/news/yft-design-0-4.png) - -4. 支持条纹背景多种样式填充,支持修改条纹颜色及随机条纹样式 - ![](/assets/img/news/yft-design-0-5.png) - -## 文字编辑 - -1. 支持文字添加横向和纵向,编辑,左对齐,右对齐,居中,字体大小,font-family,字体颜色,背景颜色,粗体,倾斜,下划线,删除线,行距,字距,描边,底纹,图片填充 - ![](/assets/img/news/yft-design-0-6.png) - -## 图片编辑 - -1. 支持上传,图片裁切,水平和垂直翻转,支持多种颜色的蒙版和自定义样式的滤镜 - ![](/assets/img/news/yft-design-0-7.png) - -## 元素编辑 - -1. 支持上传 svg 元素及模板中多种格式的 svg 元素。元素可以自定义大小,颜色及背景填充 - ![](/assets/img/news/yft-design-0-8.png) - -2. 线段支持双击端点拖拽,修改尺寸,虚线 - ![](/assets/img/news/yft-design-0-9.png) - -3. 二维码支持矢量格式印刷,透明底图,自定义二维码内容,边距大小及容错率 - ![](/assets/img/news/yft-design-0-10.png) - -4. 条行码支持矢量格式印刷,支持国际上多种码制,自定义修改码值,码宽及码高 - ![](/assets/img/news/yft-design-0-11.png) - -## 图层编辑 - -1. 图层可显示元素类型,可拖拽图层元素修改元素层级,可删除,锁定,隐藏图层,文字可在图层中编辑 - ![](/assets/img/news/yft-design-0-12.png) - -## 未来规划 - -1. 增加 psd,pdf,cdr 等不同格式导入在线编辑 -2. 图片可以通过元素裁切出不同形状,元素可自定义拖拽设计 -3. 更好的编辑体验,增加 3D 模型在线显示 +--- +title: 新晋开源项目 yft-design 加入 Dromara 社区,基于Canvas的开源版"创客贴" +author: yft +date: 2023-09-08 +cover: /assets/img/news/yft-design-0-1.png +head: + - - meta + - name: 新闻 +--- + +## yft-design 基于 Canvas 的开源版"创客贴" + +## 项目介绍 + +- dromara 开源组织成员,dromara/yft-design 作者。 +- 使用 Vue3 + TypeScript + Fabric.js + Pinia + ElementPlus 等。 +- 支持文字、图片、形状、线条、二维码 、条形码等几种常用的元素类型。 +- 每一种元素都拥有高度可编辑能力,缩略图显示,模板,支持导出 json,svg,img 等。 +- 在线设计、编辑名片,LOGO,图片,海报等。 + +## 项目运行 + +``` +npm install +npm run dev +npm run build +``` + +## 目录结构 + +``` +├── assets // 静态资源 +│ ├── fonts // 在线字体文件 +│ └── styles // 样式 +├── components // 与业务逻辑无关的通用组件 +├── configs // 配置文件 +├── extension // 扩展元素 +├── hooks // 供多个组件(模块)使用的 hooks 方法 +├── mocks // mocks 数据 +├── plugins // 自定义的 Vue 插件 +├── types // 类型定义文件 +├── store // Pinia store,参考:https://pinia.vuejs.org/ +├── utils // 通用的工具方法 +└── views // 业务组件目录 + ├── Canvas // 编辑模块 + ├── Editor // 页面模块 + └── Event // 事件模块 +``` + +## 页面编辑 + +1. 支持缩略图(右键操作)复制,粘贴,新增,删除,选择,拖动页面顺序 + +![](/assets/img/news/yft-design-0-1.png) + +## 画布编辑 + +1. 支持自定义选择画布尺寸(名片,单页,海报),印刷出血及安全线,印刷拼版,尺寸单位(mm)与(px)自由切换,快捷键滚轮缩放画布 + ![](/assets/img/news/yft-design-0-2.png) + +2. 支持渐变背景多种样式填充,支持线性渐变及经向渐变,可自定义修改角度,位置 + ![](/assets/img/news/yft-design-0-3.png) + +3. 支持网格背景多种样式填充,支持修改参数生成及随机生成和自定义生成模式 + ![](/assets/img/news/yft-design-0-4.png) + +4. 支持条纹背景多种样式填充,支持修改条纹颜色及随机条纹样式 + ![](/assets/img/news/yft-design-0-5.png) + +## 文字编辑 + +1. 支持文字添加横向和纵向,编辑,左对齐,右对齐,居中,字体大小,font-family,字体颜色,背景颜色,粗体,倾斜,下划线,删除线,行距,字距,描边,底纹,图片填充 + ![](/assets/img/news/yft-design-0-6.png) + +## 图片编辑 + +1. 支持上传,图片裁切,水平和垂直翻转,支持多种颜色的蒙版和自定义样式的滤镜 + ![](/assets/img/news/yft-design-0-7.png) + +## 元素编辑 + +1. 支持上传 svg 元素及模板中多种格式的 svg 元素。元素可以自定义大小,颜色及背景填充 + ![](/assets/img/news/yft-design-0-8.png) + +2. 线段支持双击端点拖拽,修改尺寸,虚线 + ![](/assets/img/news/yft-design-0-9.png) + +3. 二维码支持矢量格式印刷,透明底图,自定义二维码内容,边距大小及容错率 + ![](/assets/img/news/yft-design-0-10.png) + +4. 条行码支持矢量格式印刷,支持国际上多种码制,自定义修改码值,码宽及码高 + ![](/assets/img/news/yft-design-0-11.png) + +## 图层编辑 + +1. 图层可显示元素类型,可拖拽图层元素修改元素层级,可删除,锁定,隐藏图层,文字可在图层中编辑 + ![](/assets/img/news/yft-design-0-12.png) + +## 未来规划 + +1. 增加 psd,pdf,cdr 等不同格式导入在线编辑 +2. 图片可以通过元素裁切出不同形状,元素可自定义拖拽设计 +3. 更好的编辑体验,增加 3D 模型在线显示 diff --git a/src/zh/projects/README.md b/src/zh/projects/README.md index 7f74c7746a..6704f7a105 100644 --- a/src/zh/projects/README.md +++ b/src/zh/projects/README.md @@ -1,27 +1,29 @@ ---- -title: 项目 -pageInfo: false -contributors: false -editLink: false -lastUpdated: false ---- - - - - - - +--- +title: 项目 +pageInfo: false +contributors: false +editLink: false +lastUpdated: false +sidebar: false +layout: projects +--- + + diff --git a/tsconfig.json b/tsconfig.json index a577915d1b..2d9a7fb46b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,36 +1,36 @@ -{ - "compilerOptions": { - // 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查 - "allowSyntheticDefaultImports": true, - // 解析非相对模块名的基准目录 - "baseUrl": ".", - "esModuleInterop": true, - // 从 tslib 导入辅助工具函数(比如 __extends, __rest等) - "importHelpers": true, - // 指定生成哪个模块系统代码 - "module": "ESNext", - // 决定如何处理模块。 - "moduleResolution": "Bundler", - //启用所有严格类型检查选项。 - //启用--strict相当于启用--noImplicitAny,--noImplicitThis,--alwaysStrict, - //--strictNullChecks和--strictFunctionTypes和--strictPropertyInitialization。 - "strict": true, - // 生成相应的 .map文件 - "sourceMap": true, - // 忽略所有的声明文件( *.d.ts)的类型检查。 - "skipLibCheck": true, - // 指定ECMAScript目标版本 - "target": "ES2022", - // 编译过程中需要引入的库文件的列表。 - "lib": ["ESNext", "DOM"], - // 模块名到基于 baseUrl的路径映射的列表。 - "paths": { - "@/*": ["src/*"] - } - }, - "include": [ - "src/.vuepress/**/*.ts", - "src/.vuepress/**/*.d.ts", - "src/.vuepress/**/*.vue" - ] -} +{ + "compilerOptions": { + // 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查 + "allowSyntheticDefaultImports": true, + // 解析非相对模块名的基准目录 + "baseUrl": ".", + "esModuleInterop": true, + // 从 tslib 导入辅助工具函数(比如 __extends, __rest等) + "importHelpers": true, + // 指定生成哪个模块系统代码 + "module": "ESNext", + // 决定如何处理模块。 + "moduleResolution": "Bundler", + //启用所有严格类型检查选项。 + //启用--strict相当于启用--noImplicitAny,--noImplicitThis,--alwaysStrict, + //--strictNullChecks和--strictFunctionTypes和--strictPropertyInitialization。 + "strict": true, + // 生成相应的 .map文件 + "sourceMap": true, + // 忽略所有的声明文件( *.d.ts)的类型检查。 + "skipLibCheck": true, + // 指定ECMAScript目标版本 + "target": "ES2022", + // 编译过程中需要引入的库文件的列表。 + "lib": ["ESNext", "DOM"], + // 模块名到基于 baseUrl的路径映射的列表。 + "paths": { + "@/*": ["src/*"] + } + }, + "include": [ + "src/.vuepress/**/*.ts", + "src/.vuepress/**/*.d.ts", + "src/.vuepress/**/*.vue" + ] +}