From 88f84bf74a160005a9c2ae208c7ed35c42375eb4 Mon Sep 17 00:00:00 2001 From: Ahoo Wang Date: Tue, 2 Jan 2024 14:01:52 +0800 Subject: [PATCH] feat(doc): Use VitePress instead of Vuepress --- .github/workflows/document-deploy.yml | 64 +++++-- document/docs/guide/README.md | 2 +- document/docs/guide/cosid-redis.md | 2 +- documentation/docs/.vitepress/config.mts | 5 +- documentation/docs/.vitepress/configs/head.ts | 1 - .../docs/.vitepress/configs/sidebar.ts | 65 +++++-- .../advanced}/cosid-annotation.md | 0 .../api => guide/advanced}/id-converter.md | 1 - .../api => guide/advanced}/id-generator.md | 0 .../api => guide/advanced}/provider.md | 0 .../api => guide/advanced}/sharding.md | 0 .../api => guide}/cosid-generator.md | 2 +- documentation/docs/guide/cosid-proxy.md | 1 + .../docs/guide/extensions/cosid-activiti.md | 19 ++ .../docs/guide/extensions/cosid-axon.md | 18 +- .../docs/guide/extensions/cosid-flowable.md | 18 +- .../docs/guide/extensions/cosid-jackson.md | 18 +- .../docs/guide/extensions/cosid-jdbc.md | 16 +- .../docs/guide/extensions/cosid-mongo.md | 19 ++ .../docs/guide/extensions/cosid-mybatis.md | 20 +- .../docs/guide/extensions/cosid-redis.md | 18 +- .../guide/extensions/cosid-shardingsphere.md | 16 +- .../extensions/cosid-spring-boot-starter.md | 25 +++ .../extensions/cosid-spring-data-jdbc.md | 19 ++ .../docs/guide/extensions/cosid-test.md | 82 +++++++++ .../docs/guide/extensions/cosid-zookeeper.md | 16 +- .../guide/{ => faq}/Performance-CosId-Leaf.md | 11 +- documentation/docs/guide/{ => faq}/faq.md | 6 +- .../docs/guide/{ => faq}/perf-test.md | 39 ++-- documentation/docs/guide/{ => faq}/perf-vs.md | 0 documentation/docs/guide/getting-started.md | 56 ++---- documentation/docs/guide/introduction.md | 70 +++---- documentation/docs/guide/segment-chain.md | 15 +- documentation/docs/guide/segment.md | 24 +++ documentation/docs/guide/snowflake.md | 174 ++++++++++++++++++ documentation/docs/index.md | 1 + ...stributor.png => MachineIdDistributor.png} | Bin documentation/docs/reference/api/segment.md | 9 - documentation/docs/reference/api/snowflake.md | 13 -- documentation/package.json | 6 +- 40 files changed, 692 insertions(+), 179 deletions(-) rename documentation/docs/{reference/api => guide/advanced}/cosid-annotation.md (100%) rename documentation/docs/{reference/api => guide/advanced}/id-converter.md (99%) rename documentation/docs/{reference/api => guide/advanced}/id-generator.md (100%) rename documentation/docs/{reference/api => guide/advanced}/provider.md (100%) rename documentation/docs/{reference/api => guide/advanced}/sharding.md (100%) rename documentation/docs/{reference/api => guide}/cosid-generator.md (91%) create mode 100644 documentation/docs/guide/cosid-proxy.md create mode 100644 documentation/docs/guide/extensions/cosid-activiti.md create mode 100644 documentation/docs/guide/extensions/cosid-mongo.md create mode 100644 documentation/docs/guide/extensions/cosid-spring-boot-starter.md create mode 100644 documentation/docs/guide/extensions/cosid-spring-data-jdbc.md create mode 100644 documentation/docs/guide/extensions/cosid-test.md rename documentation/docs/guide/{ => faq}/Performance-CosId-Leaf.md (93%) rename documentation/docs/guide/{ => faq}/faq.md (76%) rename documentation/docs/guide/{ => faq}/perf-test.md (89%) rename documentation/docs/guide/{ => faq}/perf-vs.md (100%) create mode 100644 documentation/docs/guide/segment.md create mode 100644 documentation/docs/guide/snowflake.md rename documentation/docs/public/assets/design/{RedisMachineIdDistributor.png => MachineIdDistributor.png} (100%) delete mode 100644 documentation/docs/reference/api/segment.md delete mode 100644 documentation/docs/reference/api/snowflake.md diff --git a/.github/workflows/document-deploy.yml b/.github/workflows/document-deploy.yml index 3d2a730b63..4d5e39809f 100644 --- a/.github/workflows/document-deploy.yml +++ b/.github/workflows/document-deploy.yml @@ -11,11 +11,18 @@ # limitations under the License. # -name: Document Build and Deploy +name: Documentation Build and Deploy on: push: paths: - - 'document/**' + - 'documentation/**' + pull_request: + paths: + - 'documentation/**' + workflow_dispatch: +permissions: + contents: write + jobs: build-and-deploy: runs-on: ubuntu-latest @@ -23,23 +30,42 @@ jobs: - name: Checkout uses: actions/checkout@master - - name: Build and Deploy - uses: jenkey2011/vuepress-deploy@master + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Build VitePress site + working-directory: documentation + run: npm add -D vitepress && npm run docs:build + + - name: Deploy to gh-pages + uses: crazy-max/ghaction-github-pages@v4 env: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - TARGET_REPO: Ahoo-Wang/CosId - TARGET_BRANCH: gh-pages - BUILD_SCRIPT: cd document && yarn && yarn docs:build - BUILD_DIR: docs/.vuepress/dist/ - CNAME: cosid.ahoo.me - - - name: Build and Deploy With Site Base - uses: jenkey2011/vuepress-deploy@master + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + target_branch: gh-pages + build_dir: documentation/docs/.vitepress/dist/ + fqdn: cosid.ahoo.me + + - name: Build VitePress site with SITE_BASE + working-directory: documentation env: SITE_BASE: /cosid/ - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - TARGET_REPO: Ahoo-Wang/CosId - TARGET_BRANCH: gh-pages-with-site-base - BUILD_SCRIPT: cd document && yarn && yarn docs:build - BUILD_DIR: docs/.vuepress/dist/ - CNAME: cosid.ahoo.me \ No newline at end of file + run: npm add -D vitepress && npm run docs:build + + - name: Deploy to gh-pages-with-site-base + uses: crazy-max/ghaction-github-pages@v4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + target_branch: gh-pages-with-site-base + build_dir: documentation/docs/.vitepress/dist/ + + - name: Sync to Gitee + uses: wearerequired/git-mirror-action@master + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + with: + source-repo: "git@github.com:Ahoo-Wang/CosId.git" + destination-repo: "git@gitee.com:AhooWang/CosId.git" \ No newline at end of file diff --git a/document/docs/guide/README.md b/document/docs/guide/README.md index 7734cded29..3f0464e00b 100644 --- a/document/docs/guide/README.md +++ b/document/docs/guide/README.md @@ -154,7 +154,7 @@ UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据 - ZookeeperMachineIdDistributor: 使用**ZooKeeper**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。

- RedisMachineIdDistributor + RedisMachineIdDistributor

diff --git a/document/docs/guide/cosid-redis.md b/document/docs/guide/cosid-redis.md index bf91052f05..0b140a9133 100644 --- a/document/docs/guide/cosid-redis.md +++ b/document/docs/guide/cosid-redis.md @@ -21,7 +21,7 @@ ## SpringRedisMachineIdDistributor

- SegmentId + SegmentId

diff --git a/documentation/docs/.vitepress/config.mts b/documentation/docs/.vitepress/config.mts index d929f2f9b8..0d7b787690 100644 --- a/documentation/docs/.vitepress/config.mts +++ b/documentation/docs/.vitepress/config.mts @@ -3,6 +3,7 @@ import {SITE_BASE} from "./configs/SITE_BASE"; import {head} from "./configs/head"; import {navbar} from "./configs/navbar"; import {sidebar} from "./configs/sidebar"; +import {withMermaid} from "vitepress-plugin-mermaid"; let hostname = 'https://cosid.ahoo.me/'; if (SITE_BASE == '/wow/') { @@ -10,7 +11,7 @@ if (SITE_BASE == '/wow/') { } // https://vitepress.dev/reference/site-config -export default defineConfig({ +let userConfig = defineConfig({ lang: 'zh-CN', title: "CosId", description: "通用、灵活、高性能的分布式ID生成器", @@ -54,3 +55,5 @@ export default defineConfig({ } } }) + +export default withMermaid(userConfig) \ No newline at end of file diff --git a/documentation/docs/.vitepress/configs/head.ts b/documentation/docs/.vitepress/configs/head.ts index 4a4655def9..c1a1245c93 100644 --- a/documentation/docs/.vitepress/configs/head.ts +++ b/documentation/docs/.vitepress/configs/head.ts @@ -23,7 +23,6 @@ export const head: HeadConfig[] = [ ['meta', {'http-equiv': 'cache-control', content: 'no-cache, no-store, must-revalidate'}], ['meta', {'http-equiv': 'pragma', content: 'no-cache'}], ['meta', {'http-equiv': 'expires', content: '0'}], - ['link', {rel: 'manifest', href: `${SITE_BASE}manifest.webmanifest`}], ['meta', {name: 'application-name', content: 'CosId'}], ['meta', {name: 'theme-color', content: '#5f67ee'}], [ diff --git a/documentation/docs/.vitepress/configs/sidebar.ts b/documentation/docs/.vitepress/configs/sidebar.ts index 4d0d28b60d..78dc4220b3 100644 --- a/documentation/docs/.vitepress/configs/sidebar.ts +++ b/documentation/docs/.vitepress/configs/sidebar.ts @@ -22,10 +22,11 @@ export const sidebar: DefaultTheme.Sidebar = { items: [ {text: '简介', link: 'introduction'}, {text: '快速上手', link: 'getting-started'}, - {text: '号段链模式', link: 'segment-chain'}, - {text: 'Faq', link: 'faq'}, - {text: '性能评测', link: 'perf-test'}, - {text: 'CosId VS 美团 Leaf', link: 'Performance-CosId-Leaf'}, + {text: 'SnowflakeId', link: 'snowflake'}, + {text: 'SegmentId', link: 'segment'}, + {text: 'SegmentChainId', link: 'segment-chain'}, + {text: 'CosIdGenerator', link: 'cosid-generator'}, + {text: 'CosIdProxy', link: 'cosid-proxy'}, ], }, { base: '/guide/extensions/', @@ -34,12 +35,36 @@ export const sidebar: DefaultTheme.Sidebar = { items: [ {text: 'Redis', link: 'cosid-redis'}, {text: 'Jdbc', link: 'cosid-jdbc'}, + {text: 'MongoDB', link: 'cosid-mongo'}, {text: 'Zookeeper', link: 'cosid-zookeeper'}, {text: 'MyBatis', link: 'cosid-mybatis'}, {text: 'Jackson', link: 'cosid-jackson'}, - {text: 'ShardingSphere', link: 'cosid-shardingsphere'}, - {text: 'Axon', link: 'cosid-axon'}, + {text: 'Spring-Data-Jdbc', link: 'cosid-spring-data-jdbc'}, + {text: 'Spring-Boot-Starter', link: 'cosid-spring-boot-starter'}, + {text: 'Activiti', link: 'cosid-activiti'}, {text: 'Flowable', link: 'cosid-flowable'}, + {text: 'Axon', link: 'cosid-axon'}, + {text: 'ShardingSphere', link: 'cosid-shardingsphere'}, + {text: '兼容性测试套件', link: 'cosid-test'}, + ], + }, { + base: '/guide/faq/', + text: 'FAQ', + collapsed: false, + items: [ + {text: '常见问题', link: 'faq'}, + {text: '性能评测', link: 'perf-test'}, + {text: 'CosId VS 美团 Leaf', link: 'Performance-CosId-Leaf'}, + ], + }, { + base: '/guide/advanced/', + text: '深入', + collapsed: false, + items: [ + {text: 'IdGenerator', link: 'id-generator'}, + {text: 'IdConverter', link: 'id-converter'}, + {text: 'IdGeneratorProvider', link: 'provider'}, + {text: 'Sharding', link: 'sharding'}, ], }, { text: '配置', @@ -60,20 +85,20 @@ export const sidebar: DefaultTheme.Sidebar = { {text: 'ShardingSphere', link: 'shardingsphere'}, ], }, - { - text: 'API', - base: '/reference/api/', - collapsed: false, - items: [ - {text: 'IdGenerator', link: 'id-generator'}, - {text: 'IdConverter', link: 'id-converter'}, - {text: 'IdGeneratorProvider', link: 'provider'}, - {text: 'CosIdGenerator', link: 'cosid-generator'}, - {text: 'Segment', link: 'segment'}, - {text: 'SnowflakeId', link: 'snowflake'}, - {text: 'Sharding', link: 'sharding'}, - ], - }, + // { + // text: 'API', + // base: '/reference/api/', + // collapsed: false, + // items: [ + // {text: 'IdGenerator', link: 'id-generator'}, + // {text: 'IdConverter', link: 'id-converter'}, + // {text: 'IdGeneratorProvider', link: 'provider'}, + // {text: 'CosIdGenerator', link: 'cosid-generator'}, + // {text: 'Segment', link: 'segment'}, + // {text: 'SnowflakeId', link: 'snowflake'}, + // {text: 'Sharding', link: 'sharding'}, + // ], + // }, { text: '博客', base: '/reference/blog/', diff --git a/documentation/docs/reference/api/cosid-annotation.md b/documentation/docs/guide/advanced/cosid-annotation.md similarity index 100% rename from documentation/docs/reference/api/cosid-annotation.md rename to documentation/docs/guide/advanced/cosid-annotation.md diff --git a/documentation/docs/reference/api/id-converter.md b/documentation/docs/guide/advanced/id-converter.md similarity index 99% rename from documentation/docs/reference/api/id-converter.md rename to documentation/docs/guide/advanced/id-converter.md index cc005a04b4..34340429d9 100644 --- a/documentation/docs/reference/api/id-converter.md +++ b/documentation/docs/guide/advanced/id-converter.md @@ -48,7 +48,6 @@ public interface IdConverter { > 雪花Id转换器,将符合雪花规则的字符串,转换成 long ,或者long 转换成雪花规则字符串 - ## PrefixIdConverter > 将带有前缀的字符串转换成long,或者将long转换成带前缀字符串 diff --git a/documentation/docs/reference/api/id-generator.md b/documentation/docs/guide/advanced/id-generator.md similarity index 100% rename from documentation/docs/reference/api/id-generator.md rename to documentation/docs/guide/advanced/id-generator.md diff --git a/documentation/docs/reference/api/provider.md b/documentation/docs/guide/advanced/provider.md similarity index 100% rename from documentation/docs/reference/api/provider.md rename to documentation/docs/guide/advanced/provider.md diff --git a/documentation/docs/reference/api/sharding.md b/documentation/docs/guide/advanced/sharding.md similarity index 100% rename from documentation/docs/reference/api/sharding.md rename to documentation/docs/guide/advanced/sharding.md diff --git a/documentation/docs/reference/api/cosid-generator.md b/documentation/docs/guide/cosid-generator.md similarity index 91% rename from documentation/docs/reference/api/cosid-generator.md rename to documentation/docs/guide/cosid-generator.md index a189411f83..3134b4d1ae 100644 --- a/documentation/docs/reference/api/cosid-generator.md +++ b/documentation/docs/guide/cosid-generator.md @@ -12,7 +12,7 @@ ## 设计

- IdGenerator design diagram + IdGenerator design diagram

## Radix36CosIdGenerator diff --git a/documentation/docs/guide/cosid-proxy.md b/documentation/docs/guide/cosid-proxy.md new file mode 100644 index 0000000000..044d7301c6 --- /dev/null +++ b/documentation/docs/guide/cosid-proxy.md @@ -0,0 +1 @@ +# CosId Proxy 模块 \ No newline at end of file diff --git a/documentation/docs/guide/extensions/cosid-activiti.md b/documentation/docs/guide/extensions/cosid-activiti.md new file mode 100644 index 0000000000..5abb2bfb72 --- /dev/null +++ b/documentation/docs/guide/extensions/cosid-activiti.md @@ -0,0 +1,19 @@ +# CosId-Activiti 模块 + +## 安装 + +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" + implementation("me.ahoo.cosid:cosid-activiti:${cosidVersion}") +``` +```xml [Maven] + + + me.ahoo.cosid + cosid-activiti + ${cosid.version} + + +``` +::: \ No newline at end of file diff --git a/documentation/docs/guide/extensions/cosid-axon.md b/documentation/docs/guide/extensions/cosid-axon.md index 3416cdf193..8d9e8a50eb 100644 --- a/documentation/docs/guide/extensions/cosid-axon.md +++ b/documentation/docs/guide/extensions/cosid-axon.md @@ -1,3 +1,19 @@ # CosId-Axon 模块 -TODO \ No newline at end of file +## 安装 + +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" + implementation("me.ahoo.cosid:cosid-axon:${cosidVersion}") +``` +```xml [Maven] + + + me.ahoo.cosid + cosid-axon + ${cosid.version} + + +``` +::: \ No newline at end of file diff --git a/documentation/docs/guide/extensions/cosid-flowable.md b/documentation/docs/guide/extensions/cosid-flowable.md index 2eabe4a66c..c4a9eb76cb 100644 --- a/documentation/docs/guide/extensions/cosid-flowable.md +++ b/documentation/docs/guide/extensions/cosid-flowable.md @@ -1,3 +1,19 @@ # CosId-Flowable 模块 -TODO \ No newline at end of file +## 安装 + +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" + implementation("me.ahoo.cosid:cosid-flowable:${cosidVersion}") +``` +```xml [Maven] + + + me.ahoo.cosid + cosid-flowable + ${cosid.version} + + +``` +::: \ No newline at end of file diff --git a/documentation/docs/guide/extensions/cosid-jackson.md b/documentation/docs/guide/extensions/cosid-jackson.md index e03034bd87..a1a00dad34 100644 --- a/documentation/docs/guide/extensions/cosid-jackson.md +++ b/documentation/docs/guide/extensions/cosid-jackson.md @@ -1,6 +1,6 @@ # CosId-Jackson 模块 -> **Jackson** 序列化/反序列化注解插件,相当于隔离了应用API边界内外的 *ID* 使用方式,应用内部使用 `long`、外部使用 `String`,做到了应用无侵入,无感知。 +**Jackson** 序列化/反序列化注解插件,相当于隔离了应用API边界内外的 *ID* 使用方式,应用内部使用 `long`、外部使用 `String`,做到了应用无侵入,无感知。 ::: danger JavaScript Number 溢出问题 @@ -17,11 +17,21 @@ ## 安装 -> Kotlin DSL - -``` kotlin +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" implementation("me.ahoo.cosid:cosid-jackson:${cosidVersion}") ``` +```xml [Maven] + + + me.ahoo.cosid + cosid-jackson + ${cosid.version} + + +``` +::: ```java public class AsStringDto { diff --git a/documentation/docs/guide/extensions/cosid-jdbc.md b/documentation/docs/guide/extensions/cosid-jdbc.md index 235baf41a6..82c90ee528 100644 --- a/documentation/docs/guide/extensions/cosid-jdbc.md +++ b/documentation/docs/guide/extensions/cosid-jdbc.md @@ -4,11 +4,21 @@ ## 安装 -> Kotlin DSL - -``` kotlin +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" implementation("me.ahoo.cosid:cosid-jdbc:${cosidVersion}") ``` +```xml [Maven] + + + me.ahoo.cosid + cosid-jdbc + ${cosid.version} + + +``` +::: ## JdbcIdSegmentDistributor diff --git a/documentation/docs/guide/extensions/cosid-mongo.md b/documentation/docs/guide/extensions/cosid-mongo.md new file mode 100644 index 0000000000..57b4a5ff71 --- /dev/null +++ b/documentation/docs/guide/extensions/cosid-mongo.md @@ -0,0 +1,19 @@ +# CosId-Mongo 模块 + +## 安装 + +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" + implementation("me.ahoo.cosid:cosid-mongo:${cosidVersion}") +``` +```xml [Maven] + + + me.ahoo.cosid + cosid-mongo + ${cosid.version} + + +``` +::: \ No newline at end of file diff --git a/documentation/docs/guide/extensions/cosid-mybatis.md b/documentation/docs/guide/extensions/cosid-mybatis.md index f8a8c3d6e6..d2daf05691 100644 --- a/documentation/docs/guide/extensions/cosid-mybatis.md +++ b/documentation/docs/guide/extensions/cosid-mybatis.md @@ -1,14 +1,26 @@ # CosId-MyBatis 模块 -> [cosid-mybatis](https://github.com/Ahoo-Wang/CosId/tree/main/cosid-mybatis) 拦截**MyBatis**插入(`Insert`)请求,并解析 `@CosId` 注入**分布式ID**。 +[cosid-mybatis](https://github.com/Ahoo-Wang/CosId/tree/main/cosid-mybatis) 拦截**MyBatis**插入(`Insert`)请求,并解析 `@CosId` 注入**分布式ID**。 ## 安装 -> Kotlin DSL - -``` kotlin +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" implementation("me.ahoo.cosid:cosid-mybatis:${cosidVersion}") ``` +```xml [Maven] + + + me.ahoo.cosid + cosid-mybatis + ${cosid.version} + + +``` +::: + +## 使用 ```java public class Order { diff --git a/documentation/docs/guide/extensions/cosid-redis.md b/documentation/docs/guide/extensions/cosid-redis.md index f4e050d92e..502ce389d5 100644 --- a/documentation/docs/guide/extensions/cosid-redis.md +++ b/documentation/docs/guide/extensions/cosid-redis.md @@ -4,11 +4,21 @@ ## 安装 -> Kotlin DSL - -``` kotlin +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" implementation("me.ahoo.cosid:cosid-spring-redis:${cosidVersion}") ``` +```xml [Maven] + + + me.ahoo.cosid + cosid-spring-redis + ${cosid.version} + + +``` +::: ## SpringRedisIdSegmentDistributor @@ -21,7 +31,7 @@ ## SpringRedisMachineIdDistributor

- SegmentId + SegmentId

diff --git a/documentation/docs/guide/extensions/cosid-shardingsphere.md b/documentation/docs/guide/extensions/cosid-shardingsphere.md index 5984d495e6..a6eb2c4ccf 100644 --- a/documentation/docs/guide/extensions/cosid-shardingsphere.md +++ b/documentation/docs/guide/extensions/cosid-shardingsphere.md @@ -6,11 +6,21 @@ ## 安装 -> Kotlin DSL - -``` kotlin +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" implementation("me.ahoo.cosid:cosid-shardingsphere:${cosidVersion}") ``` +```xml [Maven] + + + me.ahoo.cosid + cosid-shardingsphere + ${cosid.version} + + +``` +::: ## CosIdKeyGenerateAlgorithm (分布式主键) diff --git a/documentation/docs/guide/extensions/cosid-spring-boot-starter.md b/documentation/docs/guide/extensions/cosid-spring-boot-starter.md new file mode 100644 index 0000000000..5fa23291b0 --- /dev/null +++ b/documentation/docs/guide/extensions/cosid-spring-boot-starter.md @@ -0,0 +1,25 @@ +# CosId-Spring-Boot-Starter 模块 + +_Spring-Boot-Starter_ 模块 集成了所有 _CosId_ 扩展,提供了自动装配的能力,使 _CosId_ 框架在 _Spring Boot_ 项目中更加便捷地使用。 + +::: tip +该模块的配置文档请参考 [配置](../../reference/config/basic)。 +::: + +## 安装 + +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" + implementation("me.ahoo.cosid:cosid-spring-boot-starter:${cosidVersion}") +``` +```xml [Maven] + + + me.ahoo.cosid + cosid-spring-boot-starter + ${cosid.version} + + +``` +::: \ No newline at end of file diff --git a/documentation/docs/guide/extensions/cosid-spring-data-jdbc.md b/documentation/docs/guide/extensions/cosid-spring-data-jdbc.md new file mode 100644 index 0000000000..4680370018 --- /dev/null +++ b/documentation/docs/guide/extensions/cosid-spring-data-jdbc.md @@ -0,0 +1,19 @@ +# CosId-Spring-Data-Jdbc 模块 + +## 安装 + +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" + implementation("me.ahoo.cosid:cosid-spring-data-jdbc:${cosidVersion}") +``` +```xml [Maven] + + + me.ahoo.cosid + cosid-spring-data-jdbc + ${cosid.version} + + +``` +::: \ No newline at end of file diff --git a/documentation/docs/guide/extensions/cosid-test.md b/documentation/docs/guide/extensions/cosid-test.md new file mode 100644 index 0000000000..d5f3d0fc4d --- /dev/null +++ b/documentation/docs/guide/extensions/cosid-test.md @@ -0,0 +1,82 @@ +# 兼容性测试套件 + +兼容性测试套件是一组用于验证特定接口实现是否符合规范的测试用例。 + +通过 _cosid-test 模块_,为自定义扩展提供了便捷和正确性保障。 +这种标准化验证方式不仅简化了扩展开发,降低了潜在错误风险,还确保了整个生态系统的一致性和稳定性。 + + +## 安装 + +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" + testImplementation("me.ahoo.cosid:cosid-test:${cosidVersion}") +``` +```xml [Maven] + + + me.ahoo.cosid + cosid-test + ${cosid.version} + test + + +``` +::: + +## Redis 扩展案例 + +### MachineIdDistributor + +```java +class SpringRedisMachineIdDistributorTest extends MachineIdDistributorSpec { + StringRedisTemplate stringRedisTemplate; + + @BeforeEach + void setup() { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration); + lettuceConnectionFactory.afterPropertiesSet(); + stringRedisTemplate = new StringRedisTemplate(lettuceConnectionFactory); + } + + @Override + protected MachineIdDistributor getDistributor() { + return new SpringRedisMachineIdDistributor(stringRedisTemplate, MachineStateStorage.IN_MEMORY, ClockBackwardsSynchronizer.DEFAULT); + } + +} +``` + +### IdSegmentDistributor + +```java +class SpringRedisIdSegmentDistributorTest extends IdSegmentDistributorSpec { + StringRedisTemplate stringRedisTemplate; + SpringRedisIdSegmentDistributorFactory distributorFactory; + protected IdSegmentDistributorDefinition idSegmentDistributorDefinition; + + @BeforeEach + void setup() { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration); + lettuceConnectionFactory.afterPropertiesSet(); + stringRedisTemplate = new StringRedisTemplate(lettuceConnectionFactory); + distributorFactory = new SpringRedisIdSegmentDistributorFactory(stringRedisTemplate); + idSegmentDistributorDefinition = new IdSegmentDistributorDefinition("SpringRedisIdSegmentDistributorTest", MockIdGenerator.INSTANCE.generateAsString(), 0, 100); + } + + + @Override + protected IdSegmentDistributorFactory getFactory() { + return distributorFactory; + } + + @Override + protected void setMaxIdBack(T distributor, long maxId) { + String adderKey = ((SpringRedisIdSegmentDistributor) distributor).getAdderKey(); + stringRedisTemplate.opsForValue().set(adderKey, String.valueOf(maxId - 1)); + } +} +``` \ No newline at end of file diff --git a/documentation/docs/guide/extensions/cosid-zookeeper.md b/documentation/docs/guide/extensions/cosid-zookeeper.md index b89c5da2fd..02da303a92 100644 --- a/documentation/docs/guide/extensions/cosid-zookeeper.md +++ b/documentation/docs/guide/extensions/cosid-zookeeper.md @@ -5,11 +5,21 @@ ## 安装 -> Kotlin DSL - -``` kotlin +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" implementation("me.ahoo.cosid:cosid-zookeeper:${cosidVersion}") ``` +```xml [Maven] + + + me.ahoo.cosid + cosid-zookeeper + ${cosid.version} + + +``` +::: ## ZookeeperIdSegmentDistributor diff --git a/documentation/docs/guide/Performance-CosId-Leaf.md b/documentation/docs/guide/faq/Performance-CosId-Leaf.md similarity index 93% rename from documentation/docs/guide/Performance-CosId-Leaf.md rename to documentation/docs/guide/faq/Performance-CosId-Leaf.md index 3c23ed351c..8c4b88d8b3 100644 --- a/documentation/docs/guide/Performance-CosId-Leaf.md +++ b/documentation/docs/guide/faq/Performance-CosId-Leaf.md @@ -14,13 +14,18 @@ ``` shell git clone git@github.com:Ahoo-Wang/CosId.git cd cosid-benchmark +``` + +::: code-group +```shell [Gradle] ./gradlew jmh ``` -or -```shell +```shell [Java] gradle jmhJar java -jar build/libs/cosid-benchmark-2.2.6-jmh.jar -wi 1 -rf json -f 1 ``` +::: + ## 报告 @@ -43,7 +48,7 @@ LeafBenchmark.generate 1000 thrpt 23550106.538 ops/s ```

- CosId VS 美团 Leaf + CosId VS 美团 Leaf

> GitHub Action 环境测试报告: [Performance: CosId vs Leaf](https://github.com/Ahoo-Wang/CosId/issues/22) diff --git a/documentation/docs/guide/faq.md b/documentation/docs/guide/faq/faq.md similarity index 76% rename from documentation/docs/guide/faq.md rename to documentation/docs/guide/faq/faq.md index 7104da67f3..53f6c14fe5 100644 --- a/documentation/docs/guide/faq.md +++ b/documentation/docs/guide/faq/faq.md @@ -4,7 +4,9 @@ 虽然并没有规定 [CosId](https://github.com/Ahoo-Wang/CosId) 的使用方式,但是强烈推荐以本地 SDK 的方式使用,用户只需要安装一下 **CosId** 的依赖包做一些简单配置( [DEMO](https://github.com/Ahoo-Wang/CosId/tree/main/cosid-example) ) 即可。 -> 分布式ID是不适合使用服务端部署模式的(C/S)。使用服务端部署模式,必然会产生网络IO(*Client*通过远程过程调用*Server*,获取ID),你想想我们费了那么大劲消除网络IO是为了什么? +:::tip +分布式ID是不适合使用服务端部署模式的(C/S)。使用服务端部署模式,必然会产生网络IO(*Client*通过远程过程调用*Server*,获取ID),你想想我们费了那么大劲消除网络IO是为了什么? +::: ## PrefetchWorker 是如何维护安全距离的? @@ -16,5 +18,5 @@ 从上文的论述中我们不难理解本机单调递增,全局趋势递增是权衡后的设计结果。 但是全局趋势递增的背面是周期内ID乱序,所以尽可能向单调递增优化(降低ID乱序程度)是优化目标,这俩点并不冲突。 -> 如果各位同学还有其他问题请至 [Issues](https://github.com/Ahoo-Wang/CosId/issues) 提交你的疑问。 +如果各位同学还有其他问题请至 [Issues](https://github.com/Ahoo-Wang/CosId/issues) 提交你的疑问。 diff --git a/documentation/docs/guide/perf-test.md b/documentation/docs/guide/faq/perf-test.md similarity index 89% rename from documentation/docs/guide/perf-test.md rename to documentation/docs/guide/faq/perf-test.md index 1fe78162c4..2503e95b58 100644 --- a/documentation/docs/guide/perf-test.md +++ b/documentation/docs/guide/faq/perf-test.md @@ -11,14 +11,17 @@ ### 吞吐量 (ops/s)

- Throughput-Of-SegmentChainId + Throughput-Of-SegmentChainId

-``` shell +::: code-group +```shell [Gradle] gradle cosid-redis:jmh -# or +``` +```shell [Java] java -jar cosid-redis/build/libs/cosid-redis-1.8.6-jmh.jar -bm thrpt -wi 1 -rf json -f 1 RedisChainIdBenchmark ``` +::: ``` Benchmark (step) Mode Cnt Score Error Units @@ -26,11 +29,16 @@ RedisChainIdBenchmark.generate 1 thrpt 5 106188349.580 ± 26035022.28 RedisChainIdBenchmark.generate 100 thrpt 5 112276460.950 ± 4091990.852 ops/s RedisChainIdBenchmark.generate 1000 thrpt 5 110181522.770 ± 15531341.449 ops/s ``` -``` shell + +::: code-group +```shell [Gradle] gradle cosid-jdbc:jmh -# or +``` +```shell [Java] java -jar cosid-jdbc/build/libs/cosid-jdbc-1.8.6-jmh.jar -bm thrpt -wi 1 -rf json -f 1 MySqlChainIdBenchmark ``` +::: + ``` Benchmark (step) Mode Cnt Score Error Units @@ -44,7 +52,7 @@ MySqlChainIdBenchmark.generate 1000 thrpt 5 115287146.614 ± 4471990.880 > [百分位数](https://zh.wikipedia.org/wiki/%E7%99%BE%E5%88%86%E4%BD%8D%E6%95%B0) ,统计学术语,若将一组数据从小到大排序,并计算相应的累计百分点,则某百分点所对应数据的值,就称为这百分点的百分位数,以Pk表示第k百分位数。百分位数是用来比较个体在群体中的相对地位量数。

- Percentile-Sample-Of-SegmentChainId + Percentile-Sample-Of-SegmentChainId

```shell @@ -83,11 +91,14 @@ MySqlChainIdBenchmark.step_1000:step_1000·p1.00 sample 342.528 ## SnowflakeId -``` shell +::: code-group +```shell [Gradle] gradle cosid-core:jmh -# or +``` +```shell [Java] java -jar cosid-core/build/libs/cosid-core-1.8.6-jmh.jar -bm thrpt -wi 1 -rf json -f 1 ``` +::: ``` Benchmark Mode Cnt Score Error Units @@ -100,9 +111,9 @@ SnowflakeIdBenchmark.secondSnowflakeId_generate thrpt 4206843. ## CosIdIntervalShardingAlgorithm -| **PreciseShardingValue** | **RangeShardingValue** | -|-------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| -| | | +| **PreciseShardingValue** | **RangeShardingValue** | +|-----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| +| | | ``` shell @@ -148,9 +159,9 @@ IntervalShardingAlgorithmBenchmark.office_range_timestamp 10000 thrpt ## CosIdModShardingAlgorithm -| **PreciseShardingValue** | **RangeShardingValue** | -|--------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| -| | | +| **PreciseShardingValue** | **RangeShardingValue** | +|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| +| | | ``` shell gradle cosid-shardingsphere:jmh diff --git a/documentation/docs/guide/perf-vs.md b/documentation/docs/guide/faq/perf-vs.md similarity index 100% rename from documentation/docs/guide/perf-vs.md rename to documentation/docs/guide/faq/perf-vs.md diff --git a/documentation/docs/guide/getting-started.md b/documentation/docs/guide/getting-started.md index 5095c088c1..5dc45b30e7 100644 --- a/documentation/docs/guide/getting-started.md +++ b/documentation/docs/guide/getting-started.md @@ -1,41 +1,21 @@ # 快速上手 -## Examples - -[CosId-Examples](https://github.com/Ahoo-Wang/CosId/tree/main/examples) - ## 安装 -> 开发者可以任选一种的分发器(Redis/JDBC/Mongodb/Zookeeper),并引入对应的依赖。 - -### Redis 分发器 +:::tip +开发者可以任选一种的分发器(`Redis`/`JDBC`/`Mongodb`/`Zookeeper`),并引入对应的依赖。 +::: -[CosId-Example-Redis](https://github.com/Ahoo-Wang/CosId/tree/main/examples/cosid-example-redis) +接下来以 `Redis` 扩展为例: [CosId-Example-Redis](https://github.com/Ahoo-Wang/CosId/tree/main/examples/cosid-example-redis) -#### Gradle Kotlin DSL - -``` kotlin - val cosidVersion = "latestVersion"; +::: code-group +```kotlin [Gradle(Kotlin)] + val cosidVersion = "latestVersion" implementation("org.springframework.boot:spring-boot-starter-data-redis") implementation("me.ahoo.cosid:cosid-spring-redis:${cosidVersion}") implementation("me.ahoo.cosid:cosid-spring-boot-starter:${cosidVersion}") ``` - -#### Maven - -```xml - - - - - 4.0.0 - demo - - latestVersion - - +```xml [Maven] org.springframework.boot @@ -53,11 +33,10 @@ ${cosid.version} - - ``` +::: -#### 应用配置 (`application.yaml`) +## 应用配置 ```yaml spring: @@ -78,9 +57,13 @@ cosid: type: redis ``` -> TIPS: 默认情况下,开启 `snowflake`/`segment` 会生成共享的(`__share__`) `IdGenerator` 注册到 `Spring` 容器 以及 `DefaultIdGeneratorProvider.INSTANCE`。 -> -> WARN: 当同时开启 `snowflake`/`segment` 时,只有其中一个共享的(`__share__`) `IdGenerator` 会注入到 `Spring` 容器(名称冲突),另一个会被忽略。 +:::tip +默认情况下,开启 `snowflake`/`segment` 会生成共享的(`__share__`) `IdGenerator` 注册到 `Spring` 容器 以及 `DefaultIdGeneratorProvider.INSTANCE`。 +::: + +:::warning +当同时开启 `snowflake`/`segment` 时,只有其中一个共享的(`__share__`) `IdGenerator` 会注入到 `Spring` 容器(名称冲突),另一个会被忽略。 +::: `IdGenerator` `Bean Name` 规则: - SegmentId: `[name]SegmentId` , 比如 : `__share__SegmentId` @@ -90,7 +73,7 @@ cosid: > 通过 `@Autowired` 注入 `IdGenerator` 。 -```java +```java {1,6} @Qualifier("__share__SegmentId") @Lazy @Autowired @@ -108,5 +91,6 @@ cosid: DefaultIdGeneratorProvider.INSTANCE.getShare(); ``` +## Examples - +开发者可以通过 [CosId-Examples](https://github.com/Ahoo-Wang/CosId/tree/main/examples) 的学习快速开启 `CosId` 之旅。 \ No newline at end of file diff --git a/documentation/docs/guide/introduction.md b/documentation/docs/guide/introduction.md index e6ebb5b881..08a58c7202 100644 --- a/documentation/docs/guide/introduction.md +++ b/documentation/docs/guide/introduction.md @@ -2,15 +2,12 @@ *[CosId](https://github.com/Ahoo-Wang/CosId)* 旨在提供通用、灵活、高性能的分布式 ID 生成器。 -- `SnowflakeId` : *单机 TPS 性能:409W/s* [JMH 基准测试](perf-test.md) , 主要解决 *时钟回拨问题* 、*机器号分配问题* 并且提供更加友好、灵活的使用体验。 -- `SegmentId`: 每次获取一段 (`Step`) ID,来降低号段分发器的网络IO请求频次提升性能。 - - `IdSegmentDistributor`: 号段分发器(号段存储器) - - `RedisIdSegmentDistributor`: 基于 *Redis* 的号段分发器。 - - `JdbcIdSegmentDistributor`: 基于 *Jdbc* 的号段分发器,支持各种关系型数据库。 - - `ZookeeperIdSegmentDistributor`: 基于 *Zookeeper* 的号段分发器。 - - `MongoIdSegmentDistributor`: 基于 *MongoDB* 的号段分发器。 -- `SegmentChainId`(**推荐**):`SegmentChainId` (*lock-free*) 是对 `SegmentId` 的增强。性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](perf-test) 。 +- `CosIdGenerator` : *单机 TPS 性能:1557W/s*,三倍于 `UUID.randomUUID()`,基于时钟的全局趋势递增ID,可以同时支持一百万个实例。 +- `SnowflakeId` : *单机 TPS 性能:409W/s* [JMH 基准测试](faq/perf-test.md) , 主要解决 *时钟回拨* 、*机器号分配*、*取模分片不均匀* 等问题并提供更加友好、灵活的使用体验。 +- `SegmentId`: 每次获取一段 (`Step`) ID,来降低号段分发器的网络IO请求频次提升性能。并提供多种号段分发器实现。 +- `SegmentChainId`:`SegmentChainId` (*lock-free*) 是对 `SegmentId` 的增强。性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](faq/perf-test.md) 。 - `PrefetchWorker` 维护安全距离(`safeDistance`), 并且支持基于饥饿状态的动态`safeDistance`扩容/收缩。 + - 适应性:相比于 `SegmentId`,`SegmentChainId` 可以根据业务场景动态调整 `Step` 来提升性能。 ## 背景(为什么需要*分布式ID*) @@ -22,8 +19,11 @@ ### 分库分表 -> 从微服务的角度来理解垂直拆分其实就是微服务拆分。以限界上下文来定义服务边界将大服务/单体应用拆分成多个自治的粒度更小的服务,因为自治性规范要求,数据库也需要进行业务拆分。 -> 但垂直拆分后的单个微服务依然会面临 TPS/存储容量 的挑战,所以这里我们重点讨论水平拆分的方式。 +:::info +从微服务的角度来理解垂直拆分其实就是微服务拆分。以限界上下文来定义服务边界将大服务/单体应用拆分成多个自治的粒度更小的服务,因为自治性规范要求,数据库也需要进行业务拆分。 + +但垂直拆分后的单个微服务依然会面临 TPS/存储容量 的挑战,所以这里我们重点讨论水平拆分的方式。 +:::

分库分表 @@ -34,7 +34,9 @@ 那么 `shardingValue` 从哪里来呢?**CosId**!!! -> 当然还有很多分布式场景需要*分布式ID*,这里不再一一列举。 +:::tip +当然还有很多分布式场景需要*分布式ID*,这里不再一一列举。 +::: ## 分布式ID方案的核心指标 @@ -53,10 +55,10 @@ - **自治性(依赖)**:主要是指对外部环境有无依赖,比如**号段模式**会强依赖第三方存储中间件来获取`NexMaxId`。自治性还会对可用性造成影响。 - **可用性**:分布式ID的可用性主要会受到自治性影响,比如**SnowflakeId**会受到时钟回拨影响,导致处于短暂时间的不可用状态。而**号段模式**会受到第三方发号器(`NexMaxId`)的可用性影响。 - [可用性 WIKI](https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%94%A8%E6%80%A7) :在一个给定的时间间隔内,对于一个功能个体来讲,总的可用时间所占的比例。 - - MTBF:平均故障间隔 - - MDT:平均修复/恢复时间 - - Availability=MTBF/(MTBF+MDT) - - 假设MTBF为1年,MDT为1小时,即`Availability=(365*24)/(365*24+1)=0.999885857778792≈99.99%`,也就是我们通常所说对可用性4个9。 + - _MTBF_:平均故障间隔 + - _MDT_:平均修复/恢复时间 + - `Availability=MTBF/(MTBF+MDT)` + - 假设*MTBF*为1年,*MDT*为1小时,即`Availability=(365*24)/(365*24+1)=0.999885857778792≈99.99%`,也就是我们通常所说对可用性4个9。 - **适应性**:是指在面对外部环境变化的自适应能力,这里我们主要说的是面对流量突发时动态伸缩分布式ID的性能, - **SegmentChainId**可以基于**饥饿状态**进行**安全距离**的动态伸缩。 - **SnowflakeId**常规位分配方案性能恒定409.6W,虽然可以通过调整位分配方案来获得不同的TPS性能,但是位分配方法的变更是破坏性的,一般根据业务场景确定位分配方案后不再变更。 @@ -84,7 +86,9 @@ 单调递增:T表示全局绝对时点,假设有Tn+1>Tn(绝对时间总是往前进的,这里不考虑相对论、时间机器等),那么必然有F(Tn+1)>F(Tn),数据库自增主键就属于这一类。 另外需要特别说明的是单调递增跟连续性递增是不同的概念。 连续性递增:`F(n+1)=(F(n)+step)`即下一次获取的ID一定等于当前`ID+Step`,当`Step=1`时类似于这样一个序列:`1->2->3->4->5`。 -> 扩展小知识:数据库的自增主键也不是连续性递增的,相信你一定遇到过这种情况,请思考一下数据库为什么这样设计? +:::tip +扩展小知识:数据库的自增主键也不是连续性递增的,相信你一定遇到过这种情况,请思考一下数据库为什么这样设计? +::: #### 有序性之趋势递增 @@ -109,32 +113,36 @@ UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据 ### SnowflakeId +*SnowflakeId* 使用`Long`(64-bit)位分区来生成ID的一种分布式ID算法。 +通用的位分配方案为:`timestamp`(41-bit)+`machineId`(10-bit)+`sequence`(12-bit)=63-bit。 +

SnowflakeId

-> *SnowflakeId*使用`Long`(64-bit)位分区来生成ID的一种分布式ID算法。 -> 通用的位分配方案为:`timestamp`(41-bit)+`machineId`(10-bit)+`sequence`(12-bit)=63-bit。 - - 41-bit`timestamp`=(1L<<41)/(1000/3600/24/365),约可以存储69年的时间戳,即可以使用的绝对时间为`EPOCH`+69年,一般我们需要自定义`EPOCH`为产品开发时间,另外还可以通过压缩其他区域的分配位数,来增加时间戳位数来延长可用时间。 - 10-bit`machineId`=(1L<<10)=1024,即相同业务可以部署1024个副本(在Kubernetes概念里没有主从副本之分,这里直接沿用Kubernetes的定义)。一般情况下没有必要使用这么多位,所以会根据部署规模需要重新定义。 - 12-bit`sequence`=(1L<<12)*1000=4096000,即单机每秒可生成约409W的ID,全局同业务集群可产生`4096000*1024=419430W=41.9亿(TPS)`。 从 *SnowflakeId* 设计上可以看出: -- :thumbsup:`timestamp`在高位,单实例*SnowflakeId*是会保证时钟总是向前的(校验本机时钟回拨),所以是本机单调递增的。受全局时钟同步/时钟回拨影响*SnowflakeId*是全局趋势递增的。 -- :thumbsup:*SnowflakeId*不对任何第三方中间件有强依赖关系,并且性能也非常高。 -- :thumbsup:位分配方案可以按照业务系统需要灵活配置,来达到最优使用效果。 -- :thumbsdown:强依赖本机时钟,潜在的时钟回拨问题会导致ID重复、处于短暂的不可用状态。 -- :thumbsdown:`machineId`需要手动设置,实际部署时如果采用手动分配`machineId`,会非常低效。 +- :thumbsup: `timestamp`在高位,单实例*SnowflakeId*是会保证时钟总是向前的(校验本机时钟回拨),所以是本机单调递增的。受全局时钟同步/时钟回拨影响*SnowflakeId*是全局趋势递增的。 +- :thumbsup: *SnowflakeId*不对任何第三方中间件有强依赖关系,并且性能也非常高。 +- :thumbsup: 位分配方案可以按照业务系统需要灵活配置,来达到最优使用效果。 +- :thumbsdown: 强依赖本机时钟,潜在的时钟回拨问题会导致ID重复、处于短暂的不可用状态。 +- :thumbsdown: `machineId`需要手动设置,实际部署时如果采用手动分配`machineId`,会非常低效。 #### SnowflakeId之机器号分配问题 在**SnowflakeId**中根据业务设计的位分配方案确定了基本上就不再有变更了,也很少需要维护。但是`machineId`总是需要配置的,而且集群中是不能重复的,否则分区原则就会被破坏而导致ID唯一性原则破坏,当集群规模较大时`machineId`的维护工作是非常繁琐,低效的。 -> 有一点需要特别说明的,**SnowflakeId**的**MachineId**是逻辑上的概念,而不是物理概念。 -> 想象一下假设**MachineId**是物理上的,那么意味着一台机器拥有只能拥有一个**MachineId**,那会产生什么问题呢? -> 目前 *[CosId](https://github.com/Ahoo-Wang/CosId)* 提供了以下五种 `MachineId` 分配器。 +:::tip +有一点需要特别说明的,**SnowflakeId** 的 **MachineId** 是逻辑上的概念,而不是物理概念,所以称之为 `WorkerId` 更为准确。 + +想象一下假设 **MachineId** 是物理上的,那么意味着一台机器拥有只能拥有一个 **MachineId**,那会产生什么问题呢? +::: + +目前 *[CosId](https://github.com/Ahoo-Wang/CosId)* 提供了以下五种 `MachineId` 分配器。 - ManualMachineIdDistributor: 手动配置`machineId`,一般只有在集群规模非常小的时候才有可能使用,不推荐。 - StatefulSetMachineIdDistributor: 使用`Kubernetes`的`StatefulSet`提供的稳定的标识ID(HOSTNAME=service-01)作为机器号。 @@ -143,7 +151,7 @@ UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据 - ZookeeperMachineIdDistributor: 使用**ZooKeeper**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。

- RedisMachineIdDistributor + MachineIdDistributor

@@ -174,7 +182,7 @@ UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据 - 自定义`SnowflakeId`位分配来缩短`SnowflakeId`的位数(53-bit)使 `ID` 提供给前端时不溢出 - 使用`SafeJavaScriptSnowflakeId`(`JavaScript` 安全的 `SnowflakeId`) -## 号段模式(SegmentId) +### 号段模式(SegmentId)

SegmentId @@ -192,7 +200,7 @@ UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据 - `Step`越小,乱序程度越小。当`Step=1`时,将无限接近单调递增。需要注意的是这里是无限接近而非等于单调递增,具体原因你可以思考一下这样一个场景: - 号段分发器T1时刻给**Instance 1**分发了`ID=1`,T2时刻给**Instance 2**分发了`ID=2`。因为机器性能、网络等原因,`Instance 2`网络IO写请求先于`Instance 1`到达。那么这个时候对于数据库来说,ID依然是乱序的。 -## 号段链模式(SegmentChainId) +### 号段链模式(SegmentChainId)

SegmentChainId @@ -201,7 +209,7 @@ UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据 **SegmentChainId**是**SegmentId**增强版,相比于**SegmentId**有以下优势: - 稳定性:**SegmentId**的稳定性问题(P9999=46.624(us/op))主要是因为号段用完之后同步进行`NextMaxId`的获取导致的(会产生网络IO)。 - - **SegmentChainId** (P9999=0.208(us/op))引入了新的角色**PrefetchWorker**用以维护和保证**安全距离**,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待`NextMaxId`获取,性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](perf-test.md) 。 + - **SegmentChainId** (P9999=0.208(us/op))引入了新的角色**PrefetchWorker**用以维护和保证**安全距离**,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待`NextMaxId`获取,性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](faq/perf-test.md) 。 - 适应性:从**SegmentId**介绍中我们知道了影响**ID乱序**的因素有俩个:集群规模、`Step`大小。集群规模是我们不能控制的,但是`Step`是可以调节的。 - `Step`应该近可能小才能使得**ID单调递增**的可能性增大。 - `Step`太小会影响吞吐量,那么我们如何合理设置`Step`呢?答案是我们无法准确预估所有时点的吞吐量需求,那么最好的办法是吞吐量需求高时,Step自动增大,吞吐量低时Step自动收缩。 diff --git a/documentation/docs/guide/segment-chain.md b/documentation/docs/guide/segment-chain.md index 67b967bbe3..92ab01915c 100644 --- a/documentation/docs/guide/segment-chain.md +++ b/documentation/docs/guide/segment-chain.md @@ -1,5 +1,18 @@ # 号段链模式 +

+ SegmentChainId +

+ +**SegmentChainId**是**SegmentId**增强版,相比于**SegmentId**有以下优势: + +- 稳定性:**SegmentId**的稳定性问题(P9999=46.624(us/op))主要是因为号段用完之后同步进行`NextMaxId`的获取导致的(会产生网络IO)。 + - **SegmentChainId** (P9999=0.208(us/op))引入了新的角色**PrefetchWorker**用以维护和保证**安全距离**,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待`NextMaxId`获取,性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](faq/perf-test.md) 。 +- 适应性:从**SegmentId**介绍中我们知道了影响**ID乱序**的因素有俩个:集群规模、`Step`大小。集群规模是我们不能控制的,但是`Step`是可以调节的。 + - `Step`应该近可能小才能使得**ID单调递增**的可能性增大。 + - `Step`太小会影响吞吐量,那么我们如何合理设置`Step`呢?答案是我们无法准确预估所有时点的吞吐量需求,那么最好的办法是吞吐量需求高时,Step自动增大,吞吐量低时Step自动收缩。 + - **SegmentChainId**引入了**饥饿状态**的概念,**PrefetchWorker**会根据**饥饿状态**检测当前**安全距离**是否需要膨胀或者收缩,以便获得吞吐量与有序性之间的权衡,这便是**SegmentChainId**的自适应性。 + ## 为什么需要*SegmentChainId*

@@ -12,7 +25,7 @@ **SegmentChainId**是**SegmentId**的增强版,相比于**SegmentId**有以下优势: -- TPS性能:可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](docs/guide/perf-test.md)。通过引入了新的角色**PrefetchWorker**用以维护和保证**安全距离**,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待`NextMaxId`获取。 +- TPS性能:可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](faq/perf-test)。通过引入了新的角色**PrefetchWorker**用以维护和保证**安全距离**,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待`NextMaxId`获取。 - 稳定性:P9999=0.208(us/op),通过上面的TPS性能描述中我们可以看到,**SegmentChainId**消除了同步等待的问题,所以稳定性问题也因此迎刃而解。 - 适应性:从**SegmentId**介绍中我们知道了影响**ID乱序**的因素有俩个:集群规模、`Step`大小。集群规模是我们不能控制的,但是`Step`是可以调节的。 - `Step`应该尽可能小才能使得**ID单调递增**的可能性增大。 diff --git a/documentation/docs/guide/segment.md b/documentation/docs/guide/segment.md new file mode 100644 index 0000000000..a63e081981 --- /dev/null +++ b/documentation/docs/guide/segment.md @@ -0,0 +1,24 @@ +# SegmentId + +

+ SegmentId +

+ +从上面的设计图中,不难看出**号段模式**基本设计思路是通过每次获取一定长度(Step)的可用ID(Id段/号段),来降低网络IO请求次数,提升性能。 + +- :thumbsdown:强依赖第三方号段分发器,可用性受到第三方分发器影响。 +- :thumbsdown:每次号段用完时获取`NextMaxId`需要进行网络IO请求,此时的性能会比较低。 +- 单实例ID单调递增,全局趋势递增。 + - 从设计图中不难看出**Instance 1**每次获取的`NextMaxId`,一定比上一次大,意味着下一次的号段一定比上一次大,所以从单实例上来看是单调递增的。 + - 多实例各自持有的不同的号段,意味着同一时刻不同实例生成的ID是乱序的,但是整体趋势的递增的,所以全局趋势递增。 +- ID乱序程度受到Step长度以及集群规模影响(从趋势递增图中不难看出)。 + - 假设集群中只有一个实例时**号段模式**就是单调递增的。 + - `Step`越小,乱序程度越小。当`Step=1`时,将无限接近单调递增。需要注意的是这里是无限接近而非等于单调递增,具体原因你可以思考一下这样一个场景: + - 号段分发器T1时刻给**Instance 1**分发了`ID=1`,T2时刻给**Instance 2**分发了`ID=2`。因为机器性能、网络等原因,`Instance 2`网络IO写请求先于`Instance 1`到达。那么这个时候对于数据库来说,ID依然是乱序的。 + + +## IdSegmentDistributor + +## SegmentChainId + +## PrefetchWorker diff --git a/documentation/docs/guide/snowflake.md b/documentation/docs/guide/snowflake.md new file mode 100644 index 0000000000..3939f111e5 --- /dev/null +++ b/documentation/docs/guide/snowflake.md @@ -0,0 +1,174 @@ +# SnowflakeId + +_SnowflakeId_ 是*Twitter*开发的一种分布式唯一ID生成算法,被广泛应用于分布式系统中。它的设计目标是生成趋势递增、全局唯一的ID,以应对大规模系统的需求。 + +

+ SnowflakeId +

+ +## 简介 + +:::info +*SnowflakeId* 使用`Long`(64-bit)位分区来生成ID的一种分布式ID算法。 + +通用的位分配方案为:`timestamp`(41-bit)+`machineId`(10-bit)+`sequence`(12-bit)=63-bit。 +::: + +- 41-bit`timestamp`=(1L<<41)/(1000/3600/24/365),约可以存储69年的时间戳,即可以使用的绝对时间为`EPOCH`+69年,一般我们需要自定义`EPOCH`为产品开发时间,另外还可以通过压缩其他区域的分配位数,来增加时间戳位数来延长可用时间。 +- 10-bit`machineId`=(1L<<10)=1024,即相同业务可以部署1024个副本(在Kubernetes概念里没有主从副本之分,这里直接沿用Kubernetes的定义)。一般情况下没有必要使用这么多位,所以会根据部署规模需要重新定义。 +- 12-bit`sequence`=(1L<<12)*1000=4096000,即单机每秒可生成约409W的ID,全局同业务集群可产生`4096000*1024=419430W=41.9亿(TPS)`。 + +从 *SnowflakeId* 设计上可以看出: + +- :thumbsup: `timestamp`在高位,单实例*SnowflakeId*是会保证时钟总是向前的(校验本机时钟回拨),所以是本机单调递增的。受全局时钟同步/时钟回拨影响*SnowflakeId*是全局趋势递增的。 +- :thumbsup: *SnowflakeId*不对任何第三方中间件有强依赖关系,并且性能也非常高。 +- :thumbsup: 位分配方案可以按照业务系统需要灵活配置,来达到最优使用效果。 +- :thumbsdown: 强依赖本机时钟,潜在的时钟回拨问题会导致ID重复、处于短暂的不可用状态。 +- :thumbsdown: `machineId`需要手动设置,实际部署时如果采用手动分配`machineId`,会非常低效。 + +## 挑战 + +### 机器号分配 + +在**SnowflakeId**中根据业务设计的位分配方案确定了基本上就不再有变更了,也很少需要维护。但是`machineId`总是需要配置的,而且集群中是不能重复的,否则分区原则就会被破坏而导致ID唯一性原则破坏,当集群规模较大时`machineId`的维护工作是非常繁琐,低效的。 + +:::tip +有一点需要特别说明的,**SnowflakeId** 的 **MachineId** 是逻辑上的概念,而不是物理概念,所以称之为 `WorkerId` 更为准确。 + +想象一下假设 **MachineId** 是物理上的,那么意味着一台机器拥有只能拥有一个 **MachineId**,那会产生什么问题呢? +::: + +目前 *[CosId](https://github.com/Ahoo-Wang/CosId)* 提供了以下五种 `MachineId` 分配器。 + +- `ManualMachineIdDistributor`: 手动配置`machineId`,一般只有在集群规模非常小的时候才有可能使用,不推荐。 +- `StatefulSetMachineIdDistributor`: 使用`Kubernetes`的`StatefulSet`提供的稳定的标识ID(HOSTNAME=service-01)作为机器号。 +- `RedisMachineIdDistributor`: 使用**Redis**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。 +- `JdbcMachineIdDistributor`: 使用**关系型数据库**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。 +- `ZookeeperMachineIdDistributor`: 使用**ZooKeeper**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。 +- `MongoMachineIdDistributor`: 使用**MongoDB**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。 + +

+ MachineIdDistributor +

+ +

+ Machine Id Safe Guard +

+ +### 时钟回拨 + +时钟回拨的致命问题是会导致ID重复、冲突(这一点不难理解),ID重复显然是不能被容忍的。 +在**SnowflakeId**算法中,按照**MachineId**分区ID,我们不难理解的是不同**MachineId**是不可能产生相同ID的。所以我们解决的时钟回拨问题是指当前**MachineId**的时钟回拨问题,而不是所有集群节点的时钟回拨问题。 + +**MachineId**时钟回拨问题大体可以分为俩种情况: + +- 运行时时钟回拨:即在运行时获取的当前时间戳比上一次获取的时间戳小。这个场景的时钟回拨是很容易处理的,一般**SnowflakeId**代码实现时都会存储`lastTimestamp`用于运行时时钟回拨的检查,并抛出时钟回拨异常。 + - 时钟回拨时直接抛出异常是不太好地实践,因为下游使用方几乎没有其他处理方案(噢,我还能怎么办呢,等吧),时钟同步是唯一的选择,当只有一种选择时就不要再让用户选择了。 + - `ClockSyncSnowflakeId`是`SnowflakeId`的包装器,当发生时钟回拨时会使用`ClockBackwardsSynchronizer`主动等待时钟同步来重新生成ID,提供更加友好的使用体验。 +- 启动时时钟回拨:即在启动服务实例时获取的当前时钟比上次关闭服务时小。此时的`lastTimestamp`是无法存储在进程内存中的。当获取的外部存储的**机器状态**大于当前时钟时钟时,会使用`ClockBackwardsSynchronizer`主动同步时钟。 + - LocalMachineStateStorage:使用本地文件存储`MachineState`(机器号、最近一次时间戳)。因为使用的是本地文件所以只有当实例的部署环境是稳定的,`LocalMachineStateStorage`才适用。 + - RedisMachineIdDistributor:将`MachineState`存储在**Redis**分布式缓存中,这样可以保证总是可以获取到上次服务实例停机时**机器状态**。 + +### 取模分片不均匀 + +_CosId_ 通过引入 `sequenceResetThreshold` 属性,巧妙地解决了取模分片不均匀的问题,这一设计在无需牺牲性能的同时,为用户提供了更加出色的使用体验。 + +### JavaScript数值溢出 + +`JavaScript`的`Number.MAX_SAFE_INTEGER`只有53-bit,如果直接将63位的`SnowflakeId`返回给前端,那么会产生值溢出的情况(所以这里我们应该知道后端传给前端的`long`值溢出问题,**迟早**会出现,只不过SnowflakeId出现得更快而已)。 +很显然溢出是不能被接受的,一般可以使用以下俩种处理方案: +- 将生成的63-bit`SnowflakeId`转换为`String`类型。 + - 直接将`long`转换成`String`。 + - 使用`SnowflakeFriendlyId`将`SnowflakeId`转换成比较友好的字符串表示:`{timestamp}-{machineId}-{sequence} -> 20210623131730192-1-0` +- 自定义`SnowflakeId`位分配来缩短`SnowflakeId`的位数(53-bit)使 `ID` 提供给前端时不溢出 + - 使用`SafeJavaScriptSnowflakeId`(`JavaScript` 安全的 `SnowflakeId`) + +## 具体实现 + +```mermaid +classDiagram +direction BT +class AbstractSnowflakeId +class ClockSyncSnowflakeId +class DefaultSnowflakeFriendlyId +class IdGenerator { +<> + +} +class MillisecondSnowflakeId +class SecondSnowflakeId +class SnowflakeFriendlyId { +<> + +} +class SnowflakeId { +<> + +} +class StringSnowflakeId + +AbstractSnowflakeId ..> SnowflakeId +ClockSyncSnowflakeId ..> IdGenerator +ClockSyncSnowflakeId ..> SnowflakeId +DefaultSnowflakeFriendlyId ..> SnowflakeFriendlyId +DefaultSnowflakeFriendlyId --> StringSnowflakeId +MillisecondSnowflakeId --> AbstractSnowflakeId +SecondSnowflakeId --> AbstractSnowflakeId +SnowflakeFriendlyId --> SnowflakeId +SnowflakeId --> IdGenerator +StringSnowflakeId ..> IdGenerator +StringSnowflakeId ..> SnowflakeId +``` + +### MillisecondSnowflakeId + +`MillisecondSnowflakeId` 是 `SnowflakeId` 的默认实现,它使用 `System.currentTimeMillis()` 作为时间戳,精确到毫秒级别。 + +### SecondSnowflakeId + +`SecondSnowflakeId` 是 `SnowflakeId` 的另一种实现,它使用 `System.currentTimeMillis() / 1000` 作为时间戳,精确到秒级别。 + +### DefaultSnowflakeFriendlyId + +`DefaultSnowflakeFriendlyId` 是 `SnowflakeId` 的包装器,它将`SnowflakeId`转换成比较友好的字符串表示:`{timestamp}-{machineId}-{sequence} -> 20210623131730192-1-0` + +### ClockSyncSnowflakeId + +`ClockSyncSnowflakeId` 是 `SnowflakeId` 的包装器,当发生时钟回拨时会使用`ClockBackwardsSynchronizer`主动等待时钟同步来重新生成ID,提供更加友好的使用体验。 + + +## 配置 + +[SnowflakeId 配置](../reference/config/snowflake) + +### 配置案例 + +```yaml +cosid: + namespace: ${spring.application.name} + machine: + enabled: true + distributor: + type: jdbc # 机器号分配器 + guarder: + enabled: true # 开启机器号守护 + snowflake: + enabled: true + zone-id: Asia/Shanghai + epoch: 1577203200000 + share: + clock-sync: true # 开启始终回拨同步 + friendly: true + provider: + short_id: + converter: + prefix: cosid_ + type: radix + radix: + char-size: 11 + pad-start: false + safe-js: + machine-bit: 3 + sequence-bit: 9 +``` + diff --git a/documentation/docs/index.md b/documentation/docs/index.md index 404a0ace42..072204804c 100644 --- a/documentation/docs/index.md +++ b/documentation/docs/index.md @@ -1,6 +1,7 @@ --- layout: home title: 通用、灵活、高性能的分布式ID生成器 + hero: name: "CosId" text: "通用、灵活、高性能的分布式ID生成器" diff --git a/documentation/docs/public/assets/design/RedisMachineIdDistributor.png b/documentation/docs/public/assets/design/MachineIdDistributor.png similarity index 100% rename from documentation/docs/public/assets/design/RedisMachineIdDistributor.png rename to documentation/docs/public/assets/design/MachineIdDistributor.png diff --git a/documentation/docs/reference/api/segment.md b/documentation/docs/reference/api/segment.md deleted file mode 100644 index 5e183a2d1e..0000000000 --- a/documentation/docs/reference/api/segment.md +++ /dev/null @@ -1,9 +0,0 @@ -# SegmentId - -TODO - -## IdSegmentDistributor - -## SegmentChainId - -## PrefetchWorker diff --git a/documentation/docs/reference/api/snowflake.md b/documentation/docs/reference/api/snowflake.md deleted file mode 100644 index ff891aba4c..0000000000 --- a/documentation/docs/reference/api/snowflake.md +++ /dev/null @@ -1,13 +0,0 @@ -# SnowflakeId - -TODO - -## MillisecondSnowflakeId - -## SecondSnowflakeId - -## SnowflakeFriendlyId - -## MachineIdDistributor - -## ClockBackwardsSynchronizer diff --git a/documentation/package.json b/documentation/package.json index 81ab34b719..0cbba5cc53 100644 --- a/documentation/package.json +++ b/documentation/package.json @@ -12,6 +12,8 @@ "author": "ahoo wang", "license": "Apache 2.0", "devDependencies": { - "vitepress": "^1.0.0-rc.33" + "mermaid": "^10.6.1", + "vitepress": "^1.0.0-rc.33", + "vitepress-plugin-mermaid": "^2.0.16" } -} \ No newline at end of file +}