diff --git a/.docsifytopdfrc.js b/.docsifytopdfrc.js deleted file mode 100644 index 840480c9..00000000 --- a/.docsifytopdfrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - contents: ["summary.md"], - pathToPublic: "pdf/advanced-java.pdf", - pdfOptions: "", - removeTemp: true, - emulateMedia: "screen", -}; diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 8c2f118c..00000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.html linguist-language=java \ No newline at end of file diff --git a/.github/workflows/compress.yml b/.github/workflows/compress.yml index 990c5a1d..4c23af1d 100644 --- a/.github/workflows/compress.yml +++ b/.github/workflows/compress.yml @@ -11,7 +11,7 @@ jobs: if: github.repository == 'doocs/advanced-java' steps: - name: Checkout Branch - uses: actions/checkout@v2 + uses: actions/checkout@v6 - name: Compress Images id: calibre diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..b8f51195 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,51 @@ +name: Build and deploy + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build with VitePress + run: pnpm run docs:build + + - name: Generate CNAME + run: echo "java.doocs.org" > docs/.vitepress/dist/CNAME + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vitepress/dist + + deploy: + needs: build + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github_pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml deleted file mode 100644 index 97d4d898..00000000 --- a/.github/workflows/rebase.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Automatic Rebase - -on: - issue_comment: - types: [created] - -jobs: - rebase: - name: Rebase - if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') - runs-on: ubuntu-latest - steps: - - name: Checkout the latest code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Automatic Rebase - uses: cirrus-actions/rebase@1.4 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml deleted file mode 100644 index bc25d942..00000000 --- a/.github/workflows/sync.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Sync - -on: - push: - branches: [main] - -jobs: - build: - runs-on: ubuntu-latest - if: github.repository == 'doocs/advanced-java' - steps: - - name: Sync To Gitee - uses: wearerequired/git-mirror-action@master - env: - SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }} - with: - source-repo: git@github.com:doocs/advanced-java.git - destination-repo: git@gitee.com:Doocs/advanced-java.git - - - name: Build Gitee Pages - uses: yanglbme/gitee-pages-action@main - with: - gitee-username: yanglbme - gitee-password: ${{ secrets.GITEE_PASSWORD }} - gitee-repo: doocs/advanced-java - branch: main diff --git a/.gitignore b/.gitignore index 38c28108..58b09838 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,6 @@ yarn-debug.log* yarn-error.log* dist -package-lock.json lib node_modules @@ -41,4 +40,7 @@ yarn-error.log* *.ntvs* *.njsproj *.sln -*.sw? \ No newline at end of file +*.sw? + +docs/.vitepress/dist +docs/.vitepress/cache \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..bf2e7648 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +shamefully-hoist=true diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 952cbbf1..00000000 --- a/.prettierrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "tabWidth": 4, - "useTabs": false, - "semi": true, - "singleQuote": true, - "trailingComma": "all", - "bracketSpacing": true, - "jsxBracketSameLine": false, - "arrowParens": "avoid" -} \ No newline at end of file diff --git a/Main.java b/Main.java new file mode 100644 index 00000000..2a610bf9 --- /dev/null +++ b/Main.java @@ -0,0 +1,8 @@ +/** + * @author yanglbme + */ +public class Main { + public static void main(String[] args) { + System.out.println("互联网 Java 工程师进阶知识完全扫盲"); + } +} \ No newline at end of file diff --git a/README.md b/README.md index 4610cce0..99266dba 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![stars](https://img.shields.io/github/stars/doocs/advanced-java?color=42b883&logo=github&style=flat-square&logoColor=ffffff)](https://github.com/doocs/advanced-java/stargazers) [![forks](https://img.shields.io/github/forks/doocs/advanced-java?color=42b883&logo=github&style=flat-square&logoColor=ffffff)](https://github.com/doocs/advanced-java/network/members) -[![actions status](https://img.shields.io/github/workflow/status/doocs/advanced-java/Sync?color=42b883&label=sync&logo=githubactions&style=flat-square&logoColor=ffffff)](https://github.com/doocs/advanced-java/actions?query=workflow%3ASync) [![license](https://img.shields.io/github/license/doocs/advanced-java?color=42b883&style=flat-square&logo=homeassistantcommunitystore&logoColor=ffffff)](./LICENSE) [![doocs](https://img.shields.io/badge/org-join%20us-42b883?style=flat-square&logo=homeassistantcommunitystore&logoColor=ffffff)](https://doocs.github.io/#/?id=how-to-join) @@ -12,9 +11,6 @@ 学习本项目之前,先来看看 [Discussions 讨论区](https://github.com/doocs/advanced-java/discussions/9)的技术面试官是怎么说的吧。本项目欢迎各位开发者朋友到 Discussions 讨论区分享自己的一些想法和实践经验。也不妨 Star 关注 [doocs/advanced-java](https://github.com/doocs/advanced-java),随时追踪项目最新动态。 -- Gitee Pages: https://doocs.gitee.io/advanced-java -- GitHub Pages: https://doocs.github.io/advanced-java - ## 高并发架构 ### [消息队列](/docs/high-concurrency/mq-interview.md) @@ -42,6 +38,7 @@ - [Redis 的过期策略都有哪些?手写一下 LRU 代码实现?](/docs/high-concurrency/redis-expiration-policies-and-lru.md) - [如何保证 Redis 高并发、高可用?Redis 的主从复制原理能介绍一下么?Redis 的哨兵原理能介绍一下么?](/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md) - [Redis 主从架构是怎样的?](/docs/high-concurrency/redis-master-slave.md) +- [Redis 哨兵集群如何实现高可用?](/docs/high-concurrency/redis-sentinel.md) - [Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?](/docs/high-concurrency/redis-persistence.md) - [Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?](/docs/high-concurrency/redis-cluster.md) - [了解什么是 Redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md) @@ -204,17 +201,11 @@ Doocs 技术社区,致力于打造一个内容完整、持续成长的互联 - -
- -
- 公众平台 -

+
+
- -
- 个人微信 -

+
+
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 00000000..7b5abc4d --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,483 @@ +import { defineConfig } from "vitepress"; + +export default defineConfig({ + title: "advanced-java", + ignoreDeadLinks: true, + themeConfig: { + search: { + provider: 'local' + }, + footer: { + message: 'Released under the CC-BY-SA-4.0 license.', + copyright: `Copyright © 2018-${new Date().getFullYear()} Doocs` + }, + logo: '/icon.png', + docFooter: { + prev: '上一篇', + next: '下一篇' + }, + editLink: { + pattern: 'https://github.com/doocs/advanced-java/edit/main/docs/:path', + text: '在 GitHub 编辑' + }, + nav: [ + { text: "首页", link: "/" }, + { text: "高并发架构", link: "/high-concurrency/mq-interview.md" }, + { + text: "分布式系统", + link: "/distributed-system/distributed-system-interview.md", + }, + { + text: "高可用架构", + link: "/high-availability/hystrix-introduction.md", + }, + { + text: "微服务架构", + link: "/micro-services/microservices-introduction.md", + }, + { text: "海量数据处理", link: "/big-data/find-common-urls.md" }, + ], + + sidebar: [ + { + text: "高并发架构", + collapsed: true, + items: [ + { + text: "消息队列", + link: "/high-concurrency/mq-interview.md", + collapsed: true, + items: [ + { + text: "为什么使用消息队列?", + link: "/high-concurrency/why-mq.md", + }, + { + text: "如何保证消息队列的高可用?", + link: "/high-concurrency/how-to-ensure-high-availability-of-message-queues.md", + }, + { + text: "如何保证消息不被重复消费?", + link: "/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md", + }, + { + text: "如何保证消息的可靠性传输?", + link: "/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md", + }, + { + text: "如何保证消息的顺序性?", + link: "/high-concurrency/how-to-ensure-the-order-of-messages.md", + }, + { + text: "如何解决消息队列的延时及过期问题?", + link: "/high-concurrency/mq-time-delay-and-expired-failure.md", + }, + { + text: "如何设计一个消息队列?", + link: "/high-concurrency/mq-design.md", + }, + ], + }, + { + text: "搜索引擎", + collapsed: true, + link: "/high-concurrency/es-introduction.md", + items: [ + { + text: "ES 的分布式架构原理", + link: "/high-concurrency/es-architecture.md", + }, + { + text: "ES 写入数据原理", + link: "/high-concurrency/es-write-query-search.md", + }, + { + text: "ES 查询性能优化", + link: "/high-concurrency/es-optimizing-query-performance.md", + }, + { + text: "ES 生产集群架构", + link: "/high-concurrency/es-production-cluster.md", + }, + ], + }, + { + text: "缓存", + collapsed: true, + items: [ + { + text: "缓存的使用方式", + link: "/high-concurrency/why-cache.md", + }, + { + text: "Redis 和 Memcached 区别", + link: "/high-concurrency/redis-single-thread-model.md", + }, + { + text: "Redis 数据类型和场景", + link: "/high-concurrency/redis-data-types.md", + }, + { + text: "Redis 过期策略", + link: "/high-concurrency/redis-expiration-policies-and-lru.md", + }, + { + text: "Redis 高并发与高可用", + link: "/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md", + }, + { + text: "Redis 主从架构", + link: "/high-concurrency/redis-master-slave.md", + }, + { + text: "Redis 持久化机制", + link: "/high-concurrency/redis-persistence.md", + }, + { + text: "哨兵集群实现高可用", + link: "/high-concurrency/redis-sentinel.md", + }, + { + text: "Redis 集群模式原理", + link: "/high-concurrency/redis-cluster.md", + }, + { + text: "缓存雪崩穿透击穿", + link: "/high-concurrency/redis-caching-avalanche-and-caching-penetration.md", + }, + { + text: "缓存与数据库一致性", + link: "/high-concurrency/redis-consistence.md", + }, + { + text: "Redis 并发竞争问题", + link: "/high-concurrency/redis-cas.md", + }, + { + text: "Redis 生产部署方案", + link: "/high-concurrency/redis-production-environment.md", + }, + ], + }, + { + text: "分库分表", + collapsed: true, + items: [ + { + text: "为什么要分库分表?", + link: "/high-concurrency/database-shard.md", + }, + { + text: "分库分表如何平滑过渡?", + link: "/high-concurrency/database-shard-method.md", + }, + { + text: "动态扩缩容方案", + link: "/high-concurrency/database-shard-dynamic-expand.md", + }, + { + text: "主键 ID 如何处理?", + link: "/high-concurrency/database-shard-global-id-generate.md", + }, + ], + }, + { + text: "读写分离", + items: [ + { + text: "如何实现读写分离?", + link: "/high-concurrency/mysql-read-write-separation.md", + }, + ], + }, + { + text: "高并发系统", + items: [ + { + text: "如何设计一个高并发系统?", + link: "/high-concurrency/high-concurrency-design.md", + }, + ], + }, + { + text: "限流", + items: [ + { + text: "限流实现方式", + link: "/high-concurrency/how-to-limit-current.md", + }, + ], + }, + ], + }, + { + text: "分布式系统", + collapsed: true, + items: [ + { + text: "面试连环炮", + link: "/distributed-system/distributed-system-interview.md", + }, + { + text: "系统拆分", + items: [ + { + text: "为什么要拆分系统?", + link: "/distributed-system/why-dubbo.md", + }, + ], + }, + { + text: "分布式服务框架", + collapsed: true, + items: [ + { + text: "Dubbo 工作原理", + link: "/distributed-system/dubbo-operating-principle.md", + }, + { + text: "Dubbo 序列化协议", + link: "/distributed-system/dubbo-serialization-protocol.md", + }, + { + text: "Dubbo 负载与容错策略", + link: "/distributed-system/dubbo-load-balancing.md", + }, + { + text: "Dubbo 的 SPI 思想", + link: "/distributed-system/dubbo-spi.md", + }, + { + text: "服务治理方案", + link: "/distributed-system/dubbo-service-management.md", + }, + { + text: "接口幂等性设计", + link: "/distributed-system/distributed-system-idempotency.md", + }, + { + text: "接口顺序性设计", + link: "/distributed-system/distributed-system-request-sequence.md", + }, + { + text: "设计 Dubbo 类似的 RPC 框架", + link: "/distributed-system/dubbo-rpc-design.md", + }, + { + text: "CAP 定理中的 P 是什么?", + link: "/distributed-system/distributed-system-cap.md", + }, + ], + }, + { + text: "分布式锁", + collapsed: true, + items: [ + { + text: "Zookeeper 应用场景", + link: "/distributed-system/zookeeper-application-scenarios.md", + }, + { + text: "Redis vs Zookeeper 实现分布式锁", + link: "/distributed-system/distributed-lock-redis-vs-zookeeper.md", + }, + ], + }, + { + text: "分布式事务", + items: [ + { + text: "分布式事务原理", + link: "/distributed-system/distributed-transaction.md", + }, + ], + }, + { + text: "分布式会话", + items: [ + { + text: "如何实现分布式 Session?", + link: "/distributed-system/distributed-session.md", + }, + ], + }, + ], + }, + { + text: "高可用架构", + collapsed: true, + items: [ + { + text: "Hystrix 高可用", + collapsed: true, + items: [ + { + text: "Hystrix 介绍", + link: "/high-availability/hystrix-introduction.md", + }, + { + text: "详情页架构设计", + link: "/high-availability/e-commerce-website-detail-page-architecture.md", + }, + { + text: "线程池隔离", + link: "/high-availability/hystrix-thread-pool-isolation.md", + }, + { + text: "信号量隔离", + link: "/high-availability/hystrix-semphore-isolation.md", + }, + { + text: "细粒度隔离策略", + link: "/high-availability/hystrix-execution-isolation.md", + }, + { + text: "执行原理", + link: "/high-availability/hystrix-process.md", + }, + { + text: "Request Cache 缓存", + link: "/high-availability/hystrix-request-cache.md", + }, + { + text: "本地降级缓存", + link: "/high-availability/hystrix-fallback.md", + }, + { + text: "断路器原理", + link: "/high-availability/hystrix-circuit-breaker.md", + }, + { + text: "限流与线程池隔离", + link: "/high-availability/hystrix-thread-pool-current-limiting.md", + }, + { + text: "Timeout 保护机制", + link: "/high-availability/hystrix-timeout.md", + }, + ], + }, + { + text: "熔断与降级", + items: [ + { + text: "Sentinel vs Hystrix", + link: "/high-availability/sentinel-vs-hystrix.md", + }, + ], + }, + ], + }, + { + text: "微服务架构", + collapsed: true, + items: [ + { + text: "微服务概念", + collapsed: true, + items: [ + { + text: "微服务架构描述", + link: "/micro-services/microservices-introduction.md", + }, + { + text: "从单体到微服务", + link: "/micro-services/migrating-from-a-monolithic-architecture-to-a-microservices-architecture.md", + }, + { + text: "事件驱动数据管理", + link: "/micro-services/event-driven-data-management-for-microservices.md", + }, + { + text: "选择部署策略", + link: "/micro-services/choose-microservice-deployment-strategy.md", + }, + ], + }, + { + text: "Spring Cloud 架构", + collapsed: true, + items: [ + { + text: "微服务间通信机制", + link: "/micro-services/what's-microservice-how-to-communicate.md", + }, + { + text: "微服务技术栈", + link: "/micro-services/micro-services-technology-stack.md", + }, + { + text: "微服务治理策略", + link: "/micro-services/micro-service-governance.md", + }, + { + text: "Eureka 服务注册发现", + link: "/micro-services/how-eureka-enable-service-discovery-and-service-registration.md", + }, + ], + }, + ], + }, + { + text: "海量数据处理", + collapsed: true, + items: [ + { text: "查找相同 URL", link: "/big-data/find-common-urls.md" }, + { text: "查找高频词", link: "/big-data/find-top-100-words.md" }, + { text: "找出最多访问 IP", link: "/big-data/find-top-1-ip.md" }, + { + text: "找出不重复整数", + link: "/big-data/find-no-repeat-number.md", + }, + { + text: "判断数是否存在", + link: "/big-data/find-a-number-if-exists.md", + }, + { + text: "查询最热门查询串", + link: "/big-data/find-hotest-query-string.md", + }, + { + text: "统计不同手机号", + link: "/big-data/count-different-phone-numbers.md", + }, + { + text: "找出中位数", + link: "/big-data/find-mid-value-in-500-millions.md", + }, + { + text: "根据频率排序查询串", + link: "/big-data/sort-the-query-strings-by-counts.md", + }, + { + text: "找出前 500 个数", + link: "/big-data/find-rank-top-500-numbers.md", + }, + ], + }, + ], + + socialLinks: [ + { icon: "github", link: "https://github.com/doocs/advanced-java" }, + ], + }, + head: [ + ['link', { rel: 'icon', type: 'image/png', href: '/favicon-32x32.png' }], + [ + 'script', + { async: '', src: 'https://www.googletagmanager.com/gtag/js?id=G-GWYHFTEDNE' } + ], + [ + 'script', + {}, + `window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', 'G-GWYHFTEDNE');` + ] + ], + cleanUrls: true, + sitemap: { + hostname: 'https://java.doocs.org' + } +}); diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue new file mode 100644 index 00000000..0a0afe7e --- /dev/null +++ b/docs/.vitepress/theme/Layout.vue @@ -0,0 +1,38 @@ + + + diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js new file mode 100644 index 00000000..419b5850 --- /dev/null +++ b/docs/.vitepress/theme/index.js @@ -0,0 +1,36 @@ +import DefaultTheme from "vitepress/theme"; +import giscusTalk from "vitepress-plugin-comment-with-giscus"; +import { useData, useRoute } from "vitepress"; +import { toRefs } from "vue"; +import Layout from "./Layout.vue"; + +export default { + extends: DefaultTheme, + Layout: Layout, + enhanceApp(ctx) { + DefaultTheme.enhanceApp(ctx); + }, + setup() { + const { frontmatter } = toRefs(useData()); + const route = useRoute(); + + giscusTalk( + { + repo: "doocs/advanced-java", + repoId: "MDEwOlJlcG9zaXRvcnkxNTE4MzQwNjI=", + mapping: "number", + inputPosition: "top", + lang: "zh-CN", + homePageShowComment: true, + term: "9", + lightTheme: "light", + darkTheme: "transparent_dark", + }, + { + frontmatter, + route, + }, + true + ); + }, +}; diff --git a/docs/big-data/README.md b/docs/big-data/README.md index a1e1cb2b..9abc72aa 100644 --- a/docs/big-data/README.md +++ b/docs/big-data/README.md @@ -19,17 +19,11 @@ - -
- -
- 公众平台 -

+
+
- -
- 个人微信 -

+
+
diff --git a/docs/big-data/count-different-phone-numbers.md b/docs/big-data/count-different-phone-numbers.md index 0a1657ee..b799f818 100644 --- a/docs/big-data/count-different-phone-numbers.md +++ b/docs/big-data/count-different-phone-numbers.md @@ -1,10 +1,10 @@ -## 如何统计不同电话号码的个数? +# 如何统计不同电话号码的个数? -### 题目描述 +## 题目描述 已知某个文件内包含一些电话号码,每个号码为 8 位数字,统计不同号码的个数。 -### 解答思路 +## 解答思路 这道题本质还是求解**数据重复**的问题,对于这类问题,一般首先考虑位图法。 @@ -14,6 +14,6 @@ 申请一个位图数组,长度为 1 亿,初始化为 0。然后遍历所有电话号码,把号码对应的位图中的位置置为 1。遍历完成后,如果 bit 为 1,则表示这个电话号码在文件中存在,否则不存在。bit 值为 1 的数量即为 不同电话号码的个数。 -### 方法总结 +## 方法总结 求解数据重复问题,记得考虑位图法。 diff --git a/docs/big-data/find-a-number-if-exists.md b/docs/big-data/find-a-number-if-exists.md index e136167d..7e159833 100644 --- a/docs/big-data/find-a-number-if-exists.md +++ b/docs/big-data/find-a-number-if-exists.md @@ -1,21 +1,21 @@ -## 如何在大量的数据中判断一个数是否存在? +# 如何在大量的数据中判断一个数是否存在? -### 题目描述 +## 题目描述 给定 40 亿个不重复的没排过序的 unsigned int 型整数,然后再给定一个数,如何快速判断这个数是否在这 40 亿个整数当中? -### 解答思路 +## 解答思路 -#### 方法一:分治法 +### 方法一:分治法 依然可以用分治法解决,方法与前面类似,就不再次赘述了。 -#### 方法二:位图法 +### 方法二:位图法 由于 unsigned int 数字的范围是 `[0, 1 << 32)`,我们用 `1<<32=4,294,967,296` 个 bit 来表示每个数字。初始位均为 0,那么总共需要内存:4,294,967,296b≈512M。 我们读取这 40 亿个整数,将对应的 bit 设置为 1。接着读取要查询的数,查看相应位是否为 1,如果为 1 表示存在,如果为 0 表示不存在。 -### 方法总结 +## 方法总结 **判断数字是否存在、判断数字是否重复的问题**,位图法是一种非常高效的方法。 diff --git a/docs/big-data/find-common-urls.md b/docs/big-data/find-common-urls.md index 7c877d83..699191ec 100644 --- a/docs/big-data/find-common-urls.md +++ b/docs/big-data/find-common-urls.md @@ -1,12 +1,12 @@ -## 如何从大量的 URL 中找出相同的 URL? +# 如何从大量的 URL 中找出相同的 URL? -### 题目描述 +## 题目描述 给定 a、b 两个文件,各存放 50 亿个 URL,每个 URL 各占 64B,内存限制是 4G。请找出 a、b 两个文件共同的 URL。 -### 解答思路 +## 解答思路 -#### 1. 分治策略 +### 1. 分治策略 每个 URL 占 64B,那么 50 亿个 URL 占用的空间大小约为 320GB。 @@ -20,19 +20,53 @@ 接着遍历 ai( `i∈[0,999]` ),把 URL 存储到一个 HashSet 集合中。然后遍历 bi 中每个 URL,看在 HashSet 集合中是否存在,若存在,说明这就是共同的 URL,可以把这个 URL 保存到一个单独的文件中。 -#### 2. 前缀树 - -一般而言,URL 的长度差距不会不大,而且前面几个字符,绝大部分相同。这种情况下,非常适合使用**字典树**(trie tree) 这种数据结构来进行存储,降低存储成本的同时,提高查询效率。 +### 2. 前缀树是否可行? +> 一般而言,URL 的长度差距不会不大,而且前面几个字符,绝大部分相同。这种情况下,非常适合使用**字典树**(trie tree) 这种数据结构来进行存储,降低存储成本的同时,提高查询效率。 +> > 由 [@ChunelFeng](https://github.com/ChunelFeng) 反馈。[#212](https://github.com/doocs/advanced-java/issues/212) -### 方法总结 +有 issue 提到是否可以直接使用 trie 树,我们来推算一下方案是否可行。 + +Background:4G 的机器,解析 320G 的数据。 + +首先题目提到 64B 大小,按一个字符一个字节来算的话就是 64 位,也就是 64 个字符组成(这里考虑极端情况)。Trie 树的定义这里就不提了,见[字典树](https://doocs.github.io/leetcode/tags/#%E5%A4%9A%E7%BA%BF%E7%A8%8B)。 + +- **树高**:URL 的长度就是最后的树的能达到的高度,及最高 64 层树。 + +- **节点下的子节点数量**:因为是 URL,除了数字和字符之外还有`%`, `/`, `:`etc。这里为了简化计算和方便理解只使用英文字符+数字总数=62+10=72。也就是说一个结点下的子节点最多有 72 个,及占 72B。 + +- **节点总大小**:因为样本 320G 足够大,我们考虑坏情况下,最后的树是 64 层高的满 72 叉树。最后的内存占用为节点数量\*节点大小: + + - 节点数量:满 N = 64 层,K = 72 叉,树节点数量计算公式(等比数列求和)为:`1 * (1-72^64)/ (1 - 72) = (72^64 - 1)/ (71)`,估算为`71^63`个节点。 + + - 节点大小:每个节点占用大小为 1B + - 当前字符 1B + - 当前位置是否存在值 1b(常见 Bool 值大小),这里按最小大小来。 + +所以总大小为:`71^63 * 1B/ 1000 ≈ 71^60 KB ≈ 71 ^ 63 GB`,这个值已经很大了,况且在方案推算过程中按照最小原则考虑。 + +--- + +写到这里有些读者可能就被吓到了,怎么出来这么多数据,trie 树不是有压缩的作用嘛? + +这里得到的结果是不正确的,因为 320G 的数据,生成的节点数也最多只会是 320G(路径完全不重复),因为无法构成上面讨论的满 N 叉树的情况,填满了其中一点而已。但是对于单机来说此部分数据单机已无法解析,并且方案不具有扩展性和稳定性 。所以单纯的 trie 树太依赖数据分布,如果你的大部分 URL 是相同的压缩度很高,单机 4G 解析 320G 有可能,但是对于面试官想听到的应用场景来说这个概率太小了。所以还是得借助分治法的方式降低内存。 + +在方案一上进行优化:在上一个方法中使用 HaseSet 来判重,hash 效率比访问 trie 树,hash 效率更高,trie 可以降低内存。 + +所以在面试中可以提到 trie 方案,在这个场景中解析 320G 离线数据为时间不敏感类型,所以可以牺牲速度来换取空间。 + +- 方案一提到单文件大小为 300MB+,使用 trie 树之后这 300MB+会有所压缩比如到 200MB+,考虑到机器性能利用率,在一些 CPU 充足的场景中可以考虑引入并发,因为压缩率 1/3,其余空间可以腾出来加载更多文件做并发提升处理速度。 + +- 如果 CPU 不充足比如单核,可以考虑增加切分的文件大小,原先的 1000 个文件,降为 200 个也就是文件大小扩大两倍,之前 300MB,现在 300\*5 = 1.5G,在 4G 的机器上可以打满内存,减少 IO 次数,减少内核态到用户态的拷贝,也可以提一嘴 DMA。 + +## 方法总结 -#### 分治策略 +### 分治策略 1. 分而治之,进行哈希取余; 1. 对每个子文件进行 HashSet 统计。 -#### 前缀树 +### 前缀树 -1. 利用字符串的公共前缀来降低存储成本,提高查询效率。 +1. 利用字符串的公共前缀牺牲速度,来降低内存占用。 diff --git a/docs/big-data/find-hotest-query-string.md b/docs/big-data/find-hotest-query-string.md index 3e3d42a7..ea92712e 100644 --- a/docs/big-data/find-hotest-query-string.md +++ b/docs/big-data/find-hotest-query-string.md @@ -1,16 +1,16 @@ -## 如何查询最热门的查询串? +# 如何查询最热门的查询串? -### 题目描述 +## 题目描述 搜索引擎会通过日志文件把用户每次检索使用的所有查询串都记录下来,每个查询串的长度不超过 255 字节。 假设目前有 1000w 个记录(这些查询串的重复度比较高,虽然总数是 1000w,但如果除去重复后,则不超过 300w 个)。请统计最热门的 10 个查询串,要求使用的内存不能超过 1G。(一个查询串的重复度越高,说明查询它的用户越多,也就越热门。) -### 解答思路 +## 解答思路 每个查询串最长为 255B,1000w 个串需要占用 约 2.55G 内存,因此,我们无法将所有字符串全部读入到内存中处理。 -#### 方法一:分治法 +### 方法一:分治法 分治法依然是一个非常实用的方法。 @@ -18,7 +18,7 @@ 方法可行,但不是最好,下面介绍其他方法。 -#### 方法二:HashMap 法 +### 方法二:HashMap 法 虽然字符串总数比较多,但去重后不超过 300w,因此,可以考虑把所有字符串及出现次数保存在一个 HashMap 中,所占用的空间为 300w\*(255+4)≈777M(其中,4 表示整数占用的 4 个字节)。由此可见,1G 的内存空间完全够用。 @@ -30,7 +30,7 @@ 遍历结束后,堆中 10 个字符串就是出现次数最多的字符串。这一步时间复杂度 `O(Nlog10)` 。 -#### 方法三:前缀树法 +### 方法三:前缀树法 方法二使用了 HashMap 来统计次数,当这些字符串有大量相同前缀时,可以考虑使用前缀树来统计字符串出现的次数,树的结点保存字符串出现次数,0 表示没有出现。 @@ -40,6 +40,6 @@ 最后依然使用小顶堆来对字符串的出现次数进行排序。 -### 方法总结 +## 方法总结 前缀树经常被用来统计字符串的出现次数。它的另外一个大的用途是字符串查找,判断是否有重复的字符串等。 diff --git a/docs/big-data/find-mid-value-in-500-millions.md b/docs/big-data/find-mid-value-in-500-millions.md index 2d240e17..8421801c 100644 --- a/docs/big-data/find-mid-value-in-500-millions.md +++ b/docs/big-data/find-mid-value-in-500-millions.md @@ -1,14 +1,14 @@ -## 如何从 5 亿个数中找出中位数? +# 如何从 5 亿个数中找出中位数? -### 题目描述 +## 题目描述 从 5 亿个数中找出中位数。数据排序后,位置在最中间的数就是中位数。当样本数为奇数时,中位数为 第 `(N+1)/2` 个数;当样本数为偶数时,中位数为 第 `N/2` 个数与第 `1+N/2` 个数的均值。 -### 解答思路 +## 解答思路 如果这道题没有内存大小限制,则可以把所有数读到内存中排序后找出中位数。但是最好的排序算法的时间复杂度都为 `O(NlogN)` 。这里使用其他方法。 -#### 方法一:双堆法 +### 方法一:双堆法 维护两个堆,一个大顶堆,一个小顶堆。大顶堆中最大的数**小于等于**小顶堆中最小的数;保证这两个堆中的元素个数的差不超过 1。 @@ -53,11 +53,11 @@ class MedianFinder { } ``` -> 见 LeetCode No.295:https://leetcode.com/problems/find-median-from-data-stream/ +> 见 [LeetCode No.295](https://leetcode.com/problems/find-median-from-data-stream/) 以上这种方法,需要把所有数据都加载到内存中。当数据量很大时,就不能这样了,因此,这种方法**适用于数据量较小的情况**。5 亿个数,每个数字占用 4B,总共需要 2G 内存。如果可用内存不足 2G,就不能使用这种方法了,下面介绍另一种方法。 -#### 方法二:分治法 +### 方法二:分治法 分治法的思想是把一个大的问题逐渐转换为规模较小的问题来求解。 @@ -71,6 +71,6 @@ class MedianFinder { > **注意**,当数据总数为偶数,如果划分后两个文件中的数据有相同个数,那么中位数就是数据较小的文件中的最大值与数据较大的文件中的最小值的平均值。 -### 方法总结 +## 方法总结 分治法,真香! diff --git a/docs/big-data/find-no-repeat-number.md b/docs/big-data/find-no-repeat-number.md index 0c3599b2..0fc3b7b2 100644 --- a/docs/big-data/find-no-repeat-number.md +++ b/docs/big-data/find-no-repeat-number.md @@ -1,16 +1,16 @@ -## 如何在大量的数据中找出不重复的整数? +# 如何在大量的数据中找出不重复的整数? -### 题目描述 +## 题目描述 在 2.5 亿个整数中找出不重复的整数。注意:内存不足以容纳这 2.5 亿个整数。 -### 解答思路 +## 解答思路 -#### 方法一:分治法 +### 方法一:分治法 与前面的题目方法类似,先将 2.5 亿个数划分到多个小文件,用 HashSet/HashMap 找出每个小文件中不重复的整数,再合并每个子结果,即为最终结果。 -#### 方法二:位图法 +### 方法二:位图法 **位图**,就是用一个或多个 bit 来标记某个元素对应的值,而键就是该元素。采用位作为单位来存储数据,可以大大节省存储空间。 @@ -58,6 +58,6 @@ for i in range(8): 当然,本题中特别说明:**内存不足以容纳这 2.5 亿个整数**,2.5 亿个整数的内存大小为:2.5e8/1024/1024/1024 \* 4=3.72GB, 如果内存大于 1GB,是可以通过位图法解决的。 -### 方法总结 +## 方法总结 **判断数字是否重复的问题**,位图法是一种非常高效的方法,当然前提是:内存要满足位图法所需要的存储空间。 diff --git a/docs/big-data/find-rank-top-500-numbers.md b/docs/big-data/find-rank-top-500-numbers.md index 6133f679..8b840c36 100644 --- a/docs/big-data/find-rank-top-500-numbers.md +++ b/docs/big-data/find-rank-top-500-numbers.md @@ -1,10 +1,10 @@ -## 如何找出排名前 500 的数? +# 如何找出排名前 500 的数? -### 题目描述 +## 题目描述 有 20 个数组,每个数组有 500 个元素,并且有序排列。如何在这 20\*500 个数中找出前 500 的数? -### 解答思路 +## 解答思路 对于 TopK 问题,最常用的方法是使用堆排序。对本题而言,假设数组降序排列,可以采用以下方法: @@ -104,6 +104,6 @@ class Test { } ``` -### 方法总结 +## 方法总结 求 TopK,不妨考虑一下堆排序? diff --git a/docs/big-data/find-top-1-ip.md b/docs/big-data/find-top-1-ip.md index 8289b6e9..2f361dad 100644 --- a/docs/big-data/find-top-1-ip.md +++ b/docs/big-data/find-top-1-ip.md @@ -1,16 +1,16 @@ -## 如何找出某一天访问百度网站最多的 IP? +# 如何找出某一天访问百度网站最多的 IP? -### 题目描述 +## 题目描述 现有海量日志数据保存在一个超大文件中,该文件无法直接读入内存,要求从中提取某天访问百度次数最多的那个 IP。 -### 解答思路 +## 解答思路 这道题只关心某一天访问百度最多的 IP,因此,可以首先对文件进行一次遍历,把这一天访问百度 IP 的相关信息记录到一个单独的大文件中。接下来采用的方法与上一题一样,大致就是先对 IP 进行哈希映射,接着使用 HashMap 统计重复 IP 的次数,最后计算出重复次数最多的 IP。 > 注:这里只需要找出出现次数最多的 IP,可以不必使用堆,直接用一个变量 max 即可。 -### 方法总结 +## 方法总结 1. 分而治之,进行哈希取余; 2. 使用 HashMap 统计频数; diff --git a/docs/big-data/find-top-100-words.md b/docs/big-data/find-top-100-words.md index 34261baa..6489768e 100644 --- a/docs/big-data/find-top-100-words.md +++ b/docs/big-data/find-top-100-words.md @@ -1,10 +1,10 @@ -## 如何从大量数据中找出高频词? +# 如何从大量数据中找出高频词? -### 题目描述 +## 题目描述 有一个 1GB 大小的文件,文件里每一行是一个词,每个词的大小不超过 16B,内存大小限制是 1MB,要求返回频数最高的 100 个词(Top 100)。 -### 解答思路 +## 解答思路 由于内存限制,我们依然无法直接将大文件的所有词一次读到内存中。因此,同样可以采用**分治策略**,把一个大文件分解成多个小文件,保证每个文件的大小小于 1MB,进而直接将单个小文件读取到内存中进行处理。 @@ -16,7 +16,7 @@ 上面我们统计了每个小文件单词出现的频数。接下来,我们可以通过维护一个**小顶堆**来找出所有词中出现频数最高的 100 个。具体方法是:依次遍历每个小文件,构建一个**小顶堆**,堆大小为 100。如果遍历到的词的出现次数大于堆顶词的出现次数,则用新词替换堆顶的词,然后重新调整为**小顶堆**,遍历结束后,小顶堆上的词就是出现频数最高的 100 个词。 -### 方法总结 +## 方法总结 1. 分而治之,进行哈希取余; 2. 使用 HashMap 统计频数; diff --git a/docs/big-data/sort-the-query-strings-by-counts.md b/docs/big-data/sort-the-query-strings-by-counts.md index f15db3a3..2fceb83b 100644 --- a/docs/big-data/sort-the-query-strings-by-counts.md +++ b/docs/big-data/sort-the-query-strings-by-counts.md @@ -1,24 +1,24 @@ -## 如何按照 query 的频度排序? +# 如何按照 query 的频度排序? -### 题目描述 +## 题目描述 有 10 个文件,每个文件大小为 1G,每个文件的每一行存放的都是用户的 query,每个文件的 query 都可能重复。要求按照 query 的频度排序。 -### 解答思路 +## 解答思路 如果 query 的重复度比较大,可以考虑一次性把所有 query 读入内存中处理;如果 query 的重复率不高,那么可用内存不足以容纳所有的 query,这时候就需要采用分治法或其他的方法来解决。 -#### 方法一:HashMap 法 +### 方法一:HashMap 法 如果 query 重复率高,说明不同 query 总数比较小,可以考虑把所有的 query 都加载到内存中的 HashMap 中。接着就可以按照 query 出现的次数进行排序。 -#### 方法二:分治法 +### 方法二:分治法 分治法需要根据数据量大小以及可用内存的大小来确定问题划分的规模。对于这道题,可以顺序遍历 10 个文件中的 query,通过 Hash 函数 `hash(query) % 10` 把这些 query 划分到 10 个小文件中。之后对每个小文件使用 HashMap 统计 query 出现次数,根据次数排序并写入到另外一个单独文件中。 接着对所有文件按照 query 的次数进行排序,这里可以使用归并排序(由于无法把所有 query 都读入内存,因此需要使用外排序)。 -### 方法总结 +## 方法总结 - 内存若够,直接读入进行排序; - 内存不够,先划分为小文件,小文件排好序后,整理使用外排序进行归并。 diff --git a/docs/big-data/topk-problems-and-solutions.md b/docs/big-data/topk-problems-and-solutions.md index 326b347f..def80ee6 100644 --- a/docs/big-data/topk-problems-and-solutions.md +++ b/docs/big-data/topk-problems-and-solutions.md @@ -1,8 +1,4 @@ -## 大数据中 TopK 问题的常用套路 - -> **作者 Chunel Feng,编程爱好者,阿里巴巴搜索引擎开发工程师。**

个人微信:ChunelFeng
个人博客:[一面之猿网](http://www.chunel.cn)
开源项目:[Caiss 智能相似搜索引擎](https://github.com/ChunelFeng/caiss) - -Doocs 社区的朋友们,大家好。我是你们的新朋友 [Chunel Feng](https://github.com/ChunelFeng)。今天想跟大家聊一些**常见的 topK 问题**。 +# 大数据中 TopK 问题的常用套路 对于海量数据到处理经常会涉及到 topK 问题。在设计数据结构和算法的时候,主要需要考虑的应该是当前算法(包括数据结构)跟给定情境(比如数据量级、数据类型)的适配程度,和当前问题最核心的瓶颈(如降低时间复杂度,还是降低空间复杂度)是什么。 @@ -19,7 +15,7 @@ Doocs 社区的朋友们,大家好。我是你们的新朋友 [Chunel Feng](ht 上面这些问题看起来很相似,但是解决的方式却千差万别。稍有不慎,就可能使得 topK 问题成为系统的瓶颈。不过也不用太担心,接下来我会总结几种常见的解决思路,遇到问题的时候,大家把这些基础思路融会贯通并且杂糅组合,即可做到见招拆招。
-### 1. 堆排序法 +## 1. 堆排序法 这里说的是堆排序法,而不是快排或者希尔排序。虽然理论时间复杂度都是 `O(nlogn)`,但是堆排在做 topK 的时候有一个优势,就是可以维护一个仅包含 k 个数字的小顶堆(想清楚,为啥是小顶堆哦),当新加入的数字大于堆顶数字的时候,将堆顶元素剔除,并加入新的数字。 @@ -49,7 +45,7 @@ int main() { > Java 中同样提供了 PriorityQueue 的数据结构。 -### 2. 类似快排法 +## 2. 类似快排法 快排大家都知道,针对 topK 问题,可以对快排进行改进。仅对部分数据进行递归计算。比如,在 100 个数字中,找最大的 10 个,第一次循环的时候,povit 被移动到了 80 的位置,则接下来仅需要在后面的 20 个数字中找最大的 10 个即可。 @@ -107,7 +103,7 @@ int main() {
-### 3. 使用 bitmap +## 3. 使用 bitmap 有时候 topK 问题会遇到数据量过大,内存无法全部加载。这个时候,可以考虑将数据存放至 bitmap 中,方便查询。 @@ -118,7 +114,7 @@ int main() { 这种做法的优势,当然是降低了空间复杂度。不过需要注意一点,bitmap 比较适合不重复且有范围(比如,数据均在 0 ~ 10 亿之间)的数据的查询。至于有重复数据的情况,可以考虑与 hash 等结构的混用。
-### 4. 使用 hash +## 4. 使用 hash 如果遇到了查询 string 类型数据的大小,可以考虑 hash 方法。 @@ -127,7 +123,7 @@ int main() { 这种方法比较适合网址或者电话号码的查询。缺点就是如果需要多次查询的话,需要多次计算 hash,并且需要根据实际情况设计多个 hash 函数。
-### 5. 字典树 +## 5. 字典树 字典树(trie)的具体结构和查询方式,不在这里赘述了,自行百度一下就有很多。这里主要说一下优缺点。 @@ -138,13 +134,13 @@ int main() { 比如,反复多次查询字符序(例如:z>y>...>b>a)最大的 k 个 url 这种,使用字典树把数据存储一遍,就非常适合。既减少了空间复杂度,也加速了查询效率。
-### 6. 混合查询 +## 6. 混合查询 以上几种方法,都是比较独立的方法。其实,在实际工作中,遇到更多的问题还是混合问题,这就需要我们对相关的内容,融会贯通并且做到活学活用。 我举个例子:我们的分布式服务跑在 10 台不同机器上,每台机器上部署的服务均被请求 10000 次,并且记录了个这 10000 次请求的耗时(耗时值为 int 数据),找出这 10\*10000 次请求中,从高到低的找出耗时最大的 50 个。看看这个问题,很现实吧。我们试着用上面介绍的方法,组合一下来求解。 -#### 方法一 +### 方法一 首先,对每台机器上的 10000 个做类似快排,找出每台机器上 top50 的耗时信息。此时,单机上的这 50 条数据是无序的。 @@ -152,7 +148,7 @@ int main() { 最后,对这 50 个数据做快排,从而得到最终结果。 -#### 方法二 +### 方法二 首先通过堆排,分别找出 10 台机器上耗时最高的 50 个数据,此时的这 50 个数据,已经是从大到小有序的了。 diff --git a/docs/distributed-system/README.md b/docs/distributed-system/README.md index cd1a7934..13d3c17b 100644 --- a/docs/distributed-system/README.md +++ b/docs/distributed-system/README.md @@ -1,35 +1,35 @@ # 分布式系统 -## [面试连环炮](/docs/distributed-system/distributed-system-interview.md) +## [面试连环炮](./distributed-system-interview.md) ## 系统拆分 -- [为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?](/docs/distributed-system/why-dubbo.md) +- [为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?](./why-dubbo.md) ## 分布式服务框架 -- [说一下 Dubbo 的工作原理?注册中心挂了可以继续通信吗?](/docs/distributed-system/dubbo-operating-principle.md) -- [Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?](/docs/distributed-system/dubbo-serialization-protocol.md) -- [Dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?](/docs/distributed-system/dubbo-load-balancing.md) -- [Dubbo 的 SPI 思想是什么?](/docs/distributed-system/dubbo-spi.md) -- [如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?](/docs/distributed-system/dubbo-service-management.md) -- [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](/docs/distributed-system/distributed-system-idempotency.md) -- [分布式服务接口请求的顺序性如何保证?](/docs/distributed-system/distributed-system-request-sequence.md) -- [如何自己设计一个类似 Dubbo 的 RPC 框架?](/docs/distributed-system/dubbo-rpc-design.md) -- [CAP 定理的 P 是什么](/docs/distributed-system/distributed-system-cap.md) +- [说一下 Dubbo 的工作原理?注册中心挂了可以继续通信吗?](./dubbo-operating-principle.md) +- [Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?](./dubbo-serialization-protocol.md) +- [Dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?](./dubbo-load-balancing.md) +- [Dubbo 的 SPI 思想是什么?](./dubbo-spi.md) +- [如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?](./dubbo-service-management.md) +- [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](./distributed-system-idempotency.md) +- [分布式服务接口请求的顺序性如何保证?](./distributed-system-request-sequence.md) +- [如何自己设计一个类似 Dubbo 的 RPC 框架?](./dubbo-rpc-design.md) +- [CAP 定理的 P 是什么](./distributed-system-cap.md) ## 分布式锁 -- [Zookeeper 都有哪些应用场景?](/docs/distributed-system/zookeeper-application-scenarios.md) -- [使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗?以上两种分布式锁的实现方式哪种效率比较高?](/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md) +- [Zookeeper 都有哪些应用场景?](./zookeeper-application-scenarios.md) +- [使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗?以上两种分布式锁的实现方式哪种效率比较高?](./distributed-lock-redis-vs-zookeeper.md) ## 分布式事务 -- [分布式事务了解吗?你们如何解决分布式事务问题的?TCC 如果出现网络连不通怎么办?XA 的一致性如何保证?](/docs/distributed-system/distributed-transaction.md) +- [分布式事务了解吗?你们如何解决分布式事务问题的?TCC 如果出现网络连不通怎么办?XA 的一致性如何保证?](./distributed-transaction.md) ## 分布式会话 -- [集群部署时的分布式 Session 如何实现?](/docs/distributed-system/distributed-session.md) +- [集群部署时的分布式 Session 如何实现?](./distributed-session.md) --- @@ -39,17 +39,11 @@ - -
- -
- 公众平台 -

+
+
- -
- 个人微信 -

+
+
diff --git a/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md b/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md index a7d6ce59..d4093fba 100644 --- a/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md +++ b/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md @@ -1,3 +1,5 @@ +# Redis vs Zookeeper 实现分布式锁 + ## 面试题 一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? diff --git a/docs/distributed-system/distributed-session.md b/docs/distributed-system/distributed-session.md index babf23b7..a78c3e4d 100644 --- a/docs/distributed-system/distributed-session.md +++ b/docs/distributed-system/distributed-session.md @@ -1,3 +1,5 @@ +# 如何实现分布式 Session? + ## 面试题 集群部署时的分布式 Session 如何实现? diff --git a/docs/distributed-system/distributed-system-cap.md b/docs/distributed-system/distributed-system-cap.md index 33bb333a..96c49155 100644 --- a/docs/distributed-system/distributed-system-cap.md +++ b/docs/distributed-system/distributed-system-cap.md @@ -1,3 +1,5 @@ +# 分布式系统中的 CAP 定理 + ## 分布式系统 CAP 定理 P 代表什么含义 作者之前在看 CAP 定理时抱有很大的疑惑,CAP 定理的定义是指在分布式系统中三者只能满足其二,也就是存在分布式 CA 系统的。作者在网络上查阅了很多关于 CAP 文章,虽然这些文章对于 P 的解释五花八门,但总结下来这些观点大多都是指 P 是不可缺少的,也就是说在分布式系统只能是 AP 或者 CP,这种理论与我之前所认识的理论(存在分布式 CA 系统)是冲突的,所以才有了疑惑。 diff --git a/docs/distributed-system/distributed-system-idempotency.md b/docs/distributed-system/distributed-system-idempotency.md index 55199773..dc6453cf 100644 --- a/docs/distributed-system/distributed-system-idempotency.md +++ b/docs/distributed-system/distributed-system-idempotency.md @@ -1,3 +1,5 @@ +# 分布式服务接口的幂等性如何设计? + ## 面试题 分布式服务接口的幂等性如何设计(比如不能重复扣款)? diff --git a/docs/distributed-system/distributed-system-interview.md b/docs/distributed-system/distributed-system-interview.md index dba56dfc..e69cb887 100644 --- a/docs/distributed-system/distributed-system-interview.md +++ b/docs/distributed-system/distributed-system-interview.md @@ -1,4 +1,4 @@ -## 分布式系统面试连环炮 +# 分布式系统面试连环炮 有一些同学,之前呢主要是做传统行业,或者外包项目,一直是在那种小的公司,技术一直都搞的比较简单。他们有共同的一个问题,就是都没怎么搞过分布式系统,现在互联网公司,一般都是做分布式的系统,大家都不是做底层的分布式系统、分布式存储系统 Hadoop HDFS、分布式计算系统 Hadoop MapReduce / Spark、分布式流式计算系统 Storm。 @@ -10,11 +10,11 @@ 面试官可能会问你以下问题。 -### 为什么要进行系统拆分? +## 为什么要进行系统拆分? - 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?Dubbo 和 thrift 有什么区别呢? -### 分布式服务框架 +## 分布式服务框架 - 说一下的 Dubbo 的工作原理?注册中心挂了可以继续通信吗? - Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的? @@ -25,14 +25,14 @@ - 分布式服务接口请求的顺序性如何保证? - 如何自己设计一个类似 Dubbo 的 RPC 框架? -### 分布式锁 +## 分布式锁 - 使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? -### 分布式事务 +## 分布式事务 - 分布式事务了解吗?你们如何解决分布式事务问题的?TCC 如果出现网络连不通怎么办?XA 的一致性如何保证? -### 分布式会话 +## 分布式会话 - 集群部署时的分布式 Session 如何实现? diff --git a/docs/distributed-system/distributed-system-request-sequence.md b/docs/distributed-system/distributed-system-request-sequence.md index ac0cd1ef..45187f8c 100644 --- a/docs/distributed-system/distributed-system-request-sequence.md +++ b/docs/distributed-system/distributed-system-request-sequence.md @@ -1,3 +1,5 @@ +# 分布式服务接口请求的顺序性如何保证? + ## 面试题 分布式服务接口请求的顺序性如何保证? diff --git a/docs/distributed-system/distributed-transaction.md b/docs/distributed-system/distributed-transaction.md index 8b2a2b9e..5b55aa14 100644 --- a/docs/distributed-system/distributed-transaction.md +++ b/docs/distributed-system/distributed-transaction.md @@ -1,3 +1,5 @@ +# 分布式事务原理 + ## 面试题 分布式事务了解吗?你们是如何解决分布式事务问题的? diff --git a/docs/distributed-system/dubbo-load-balancing.md b/docs/distributed-system/dubbo-load-balancing.md index 1dc27247..1d13be9e 100644 --- a/docs/distributed-system/dubbo-load-balancing.md +++ b/docs/distributed-system/dubbo-load-balancing.md @@ -1,3 +1,5 @@ +# Dubbo 负载均衡策略和集群容错策略 + ## 面试题 dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢? diff --git a/docs/distributed-system/dubbo-operating-principle.md b/docs/distributed-system/dubbo-operating-principle.md index 89e9c216..9530f140 100644 --- a/docs/distributed-system/dubbo-operating-principle.md +++ b/docs/distributed-system/dubbo-operating-principle.md @@ -1,3 +1,5 @@ +# Dubbo 的工作原理 + ## 面试题 说一下的 dubbo 的工作原理?注册中心挂了可以继续通信吗?说说一次 rpc 请求的流程? diff --git a/docs/distributed-system/dubbo-rpc-design.md b/docs/distributed-system/dubbo-rpc-design.md index cbc90f17..ad00f737 100644 --- a/docs/distributed-system/dubbo-rpc-design.md +++ b/docs/distributed-system/dubbo-rpc-design.md @@ -1,3 +1,5 @@ +# 设计一个类似 Dubbo 的 RPC 框架 + ## 面试题 如何自己设计一个类似 Dubbo 的 RPC 框架? diff --git a/docs/distributed-system/dubbo-serialization-protocol.md b/docs/distributed-system/dubbo-serialization-protocol.md index c1e53f1e..b9989fe6 100644 --- a/docs/distributed-system/dubbo-serialization-protocol.md +++ b/docs/distributed-system/dubbo-serialization-protocol.md @@ -1,3 +1,5 @@ +# Dubbo 的序列化协议 + ## 面试题 dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的? diff --git a/docs/distributed-system/dubbo-service-management.md b/docs/distributed-system/dubbo-service-management.md index 9581fdcb..242d21b7 100644 --- a/docs/distributed-system/dubbo-service-management.md +++ b/docs/distributed-system/dubbo-service-management.md @@ -1,3 +1,5 @@ +# 如何基于 Dubbo 进行服务治理 + ## 面试题 如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试? diff --git a/docs/distributed-system/dubbo-spi.md b/docs/distributed-system/dubbo-spi.md index 16b8d224..8ca6d714 100644 --- a/docs/distributed-system/dubbo-spi.md +++ b/docs/distributed-system/dubbo-spi.md @@ -1,3 +1,5 @@ +# Dubbo 的 SPI 机制 + ## 面试题 dubbo 的 spi 思想是什么? diff --git a/docs/distributed-system/why-dubbo.md b/docs/distributed-system/why-dubbo.md index d86fca3a..0c98a54f 100644 --- a/docs/distributed-system/why-dubbo.md +++ b/docs/distributed-system/why-dubbo.md @@ -1,3 +1,5 @@ +# 为什么要拆分系统? + ## 面试题 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 dubbo 可以吗? diff --git a/docs/distributed-system/zookeeper-application-scenarios.md b/docs/distributed-system/zookeeper-application-scenarios.md index c3ee1250..248a0c1f 100644 --- a/docs/distributed-system/zookeeper-application-scenarios.md +++ b/docs/distributed-system/zookeeper-application-scenarios.md @@ -1,3 +1,5 @@ +# Zookeeper 的使用场景 + ## 面试题 zookeeper 都有哪些使用场景? diff --git a/docs/extra-page/README.md b/docs/extra-page/README.md index a755da75..fd7d840a 100644 --- a/docs/extra-page/README.md +++ b/docs/extra-page/README.md @@ -7,9 +7,7 @@ ## 项目 Pages 站点 -- [Netlify](https://adjava.netlify.app) -- [Gitee Pages](https://doocs.gitee.io/advanced-java) -- [GitHub Pages](https://doocs.github.io/advanced-java) +https://java.doocs.org ## 维权行动 @@ -23,17 +21,11 @@ - -
- -
- 公众平台 -

+
+
- -
- 个人微信 -

+
+
diff --git a/docs/extra-page/cover.md b/docs/extra-page/cover.md deleted file mode 100644 index 0ded759a..00000000 --- a/docs/extra-page/cover.md +++ /dev/null @@ -1,8 +0,0 @@ -[![logo](./images/icon.png)](https://github.com/doocs/advanced-java) - -# 互联网 Java 工程师进阶知识完全扫盲 - -> 本系列知识由 Doocs 技术社区总结发布,内容涵盖高并发、分布式、高可用、微服务、海量数据处理等 - -[社区首页](https://doocs.github.io) -[开始学习](#互联网-java-工程师进阶知识完全扫盲) diff --git a/docs/extra-page/subscriptions-for-doocs.md b/docs/extra-page/subscriptions-for-doocs.md index 147ffa79..d4a05805 100644 --- a/docs/extra-page/subscriptions-for-doocs.md +++ b/docs/extra-page/subscriptions-for-doocs.md @@ -3,7 +3,7 @@ GitHub 开源社区 Doocs 旗下唯一公众号“**Doocs**”,专注于挖掘 IT 技术知识,助力开发者成长。
-
+
来成为公众号的首批粉丝吗?一定不会辜负你们的期待。 diff --git a/docs/high-availability/README.md b/docs/high-availability/README.md index 66d562eb..296b2dfe 100644 --- a/docs/high-availability/README.md +++ b/docs/high-availability/README.md @@ -24,7 +24,7 @@ - 如何进行熔断? - 熔断框架都有哪些?具体实现原理知道吗? -- [熔断框架如何做技术选型?选用 Sentinel 还是 Hystrix?](/docs/high-availability/sentinel-vs-hystrix.md) +- [熔断框架如何做技术选型?选用 Sentinel 还是 Hystrix?](./sentinel-vs-hystrix.md) ## 降级 @@ -38,17 +38,11 @@ - -
- -
- 公众平台 -

+
+
- -
- 个人微信 -

+
+
diff --git a/docs/high-availability/e-commerce-website-detail-page-architecture.md b/docs/high-availability/e-commerce-website-detail-page-architecture.md index 8f9c0321..b9a27bc8 100644 --- a/docs/high-availability/e-commerce-website-detail-page-architecture.md +++ b/docs/high-availability/e-commerce-website-detail-page-architecture.md @@ -1,6 +1,6 @@ -## 电商网站的商品详情页系统架构 +# 电商网站的商品详情页系统架构 -### 小型电商网站的商品详情页系统架构 +## 小型电商网站的商品详情页系统架构 小型电商网站的页面展示采用页面全量静态化的思想。数据库中存放了所有的商品信息,页面静态化系统,将数据填充进静态模板中,形成静态化页面,推入 Nginx 服务器。用户浏览网站页面时,取用一个已经静态化好的 html 页面,直接返回回去,不涉及任何的业务逻辑处理。 @@ -24,7 +24,7 @@ **坏处**在于,仅仅适用于一些小型的网站,比如页面的规模在几十到几万不等。对于一些大型的电商网站,亿级数量的页面,你说你每次页面模板修改了,都需要将这么多页面全量静态化,靠谱吗?每次渲染花个好几天时间,那你整个网站就废掉了。 -### 大型电商网站的商品详情页系统架构 +## 大型电商网站的商品详情页系统架构 大型电商网站商品详情页的系统设计中,当商品数据发生变更时,会将变更消息压入 MQ 消息队列中。**缓存服务**从消息队列中消费这条消息时,感知到有数据发生变更,便通过调用数据服务接口,获取变更后的数据,然后将整合好的数据推送至 redis 中。Nginx 本地缓存的数据是有一定的时间期限的,比如说 10 分钟,当数据过期之后,它就会从 redis 获取到最新的缓存数据,并且缓存到自己本地。 diff --git a/docs/high-availability/hystrix-circuit-breaker.md b/docs/high-availability/hystrix-circuit-breaker.md index 7bd7c7e1..593b67a6 100644 --- a/docs/high-availability/hystrix-circuit-breaker.md +++ b/docs/high-availability/hystrix-circuit-breaker.md @@ -1,4 +1,4 @@ -## 深入 Hystrix 断路器执行原理 +# 深入 Hystrix 断路器执行原理 ### 状态机 diff --git a/docs/high-availability/hystrix-execution-isolation.md b/docs/high-availability/hystrix-execution-isolation.md index 7ee39d50..e96da1c0 100644 --- a/docs/high-availability/hystrix-execution-isolation.md +++ b/docs/high-availability/hystrix-execution-isolation.md @@ -1,4 +1,4 @@ -## Hystrix 隔离策略细粒度控制 +# Hystrix 隔离策略细粒度控制 Hystrix 实现资源隔离,有两种策略: @@ -7,7 +7,7 @@ Hystrix 实现资源隔离,有两种策略: 对资源隔离这一块东西,其实可以做一定细粒度的一些控制。 -### execution.isolation.strategy +## execution.isolation.strategy 指定了 HystrixCommand.run() 的资源隔离策略:`THREAD` or `SEMAPHORE`,一种基于线程池,一种基于信号量。 @@ -29,7 +29,7 @@ HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolat 而使用信号量的场景,通常是针对超大并发量的场景下,每个服务实例每秒都几百的 `QPS`,那么此时你用线程池的话,线程一般不会太多,可能撑不住那么高的并发,如果要撑住,可能要耗费大量的线程资源,那么就是用信号量,来进行限流保护。一般用信号量常见于那种基于纯内存的一些业务逻辑服务,而不涉及到任何网络访问请求。 -### command key & command group +## command key & command group 我们使用线程池隔离,要怎么对**依赖服务**、**依赖服务接口**、**线程池**三者做划分呢? @@ -47,7 +47,7 @@ public CommandHelloWorld(String name) { command group 是一个非常重要的概念,默认情况下,就是通过 command group 来定义一个线程池的,而且还会通过 command group 来聚合一些监控和报警信息。同一个 command group 中的请求,都会进入同一个线程池中。 -### command thread pool +## command thread pool ThreadPoolKey 代表了一个 HystrixThreadPool,用来进行统一监控、统计、缓存。默认的 ThreadPoolKey 就是 command group 的名称。每个 command 都会跟它的 ThreadPoolKey 对应的 ThreadPool 绑定在一起。 @@ -64,7 +64,7 @@ public CommandHelloWorld(String name) { } ``` -### command key & command group & command thread pool +## command key & command group & command thread pool **command key** ,代表了一类 command,一般来说,代表了下游依赖服务的某个接口。 @@ -84,7 +84,7 @@ command key -> 自己的 thread pool key 说白点,就是说如果你的 command key 要用自己的线程池,可以定义自己的 thread pool key,就 ok 了。 -### coreSize +## coreSize 设置线程池的大小,默认是 10。一般来说,用这个默认的 10 个线程大小就够了。 @@ -92,7 +92,7 @@ command key -> 自己的 thread pool key HystrixThreadPoolProperties.Setter().withCoreSize(int value); ``` -### queueSizeRejectionThreshold +## queueSizeRejectionThreshold 如果说线程池中的 10 个线程都在工作中,没有空闲的线程来做其它的事情,此时再有请求过来,会先进入队列积压。如果说队列积压满了,再有请求过来,就直接 reject,拒绝请求,执行 fallback 降级的逻辑,快速返回。 @@ -104,7 +104,7 @@ HystrixThreadPoolProperties.Setter().withCoreSize(int value); HystrixThreadPoolProperties.Setter().withQueueSizeRejectionThreshold(int value); ``` -### execution.isolation.semaphore.maxConcurrentRequests +## execution.isolation.semaphore.maxConcurrentRequests 设置使用 SEMAPHORE 隔离策略的时候允许访问的最大并发量,超过这个最大并发量,请求直接被 reject。 diff --git a/docs/high-availability/hystrix-fallback.md b/docs/high-availability/hystrix-fallback.md index 03709709..52bbe106 100644 --- a/docs/high-availability/hystrix-fallback.md +++ b/docs/high-availability/hystrix-fallback.md @@ -1,4 +1,4 @@ -## 基于本地缓存的 fallback 降级机制 +# 基于本地缓存的 fallback 降级机制 Hystrix 出现以下四种情况,都会去调用 fallback 降级机制: @@ -7,7 +7,7 @@ Hystrix 出现以下四种情况,都会去调用 fallback 降级机制: - Hystrix 调用各种接口,或者访问外部依赖,比如 MySQL、Redis、Zookeeper、Kafka 等等,出现了任何异常的情况。 - 访问外部依赖的时候,访问时间过长,报了 TimeoutException 异常。 -### 两种最经典的降级机制 +## 两种最经典的降级机制 - 纯内存数据
在降级逻辑中,你可以在内存中维护一个 ehcache,作为一个纯内存的基于 LRU 自动清理的缓存,让数据放在缓存内。如果说外部依赖有异常,fallback 这里直接尝试从 ehcache 中获取数据。 @@ -23,7 +23,7 @@ Hystrix 出现以下四种情况,都会去调用 fallback 降级机制: 假如说,品牌服务接口挂掉了,那么我们可以尝试从本地内存中,获取一份稍过期的数据,先凑合着用。 -### 步骤一:本地缓存获取数据 +## 步骤一:本地缓存获取数据 本地获取品牌名称的代码大致如下。 @@ -51,7 +51,7 @@ public class BrandCache { } ``` -### 步骤二:实现 GetBrandNameCommand +## 步骤二:实现 GetBrandNameCommand 在 GetBrandNameCommand 中,run() 方法的正常逻辑是去调用品牌服务的接口获取到品牌名称,如果调用失败,报错了,那么就会去调用 fallback 降级机制。 @@ -95,7 +95,7 @@ public class GetBrandNameCommand extends HystrixCommand { `FallbackIsolationSemaphoreMaxConcurrentRequests` 用于设置 fallback 最大允许的并发请求量,默认值是 10,是通过 semaphore 信号量的机制去限流的。如果超出了这个最大值,那么直接 reject。 -### 步骤三:CacheController 调用接口 +## 步骤三:CacheController 调用接口 在 CacheController 中,我们通过 productInfo 获取 brandId,然后创建 GetBrandNameCommand 并执行,去尝试获取 brandName。这里执行会报错,因为我们在 run() 方法中直接抛出异常,Hystrix 就会去调用 getFallback() 方法走降级逻辑。 diff --git a/docs/high-availability/hystrix-introduction.md b/docs/high-availability/hystrix-introduction.md index f0223345..483f4226 100644 --- a/docs/high-availability/hystrix-introduction.md +++ b/docs/high-availability/hystrix-introduction.md @@ -1,8 +1,8 @@ -## 用 Hystrix 构建高可用服务架构 +# 用 Hystrix 构建高可用服务架构 参考 [Hystrix Home](https://github.com/Netflix/Hystrix/wiki#what)。 -### Hystrix 是什么? +## Hystrix 是什么? 在分布式系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是**依赖服务**,有的时候某些依赖服务出现故障也是很正常的。 @@ -12,7 +12,7 @@ Hystrix 通过将依赖服务进行**资源隔离**,进而阻止某个依赖 **总而言之,Hystrix 通过这些方法帮助我们提升分布式系统的可用性和稳定性。** -### Hystrix 的历史 +## Hystrix 的历史 Hystrix 是高可用性保障的一个框架。Netflix(可以认为是国外的优酷或者爱奇艺之类的视频网站)的 API 团队从 2011 年开始做一些提升系统可用性和稳定性的工作,Hystrix 就是从那时候开始发展出来的。 @@ -22,7 +22,7 @@ Hystrix 是高可用性保障的一个框架。Netflix(可以认为是国外 [2018 年 11 月,Hystrix 在其 Github 主页宣布,不再开放新功能,推荐开发者使用其他仍然活跃的开源项目](https://github.com/Netflix/Hystrix/blob/master/README.md#hystrix-status)。维护模式的转变绝不意味着 Hystrix 不再有价值。相反,Hystrix 激发了很多伟大的想法和项目,我们高可用的这一块知识还是会针对 Hystrix 进行讲解。 -### Hystrix 的设计原则 +## Hystrix 的设计原则 - 对依赖服务调用时出现的调用延迟和调用失败进行**控制和容错保护**。 - 在复杂的分布式系统中,阻止某一个依赖服务的故障在整个系统中蔓延。比如某一个服务故障了,导致其它服务也跟着故障。 @@ -40,7 +40,7 @@ Hystrix 是高可用性保障的一个框架。Netflix(可以认为是国外 Hystrix 可以对其进行资源隔离,比如限制服务 B 只有 40 个线程调用服务 C。当此 40 个线程被 hang 住时,其它 60 个线程依然能正常调用工作。从而确保整个系统不会被拖垮。 -### Hystrix 更加细节的设计原则 +## Hystrix 更加细节的设计原则 - 阻止任何一个依赖服务耗尽所有的资源,比如 tomcat 中的所有线程资源。 - 避免请求排队和积压,采用限流和 `fail fast` 来控制故障。 diff --git a/docs/high-availability/hystrix-process.md b/docs/high-availability/hystrix-process.md index 51835338..984fbf58 100644 --- a/docs/high-availability/hystrix-process.md +++ b/docs/high-availability/hystrix-process.md @@ -1,4 +1,4 @@ -## 深入 Hystrix 执行时内部原理 +# 深入 Hystrix 执行时内部原理 前面我们了解了 Hystrix 最基本的支持高可用的技术:**资源隔离** + **限流**。 @@ -14,7 +14,7 @@ ![hystrix-process](./images/new-hystrix-process.jpg) -### 步骤一:创建 command +## 步骤一:创建 command 一个 HystrixCommand 或 HystrixObservableCommand 对象,代表了对某个依赖服务发起的一次请求或者调用。创建的时候,可以在构造函数中传入任何需要的参数。 @@ -29,7 +29,7 @@ HystrixCommand hystrixCommand = new HystrixCommand(arg1, arg2); HystrixObservableCommand hystrixObservableCommand = new HystrixObservableCommand(arg1, arg2); ``` -### 步骤二:调用 command 执行方法 +## 步骤二:调用 command 执行方法 执行 command,就可以发起一次对依赖服务的调用。 @@ -69,21 +69,21 @@ final Future delegate = toObservable().toBlocking().toFuture(); 也就是说,先通过 toObservable() 获得 Future 对象,然后调用 Future 的 get() 方法。那么,其实无论是哪种方式执行 command,最终都是依赖于 toObservable() 去执行的。 -### 步骤三:检查是否开启缓存(不太常用) +## 步骤三:检查是否开启缓存(不太常用) 从这一步开始,就进入到 Hystrix 底层运行原理啦,看一下 Hystrix 一些更高级的功能和特性。 如果这个 command 开启了请求缓存 Request Cache,而且这个调用的结果在缓存中存在,那么直接从缓存中返回结果。否则,继续往后的步骤。 -### 步骤四:检查是否开启了断路器 +## 步骤四:检查是否开启了断路器 检查这个 command 对应的依赖服务是否开启了断路器。如果断路器被打开了,那么 Hystrix 就不会执行这个 command,而是直接去执行 fallback 降级机制,返回降级结果。 -### 步骤五:检查线程池/队列/信号量是否已满 +## 步骤五:检查线程池/队列/信号量是否已满 如果这个 command 线程池和队列已满,或者 semaphore 信号量已满,那么也不会执行 command,而是直接去调用 fallback 降级机制,同时发送 reject 信息给断路器统计。 -### 步骤六:执行 command +## 步骤六:执行 command 调用 HystrixObservableCommand 对象的 construct() 方法,或者 HystrixCommand 的 run() 方法来实际执行这个 command。 @@ -129,13 +129,13 @@ observable.subscribe(new Observer() { 如果没有 timeout,也正常执行的话,那么调用线程就会拿到一些调用依赖服务获取到的结果,然后 Hystrix 也会做一些 logging 记录和 metric 度量统计。 -### 步骤七:断路健康检查 +## 步骤七:断路健康检查 Hystrix 会把每一个依赖服务的调用成功、失败、Reject、Timeout 等事件发送给 circuit breaker 断路器。断路器就会对这些事件的次数进行统计,根据异常事件发生的比例来决定是否要进行断路(熔断)。如果打开了断路器,那么在接下来一段时间内,会直接断路,返回降级结果。 如果在之后,断路器尝试执行 command,调用没有出错,返回了正常结果,那么 Hystrix 就会把断路器关闭。 -### 步骤八:调用 fallback 降级机制 +## 步骤八:调用 fallback 降级机制 在以下几种情况中,Hystrix 会调用 fallback 降级机制。 @@ -160,7 +160,7 @@ Hystrix 会把每一个依赖服务的调用成功、失败、Reject、Timeout - 对于 observe(),返回一个 Observable 对象,但是调用 subscribe() 方法订阅它时,立即抛出调用者的 onError() 方法。 - 对于 toObservable(),返回一个 Observable 对象,但是调用 subscribe() 方法订阅它时,立即抛出调用者的 onError() 方法。 -### 不同的执行方式 +## 不同的执行方式 - execute(),获取一个 Future.get(),然后拿到单个结果。 - queue(),返回一个 Future。 diff --git a/docs/high-availability/hystrix-request-cache.md b/docs/high-availability/hystrix-request-cache.md index bd5e963b..2c196022 100644 --- a/docs/high-availability/hystrix-request-cache.md +++ b/docs/high-availability/hystrix-request-cache.md @@ -1,4 +1,4 @@ -## 基于 request cache 请求缓存技术优化批量商品数据查询接口 +# 基于 request cache 请求缓存技术优化批量商品数据查询接口 Hystrix command 执行时 8 大步骤第三步,就是检查 Request cache 是否有缓存。 @@ -20,7 +20,7 @@ HystrixCommand 和 HystrixObservableCommand 都可以指定一个缓存 key, 我们对批量查询商品数据的接口,可以用 request cache 做一个优化,就是说一次请求,就是一次 request context,对相同的商品查询只执行一次,其余重复的都走 request cache。 -### 实现 Hystrix 请求上下文过滤器并注册 +## 实现 Hystrix 请求上下文过滤器并注册 定义 HystrixRequestContextFilter 类,实现 Filter 接口。 @@ -73,7 +73,7 @@ public class EshopApplication { } ``` -### command 重写 getCacheKey() 方法 +## command 重写 getCacheKey() 方法 在 GetProductInfoCommand 中,重写 getCacheKey() 方法,这样的话,每一次请求的结果,都会放在 Hystrix 请求上下文中。下一次同一个 productId 的数据请求,直接取缓存,无须再调用 run() 方法。 @@ -122,7 +122,7 @@ public class GetProductInfoCommand extends HystrixCommand { 这里写了一个 flushCache() 方法,用于我们开发手动删除缓存。 -### controller 调用 command 查询商品信息 +## controller 调用 command 查询商品信息 在一次 web 请求上下文中,传入商品 id 列表,查询多条商品数据信息。对于每个 productId,都创建一个 command。 @@ -153,7 +153,7 @@ public class CacheController { } ``` -### 发起请求 +## 发起请求 调用接口,查询多个商品的信息。 @@ -177,7 +177,7 @@ http://localhost:8080/getProductInfos?productIds=1,1,1,2,2,5 第一次查询 productId=1 的数据,会调用接口进行查询,不是从缓存中取结果。而随后再出现查询 productId=1 的请求,就直接取缓存了,这样的话,效率明显高很多。 -### 删除缓存 +## 删除缓存 我们写一个 UpdateProductInfoCommand,在更新商品信息之后,手动调用之前写的 flushCache(),手动将缓存删除。 diff --git a/docs/high-availability/hystrix-semphore-isolation.md b/docs/high-availability/hystrix-semphore-isolation.md index e225ccd1..13d5cfb0 100644 --- a/docs/high-availability/hystrix-semphore-isolation.md +++ b/docs/high-availability/hystrix-semphore-isolation.md @@ -1,4 +1,4 @@ -## 基于 Hystrix 信号量机制实现资源隔离 +# 基于 Hystrix 信号量机制实现资源隔离 Hystrix 里面核心的一项功能,其实就是所谓的**资源隔离**,要解决的最最核心的问题,就是将多个依赖服务的调用分别隔离到各自的资源池内。避免说对某一个依赖服务的调用,因为依赖服务的接口调用的延迟或者失败,导致服务所有的线程资源全部耗费在这个服务的接口调用上。一旦说某个服务的线程资源全部耗尽的话,就可能导致服务崩溃,甚至说这种故障会不断蔓延。 @@ -11,13 +11,13 @@ Hystrix 实现资源隔离,主要有两种技术: 前面已经说过线程池技术了,这一小节就来说说信号量机制实现资源隔离,以及这两种技术的区别与具体应用场景。 -### 信号量机制 +## 信号量机制 信号量的资源隔离只是起到一个开关的作用,比如,服务 A 的信号量大小为 10,那么就是说它同时只允许有 10 个 tomcat 线程来访问服务 A,其它的请求都会被拒绝,从而达到资源隔离和限流保护的作用。 ![hystrix-semphore](./images/hystrix-semphore.png) -### 线程池与信号量区别 +## 线程池与信号量区别 线程池隔离技术,并不是说去控制类似 tomcat 这种 web 容器的线程。更加严格的意义上来说,Hystrix 的线程池隔离技术,控制的是 tomcat 线程的执行。Hystrix 线程池满后,会确保说,tomcat 的线程不会因为依赖服务的接口调用延迟或故障而被 hang 住,tomcat 其它的线程不会卡死,可以快速返回,然后支撑其它的事情。 @@ -30,7 +30,7 @@ Hystrix 实现资源隔离,主要有两种技术: - **线程池技术**,适合绝大多数场景,比如说我们对依赖服务的网络请求的调用和访问、需要对调用的 timeout 进行控制(捕捉 timeout 超时异常)。 - **信号量技术**,适合说你的访问不是对外部依赖的访问,而是对内部的一些比较复杂的业务逻辑的访问,并且系统内部的代码,其实不涉及任何的网络请求,那么只要做信号量的普通限流就可以了,因为不需要去捕获 timeout 类似的问题。 -### 信号量简单 Demo +## 信号量简单 Demo 业务背景里,比较适合信号量的是什么场景呢? diff --git a/docs/high-availability/hystrix-thread-pool-current-limiting.md b/docs/high-availability/hystrix-thread-pool-current-limiting.md index fc3e14d1..116320f9 100644 --- a/docs/high-availability/hystrix-thread-pool-current-limiting.md +++ b/docs/high-availability/hystrix-thread-pool-current-limiting.md @@ -1,4 +1,4 @@ -## 深入 Hystrix 线程池隔离与接口限流 +# 深入 Hystrix 线程池隔离与接口限流 前面讲了 Hystrix 的 request cache 请求缓存、fallback 优雅降级、circuit breaker 断路器快速熔断,这一讲,我们来详细说说 Hystrix 的线程池隔离与接口限流。 @@ -8,7 +8,7 @@ Hystrix 通过判断线程池或者信号量是否已满,超出容量的请求 限流是限制对后端的服务的访问量,比如说你对 MySQL、Redis、Zookeeper 以及其它各种后端中间件的资源的访问的限制,其实是为了避免过大的流量直接打死后端的服务。 -### 线程池隔离技术的设计 +## 线程池隔离技术的设计 Hystrix 采用了 Bulkhead Partition 舱壁隔离技术,来将外部依赖进行资源隔离,进而避免任何外部依赖的故障导致本服务崩溃。 @@ -18,7 +18,7 @@ Hystrix 采用了 Bulkhead Partition 舱壁隔离技术,来将外部依赖进 Hystrix 对每个外部依赖用一个单独的线程池,这样的话,如果对那个外部依赖调用延迟很严重,最多就是耗尽那个依赖自己的线程池而已,不会影响其他的依赖调用。 -### Hystrix 应用线程池机制的场景 +## Hystrix 应用线程池机制的场景 - 每个服务都会调用几十个后端依赖服务,那些后端依赖服务通常是由很多不同的团队开发的。 - 每个后端依赖服务都会提供它自己的 client 调用库,比如说用 thrift 的话,就会提供对应的 thrift 依赖。 @@ -34,7 +34,7 @@ Hystrix 对每个外部依赖用一个单独的线程池,这样的话,如果 简单来说,就是你必须默认 client 调用库很不靠谱,而且随时可能发生各种变化,所以就要用强制隔离的方式来确保任何服务的故障不会影响当前服务。 -### 线程池机制的优点 +## 线程池机制的优点 - 任何一个依赖服务都可以被隔离在自己的线程池内,即使自己的线程池资源填满了,也不会影响任何其他的服务调用。 - 服务可以随时引入一个新的依赖服务,因为即使这个新的依赖服务有问题,也不会影响其他任何服务的调用。 @@ -44,7 +44,7 @@ Hystrix 对每个外部依赖用一个单独的线程池,这样的话,如果 简单来说,最大的好处,就是资源隔离,确保说任何一个依赖服务故障,不会拖垮当前的这个服务。 -### 线程池机制的缺点 +## 线程池机制的缺点 - 线程池机制最大的缺点就是增加了 CPU 的开销。
除了 tomcat 本身的调用线程之外,还有 Hystrix 自己管理的线程池。 @@ -58,7 +58,7 @@ semaphore 技术可以用来限流和削峰,但是不能用来对调用延迟 `execution.isolation.strategy` 设置为 `SEMAPHORE`,那么 Hystrix 就会用 semaphore 机制来替代线程池机制,来对依赖服务的访问进行限流。如果通过 semaphore 调用的时候,底层的网络调用延迟很严重,那么是无法 timeout 的,只能一直 block 住。一旦请求数量超过了 semaphore 限定的数量之后,就会立即开启限流。 -### 接口限流 Demo +## 接口限流 Demo 假设一个线程池大小为 8,等待队列的大小为 10。timeout 时长我们设置长一些,20s。 diff --git a/docs/high-availability/hystrix-thread-pool-isolation.md b/docs/high-availability/hystrix-thread-pool-isolation.md index 6862ec37..735e741f 100644 --- a/docs/high-availability/hystrix-thread-pool-isolation.md +++ b/docs/high-availability/hystrix-thread-pool-isolation.md @@ -1,4 +1,4 @@ -## 基于 Hystrix 线程池技术实现资源隔离 +# 基于 Hystrix 线程池技术实现资源隔离 [上一讲](./e-commerce-website-detail-page-architecture.md)提到,如果从 Nginx 开始,缓存都失效了,Nginx 会直接通过缓存服务调用商品服务获取最新商品数据(我们基于电商项目做个讨论),有可能出现调用延时而把缓存服务资源耗尽的情况。这里,我们就来说说,怎么通过 Hystrix 线程池技术实现资源隔离。 @@ -6,7 +6,7 @@ Hystrix 进行资源隔离,其实是提供了一个抽象,叫做 Command。这也是 Hystrix 最最基本的资源隔离技术。 -### 利用 HystrixCommand 获取单条数据 +## 利用 HystrixCommand 获取单条数据 我们通过将调用商品服务的操作封装在 HystrixCommand 中,限定一个 key,比如下面的 `GetProductInfoCommandGroup`,在这里我们可以简单认为这是一个线程池,每次调用商品服务,就只会用该线程池中的资源,不会再去用其它线程资源了。 @@ -47,7 +47,7 @@ public String getProductInfo(Long productId) { 上面执行的是 execute() 方法,其实是同步的。也可以对 command 调用 queue() 方法,它仅仅是将 command 放入线程池的一个等待队列,就立即返回,拿到一个 Future 对象,后面可以继续做其它一些事情,然后过一段时间对 Future 调用 get() 方法获取数据。这是异步的。 -### 利用 HystrixObservableCommand 批量获取数据 +## 利用 HystrixObservableCommand 批量获取数据 只要是获取商品数据,全部都绑定到同一个线程池里面去,我们通过 HystrixObservableCommand 的一个线程去执行,而在这个线程里面,批量把多个 productId 的 productInfo 拉回来。 diff --git a/docs/high-availability/hystrix-timeout.md b/docs/high-availability/hystrix-timeout.md index 91a367c1..23789761 100644 --- a/docs/high-availability/hystrix-timeout.md +++ b/docs/high-availability/hystrix-timeout.md @@ -1,4 +1,4 @@ -## 基于 timeout 机制为服务接口调用超时提供安全保护 +# 基于 timeout 机制为服务接口调用超时提供安全保护 一般来说,在调用依赖服务的接口的时候,比较常见的一个问题就是**超时**。超时是在一个复杂的分布式系统中,导致系统不稳定,或者系统抖动。出现大量超时,线程资源会被 hang 死,从而导致吞吐量大幅度下降,甚至服务崩溃。 @@ -12,7 +12,7 @@ Peter Steiner 说过,"[On the Internet, nobody knows you're a dog](https://en. 如果你不对各种依赖服务接口的调用做超时控制,来给你的服务提供安全保护措施,那么很可能你的服务就被各种垃圾的依赖服务的性能给拖死了。大量的接口调用很慢,大量的线程被卡死。如果你做了资源的隔离,那么也就是线程池的线程被卡死,但其实我们可以做超时控制,没必要让它们全卡死。 -### TimeoutMilliseconds +## TimeoutMilliseconds 在 Hystrix 中,我们可以手动设置 timeout 时长,如果一个 command 运行时间超过了设定的时长,那么就被认为是 timeout,然后 Hystrix command 标识为 timeout,同时执行 fallback 降级逻辑。 @@ -23,7 +23,7 @@ HystrixCommandProperties.Setter() ..withExecutionTimeoutInMilliseconds(int) ``` -### TimeoutEnabled +## TimeoutEnabled 这个参数用于控制是否要打开 timeout 机制,默认值是 true。 diff --git a/docs/high-availability/sentinel-vs-hystrix.md b/docs/high-availability/sentinel-vs-hystrix.md index 1c9133c6..931ce3f3 100644 --- a/docs/high-availability/sentinel-vs-hystrix.md +++ b/docs/high-availability/sentinel-vs-hystrix.md @@ -27,7 +27,7 @@ Sentinel 项目地址:https://github.com/alibaba/Sentinel Hystrix 的资源模型设计上采用了命令模式,将对外部资源的调用和 fallback 逻辑封装成一个命令对象 `HystrixCommand` 或 `HystrixObservableCommand`,其底层的执行是基于 RxJava 实现的。每个 Command 创建时都要指定 `commandKey` 和 `groupKey`(用于区分资源)以及对应的隔离策略(线程池隔离 or 信号量隔离)。线程池隔离模式下需要配置线程池对应的参数(线程池名称、容量、排队超时等),然后 Command 就会在指定的线程池按照指定的容错策略执行;信号量隔离模式下需要配置最大并发数,执行 Command 时 Hystrix 就会限制其并发调用。 -**注**:关于 Hystrix 的详细介绍及代码演示,可以参考本项目[高可用架构](/docs/high-availability/README.md)-Hystrix 部分的详细说明。 +**注**:关于 Hystrix 的详细介绍及代码演示,可以参考本项目[高可用架构](./README.md)-Hystrix 部分的详细说明。 Sentinel 的设计则更为简单。相比 Hystrix Command 强依赖隔离规则,Sentinel 的资源定义与规则配置的耦合度更低。Hystrix 的 Command 强依赖于隔离规则配置的原因是隔离规则会直接影响 Command 的执行。在执行的时候 Hystrix 会解析 Command 的隔离规则来创建 RxJava Scheduler 并在其上调度执行,若是线程池模式则 Scheduler 底层的线程池为配置的线程池,若是信号量模式则简单包装成当前线程执行的 Scheduler。 diff --git a/docs/high-concurrency/README.md b/docs/high-concurrency/README.md index 3ee6e07b..b80d8922 100644 --- a/docs/high-concurrency/README.md +++ b/docs/high-concurrency/README.md @@ -1,51 +1,51 @@ # 高并发架构 -## [消息队列](/docs/high-concurrency/mq-interview.md) +## [消息队列](./mq-interview.md) -- [为什么使用消息队列?消息队列有什么优点和缺点?Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么优点和缺点?](/docs/high-concurrency/why-mq.md) -- [如何保证消息队列的高可用?](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md) -- [如何保证消息不被重复消费?(如何保证消息消费的幂等性)](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md) -- [如何保证消息的可靠性传输?(如何处理消息丢失的问题)](/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md) -- [如何保证消息的顺序性?](/docs/high-concurrency/how-to-ensure-the-order-of-messages.md) -- [如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?](/docs/high-concurrency/mq-time-delay-and-expired-failure.md) -- [如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。](/docs/high-concurrency/mq-design.md) +- [为什么使用消息队列?消息队列有什么优点和缺点?Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么优点和缺点?](./why-mq.md) +- [如何保证消息队列的高可用?](./how-to-ensure-high-availability-of-message-queues.md) +- [如何保证消息不被重复消费?(如何保证消息消费的幂等性)](./how-to-ensure-that-messages-are-not-repeatedly-consumed.md) +- [如何保证消息的可靠性传输?(如何处理消息丢失的问题)](./how-to-ensure-the-reliable-transmission-of-messages.md) +- [如何保证消息的顺序性?](./how-to-ensure-the-order-of-messages.md) +- [如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?](./mq-time-delay-and-expired-failure.md) +- [如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。](./mq-design.md) -## [搜索引擎](/docs/high-concurrency/es-introduction.md) +## [搜索引擎](./es-introduction.md) -- [ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊)?](/docs/high-concurrency/es-architecture.md) -- [ES 写入数据的工作原理是什么啊?ES 查询数据的工作原理是什么啊?底层的 Lucene 介绍一下呗?倒排索引了解吗?](/docs/high-concurrency/es-write-query-search.md) -- [ES 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](/docs/high-concurrency/es-optimizing-query-performance.md) -- [ES 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](/docs/high-concurrency/es-production-cluster.md) +- [ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊)?](./es-architecture.md) +- [ES 写入数据的工作原理是什么啊?ES 查询数据的工作原理是什么啊?底层的 Lucene 介绍一下呗?倒排索引了解吗?](./es-write-query-search.md) +- [ES 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](./es-optimizing-query-performance.md) +- [ES 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](./es-production-cluster.md) ## 缓存 -- [在项目中缓存是如何使用的?缓存如果使用不当会造成什么后果?](/docs/high-concurrency/why-cache.md) -- [Redis 和 Memcached 有什么区别?Redis 的线程模型是什么?为什么单线程的 Redis 比多线程的 Memcached 效率要高得多?](/docs/high-concurrency/redis-single-thread-model.md) -- [Redis 都有哪些数据类型?分别在哪些场景下使用比较合适?](/docs/high-concurrency/redis-data-types.md) -- [Redis 的过期策略都有哪些?手写一下 LRU 代码实现?](/docs/high-concurrency/redis-expiration-policies-and-lru.md) -- [如何保证 Redis 高并发、高可用?Redis 的主从复制原理能介绍一下么?Redis 的哨兵原理能介绍一下么?](/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md) -- [Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?](/docs/high-concurrency/redis-persistence.md) -- [Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?](/docs/high-concurrency/redis-cluster.md) -- [了解什么是 redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md) -- [如何保证缓存与数据库的双写一致性?](/docs/high-concurrency/redis-consistence.md) -- [Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?](/docs/high-concurrency/redis-cas.md) -- [生产环境中的 Redis 是怎么部署的?](/docs/high-concurrency/redis-production-environment.md) -- [有了解过 Redis rehash 的过程吗?](/docs/high-concurrency/redis-rehash.md) +- [在项目中缓存是如何使用的?缓存如果使用不当会造成什么后果?](./why-cache.md) +- [Redis 和 Memcached 有什么区别?Redis 的线程模型是什么?为什么单线程的 Redis 比多线程的 Memcached 效率要高得多?](./redis-single-thread-model.md) +- [Redis 都有哪些数据类型?分别在哪些场景下使用比较合适?](./redis-data-types.md) +- [Redis 的过期策略都有哪些?手写一下 LRU 代码实现?](./redis-expiration-policies-and-lru.md) +- [如何保证 Redis 高并发、高可用?Redis 的主从复制原理能介绍一下么?Redis 的哨兵原理能介绍一下么?](./how-to-ensure-high-concurrency-and-high-availability-of-redis.md) +- [Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?](./redis-persistence.md) +- [Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?](./redis-cluster.md) +- [了解什么是 redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](./redis-caching-avalanche-and-caching-penetration.md) +- [如何保证缓存与数据库的双写一致性?](./redis-consistence.md) +- [Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?](./redis-cas.md) +- [生产环境中的 Redis 是怎么部署的?](./redis-production-environment.md) +- [有了解过 Redis rehash 的过程吗?](./redis-rehash.md) ## 分库分表 -- [为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?你们具体是如何对数据库如何进行垂直拆分或水平拆分的?](/docs/high-concurrency/database-shard.md) -- [现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表动态切换到分库分表上?](/docs/high-concurrency/database-shard-method.md) -- [如何设计可以动态扩容缩容的分库分表方案?](/docs/high-concurrency/database-shard-dynamic-expand.md) -- [分库分表之后,id 主键如何处理?](/docs/high-concurrency/database-shard-global-id-generate.md) +- [为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?你们具体是如何对数据库如何进行垂直拆分或水平拆分的?](./database-shard.md) +- [现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表动态切换到分库分表上?](./database-shard-method.md) +- [如何设计可以动态扩容缩容的分库分表方案?](./database-shard-dynamic-expand.md) +- [分库分表之后,id 主键如何处理?](./database-shard-global-id-generate.md) ## 读写分离 -- [如何实现 MySQL 的读写分离?MySQL 主从复制原理是啥?如何解决 MySQL 主从同步的延时问题?](/docs/high-concurrency/mysql-read-write-separation.md) +- [如何实现 MySQL 的读写分离?MySQL 主从复制原理是啥?如何解决 MySQL 主从同步的延时问题?](./mysql-read-write-separation.md) ## 高并发系统 -- [如何设计一个高并发系统?](/docs/high-concurrency/high-concurrency-design.md) +- [如何设计一个高并发系统?](./high-concurrency-design.md) --- @@ -55,17 +55,11 @@ GitHub 技术社区 [Doocs](https://github.com/doocs) 旗下唯一公众号「** - -
- -
- 公众平台 -

+
+
- -
- 个人微信 -

+
+
diff --git a/docs/high-concurrency/database-shard-dynamic-expand.md b/docs/high-concurrency/database-shard-dynamic-expand.md index 8df5e4b2..51d9bbed 100644 --- a/docs/high-concurrency/database-shard-dynamic-expand.md +++ b/docs/high-concurrency/database-shard-dynamic-expand.md @@ -1,3 +1,5 @@ +# 动态扩缩容方案 + ## 面试题 如何设计可以动态扩容缩容的分库分表方案? @@ -35,7 +37,7 @@ 我可以告诉各位同学,这个分法,第一,基本上国内的互联网肯定都是够用了,第二,无论是并发支撑还是数据量支撑都没问题。 -每个库正常承载的写入并发量是 1000,那么 32 个库就可以承载 32 _ 1000 = 32000 的写并发,如果每个库承载 1500 的写并发,32 _ 1500 = 48000 的写并发,接近 5 万每秒的写入并发,前面再加一个 MQ,削峰,每秒写入 MQ 8 万条数据,每秒消费 5 万条数据。 +每个库正常承载的写入并发量是 1000,那么 32 个库就可以承载 $32 \times 1000 = 32000$ 的写并发,如果每个库承载 1500 的写并发,总共就是 $32 \times 1500 = 48000$ 的写并发,接近 5 万每秒的写入并发,前面再加一个 MQ,削峰,每秒写入 MQ 8 万条数据,每秒消费 5 万条数据。 有些除非是国内排名非常靠前的这些公司,他们的最核心的系统的数据库,可能会出现几百台数据库的这么一个规模,128 个库,256 个库,512 个库。 @@ -45,7 +47,7 @@ 谈分库分表的扩容,**第一次分库分表,就一次性给他分个够**,32 个库,1024 张表,可能对大部分的中小型互联网公司来说,已经可以支撑好几年了。 -一个实践是利用 `32 * 32` 来分库分表,即分为 32 个库,每个库里一个表分为 32 张表。一共就是 1024 张表。根据某个 id 先根据 32 取模路由到库,再根据 32 取模路由到库里的表。 +一个实践是利用 $32 \times 32$ 来分库分表,即分为 32 个库,每个库里一个表分为 32 张表。一共就是 1024 张表。根据某个 id 先根据 32 取模路由到库,再根据 32 取模路由到库里的表。 | orderId | id % 32 (库) | id / 32 % 32 (表) | | ------- | ------------ | ----------------- | @@ -56,7 +58,7 @@ 刚开始的时候,这个库可能就是逻辑库,建在一个数据库上的,就是一个 MySQL 服务器可能建了 n 个库,比如 32 个库。后面如果要拆分,就是不断在库和 MySQL 服务器之间做迁移就可以了。然后系统配合改一下配置即可。 -比如说最多可以扩展到 32 个数据库服务器,每个数据库服务器是一个库。如果还是不够?最多可以扩展到 1024 个数据库服务器,每个数据库服务器上面一个库一个表。因为最多是 1024 个表。 +比如说最多可以扩展到 32 个数据库服务器,每个数据库服务器是一个库。如果还是不够?最多可以扩展到 1024 个数据库服务器,每个数据库服务器上面一个库一个表,那么最多是 1024 个表。 这么搞,是不用自己写代码做数据迁移的,都交给 DBA 来搞好了,但是 DBA 确实是需要做一些库表迁移的工作,但是总比你自己写代码,然后抽数据导数据来的效率高得多吧。 @@ -64,7 +66,7 @@ 这里对步骤做一个总结: -1. 设定好几台数据库服务器,每台服务器上几个库,每个库多少个表,推荐是 32 库 \* 32 表,对于大部分公司来说,可能几年都够了。 +1. 设定好几台数据库服务器,每台服务器上几个库,每个库多少个表,推荐是 $32 库 \times 32 表$,对于大部分公司来说,可能几年都够了。 2. 路由的规则,orderId 模 32 = 库,orderId / 32 模 32 = 表 3. 扩容的时候,申请增加更多的数据库服务器,装好 MySQL,呈倍数扩容,4 台服务器,扩到 8 台服务器,再到 16 台服务器。 4. 由 DBA 负责将原先数据库服务器的库,迁移到新的数据库服务器上去,库迁移是有一些便捷的工具的。 diff --git a/docs/high-concurrency/database-shard-global-id-generate.md b/docs/high-concurrency/database-shard-global-id-generate.md index 905fd0f6..ffba2734 100644 --- a/docs/high-concurrency/database-shard-global-id-generate.md +++ b/docs/high-concurrency/database-shard-global-id-generate.md @@ -1,3 +1,5 @@ +# 主键 ID 如何处理? + ## 面试题 分库分表之后,id 主键如何处理? @@ -51,7 +53,7 @@ snowflake 算法是 twitter 开源的分布式 id 生成算法,采用 Scala - 1 bit:不用,为啥呢?因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。 - 41 bits:表示的是时间戳,单位是毫秒。41 bits 可以表示的数字多达 `2^41 - 1` ,也就是可以标识 `2^41 - 1` 个毫秒值,换算成年就是表示 69 年的时间。 - 10 bits:记录工作机器 id,代表的是这个服务最多可以部署在 2^10 台机器上,也就是 1024 台机器。但是 10 bits 里 5 个 bits 代表机房 id,5 个 bits 代表机器 id。意思就是最多代表 `2^5` 个机房(32 个机房),每个机房里可以代表 `2^5` 个机器(32 台机器)。 -- 12 bits:这个是用来记录同一个毫秒内产生的不同 id,12 bits 可以代表的最大正整数是 `2^12 - 1 = 4096` ,也就是说可以用这个 12 bits 代表的数字来区分**同一个毫秒内**的 4096 个不同的 id。 +- 12 bits:这个是用来记录同一个毫秒内产生的不同 id,12 bits 可以代表的最大数字是 `2^12 - 1 = 4095` ,也就是说可以用这个 12 bits 代表的数字来区分**同一个毫秒内**的 4096 个(数字 0 到数字 4095)不同的 id。 ```sh 0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000000 diff --git a/docs/high-concurrency/database-shard-method.md b/docs/high-concurrency/database-shard-method.md index 4e128371..1430be92 100644 --- a/docs/high-concurrency/database-shard-method.md +++ b/docs/high-concurrency/database-shard-method.md @@ -1,3 +1,5 @@ +# 分库分表如何平滑过渡? + ## 面试题 现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表**动态切换**到分库分表上? diff --git a/docs/high-concurrency/database-shard.md b/docs/high-concurrency/database-shard.md index 47a6cbff..94e27476 100644 --- a/docs/high-concurrency/database-shard.md +++ b/docs/high-concurrency/database-shard.md @@ -1,3 +1,5 @@ +# 为什么要分库分表? + ## 面试题 为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?你们具体是如何对数据库如何进行垂直拆分或水平拆分的? diff --git a/docs/high-concurrency/es-architecture.md b/docs/high-concurrency/es-architecture.md index 93aad028..60f6c5b5 100644 --- a/docs/high-concurrency/es-architecture.md +++ b/docs/high-concurrency/es-architecture.md @@ -1,3 +1,5 @@ +# ES 的分布式架构原理 + ## 面试题 ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊)? @@ -16,7 +18,7 @@ ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊 ElasticSearch 设计的理念就是分布式搜索引擎,底层其实还是基于 lucene 的。核心思想就是在多台机器上启动多个 ES 进程实例,组成了一个 ES 集群。 -ES 中存储数据的**基本单位是索引**,比如说你现在要在 ES 中存储一些订单数据,你就应该在 ES 中创建一个索引 `order_idx` ,所有的订单数据就都写到这个索引里面去,一个索引差不多就是相当于是 mysql 里的一张表。 +ES 中存储数据的**基本单位是索引**,比如说你现在要在 ES 中存储一些订单数据,你就应该在 ES 中创建一个索引 `order_idx` ,所有的订单数据就都写到这个索引里面去,一个索引差不多就是相当于是 mysql 里的一个数据库。 ``` index -> type -> mapping -> document -> field @@ -24,7 +26,7 @@ index -> type -> mapping -> document -> field 这样吧,为了做个更直白的介绍,我在这里做个类比。但是切记,不要划等号,类比只是为了便于理解。 -index 相当于 mysql 里的一张表。而 type 没法跟 mysql 里去对比,一个 index 里可以有多个 type,每个 type 的字段都是差不多的,但是有一些略微的差别。假设有一个 index,是订单 index,里面专门是放订单数据的。就好比说你在 mysql 中建表,有些订单是实物商品的订单,比如一件衣服、一双鞋子;有些订单是虚拟商品的订单,比如游戏点卡,话费充值。就两种订单大部分字段是一样的,但是少部分字段可能有略微的一些差别。 +index 相当于 mysql 数据库。而 type 没法跟 mysql 里去对比,一个 index 里可以有多个 type,每个 type 的字段都是差不多的,但是有一些略微的差别。假设有一个 index,是订单 index,里面专门是放订单数据的。就好比说你在 mysql 中建表,有些订单是实物商品的订单,比如一件衣服、一双鞋子;有些订单是虚拟商品的订单,比如游戏点卡,话费充值。就两种订单大部分字段是一样的,但是少部分字段可能有略微的一些差别。 所以就会在订单 index 里,建两个 type,一个是实物商品订单 type,一个是虚拟商品订单 type,这两个 type 大部分字段是一样的,少部分字段是不一样的。 diff --git a/docs/high-concurrency/es-introduction.md b/docs/high-concurrency/es-introduction.md index 22d3f01c..0fc11586 100644 --- a/docs/high-concurrency/es-introduction.md +++ b/docs/high-concurrency/es-introduction.md @@ -1,3 +1,5 @@ +# 搜索引擎介绍 + ## Lucene 和 ES 的前世今生 Lucene 是最先进、功能最强大的搜索库。如果直接基于 Lucene 开发,非常复杂,即便写一些简单的功能,也要写大量的 Java 代码,需要深入理解原理。 diff --git a/docs/high-concurrency/es-optimizing-query-performance.md b/docs/high-concurrency/es-optimizing-query-performance.md index d5f6e0a8..b59a2104 100644 --- a/docs/high-concurrency/es-optimizing-query-performance.md +++ b/docs/high-concurrency/es-optimizing-query-performance.md @@ -1,3 +1,5 @@ +# ES 查询性能优化 + ## 面试题 ES 在数据量很大的情况下(数十亿级别)如何提高查询效率啊? diff --git a/docs/high-concurrency/es-production-cluster.md b/docs/high-concurrency/es-production-cluster.md index fd709352..0134cc72 100644 --- a/docs/high-concurrency/es-production-cluster.md +++ b/docs/high-concurrency/es-production-cluster.md @@ -1,3 +1,5 @@ +# ES 生产集群架构 + ## 面试题 ES 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片? diff --git a/docs/high-concurrency/es-write-query-search.md b/docs/high-concurrency/es-write-query-search.md index 3e7e304a..600a2a8b 100644 --- a/docs/high-concurrency/es-write-query-search.md +++ b/docs/high-concurrency/es-write-query-search.md @@ -1,3 +1,5 @@ +# ES 写入数据原理 + ## 面试题 ES 写入数据的工作原理是什么啊?ES 查询数据的工作原理是什么啊?底层的 Lucene 介绍一下呗?倒排索引了解吗? diff --git a/docs/high-concurrency/high-concurrency-design.md b/docs/high-concurrency/high-concurrency-design.md index 8ecb0941..9af4256a 100644 --- a/docs/high-concurrency/high-concurrency-design.md +++ b/docs/high-concurrency/high-concurrency-design.md @@ -1,3 +1,5 @@ +# 如何设计一个高并发系统 + ## 面试题 如何设计一个高并发系统? diff --git a/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md b/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md index 520fe058..edab29b6 100644 --- a/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md +++ b/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md @@ -1,10 +1,12 @@ +# 如何保证消息队列的高可用? + ## 面试题 如何保证消息队列的高可用? ## 面试官心理分析 -如果有人问到你 MQ 的知识,**高可用是必问的**。[上一讲](/docs/high-concurrency/why-mq.md)提到,MQ 会导致**系统可用性降低**。所以只要你用了 MQ,接下来问的一些要点肯定就是围绕着 MQ 的那些缺点怎么来解决了。 +如果有人问到你 MQ 的知识,**高可用是必问的**。[上一讲](./why-mq.md)提到,MQ 会导致**系统可用性降低**。所以只要你用了 MQ,接下来问的一些要点肯定就是围绕着 MQ 的那些缺点怎么来解决了。 要是你傻乎乎的就干用了一个 MQ,各种问题从来没考虑过,那你就杯具了,面试官对你的感觉就是,只会简单使用一些技术,没任何思考,马上对你的印象就不太好了。这样的同学招进来要是做个 20k 薪资以内的普通小弟还凑合,要是做薪资 20k+ 的高工,那就惨了,让你设计个系统,里面肯定一堆坑,出了事故公司受损失,团队一起背锅。 @@ -36,9 +38,11 @@ RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模 所以这个事儿就比较尴尬了,这就**没有什么所谓的高可用性**,**这方案主要是提高吞吐量的**,就是说让集群中多个节点来服务某个 queue 的读写操作。 -#### 镜像集群模式(高可用性) +#### 镜像集群模式(⚠️ 已废弃,不推荐使用) + +> **⚠️ 重要更新**:自 RabbitMQ 4.0 版本起,镜像集群模式已被正式废弃,不再支持新增镜像策略。建议现有用户尽快迁移至 Quorum Queues。 -这种模式,才是所谓的 RabbitMQ 的高可用模式。跟普通集群模式不一样的是,在镜像集群模式下,你创建的 queue,无论是元数据还是 queue 里的消息都会**存在于多个实例上**,就是说,每个 RabbitMQ 节点都有这个 queue 的一个**完整镜像**,包含 queue 的全部数据的意思。然后每次你写消息到 queue 的时候,都会自动把**消息同步**到多个实例的 queue 上。 +这种模式,曾经是所谓的 RabbitMQ 高可用模式。跟普通集群模式不一样的是,在镜像集群模式下,你创建的 queue,无论是元数据还是 queue 里的消息都会**存在于多个实例上**,就是说,每个 RabbitMQ 节点都有这个 queue 的一个**完整镜像**,包含 queue 的全部数据的意思。然后每次你写消息到 queue 的时候,都会自动把**消息同步**到多个实例的 queue 上。 ![mq-8](./images/mq-8.png) @@ -46,6 +50,36 @@ RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模 这样的话,好处在于,你任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据。坏处在于,第一,这个性能开销也太大了吧,消息需要同步到所有机器上,导致网络带宽压力和消耗很重!第二,这么玩儿,不是分布式的,就**没有扩展性可言**了,如果某个 queue 负载很重,你加机器,新增的机器也包含了这个 queue 的所有数据,并**没有办法线性扩展**你的 queue。你想,如果这个 queue 的数据量很大,大到这个机器上的容量无法容纳了,此时该怎么办呢? + +#### Quorum Queues(仲裁队列,推荐) + +从 RabbitMQ 3.8 版本开始,引入了 Quorum Queues(仲裁队列)作为新的高可用解决方案,旨在替代传统的镜像集群模式。 + +**核心原理**:基于 Raft 共识算法,Quorum Queues 在多个节点之间通过 quorum 复制实现数据一致性。 + +**主要优势**: +- **数据一致性**:基于 Raft 算法,保证消息在 quorum 多数节点确认后才算写入成功 +- **自动故障转移**:leader 节点宕机后,自动重新选举,无需手动干预 +- **线性扩展**:可以独立扩展副本数量,不受单节点容量限制 +- **持久化优化**:支持段式存储,滚动刷新时性能更优 + +**配置示例**: +```bash +rabbitmqctl set_policy ha-quorum "^quorum\." '{"ha-mode":" quorum"}' +``` + +**适用场景**: +- 对数据可靠性要求高的生产环境 +- 需要更好扩展性的分布式部署 +- 替代传统镜像集群模式的升级路径 + +**注意事项**: +- Quorum Queues 仅支持持久化消息(persistent),不适用于临时队列 +- 资源消耗高于普通队列,需合理规划节点数量 +- 最低需要 3 节点才能形成有效的 quorum + +**迁移建议**:对于现有使用镜像集群的队列,建议逐步迁移到 Quorum Queues。迁移过程中需注意消息持久化配置和消费者兼容性。 + ### Kafka 的高可用性 Kafka 一个最基本的架构认识:由多个 broker 组成,每个 broker 是一个节点;你创建一个 topic,这个 topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition 就放一部分数据。 diff --git a/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md b/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md index 7ed909c8..bbadee31 100644 --- a/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md +++ b/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md @@ -1,3 +1,5 @@ +# 如何保证 redis 的高并发和高可用? + ## 面试题 如何保证 redis 的高并发和高可用?redis 的主从复制原理能介绍一下么?redis 的哨兵原理能介绍一下么? @@ -14,8 +16,8 @@ 由于此节内容较多,因此,会分为两个小节进行讲解。 -- [redis 主从架构](/docs/high-concurrency/redis-master-slave.md) -- [redis 基于哨兵实现高可用](/docs/high-concurrency/redis-sentinel.md) +- [redis 主从架构](./redis-master-slave.md) +- [redis 基于哨兵实现高可用](./redis-sentinel.md) redis 实现**高并发**主要依靠**主从架构**,一主多从,一般来说,很多项目其实就足够了,单主用来写入数据,单机几万 QPS,多从用来查询数据,多个从实例可以提供每秒 10w 的 QPS。 diff --git a/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md b/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md index 43367435..4e53f36f 100644 --- a/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md +++ b/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md @@ -1,3 +1,5 @@ +# 如何保证消息不被重复消费? + ## 面试题 如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性? diff --git a/docs/high-concurrency/how-to-ensure-the-order-of-messages.md b/docs/high-concurrency/how-to-ensure-the-order-of-messages.md index 7afeb0da..132608cd 100644 --- a/docs/high-concurrency/how-to-ensure-the-order-of-messages.md +++ b/docs/high-concurrency/how-to-ensure-the-order-of-messages.md @@ -1,3 +1,5 @@ +# 如何保证消息的顺序性? + ## 面试题 如何保证消息的顺序性? diff --git a/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md b/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md index ab56eb65..14bbd765 100644 --- a/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md +++ b/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md @@ -1,10 +1,12 @@ +# 如何保证消息的可靠性传输? + ## 面试题 如何保证消息的可靠性传输?或者说,如何处理消息丢失的问题? ## 面试官心理分析 -这个是肯定的,用 MQ 有个基本原则,就是**数据不能多一条,也不能少一条**,不能多,就是前面说的[重复消费和幂等性问题](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md)。不能少,就是说这数据别搞丢了。那这个问题你必须得考虑一下。 +这个是肯定的,用 MQ 有个基本原则,就是**数据不能多一条,也不能少一条**,不能多,就是前面说的[重复消费和幂等性问题](./how-to-ensure-that-messages-are-not-repeatedly-consumed.md)。不能少,就是说这数据别搞丢了。那这个问题你必须得考虑一下。 如果说你这个是用 MQ 来传递非常核心的消息,比如说计费、扣费的一些消息,那必须确保这个 MQ 传递过程中**绝对不会把计费消息给弄丢**。 @@ -165,3 +167,44 @@ RabbitMQ 如果丢失了数据,主要是因为你消费的时候,**刚消费 #### 生产者会不会弄丢数据? 如果按照上述的思路设置了 `acks=all` ,一定不会丢,要求是,你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。 + +### RocketMQ + +#### 消息丢失的场景 + +1. 生产者发送消息到 MQ 有可能丢失消息 +2. MQ 收到消息后写入硬盘可能丢失消息 +3. 消息写入硬盘后,硬盘坏了丢失消息 +4. 消费者消费 MQ 也可能丢失消息 +5. 整个 MQ 节点挂了丢失消息 + +#### 生产者发送消息时如何保证不丢失? + +解决发送时消息丢失的问题可以采用 RocketMQ 自带的**事务消息**机制 + +事务消息原理:首先生产者会发送一个**half 消息**(对原始消息的封装),该消息对消费者不可见,MQ 通过 ACK 机制返回消息接受状态, 生产者执行本地事务并且返回给 MQ 一个状态(Commit、RollBack 等),如果是 Commit 的话 MQ 就会把消息给到下游, RollBack 的话就会丢弃该消息,状态如果为 UnKnow 的话会过一段时间回查本地事务状态,默认回查 15 次,一直是 UnKnow 状态的话就会丢弃此消息。 + +为什么先发一个 half 消息,作用就是先判断下 MQ 有没有问题,服务正不正常。 + +#### MQ 收到消息后写入硬盘如何保证不丢失? + +数据存盘绕过缓存,改为同步刷盘,这一步需要修改 Broker 的配置文件,将 flushDiskType 改为 SYNC_FLUSH 同步刷盘策略,默认的是 ASYNC_FLUSH 异步刷盘,一旦同步刷盘返回成功,那么就一定保证消息已经持久化到磁盘中了。 + +#### 消息写入硬盘后,硬盘坏了如何保证不丢失? + +为了保证磁盘损坏导致丢失数据,RocketMQ 采用主从机构,集群部署,Leader 中的数据在多个 Follower 中都存有备份,防止单点故障导致数据丢失。 + +Master 节点挂了怎么办?Master 节点挂了之后 DLedger 登场 + +- 接管 MQ 的 commitLog +- 选举从节点 +- 文件复制 uncommited 状态 多半从节点收到之后改为 commited + +#### 消费者消费 MQ 如何保证不丢失? + +1. 如果是网络问题导致的消费失败可以进行重试机制,默认每条消息重试 16 次 +2. 多线程异步消费失败,MQ 认为已经消费成功但是实际上对于业务逻辑来说消息是没有落地的,解决方案就是按照 mq 官方推荐的先执行本地事务再返回成功状态。 + +#### 整个 MQ 节点挂了如何保证不丢失? + +这种极端情况可以消息发送失败之后先存入本地,例如放到缓存中,另外启动一个线程扫描缓存的消息去重试发送。 diff --git a/docs/high-concurrency/how-to-limit-current.md b/docs/high-concurrency/how-to-limit-current.md index f868964f..d3616aea 100644 --- a/docs/high-concurrency/how-to-limit-current.md +++ b/docs/high-concurrency/how-to-limit-current.md @@ -288,25 +288,25 @@ public class TokenBucket { ```yaml spring: - cloud: - gateway: - routes: - - id: requestratelimiter_route - - uri: lb://pigx-upms - order: 10000 - predicates: - - Path=/admin/** - - filters: - - name: RequestRateLimiter - - args: - redis-rate-limiter.replenishRate: 1 # 令牌桶的容积 - redis-rate-limiter.burstCapacity: 3 # 流速 每秒 - key-resolver: '#{@remoteAddrKeyResolver}' #SPEL表达式去的对应的bean - - - StripPrefix=1 + cloud: + gateway: + routes: + - id: requestratelimiter_route + + uri: lb://pigx-upms + order: 10000 + predicates: + - Path=/admin/** + + filters: + - name: RequestRateLimiter + + args: + redis-rate-limiter.replenishRate: 1 # 令牌桶的容积 + redis-rate-limiter.burstCapacity: 3 # 流速 每秒 + key-resolver: '#{@remoteAddrKeyResolver}' #SPEL表达式去的对应的bean + + - StripPrefix=1 ``` ```java @@ -329,37 +329,37 @@ KeyResolver remoteAddrKeyResolver() { ```yaml spring: - cloud: - nacos: - discovery: - server-addr: localhost:8848 - sentinel: - transport: - dashboard: localhost:8080 - port: 8720 - datasource: - ds: - nacos: - server-addr: localhost:8848 - dataId: spring-cloud-sentinel-nacos - groupId: DEFAULT_GROUP - rule-type: flow - namespace: xxxxxxxx + cloud: + nacos: + discovery: + server-addr: localhost:8848 + sentinel: + transport: + dashboard: localhost:8080 + port: 8720 + datasource: + ds: + nacos: + server-addr: localhost:8848 + dataId: spring-cloud-sentinel-nacos + groupId: DEFAULT_GROUP + rule-type: flow + namespace: xxxxxxxx ``` - 配置内容在 nacos 上进行编辑 ```json [ - { - "resource": "/hello", - "limitApp": "default", - "grade": 1, - "count": 1, - "strategy": 0, - "controlBehavior": 0, - "clusterMode": false - } + { + "resource": "/hello", + "limitApp": "default", + "grade": 1, + "count": 1, + "strategy": 0, + "controlBehavior": 0, + "clusterMode": false + } ] ``` diff --git a/docs/high-concurrency/mq-design.md b/docs/high-concurrency/mq-design.md index 08f47ec5..c81f85eb 100644 --- a/docs/high-concurrency/mq-design.md +++ b/docs/high-concurrency/mq-design.md @@ -1,3 +1,5 @@ +# 如何设计一个消息队列? + ## 面试题 如果让你写一个消息队列,该如何进行架构设计?说一下你的思路。 diff --git a/docs/high-concurrency/mq-interview.md b/docs/high-concurrency/mq-interview.md index 25abde68..e89b2aa6 100644 --- a/docs/high-concurrency/mq-interview.md +++ b/docs/high-concurrency/mq-interview.md @@ -1,4 +1,4 @@ -## 消息队列面试场景 +# 消息队列面试场景 **面试官**:你好。 diff --git a/docs/high-concurrency/mq-time-delay-and-expired-failure.md b/docs/high-concurrency/mq-time-delay-and-expired-failure.md index 94641a40..4a0ad988 100644 --- a/docs/high-concurrency/mq-time-delay-and-expired-failure.md +++ b/docs/high-concurrency/mq-time-delay-and-expired-failure.md @@ -1,3 +1,5 @@ +# 如何解决消息队列的延时以及过期失效问题? + ## 面试题 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决? diff --git a/docs/high-concurrency/mysql-read-write-separation.md b/docs/high-concurrency/mysql-read-write-separation.md index 1aed9650..b35b083d 100644 --- a/docs/high-concurrency/mysql-read-write-separation.md +++ b/docs/high-concurrency/mysql-read-write-separation.md @@ -1,3 +1,5 @@ +# 如何实现读写分离? + ## 面试题 你们有没有做 MySQL 读写分离?如何实现 MySQL 的读写分离?MySQL 主从复制原理的是啥?如何解决 MySQL 主从同步的延时问题? diff --git a/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md b/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md index eebf9e16..05be3b92 100644 --- a/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md +++ b/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md @@ -1,3 +1,5 @@ +# 缓存雪崩、穿透和击穿 + ## 面试题 了解什么是 Redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透? diff --git a/docs/high-concurrency/redis-cas.md b/docs/high-concurrency/redis-cas.md index e43d2792..c1852bf8 100644 --- a/docs/high-concurrency/redis-cas.md +++ b/docs/high-concurrency/redis-cas.md @@ -1,3 +1,5 @@ +# Redis 的并发竞争问题 + ## 面试题 Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗? diff --git a/docs/high-concurrency/redis-cluster.md b/docs/high-concurrency/redis-cluster.md index 74ed7292..69c3d497 100644 --- a/docs/high-concurrency/redis-cluster.md +++ b/docs/high-concurrency/redis-cluster.md @@ -1,3 +1,5 @@ +# Redis 集群模式原理 + ## 面试题 Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗? diff --git a/docs/high-concurrency/redis-consistence.md b/docs/high-concurrency/redis-consistence.md index aa0ff7db..150ba670 100644 --- a/docs/high-concurrency/redis-consistence.md +++ b/docs/high-concurrency/redis-consistence.md @@ -1,3 +1,5 @@ +# 缓存与数据库一致性问题 + ## 面试题 如何保证缓存与数据库的双写一致性? diff --git a/docs/high-concurrency/redis-data-types.md b/docs/high-concurrency/redis-data-types.md index a5eab594..1681ea2a 100644 --- a/docs/high-concurrency/redis-data-types.md +++ b/docs/high-concurrency/redis-data-types.md @@ -1,3 +1,5 @@ +# Redis 数据类型和使用场景 + ## 面试题 Redis 都有哪些数据类型?分别在哪些场景下使用比较合适? diff --git a/docs/high-concurrency/redis-expiration-policies-and-lru.md b/docs/high-concurrency/redis-expiration-policies-and-lru.md index c91d6b9d..54da3fe2 100644 --- a/docs/high-concurrency/redis-expiration-policies-and-lru.md +++ b/docs/high-concurrency/redis-expiration-policies-and-lru.md @@ -1,3 +1,5 @@ +# Redis 的过期策略和 LRU 算法 + ## 面试题 Redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现? diff --git a/docs/high-concurrency/redis-master-slave.md b/docs/high-concurrency/redis-master-slave.md index 34cac22c..bcb451e8 100644 --- a/docs/high-concurrency/redis-master-slave.md +++ b/docs/high-concurrency/redis-master-slave.md @@ -15,9 +15,9 @@ Redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并 - slave node 在做复制的时候,也不会 block 对自己的查询操作,它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了; - slave node 主要用来进行横向扩容,做读写分离,扩容的 slave node 可以提高读的吞吐量。 -注意,如果采用了主从架构,那么建议必须**开启** master node 的[持久化](/docs/high-concurrency/redis-persistence.md),不建议用 slave node 作为 master node 的数据热备,因为那样的话,如果你关掉 master 的持久化,可能在 master 宕机重启的时候数据是空的,然后可能一经过复制, slave node 的数据也丢了。 +注意,如果采用了主从架构,那么建议必须**开启** master node 的[持久化](./redis-persistence.md),不建议用 slave node 作为 master node 的数据热备,因为那样的话,如果你关掉 master 的持久化,可能在 master 宕机重启的时候数据是空的,然后可能一经过复制, slave node 的数据也丢了。 -另外,master 的各种备份方案,也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master,这样才能**确保启动的时候,是有数据的**,即使采用了后续讲解的[高可用机制](/docs/high-concurrency/redis-sentinel.md),slave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空。 +另外,master 的各种备份方案,也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master,这样才能**确保启动的时候,是有数据的**,即使采用了后续讲解的[高可用机制](./redis-sentinel.md),slave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空。 ## Redis 主从复制的核心原理 @@ -100,4 +100,4 @@ Redis 的高可用架构,叫做 `failover` **故障转移**,也可以叫做 master node 在故障时,自动检测,并且将某个 slave node 自动切换为 master node 的过程,叫做主备切换。这个过程,实现了 Redis 的主从架构下的高可用。 -后面会详细说明 Redis [基于哨兵的高可用性](/docs/high-concurrency/redis-sentinel.md)。 +后面会详细说明 Redis [基于哨兵的高可用性](./redis-sentinel.md)。 diff --git a/docs/high-concurrency/redis-persistence.md b/docs/high-concurrency/redis-persistence.md index b08acb01..20605e2d 100644 --- a/docs/high-concurrency/redis-persistence.md +++ b/docs/high-concurrency/redis-persistence.md @@ -1,3 +1,5 @@ +# Redis 持久化机制 + ## 面试题 Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的? diff --git a/docs/high-concurrency/redis-production-environment.md b/docs/high-concurrency/redis-production-environment.md index 5d78123e..74aab390 100644 --- a/docs/high-concurrency/redis-production-environment.md +++ b/docs/high-concurrency/redis-production-environment.md @@ -1,3 +1,5 @@ +# Redis 生产部署方案 + ## 面试题 生产环境中的 Redis 是怎么部署的? diff --git a/docs/high-concurrency/redis-rehash.md b/docs/high-concurrency/redis-rehash.md index 552ffd63..52deb3e6 100644 --- a/docs/high-concurrency/redis-rehash.md +++ b/docs/high-concurrency/redis-rehash.md @@ -1,3 +1,5 @@ +# Redis rehash 过程 + ## 面试题 有了解过 Redis rehash 的过程吗? diff --git a/docs/high-concurrency/redis-single-thread-model.md b/docs/high-concurrency/redis-single-thread-model.md index 9d3ad227..17a42740 100644 --- a/docs/high-concurrency/redis-single-thread-model.md +++ b/docs/high-concurrency/redis-single-thread-model.md @@ -1,3 +1,5 @@ +# Redis 和 Memcached 的区别 + ## 面试题 Redis 和 Memcached 有什么区别?Redis 的线程模型是什么?为什么 Redis 单线程却能支撑高并发? @@ -14,7 +16,7 @@ Redis 和 Memcached 有什么区别?Redis 的线程模型是什么?为什么 #### Redis 支持复杂的数据结构 -Redis 相比 Memcached 来说,拥有[更多的数据结构](/docs/high-concurrency/redis-data-types.md),能支持更丰富的数据操作。如果需要缓存能够支持更复杂的结构和操作, Redis 会是不错的选择。 +Redis 相比 Memcached 来说,拥有[更多的数据结构](./redis-data-types.md),能支持更丰富的数据操作。如果需要缓存能够支持更复杂的结构和操作, Redis 会是不错的选择。 #### Redis 原生支持集群模式 diff --git a/docs/high-concurrency/why-cache.md b/docs/high-concurrency/why-cache.md index ec684200..27eb0f94 100644 --- a/docs/high-concurrency/why-cache.md +++ b/docs/high-concurrency/why-cache.md @@ -1,3 +1,5 @@ +# 缓存的使用方式 + ## 面试题 项目中缓存是如何使用的?为什么要用缓存?缓存使用不当会造成什么后果? @@ -40,8 +42,8 @@ mysql 这么重的数据库,压根儿设计不是让你玩儿高并发的, 常见的缓存问题有以下几个: -- [缓存与数据库双写不一致](/docs/high-concurrency/redis-consistence.md) -- [缓存雪崩、缓存穿透、缓存击穿](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md) -- [缓存并发竞争](/docs/high-concurrency/redis-cas.md) +- [缓存与数据库双写不一致](./redis-consistence.md) +- [缓存雪崩、缓存穿透、缓存击穿](./redis-caching-avalanche-and-caching-penetration.md) +- [缓存并发竞争](./redis-cas.md) 点击超链接,可直接查看缓存相关问题及解决方案。 diff --git a/docs/high-concurrency/why-mq.md b/docs/high-concurrency/why-mq.md index c4c8ddab..47e54b2f 100644 --- a/docs/high-concurrency/why-mq.md +++ b/docs/high-concurrency/why-mq.md @@ -1,3 +1,5 @@ +# 为什么使用消息队列? + ## 面试题 - 为什么使用消息队列? @@ -36,7 +38,7 @@ #### 解耦 -看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃...... +看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 D 系统现在不需要了呢?A 系统负责人几乎崩溃...... ![mq-1](./images/mq-1.png) @@ -86,11 +88,11 @@ - 系统可用性降低 - 系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,ABCD 四个系统还好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整?MQ 一挂,整套系统崩溃,你不就完了?如何保证消息队列的高可用,可以[点击这里查看](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md)。 + 系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,ABCD 四个系统还好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整?MQ 一挂,整套系统崩溃,你不就完了?如何保证消息队列的高可用,可以[点击这里查看](./how-to-ensure-high-availability-of-message-queues.md)。 - 系统复杂度提高 - 硬生生加个 MQ 进来,你怎么[保证消息没有重复消费](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md)?怎么[处理消息丢失的情况](/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md)?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。 + 硬生生加个 MQ 进来,你怎么[保证消息没有重复消费](./how-to-ensure-that-messages-are-not-repeatedly-consumed.md)?怎么[处理消息丢失的情况](./how-to-ensure-the-reliable-transmission-of-messages.md)?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。 - 一致性问题 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..99b7329e --- /dev/null +++ b/docs/index.md @@ -0,0 +1,35 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "advanced-java" + text: "互联网 Java 工程师进阶知识完全扫盲" + tagline: Doocs 技术社区出品 + actions: + - theme: alt + text: GitHub 首页 + link: https://github.com/doocs/advanced-java + - theme: brand + text: 开始学习 + link: /high-concurrency/mq-interview + + +features: + - title: "⚡ 高并发架构" + details: 包含消息队列、搜索引擎、缓存、分库分表、读写分离等核心知识点 + link: /high-concurrency/mq-interview + - title: "🌐 分布式系统" + details: 涉及 Dubbo、分布式锁、事务、会话等设计与实现原理 + link: /distributed-system/distributed-system-interview + - title: "🛡️ 高可用架构" + details: 涵盖限流、熔断、降级、Hystrix 实现等高可用保障手段 + link: /high-availability/hystrix-introduction + - title: "🧩 微服务架构" + details: 深入了解微服务基础、Spring Cloud、服务治理与通信机制 + link: /micro-services/microservices-introduction + - title: "📊 海量数据处理" + details: 探索应对大数据场景下的经典算法与设计题 + link: /big-data/find-common-urls +--- + diff --git a/docs/micro-services/README.md b/docs/micro-services/README.md index 963bb9ad..ebbbcb3a 100644 --- a/docs/micro-services/README.md +++ b/docs/micro-services/README.md @@ -28,17 +28,11 @@ - -
- -
- 公众平台 -

+
+
- -
- 个人微信 -

+
+
diff --git a/docs/micro-services/advantages-and-disadvantages-of-microservice.md b/docs/micro-services/advantages-and-disadvantages-of-microservice.md index a3967b70..f3e35974 100644 --- a/docs/micro-services/advantages-and-disadvantages-of-microservice.md +++ b/docs/micro-services/advantages-and-disadvantages-of-microservice.md @@ -1,4 +1,6 @@ -# 开发单体式应用 +# 微服务架构的优势与不足 + +## 开发单体式应用 假设你正准备开发一款与 Uber 和 Hailo 竞争的出租车调度软件,经过初步会议和需求分析,你可能会手动或者使用基于 Rails、Spring Boot、Play 或者 Maven 的生成器开始这个新项目,它的六边形架构是模块化的 ,架构图如下: @@ -10,7 +12,7 @@ 这种应用开发风格很常见,因为 IDE 和其它工具都擅长开发一个简单应用,这类应用也很易于调试,只需要简单运行此应用,用 Selenium 链接 UI 就可以完成端到端测试。单体式应用也易于部署,只需要把打包应用拷贝到服务器端,通过在负载均衡器后端运行多个拷贝就可以轻松实现应用扩展。在早期这 类应用运行的很好。 -# 单体式应用的不足 +## 单体式应用的不足 不幸的是,这种简单方法却有很大的局限性。一个简单的应用会随着时间推移逐渐变大。在每次的 sprint 中, 开发团队都会面对新“故事”,然后开发许多新代码。几年后,这个小而简单的应用会变成了一个巨大的怪物。这儿有一个例子,我最近和一个开发者讨论,他正在 写一个工具,用来分析他们一个拥有数百万行代码的应用中 JAR 文件之间的依赖关系。我很确信这个代码正是很多开发者经过多年努力开发出来的一个怪物。 @@ -30,7 +32,7 @@ 那么如何应对呢? -# 微处理架构——处理复杂事物 +## 微处理架构——处理复杂事物 许多公司,比如 Amazon、eBay 和 NetFlix,通过采用微处理结构模式解决了上述问题。其思路不是开发一个巨大的单体式的应用,而是将应用分解为小的、互相连接的微服务。 @@ -64,7 +66,7 @@ 表面上看来,微服务架构模式有点像 SOA,他们都由多个服务构成。但是,可以从另外一个角度看此问题,微服务架构模式是一个不包含 Web 服务 (WS-)和 ESB 服务的 SOA。微服务应用乐于采用简单轻量级协议,比如 REST,而不是 WS-,在微服务内部避免使用 ESB 以及 ESB 类似功能。微服 务架构模式也拒绝使用 canonical schema 等 SOA 概念。 -# 微服务架构的好处 +## 微服务架构的好处 微服务架构模式有很多好处。首先,通过分解巨大单体式应用为多个服务方法解决了复杂性问题。在功能不变的情况下,应用 被分解为多个可管理的分支或服务。每个服务都有一个用 RPC-或者消息驱动 API 定义清楚的边界。微服务架构模式给采用单体式编码方式很难实现的功能提供 了模块化的解决方案,由此,单个服务很容易开发、理解和维护。 @@ -74,7 +76,7 @@ 最后,微服务架构模式使得每个服务独立扩展。你可以根据每个服务的规模来部署满足需求的规模。甚至于,你可以使用更适合于服务资源需求的硬件。比 如,你可以在 EC2 Compute Optimized instances 上部署 CPU 敏感的服务,而在 EC2 memory-optimized instances 上部署内存数据库。 -# 微服务架构的不足 +## 微服务架构的不足 Fred Brooks 在 30 年前写道,“there are no silver bullets”,像任何其它科技一样,微服务架构也有不足。其中一个跟他的名字类似,『微服务』强调了服务大小,实际上,有一些开发者鼓吹建立稍微大一 些的,10-100 LOC 服务组。尽管小服务更乐于被采用,但是不要忘了这只是终端的选择而不是最终的目的。微服务的目的是有效的拆分应用,实现敏捷开发和部署。 @@ -90,6 +92,6 @@ Fred Brooks 在 30 年前写道,“there are no silver bullets”,像任何 一种自动化方法是使用 PaaS 服务,例如 Cloud Foundry。 PaaS 给开发者提供一个部署和管理微服务的简单方法,它把所有这些问题都打包内置解决了。同时,配置 PaaS 的系统和网络专家可以采用最佳实践和策略来 简化这些问题。另外一个自动部署微服务应用的方法是开发对于你来说最基础的 PaaS 系统。一个典型的开始点是使用一个集群化方案,比如配合 Docker 使 用 Mesos 或者 Kubernetes。后面的系列我们会看看如何基于软件部署方法例如 NGINX,可以方便的在微服务层面提供缓存、权限控制、API 统 计和监控。 -# 总结 +## 总结 构建复杂的应用真的是非常困难。单体式的架构更适合轻量级的简单应用。如果你用它来开发复杂应用,那真的会很糟糕。微服务架构模式可以用来构建复杂应用,当然,这种架构模型也有自己的缺点和挑战。 diff --git a/docs/micro-services/choose-microservice-deployment-strategy.md b/docs/micro-services/choose-microservice-deployment-strategy.md index 1fdbafbe..0795d5d0 100644 --- a/docs/micro-services/choose-microservice-deployment-strategy.md +++ b/docs/micro-services/choose-microservice-deployment-strategy.md @@ -1,4 +1,6 @@ -# 前言 +# 选择微服务部署策略 + +## 前言 部署一个单体式应用意味运行大型应用的多个副本,典型的提供若干个(N)服务器(物理或者虚拟),运行若干个(M)个应用实例。部署单体式应用不会很直接,但是肯定比部署微服务应用简单些。 @@ -6,7 +8,7 @@ 有一些微服务部署的模式,先讨论一下每个主机多服务实例的模式。 -# 单主机多服务实例模式 +## 单主机多服务实例模式 部署微服务的一种方法就是单主机多服务实例模式,使用这种模式,需要提供若干台物理或者虚拟机,每台机器上运行多个服务实例。很多情况下,这是传统的应用部署方法。每个服务实例运行一个或者多个主机的 well-known 端口,主机可以看做宠物。 @@ -32,11 +34,11 @@ 可以看到,尽管熟悉,但是单主机多服务实例有很多严重缺陷。下面看看是否有其他部署微服务方式能够避免这些问题。 -# 单主机单服务实例模式 +## 单主机单服务实例模式 另外一种部署微服务方式是单主机单实例模式。当使用这种模式,每个主机上服务实例都是各自独立的。有两种不同实现模式:单虚拟机单实例和单容器单实例。 -## 单虚拟机单实例模式 +### 单虚拟机单实例模式 但是用单虚拟机单实例模式,一般将服务打包成虚拟机映像(image),例如一个 Amazon EC2 AMI。每个服务实例是一个使用此映像启动的 VM(例如,EC2 实例)。下图展示了此架构: @@ -66,7 +68,7 @@ CloudNative 公司有一个用于创建 EC2 AMI 的 SaaS 应用,Bakery。用 那么我们来看看另外一种仍然具有虚机特性,但是比较轻量的微服务部署方法。 -# 单容器单服务实例模式 +## 单容器单服务实例模式 当使用这种模式时,每个服务实例都运行在各自容器中。容器是运行在操作系统层面的虚拟化机制。一个容器包含若干运行在沙箱中的进程。从进程角度来看,他们有各自的命名空间和根文件系统;可以限制容器的内存和 CPU 资源。某些容器还具有 I/O 限制,这类容器技术包括 Docker 和 Solaris Zones。 @@ -92,7 +94,7 @@ CloudNative 公司有一个用于创建 EC2 AMI 的 SaaS 应用,Bakery。用 除了这些之外,server-less 部署技术,避免了前述容器和 VM 技术的缺陷,吸引了越来越多的注意。下面我们来看看。 -# Serverless 部署 +## Serverless 部署 AWS Lambda 是 serverless 部署技术的例子,支持 Java,Node.js 和 Python 服务;需要将服务打包成 ZIP 文件上载到 AWS Lambda 就可以部署。可以提供元数据,提供处理服务请求函数的名字(一个事件)。AWS Lambda 自动运行处理请求足够多的微服务,然而只根据运行时间和消耗内存量来计费。当然细节决定成败,AWS Lambda 也有限制。但是大家都不需要担心服务器,虚拟机或者容器内的任何方面绝对吸引人。 diff --git a/docs/micro-services/event-driven-data-management-for-microservices.md b/docs/micro-services/event-driven-data-management-for-microservices.md index 13023632..e4edbcda 100644 --- a/docs/micro-services/event-driven-data-management-for-microservices.md +++ b/docs/micro-services/event-driven-data-management-for-microservices.md @@ -1,13 +1,15 @@ -# 1.1 微服务和分布式数据管理问题 +# 微服务的事件驱动数据管理 + +## 1.1 微服务和分布式数据管理问题 单体式应用一般都会有一个关系型数据库,由此带来的好处是应用可以使用 ACID transactions,可以带来一些重要的操作特性: 1. 原子性 – 任何改变都是原子性的 2. 一致性 – 数据库状态一直是一致性的 -3. 隔离性 – 即使交易并发执行,看起来也是串行的 -4. Durable – 一旦交易提交了就不可回滚 +3. 隔离性 – 即使事务并发执行,看起来也是串行的 +4. Durable – 一旦事务提交了就不可回滚 -鉴于以上特性,应用可以简化为:开始一个交易,改变(插入,删除,更新)很多行,然后提交这些交易。 +鉴于以上特性,应用可以简化为:开始一个事务,改变(插入,删除,更新)很多行,然后提交这些事务。 使用关系型数据库带来另外一个优势在于提供 SQL(功能强大,可声明的,表转化的查询语言)支持。用户可以非常容易通过查询将多个表的数据组合起来,RDBMS 查询调度器决定最佳实现方式,用户不需要担心例如如何访问数据库等底层问题。另外,因为所有应用的数据都在一个数据库中,很容易去查询。 @@ -27,7 +29,7 @@ 第二个挑战是如何完成从多个服务中搜索数据。例如,设想应用需要显示客户和他的订单。如果订单服务提供 API 来接受用户订单信息,那么用户可以使用类应用型的 join 操作接收数据。应用从用户服务接受用户信息,从订单服务接受此用户订单。假设,订单服务只支持通过私有键(key)来查询订单(也许是在使用只支持基于主键接受的 NoSQL 数据库),此时,没有合适的方法来接收所需数据。 -# 1.2 事件驱动架构 +## 1.2 事件驱动架构 对许多应用来说,这个解决方案就是使用事件驱动架构(event-driven architecture)。在这种架构中,当某件重要事情发生时,微服务会发布一个事件,例如更新一个业务实体。当订阅这些事件的微服务接收此事件时,就可以更新自己的业务实体,也可能会引发更多的事件发布。 @@ -47,7 +49,7 @@ 更复杂的场景可以引入更多步骤,例如在检查用户信用的同时预留库存等。 -考虑到(a)每个服务原子性更新数据库和发布事件,然后,(b)消息代理确保事件传递至少一次,然后可以跨多个服务完成业务交易(此交易不是 ACID 交易)。这种模式提供弱确定性,例如最终一致性 eventual consistency。这种交易类型被称作 BASE model。 +考虑到(a)每个服务原子性更新数据库和发布事件,然后,(b)消息代理确保事件传递至少一次,然后可以跨多个服务完成业务交易(此交易不是 ACID 事务)。这种模式提供弱确定性,例如最终一致性 eventual consistency。这种交易类型被称作 BASE model。 亦可以使用事件来维护不同微服务拥有数据预连接(pre-join)的实现视图。维护此视图的服务订阅相关事件并且更新视图。例如,客户订单视图更新服务(维护客户订单视图)会订阅由客户服务和订单服务发布的事件。 @@ -55,13 +57,13 @@ 当客户订单视图更新服务收到客户或者订单事件,就会更新 客户订单视图数据集。可以使用文档数据库(例如 MongoDB)来实现客户订单视图,为每个用户存储一个文档。客户订单视图查询服务负责响应对客户以及最近订单(通过查询客户订单视图数据集)的查询。 -事件驱动架构也是既有优点也有缺点,此架构可以使得交易跨多个服务且提供最终一致性,并且可以使应用维护最终视图;而缺点在于编程模式比 ACID 交易模式更加复杂:为了从应用层级失效中恢复,还需要完成补偿性交易,例如,如果信用检查不成功则必须取消订单;另外,应用必须应对不一致的数据,这是因为临时(in-flight)交易造成的改变是可见的,另外当应用读取未更新的最终视图时也会遇见数据不一致问题。另外一个缺点在于订阅者必须检测和忽略冗余事件。 +事件驱动架构也是既有优点也有缺点,此架构可以使得事务跨多个服务且提供最终一致性,并且可以使应用维护最终视图;而缺点在于编程模式比 ACID 事务模式更加复杂:为了从应用层级失效中恢复,还需要完成补偿性事务,例如,如果信用检查不成功则必须取消订单;另外,应用必须应对不一致的数据,这是因为临时(in-flight)事务造成的改变是可见的,另外当应用读取未更新的最终视图时也会遇见数据不一致问题。另外一个缺点在于订阅者必须检测和忽略冗余事件。 -# 1.3 原子操作 Achieving Atomicity +## 1.3 原子操作 Achieving Atomicity -事件驱动架构还会碰到数据库更新和发布事件原子性问题。例如,订单服务必须向 ORDER 表插入一行,然后发布 Order Created event,这两个操作需要原子性。如果更新数据库后,服务瘫了(crashes)造成事件未能发布,系统变成不一致状态。确保原子操作的标准方式是使用一个分布式交易,其中包括数据库和消息代理。然而,基于以上描述的 CAP 理论,这却并不是我们想要的。 +事件驱动架构还会碰到数据库更新和发布事件原子性问题。例如,订单服务必须向 ORDER 表插入一行,然后发布 Order Created event,这两个操作需要原子性。如果更新数据库后,服务瘫了(crashes)造成事件未能发布,系统变成不一致状态。确保原子操作的标准方式是使用一个分布式事务,其中包括数据库和消息代理。然而,基于以上描述的 CAP 理论,这却并不是我们想要的。 -## 1.3.1 使用本地交易发布事件 +### 1.3.1 使用本地事务发布事件 获得原子性的一个方法是对发布事件应用采用 multi-step process involving only local transactions,技巧在于一个 EVENT 表,此表在存储业务实体数据库中起到消息列表功能。应用发起一个(本地)数据库交易,更新业务实体状态,向 EVENT 表中插入一个事件,然后提交此次交易。另外一个独立应用进程或者线程查询此 EVENT 表,向消息代理发布事件,然后使用本地交易标志此事件为已发布,如下图所示: @@ -73,7 +75,7 @@ 此方法因为应用采用了本地交易更新状态和发布事件而不需要 2PC,现在再看看另外一种应用简单更新状态获得原子性的方法。 -## 1.3.2 挖掘数据库交易日志 +### 1.3.2 挖掘数据库交易日志 另外一种不需要 2PC 而获得线程或者进程发布事件原子性的方式就是挖掘数据库交易或者提交日志。应用更新数据库,在数据库交易日志中产生变化,交易日志挖掘进程或者线程读这些交易日志,将日志发布给消息代理。如下图所见: @@ -87,7 +89,7 @@ 交易日志挖掘方法通过应用直接更新数据库而不需要 2PC 介入。下面我们再看一种完全不同的方法:不需要更新只依赖事件的方法。 -## 1.3.3 使用事件源 +### 1.3.3 使用事件源 Event sourcing (事件源)通过使用根本不同的事件中心方式来获得不需 2PC 的原子性,保证业务实体的一致性。 这种应用保存业务实体一系列状态改变事件,而不是存储实体现在的状态。应用可以通过重放事件来重建实体现在状态。只要业务实体发生变化,新事件就会添加到时间表中。因为保存事件是单一操作,因此肯定是原子性的。 @@ -103,8 +105,8 @@ Event sourcing (事件源)通过使用根本不同的事件中心方式来 事件源方法也有不少缺点,因为采用不同或者不太熟悉的变成模式,使得重新学习不太容易;事件存储只支持主键查询业务实体,必须使用 Command Query Responsibility Segregation (CQRS) 来完成查询业务,因此,应用必须处理最终一致数据。 -# 1.4 总结 +## 1.4 总结 -在微服务架构中,每个微服务都有自己私有的数据集。不同微服务可能使用不同的 SQL 或者 NoSQL 数据库。尽管数据库架构有很强的优势,但是也面对数据分布式管理的挑战。第一个挑战就是如何在多服务之间维护业务交易一致性;第二个挑战是如何从多服务环境中获取一致性数据。 +在微服务架构中,每个微服务都有自己私有的数据集。不同微服务可能使用不同的 SQL 或者 NoSQL 数据库。尽管数据库架构有很强的优势,但是也面对数据分布式管理的挑战。第一个挑战就是如何在多服务之间维护业务事务一致性;第二个挑战是如何从多服务环境中获取一致性数据。 最佳解决办法是采用事件驱动架构。其中碰到的一个挑战是如何原子性的更新状态和发布事件。有几种方法可以解决此问题,包括将数据库视为消息队列、交易日志挖掘和事件源。 diff --git a/docs/micro-services/how-eureka-enable-service-discovery-and-service-registration.md b/docs/micro-services/how-eureka-enable-service-discovery-and-service-registration.md index 9dbfc8f1..fe5b0503 100644 --- a/docs/micro-services/how-eureka-enable-service-discovery-and-service-registration.md +++ b/docs/micro-services/how-eureka-enable-service-discovery-and-service-registration.md @@ -48,7 +48,7 @@ At Netflix, Eureka is used for the following purposes apart from playing a criti ### 服务续约(Renew) -服务续约会由服务提供者(比如 `Demo` 中的 `service-provider` )定期调用,类似于心跳,用来告知注册中心 `Eureka Server` 自己的状态,避免被 `Eureka Server` 认为服务时效将其剔除下线。服务续约就是发送一个 `PUT` 请求带上当前实例信息到类 `InstanceResource` 的 `renewLease` 方法进行服务续约操作。 +服务续约会由服务提供者(比如 `Demo` 中的 `service-provider` )定期调用,类似于心跳,用来告知注册中心 `Eureka Server` 自己的状态,避免被 `Eureka Server` 认为服务失效将其剔除下线。服务续约就是发送一个 `PUT` 请求带上当前实例信息到类 `InstanceResource` 的 `renewLease` 方法进行服务续约操作。 ![eureka-server-instanceresource-renew.png](./images/eureka-server-instanceresource-renew.png) diff --git a/docs/micro-services/microservices-introduction.md b/docs/micro-services/microservices-introduction.md index 18c0a606..27c488b1 100644 --- a/docs/micro-services/microservices-introduction.md +++ b/docs/micro-services/microservices-introduction.md @@ -1,4 +1,4 @@ -# 微服务 +# 关于微服务架构的描述 > 翻译自 [Martin Fowler](https://martinfowler.com/) 网站 [Microservices](https://martinfowler.com/articles/microservices.html) 一文。文章篇幅较长,阅读需要一点耐心。
本人水平有限,若有不妥之处,还请各位帮忙指正,谢谢。 diff --git a/docs/micro-services/migrating-from-a-monolithic-architecture-to-a-microservices-architecture.md b/docs/micro-services/migrating-from-a-monolithic-architecture-to-a-microservices-architecture.md index 9c616c46..9484ebfe 100644 --- a/docs/micro-services/migrating-from-a-monolithic-architecture-to-a-microservices-architecture.md +++ b/docs/micro-services/migrating-from-a-monolithic-architecture-to-a-microservices-architecture.md @@ -12,7 +12,7 @@ Martin Fowler 将这种现代化策略成为绞杀(Strangler)应用,名字 我们来看看其他可行策略。 -# 策略 1——停止挖掘 +## 策略 1——停止挖掘 Law of Holes 是说当自己进洞就应该停止挖掘。对于单体式应用不可管理时这是最佳建议。换句话说,应该停止让单体式应用继续变大,也就是说当开发新功能时不应该为旧单体应用添加新代码,最佳方法应该是将新功能开发成独立微服务。如下图所示: @@ -34,7 +34,7 @@ Law of Holes 是说当自己进洞就应该停止挖掘。对于单体式应用 然而,这方法并不解决任何单体式本身问题,为了解决单体式本身问题必须深入单体应用 ​ 做出改变。我们来看看这么做的策略。 -# 策略 2——将前端和后端分离 +## 策略 2——将前端和后端分离 减小单体式应用复杂度的策略是讲表现层和业务逻辑、数据访问层分开。典型的企业应用至少有三个不同元素构成: @@ -52,11 +52,11 @@ Law of Holes 是说当自己进洞就应该停止挖掘。对于单体式应用 然而,这种策略只是部分的解决方案。很可能应用的两部分之一或者全部都是不可管理的,因此需要使用第三种策略来消除剩余的单体架构。 -# 策略 3——抽出服务 +## 策略 3——抽出服务 第三种迁移策略就是从单体应用中抽取出某些模块成为独立微服务。每当抽取一个模块变成微服务,单体应用就变简单一些;一旦转换足够多的模块,单体应用本身已经不成为问题了,要么消失了,要么简单到成为一个服务。 -## 排序那个模块应该被转成微服务 +### 排序那个模块应该被转成微服务 一个巨大的复杂单体应用由成十上百个模块构成,每个都是被抽取对象。决定第一个被抽取模块一般都是挑战,一般最好是从最容易抽取的模块开始,这会让开发者积累足够经验,这些经验可以为后续模块化工作带来巨大好处。 @@ -66,7 +66,7 @@ Law of Holes 是说当自己进洞就应该停止挖掘。对于单体式应用 查找现有粗粒度边界来决定哪个模块应该被抽取,也是很有益的,这使得移植工作更容易和简单。例如,只与其他应用异步同步消息的模块就是一个明显边界,可以很简单容易地将其转换为微服务。 -## 如何抽取模块 +### 如何抽取模块 抽取模块第一步就是定义好模块和单体应用之间粗粒度接口,由于单体应用需要微服务的数据,反之亦然,因此更像是一个双向 API。因为必须在负责依赖关系和细粒度接口模式之间做好平衡,因此开发这种 API 很有挑战性,尤其对使用域模型模式的业务逻辑层来说更具有挑战,因此经常需要改变代码来解决依赖性问题,如图所示: diff --git a/docs/public/favicon-16x16.png b/docs/public/favicon-16x16.png new file mode 100644 index 00000000..d0010b98 Binary files /dev/null and b/docs/public/favicon-16x16.png differ diff --git a/docs/public/favicon-32x32.png b/docs/public/favicon-32x32.png new file mode 100644 index 00000000..292ae859 Binary files /dev/null and b/docs/public/favicon-32x32.png differ diff --git a/docs/public/icon.png b/docs/public/icon.png new file mode 100644 index 00000000..1de9f42e Binary files /dev/null and b/docs/public/icon.png differ diff --git a/images/favicon-16x16.png b/images/favicon-16x16.png index c846c93f..d0010b98 100644 Binary files a/images/favicon-16x16.png and b/images/favicon-16x16.png differ diff --git a/images/favicon-32x32.png b/images/favicon-32x32.png index 2d79130a..292ae859 100644 Binary files a/images/favicon-32x32.png and b/images/favicon-32x32.png differ diff --git a/images/icon.png b/images/icon.png index a39d36b6..1de9f42e 100644 Binary files a/images/icon.png and b/images/icon.png differ diff --git a/images/qrcode-for-doocs.jpg b/images/qrcode-for-doocs.jpg index 56c85c36..487ce6b2 100644 Binary files a/images/qrcode-for-doocs.jpg and b/images/qrcode-for-doocs.jpg differ diff --git a/images/qrcode-for-yanglbme.jpg b/images/qrcode-for-yanglbme.jpg index 6a39ef38..cfbb3444 100644 Binary files a/images/qrcode-for-yanglbme.jpg and b/images/qrcode-for-yanglbme.jpg differ diff --git a/images/starcharts.svg b/images/starcharts.svg index 77b53727..294d232c 100644 --- a/images/starcharts.svg +++ b/images/starcharts.svg @@ -1,4 +1,4 @@ - + \n2018-11-232019-05-192019-11-122020-05-072020-10-312021-04-262021-10-202022-04-152022-10-09Time2018-11-232019-10-062020-08-192021-07-032022-05-162023-03-302024-02-112024-12-252025-11-07Time0840016800252003350041900503005870067000Stargazers990019800297003950049400593006920079000Stargazers \ No newline at end of file +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 944 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 945 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 946 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 947 13 +L 948 13 +L 948 13 +L 948 13 +L 948 13 +L 948 13 +L 948 13 +L 948 13 +L 948 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 949 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13 +L 950 13" style="stroke-width:2;stroke:rgba(129,199,239,1.0);fill:none"/> \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index c3579316..00000000 --- a/index.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - 互联网 Java 工程师进阶知识完全扫盲 - Doocs 技术社区 - - - - - - - - - - - - - -
本系列知识由 Doocs 技术社区总结发布
- - - - - - - - - - - - - - \ No newline at end of file diff --git a/package.json b/package.json index f75a7b18..dc0d8f37 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,16 @@ { - "scripts": { - "dev": "docsify serve --open", - "convert": "docsify-pdf-converter" - }, "name": "advanced-java", - "description": "互联网 Java 工程师进阶知识完全扫盲@doocs,https://github.com/doocs/advanced-java", - "version": "1.0.0", - "main": ".docsifytopdfrc.js", - "directories": { - "doc": "docs" - }, - "repository": { - "type": "git", - "url": "https://github.com/doocs/advanced-java.git" + "private": true, + "packageManager": "pnpm@9.12.3", + "scripts": { + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs" }, - "author": "yanglbme", - "license": "CC-BY-SA-4.0", - "bugs": { - "url": "https://github.com/doocs/advanced-java/issues" + "dependencies": { + "vitepress-plugin-comment-with-giscus": "^1.1.15" }, - "homepage": "https://github.com/doocs/advanced-java#readme", "devDependencies": { - "docsify-pdf-converter": "^2.0.7" + "vitepress": "2.0.0-alpha.17" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..29a62b99 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1460 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + vitepress-plugin-comment-with-giscus: + specifier: ^1.1.15 + version: 1.1.15(vue@3.5.32) + devDependencies: + vitepress: + specifier: 2.0.0-alpha.17 + version: 2.0.0-alpha.17(postcss@8.5.10) + +packages: + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@docsearch/css@4.6.2': + resolution: {integrity: sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==} + + '@docsearch/js@4.6.2': + resolution: {integrity: sha512-qj1yoxl3y4GKoK7+VM6fq/rQqPnvUmg3IKzJ9x0VzN14QVzdB/SG/J6VfV1BWT5RcPUFxIcVwoY1fwHM2fSRRw==} + + '@docsearch/sidepanel-js@4.6.2': + resolution: {integrity: sha512-Pni85AP/GwRj7fFg8cBJp0U04tzbueBvWSd3gysgnOsVnQVSZwSYncfErUScLE1CAtR+qocPDFjmYR9AMRNJtQ==} + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@giscus/vue@2.4.0': + resolution: {integrity: sha512-QOxKHgsMT91myyQagP2v20YYAei1ByZuc3qcaYxbHx4AwOeyVrybDIuRFwG9YDv6OraC86jYnU4Ixd37ddC/0A==} + peerDependencies: + vue: '>=3.2.0' + + '@iconify-json/simple-icons@1.2.78': + resolution: {integrity: sha512-I3lkNp0Qu7q2iZWkdcf/I2hqGhzK6qxdILh9T7XqowQrnpmG/BayDsiCf6PktDoWlW0U971xA5g+panm+NFrfQ==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@lit-labs/ssr-dom-shim@1.5.1': + resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==} + + '@lit/reactive-element@2.1.2': + resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} + + '@rolldown/pluginutils@1.0.0-rc.13': + resolution: {integrity: sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==} + + '@rollup/rollup-android-arm-eabi@4.60.1': + resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.1': + resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.1': + resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.1': + resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.1': + resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.1': + resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': + resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.60.1': + resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.60.1': + resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.60.1': + resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.60.1': + resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.1': + resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.60.1': + resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.1': + resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.60.1': + resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.60.1': + resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.60.1': + resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.60.1': + resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.60.1': + resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.60.1': + resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.1': + resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.1': + resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.1': + resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.1': + resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.1': + resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} + cpu: [x64] + os: [win32] + + '@shikijs/core@3.23.0': + resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} + + '@shikijs/engine-javascript@3.23.0': + resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} + + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} + + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + + '@shikijs/transformers@3.23.0': + resolution: {integrity: sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==} + + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitejs/plugin-vue@6.0.6': + resolution: {integrity: sha512-u9HHgfrq3AjXlysn0eINFnWQOJQLO9WN6VprZ8FXl7A2bYisv3Hui9Ij+7QZ41F/WYWarHjwBbXtD7dKg3uxbg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + + '@vue/compiler-core@3.5.32': + resolution: {integrity: sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==} + + '@vue/compiler-dom@3.5.32': + resolution: {integrity: sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==} + + '@vue/compiler-sfc@3.5.32': + resolution: {integrity: sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==} + + '@vue/compiler-ssr@3.5.32': + resolution: {integrity: sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==} + + '@vue/devtools-api@8.1.1': + resolution: {integrity: sha512-bsDMJ07b3GN1puVwJb/fyFnj/U2imyswK5UQVLZwVl7O05jDrt6BHxeG5XffmOOdasOj/bOmIjxJvGPxU7pcqw==} + + '@vue/devtools-kit@8.1.1': + resolution: {integrity: sha512-gVBaBv++i+adg4JpH71k9ppl4soyR7Y2McEqO5YNgv0BI1kMZ7BDX5gnwkZ5COYgiCyhejZG+yGNrBAjj6Coqg==} + + '@vue/devtools-shared@8.1.1': + resolution: {integrity: sha512-+h4ttmJYl/txpxHKaoZcaKpC+pvckgLzIDiSQlaQ7kKthKh8KuwoLW2D8hPJEnqKzXOvu15UHEoGyngAXCz0EQ==} + + '@vue/reactivity@3.5.32': + resolution: {integrity: sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==} + + '@vue/runtime-core@3.5.32': + resolution: {integrity: sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==} + + '@vue/runtime-dom@3.5.32': + resolution: {integrity: sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==} + + '@vue/server-renderer@3.5.32': + resolution: {integrity: sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==} + peerDependencies: + vue: 3.5.32 + + '@vue/shared@3.5.32': + resolution: {integrity: sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==} + + '@vueuse/core@14.2.1': + resolution: {integrity: sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ==} + peerDependencies: + vue: ^3.5.0 + + '@vueuse/integrations@14.2.1': + resolution: {integrity: sha512-2LIUpBi/67PoXJGqSDQUF0pgQWpNHh7beiA+KG2AbybcNm+pTGWT6oPGlBgUoDWmYwfeQqM/uzOHqcILpKL7nA==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 || ^8 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 || ^8 + vue: ^3.5.0 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@14.2.1': + resolution: {integrity: sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw==} + + '@vueuse/shared@14.2.1': + resolution: {integrity: sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw==} + peerDependencies: + vue: ^3.5.0 + + birpc@2.9.0: + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + focus-trap@8.0.1: + resolution: {integrity: sha512-9ptSG6z51YQOstI/oN4XuVGP/03u2nh0g//qz7L6zX0i6PZiPnkcf3GenXq7N2hZnASXaMxTPpbKwdI+PFvxlw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + giscus@1.6.0: + resolution: {integrity: sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + lit-element@4.2.2: + resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} + + lit-html@3.3.2: + resolution: {integrity: sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==} + + lit@3.3.2: + resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + minisearch@7.2.0: + resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.5: + resolution: {integrity: sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==} + + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.10: + resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} + engines: {node: ^10 || ^12 || >=14} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + rollup@4.60.1: + resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + shiki@3.23.0: + resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@7.3.2: + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitepress-plugin-comment-with-giscus@1.1.15: + resolution: {integrity: sha512-1DJjgN+7SYvn5ZkjuSXPmz7nlqfcrh4qCGGviiZghA2ELXnaO2m9WY7m+RisPSaqCn90xqe0JbO2T4NMq8iUBg==} + + vitepress@2.0.0-alpha.17: + resolution: {integrity: sha512-Z3VPUpwk/bHYqt1uMVOOK1/4xFiWQov1GNc2FvMdz6kvje4JRXEOngVI9C+bi5jeedMSHiA4dwKkff1NCvbZ9Q==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + oxc-minify: '*' + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + oxc-minify: + optional: true + postcss: + optional: true + + vue@3.5.32: + resolution: {integrity: sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@docsearch/css@4.6.2': {} + + '@docsearch/js@4.6.2': {} + + '@docsearch/sidepanel-js@4.6.2': {} + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@giscus/vue@2.4.0(vue@3.5.32)': + dependencies: + giscus: 1.6.0 + vue: 3.5.32 + + '@iconify-json/simple-icons@1.2.78': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@lit-labs/ssr-dom-shim@1.5.1': {} + + '@lit/reactive-element@2.1.2': + dependencies: + '@lit-labs/ssr-dom-shim': 1.5.1 + + '@rolldown/pluginutils@1.0.0-rc.13': {} + + '@rollup/rollup-android-arm-eabi@4.60.1': + optional: true + + '@rollup/rollup-android-arm64@4.60.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.1': + optional: true + + '@rollup/rollup-darwin-x64@4.60.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.1': + optional: true + + '@shikijs/core@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.5 + + '@shikijs/engine-oniguruma@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/themes@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/transformers@3.23.0': + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/types': 3.23.0 + + '@shikijs/types@3.23.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + + '@types/trusted-types@2.0.7': {} + + '@types/unist@3.0.3': {} + + '@types/web-bluetooth@0.0.21': {} + + '@ungap/structured-clone@1.3.0': {} + + '@vitejs/plugin-vue@6.0.6(vite@7.3.2)(vue@3.5.32)': + dependencies: + '@rolldown/pluginutils': 1.0.0-rc.13 + vite: 7.3.2 + vue: 3.5.32 + + '@vue/compiler-core@3.5.32': + dependencies: + '@babel/parser': 7.29.2 + '@vue/shared': 3.5.32 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.32': + dependencies: + '@vue/compiler-core': 3.5.32 + '@vue/shared': 3.5.32 + + '@vue/compiler-sfc@3.5.32': + dependencies: + '@babel/parser': 7.29.2 + '@vue/compiler-core': 3.5.32 + '@vue/compiler-dom': 3.5.32 + '@vue/compiler-ssr': 3.5.32 + '@vue/shared': 3.5.32 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.10 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.32': + dependencies: + '@vue/compiler-dom': 3.5.32 + '@vue/shared': 3.5.32 + + '@vue/devtools-api@8.1.1': + dependencies: + '@vue/devtools-kit': 8.1.1 + + '@vue/devtools-kit@8.1.1': + dependencies: + '@vue/devtools-shared': 8.1.1 + birpc: 2.9.0 + hookable: 5.5.3 + perfect-debounce: 2.1.0 + + '@vue/devtools-shared@8.1.1': {} + + '@vue/reactivity@3.5.32': + dependencies: + '@vue/shared': 3.5.32 + + '@vue/runtime-core@3.5.32': + dependencies: + '@vue/reactivity': 3.5.32 + '@vue/shared': 3.5.32 + + '@vue/runtime-dom@3.5.32': + dependencies: + '@vue/reactivity': 3.5.32 + '@vue/runtime-core': 3.5.32 + '@vue/shared': 3.5.32 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.32(vue@3.5.32)': + dependencies: + '@vue/compiler-ssr': 3.5.32 + '@vue/shared': 3.5.32 + vue: 3.5.32 + + '@vue/shared@3.5.32': {} + + '@vueuse/core@14.2.1(vue@3.5.32)': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.2.1 + '@vueuse/shared': 14.2.1(vue@3.5.32) + vue: 3.5.32 + + '@vueuse/integrations@14.2.1(focus-trap@8.0.1)(vue@3.5.32)': + dependencies: + '@vueuse/core': 14.2.1(vue@3.5.32) + '@vueuse/shared': 14.2.1(vue@3.5.32) + vue: 3.5.32 + optionalDependencies: + focus-trap: 8.0.1 + + '@vueuse/metadata@14.2.1': {} + + '@vueuse/shared@14.2.1(vue@3.5.32)': + dependencies: + vue: 3.5.32 + + birpc@2.9.0: {} + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + comma-separated-tokens@2.0.3: {} + + csstype@3.2.3: {} + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + entities@7.0.1: {} + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + estree-walker@2.0.2: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + focus-trap@8.0.1: + dependencies: + tabbable: 6.4.0 + + fsevents@2.3.3: + optional: true + + giscus@1.6.0: + dependencies: + lit: 3.3.2 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hookable@5.5.3: {} + + html-void-elements@3.0.0: {} + + lit-element@4.2.2: + dependencies: + '@lit-labs/ssr-dom-shim': 1.5.1 + '@lit/reactive-element': 2.1.2 + lit-html: 3.3.2 + + lit-html@3.3.2: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@3.3.2: + dependencies: + '@lit/reactive-element': 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.2 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + mark.js@8.11.1: {} + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + minisearch@7.2.0: {} + + nanoid@3.3.11: {} + + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.5: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.1.0 + regex-recursion: 6.0.2 + + perfect-debounce@2.1.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss@8.5.10: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + property-information@7.1.0: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + rollup@4.60.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.1 + '@rollup/rollup-android-arm64': 4.60.1 + '@rollup/rollup-darwin-arm64': 4.60.1 + '@rollup/rollup-darwin-x64': 4.60.1 + '@rollup/rollup-freebsd-arm64': 4.60.1 + '@rollup/rollup-freebsd-x64': 4.60.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.1 + '@rollup/rollup-linux-arm-musleabihf': 4.60.1 + '@rollup/rollup-linux-arm64-gnu': 4.60.1 + '@rollup/rollup-linux-arm64-musl': 4.60.1 + '@rollup/rollup-linux-loong64-gnu': 4.60.1 + '@rollup/rollup-linux-loong64-musl': 4.60.1 + '@rollup/rollup-linux-ppc64-gnu': 4.60.1 + '@rollup/rollup-linux-ppc64-musl': 4.60.1 + '@rollup/rollup-linux-riscv64-gnu': 4.60.1 + '@rollup/rollup-linux-riscv64-musl': 4.60.1 + '@rollup/rollup-linux-s390x-gnu': 4.60.1 + '@rollup/rollup-linux-x64-gnu': 4.60.1 + '@rollup/rollup-linux-x64-musl': 4.60.1 + '@rollup/rollup-openbsd-x64': 4.60.1 + '@rollup/rollup-openharmony-arm64': 4.60.1 + '@rollup/rollup-win32-arm64-msvc': 4.60.1 + '@rollup/rollup-win32-ia32-msvc': 4.60.1 + '@rollup/rollup-win32-x64-gnu': 4.60.1 + '@rollup/rollup-win32-x64-msvc': 4.60.1 + fsevents: 2.3.3 + + shiki@3.23.0: + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/engine-javascript': 3.23.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + tabbable@6.4.0: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + trim-lines@3.0.1: {} + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@7.3.2: + dependencies: + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.10 + rollup: 4.60.1 + tinyglobby: 0.2.16 + optionalDependencies: + fsevents: 2.3.3 + + vitepress-plugin-comment-with-giscus@1.1.15(vue@3.5.32): + dependencies: + '@giscus/vue': 2.4.0(vue@3.5.32) + transitivePeerDependencies: + - vue + + vitepress@2.0.0-alpha.17(postcss@8.5.10): + dependencies: + '@docsearch/css': 4.6.2 + '@docsearch/js': 4.6.2 + '@docsearch/sidepanel-js': 4.6.2 + '@iconify-json/simple-icons': 1.2.78 + '@shikijs/core': 3.23.0 + '@shikijs/transformers': 3.23.0 + '@shikijs/types': 3.23.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 6.0.6(vite@7.3.2)(vue@3.5.32) + '@vue/devtools-api': 8.1.1 + '@vue/shared': 3.5.32 + '@vueuse/core': 14.2.1(vue@3.5.32) + '@vueuse/integrations': 14.2.1(focus-trap@8.0.1)(vue@3.5.32) + focus-trap: 8.0.1 + mark.js: 8.11.1 + minisearch: 7.2.0 + shiki: 3.23.0 + vite: 7.3.2 + vue: 3.5.32 + optionalDependencies: + postcss: 8.5.10 + transitivePeerDependencies: + - '@types/node' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jiti + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - sass + - sass-embedded + - sortablejs + - stylus + - sugarss + - terser + - tsx + - typescript + - universal-cookie + - yaml + + vue@3.5.32: + dependencies: + '@vue/compiler-dom': 3.5.32 + '@vue/compiler-sfc': 3.5.32 + '@vue/runtime-dom': 3.5.32 + '@vue/server-renderer': 3.5.32(vue@3.5.32) + '@vue/shared': 3.5.32 + + zwitch@2.0.4: {} diff --git a/summary.md b/summary.md deleted file mode 100644 index 881feb09..00000000 --- a/summary.md +++ /dev/null @@ -1,145 +0,0 @@ -- 高并发架构 - - - [消息队列](/docs/high-concurrency/mq-interview.md) - - - [为什么使用消息队列?](/docs/high-concurrency/why-mq.md) - - [如何保证消息队列的高可用?](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md) - - [如何保证消息不被重复消费?](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md) - - [如何保证消息的可靠性传输?](/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md) - - [如何保证消息的顺序性?](/docs/high-concurrency/how-to-ensure-the-order-of-messages.md) - - [如何解决消息队列的延时以及过期失效问题?](/docs/high-concurrency/mq-time-delay-and-expired-failure.md) - - [如何设计一个消息队列?](/docs/high-concurrency/mq-design.md) - - - [搜索引擎](/docs/high-concurrency/es-introduction.md) - - - [ES 的分布式架构原理是什么?](/docs/high-concurrency/es-architecture.md) - - [ES 写入数据的工作原理是什么?](/docs/high-concurrency/es-write-query-search.md) - - [ES 在数十亿级别数量下如何提高查询效率?](/docs/high-concurrency/es-optimizing-query-performance.md) - - [ES 生产集群的部署架构是什么?](/docs/high-concurrency/es-production-cluster.md) - - - 缓存 - - - [在项目中缓存是如何使用的?](/docs/high-concurrency/why-cache.md) - - [Redis 和 Memcached 有什么区别?](/docs/high-concurrency/redis-single-thread-model.md) - - [Redis 都有哪些数据类型以及适用场景?](/docs/high-concurrency/redis-data-types.md) - - [Redis 的过期策略都有哪些?](/docs/high-concurrency/redis-expiration-policies-and-lru.md) - - [如何保证 Redis 高并发、高可用?](/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md) - - [Redis 主从架构是怎样的?](/docs/high-concurrency/redis-master-slave.md) - - [Redis 的持久化有哪几种方式?](/docs/high-concurrency/redis-persistence.md) - - [Redis 如何基于哨兵集群实现高可用?](/docs/high-concurrency/redis-sentinel.md) - - [Redis 集群模式的工作原理能说一下么?](/docs/high-concurrency/redis-cluster.md) - - [Redis 的雪崩、穿透和击穿,如何应对?](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md) - - [如何保证缓存与数据库双写一致性?](/docs/high-concurrency/redis-consistence.md) - - [如何解决 Redis 的并发竞争问题?](/docs/high-concurrency/redis-cas.md) - - [生产环境中的 Redis 是怎么部署的?](/docs/high-concurrency/redis-production-environment.md) - - - 分库分表 - - - [为什么要分库分表?](/docs/high-concurrency/database-shard.md) - - [分库分表如何平滑过渡?](/docs/high-concurrency/database-shard-method.md) - - [设计一个动态扩容缩容的分库分表方案?](/docs/high-concurrency/database-shard-dynamic-expand.md) - - [分库分表之后,id 主键如何处理?](/docs/high-concurrency/database-shard-global-id-generate.md) - - - 读写分离 - - - [如何实现 MySQL 的读写分离?](/docs/high-concurrency/mysql-read-write-separation.md) - - - 高并发系统 - - [如何设计一个高并发系统?](/docs/high-concurrency/high-concurrency-design.md) - -* 分布式系统 - - - [面试连环炮](/docs/distributed-system/distributed-system-interview.md) - - 系统拆分 - - - [为什么要进行系统拆分?](/docs/distributed-system/why-dubbo.md) - - - 分布式服务框架 - - - [说一下 Dubbo 的工作原理?](/docs/distributed-system/dubbo-operating-principle.md) - - [Dubbo 支持哪些序列化协议?](/docs/distributed-system/dubbo-serialization-protocol.md) - - [Dubbo 负载均衡策略和集群容错策略?](/docs/distributed-system/dubbo-load-balancing.md) - - [Dubbo 的 SPI 思想是什么?](/docs/distributed-system/dubbo-spi.md) - - [如何基于 Dubbo 进行服务治理?](/docs/distributed-system/dubbo-service-management.md) - - [分布式服务接口的幂等性如何设计?](/docs/distributed-system/distributed-system-idempotency.md) - - [分布式服务接口请求的顺序性如何保证?](/docs/distributed-system/distributed-system-request-sequence.md) - - [如何自己设计一个类似 Dubbo 的 RPC 框架?](/docs/distributed-system/dubbo-rpc-design.md) - - [CAP 定理的 P 是什么?](/docs/distributed-system/distributed-system-cap.md) - - - 分布式锁 - - - [Zookeeper 都有哪些应用场景?](/docs/distributed-system/zookeeper-application-scenarios.md) - - [分布式锁如何设计?](/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md) - - - 分布式事务 - - - [分布式事务了解吗?](/docs/distributed-system/distributed-transaction.md) - - - 分布式会话 - - [集群分布式 Session 如何实现?](/docs/distributed-system/distributed-session.md) - -* 高可用架构 - - - 基于 Hystrix 实现高可用 - - - [Hystrix 介绍](/docs/high-availability/hystrix-introduction.md) - - [电商网站详情页系统架构](/docs/high-availability/e-commerce-website-detail-page-architecture.md) - - [Hystrix 线程池技术实现资源隔离](/docs/high-availability/hystrix-thread-pool-isolation.md) - - [Hystrix 信号量机制实现资源隔离](/docs/high-availability/hystrix-semphore-isolation.md) - - [Hystrix 隔离策略细粒度控制](/docs/high-availability/hystrix-execution-isolation.md) - - [深入 Hystrix 执行时内部原理](/docs/high-availability/hystrix-process.md) - - [基于 request cache 请求缓存技术优化批量商品数据查询接口](/docs/high-availability/hystrix-request-cache.md) - - [基于本地缓存的 fallback 降级机制](/docs/high-availability/hystrix-fallback.md) - - [深入 Hystrix 断路器执行原理](/docs/high-availability/hystrix-circuit-breaker.md) - - [深入 Hystrix 线程池隔离与接口限流](/docs/high-availability/hystrix-thread-pool-current-limiting.md) - - [基于 timeout 机制为服务接口调用超时提供安全保护](/docs/high-availability/hystrix-timeout.md) - - - 高可用系统 - - - 如何设计一个高可用系统? - - - 限流 - - - [如何限流?说一下具体的实现?](/docs/high-concurrency/how-to-limit-current.md) - - - 熔断 - - - 如何进行熔断? - - 熔断框架都有哪些?具体实现原理知道吗? - - [熔断框架,选用 Sentinel 还是 Hystrix?](/docs/high-availability/sentinel-vs-hystrix.md) - - - 降级 - - 如何进行降级? - -* 微服务架构 - - - 微服务的一些概念 - - - [关于微服务架构的描述](/docs/micro-services/microservices-introduction.md) - - [从单体式架构迁移到微服务架构](/docs/micro-services/migrating-from-a-monolithic-architecture-to-a-microservices-architecture.md) - - [微服务的事件驱动数据管理](/docs/micro-services/event-driven-data-management-for-microservices.md) - - [选择微服务部署策略](/docs/micro-services/choose-microservice-deployment-strategy.md) - - - Spring Cloud 微服务架构 - - [什么是微服务?微服务之间是如何独立通讯的?](/docs/micro-services/what's-microservice-how-to-communicate.md) - - Spring Cloud 和 Dubbo 有哪些区别? - - Spring Boot 和 Spring Cloud,谈谈你对它们的理解? - - 什么是服务熔断?什么是服务降级? - - 微服务的优缺点分别是什么?说一下你在项目开发中碰到的坑? - - [你所知道的微服务技术栈都有哪些?](/docs/micro-services/micro-services-technology-stack.md) - - [微服务治理策略](/docs/micro-services/micro-service-governance.md) - - Eureka 和 Zookeeper 都可以提供服务注册与发现的功能,它们有什么区别? - - [谈谈服务发现组件 Eureka 的主要调用过程?](/docs/micro-services/how-eureka-enable-service-discovery-and-service-registration.md) - -* 海量数据处理 - - 10 道经典的海量数据处理面试题 - - [如何从大量的 URL 中找出相同的 URL?](/docs/big-data/find-common-urls.md) - - [如何从大量数据中找出高频词?](/docs/big-data/find-top-100-words.md) - - [如何找出某一天访问百度网站最多的 IP?](/docs/big-data/find-top-1-ip.md) - - [如何在大量的数据中找出不重复的整数?](/docs/big-data/find-no-repeat-number.md) - - [如何在大量的数据中判断一个数是否存在?](/docs/big-data/find-a-number-if-exists.md) - - [如何查询最热门的查询串?](/docs/big-data/find-hotest-query-string.md) - - [如何统计不同电话号码的个数?](/docs/big-data/count-different-phone-numbers.md) - - [如何从 5 亿个数中找出中位数?](/docs/big-data/find-mid-value-in-500-millions.md) - - [如何按照 query 的频度排序?](/docs/big-data/sort-the-query-strings-by-counts.md) - - [如何找出排名前 500 的数?](/docs/big-data/find-rank-top-500-numbers.md) diff --git a/vercel.json b/vercel.json deleted file mode 100644 index ad258d6a..00000000 --- a/vercel.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "github": { - "silent": true - } -} \ No newline at end of file