├── .gitignore ├── .nojekyll ├── .vscode └── launch.json ├── HashMapExample.java ├── README.md ├── _navbar.md ├── docs ├── Algorithm │ ├── 二叉树层次遍历.md │ ├── 二叉树锯齿遍历.md │ ├── 反转合并有序链表.md │ ├── 图相关的算法题.md │ ├── 找出数组中最小的k个数.md │ ├── 接雨水.md │ └── 树的遍历.md ├── Case │ ├── 10亿个数中找到最大的一个数以及最大的K个数.md │ ├── ES监听binlog.md │ ├── 假如明天是活动高峰?QPS 预计会翻10倍,你要怎么做?.md │ ├── 判断链表是否有回环.md │ ├── 实时输出最近一个小时内访问频率最高的10个IP.md │ ├── 实现秒杀功能.md │ ├── 秒杀场景.md │ ├── 翻转字符串.md │ ├── 评论系统.md │ └── 超卖问题.md ├── Go │ ├── CSP并发模型.md │ ├── Context的使用场景.md │ ├── GC 是怎样监听你的应用的.md │ ├── GC垃圾回收算法.md │ ├── GPM调度模型.md │ ├── Go协程的栈内存管理.md │ ├── HTTP Client大量长连接保持.md │ ├── Interface内部实现的理解.md │ ├── Stop the World (STW).md │ ├── chan底层原理.md │ ├── dlv分析golang进程.md │ ├── gin路由树.md │ ├── go runtime 简析.md │ ├── gofunc过程.md │ ├── gopark函数和goready函数原理分析.md │ ├── goroutine什么情况下会阻塞.md │ ├── mutex怎么使用,乐观和悲观锁的实现.md │ ├── slice实践以及底层实现.md │ ├── sync.Pool.md │ ├── 乐观锁与悲观锁与Golang.md │ ├── 互斥锁实现原理剖析.md │ ├── 内存泄露的发现与排查.md │ ├── 如何回收goroutine.md │ ├── 常用包.md │ ├── 特权 Goroutine g0.md │ ├── 线程的实现模型.md │ ├── 结构体是否可以比较.md │ ├── 详解通信数据协议ProtoBuf.md │ ├── 读写分离 sync.Map.md │ ├── 读写锁的实现及底层原理.md │ └── 长连接和短连接的学习.md ├── Kafka │ ├── Kafka事务.md │ ├── Kafka高效率原因.md │ ├── 为什么要使用消息队列 .md │ ├── 如何保证高可用.md │ ├── 架构原理及存储机制.md │ ├── 消费者策略、Rebalance机制、Offset存储机制.md │ └── 消费调优.md ├── Mongo │ └── MongoDB的优势有哪些.md ├── Mysql │ ├── MVVC原理.md │ ├── MongoDB和MySQL的区别.md │ ├── MyISAM和InnoDB.md │ ├── MySQL 主从复制原理.md │ ├── MySQL中高性能索引的策略.md │ ├── MySQL主从复制.md │ ├── Mysql有哪几种索引.md │ ├── Where和having的区别.md │ ├── bin log和redo log的区别.md │ ├── binlog、redo log和undo log.md │ ├── b树与b+树的区别.md │ ├── mysql事务是怎么保证最终一致性.md │ ├── mysql索引数据结构及优化.md │ ├── sql语句性能分析.md │ ├── 一条SQL的执行过程.md │ ├── 为什么MySQL数据库索引选择使用B+树.md │ ├── 主键索引和非主键索引的区别.md │ ├── 事务什么场景下会失效.md │ ├── 事务特性.md │ ├── 二阶段提交.md │ ├── 什么情况下会导致索引失效.md │ ├── 什么情况下建立索引,哪些情况不行.md │ ├── 关于mysql索引最左匹配原则的理解.md │ ├── 分表分库完,每个表也有百万级别,查询速度很慢怎么解决.md │ ├── 原子性与一致性.md │ ├── 四种事务隔离级别.md │ ├── 垂直分表和水平分表和分表的跨表查询 .md │ ├── 如何解决幻读的.md │ ├── 当前读与快照读.md │ ├── 悲观锁与乐观锁区别及使用场景.md │ ├── 数据库三范式.md │ ├── 数据库的垂直切分与水平切分.md │ ├── 查询语句的执行顺序.md │ ├── 百万到千万级别数据量的优化方案.md │ ├── 索引失效的几种情况.md │ ├── 联合索引.md │ ├── 聚簇索引和非聚簇索引.md │ ├── 表锁和行锁机制.md │ ├── 记录锁、间隙锁与临键锁.md │ ├── 说说脏读、不可重复读、幻读,怎么解决的.md │ ├── 读写分离,发现读不到数据.md │ └── 高并发下怎么做余额扣减.md ├── Network │ ├── ARP协议工作原理.md │ ├── HTTP 1.0,1.1,2.0 的区别.md │ ├── HTTP 和 HTTPS 的区别.md │ ├── HTTP 的方法有哪些.md │ ├── HTTP协议中的OPTIONS.md │ ├── HTTP状态码.md │ ├── HTTP的结构.md │ ├── Linux端口范围.md │ ├── OSI 七层模型与 TCP&IP 五层模型.md │ ├── RestFul 与 RPC 的区别.md │ ├── TCP 三次握手以及四次挥手的流程.md │ ├── TCP 中常见的拥塞控制算法有哪些.md │ ├── TCP 半连接发生场景.md │ ├── TCP 协议的延迟 ACK 和累计应答.md │ ├── TCP 可靠传输的保证.md │ ├── TCP 滑动窗口以及重传机制.md │ ├── TCP 的 TIME_WAIT.md │ ├── TCP 的 keepalive.md │ ├── TCP 的报文头部结构.md │ ├── TCP粘包.md │ ├── WebSocket.md │ ├── osi网络模型.md │ ├── ping协议.md │ ├── tcp与udp区别.md │ ├── udp之http3quic协议.md │ ├── 一次 HTTP 的请求过程.md │ ├── 了解网络攻击吗.md │ ├── 什么是 SYN flood.md │ ├── 从系统层面上,UDP如何保证尽量可靠.md │ ├── 图解HTTPS.md │ ├── 查看服务器是否被攻击的方法.md │ ├── 网络七层协议.md │ └── 限流策略.md ├── README.md ├── Redis │ ├── AOF和RDB的过期删除策略.md │ ├── AOF文件太大怎么办.md │ ├── Redis 哈希槽.md │ ├── Redis 的同步机制.md │ ├── Redis布隆过滤器.md │ ├── Redlock(redis分布式锁)原理分析.md │ ├── Sentinel集群选举机制.md │ ├── String数据结构sds.md │ ├── bgsave时数据拷贝过程.md │ ├── pipeline.md │ ├── redis中hash扩容过程.md │ ├── redis主从复制怎么实现.md │ ├── redis如何判断Key是否存在.md │ ├── redis如何实现延时队列.md │ ├── redis扩容方式和触发方式.md │ ├── redis热点问题怎么解决.md │ ├── redis的五大数据类型实现原理.md │ ├── redis缓存为什么要延时双删.md │ ├── 事务及Lua脚本操作.md │ ├── 假设Redis 的 master 节点宕机了,你会怎么进行数据恢复?.md │ ├── 关于影响Redis性能的几点因素.md │ ├── 内存淘汰策略和过期删除策略.md │ ├── 基本数据类型,以及底层数据结构.md │ ├── 如何处理网络延迟和网络异常.md │ ├── 持久化策略RDB和AOF.md │ ├── 数据结构 Stream.md │ ├── 缓存三兄弟.md │ └── 缓存穿透、击穿、雪崩、预热、更新、降级.md ├── Source │ ├── go-microv5.md │ └── moleculer.md └── Theory │ ├── Ctrl C发生了什么.md │ ├── LRU 算法及其实现方式.md │ ├── Linux 常用命令.md │ ├── Linux中的零拷贝技术.md │ ├── Linux系统态与用户态.md │ ├── Md5过程.md │ ├── Protobuf 的底层.md │ ├── cap理论.md │ ├── dns是怎么解析的.md │ ├── epoll怎么解决io效率问题.md │ ├── hash冲突.md │ ├── hash函数.md │ ├── kill底层发生了什么.md │ ├── linux文件的权限.md │ ├── linux进程调度算法.md │ ├── raft算法.md │ ├── rpc实现原理.md │ ├── socket 中 select 与 epoll.md │ ├── 乐观锁和悲观锁.md │ ├── 互斥锁.md │ ├── 共享内存.md │ ├── 内核态和用户态.md │ ├── 分布式id算法.md │ ├── 分布式事务.md │ ├── 分布式共识算法.md │ ├── 分布式锁及优化思路.md │ ├── 协程和线程的区别.md │ ├── 堆和栈访问效率哪个更高.md │ ├── 如何设计一个哈希表.md │ ├── 孤儿进程和僵尸进程以及僵死进程的解决方案.md │ ├── 布式系统的一致性模型(CAP 定理和 PAXOS).md │ ├── 常见的乐观锁实现方式有几种.md │ ├── 常见的排序算法.md │ ├── 抢占分布式锁时.md │ ├── 指针和引用的区别.md │ ├── 操作系统中的中断.md │ ├── 时间和空间复杂度.md │ ├── 死锁概念,死锁产生的四个必要条件,如何避免和预防死锁.md │ ├── 物理内存、虚拟内存和共享内存.md │ ├── 红黑树.md │ ├── 红黑树的性质.md │ ├── 线程间有哪些通信方式.md │ ├── 网络io模型.md │ ├── 自旋锁.md │ ├── 进程和线程之间有什么区别.md │ ├── 进程和线程的同步方式.md │ ├── 进程间通信方式.md │ ├── 通过分析系统,定位服务器问题.md │ ├── 逻辑地址和物理地址的转化.md │ └── 雪花算法.md ├── docsify ├── .codesandbox │ └── ci.json ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ ├── config.yml │ │ └── feature_request.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── semantic.yml │ └── workflows │ │ └── test.yml ├── .gitignore ├── .gitpod.yml ├── .npmignore ├── .prettierrc.js ├── .vscode │ ├── launch.json │ └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── HISTORY.md ├── LICENSE ├── README.md ├── SECURITY.md ├── babel.config.js ├── build │ ├── build.js │ ├── cover.js │ ├── css.js │ ├── emoji.js │ ├── mincss.js │ ├── release.sh │ └── ssr.js ├── docs │ ├── .nojekyll │ ├── CNAME │ ├── README.md │ ├── _coverpage.md │ ├── _images │ │ ├── deploy-github-pages.png │ │ ├── nested-navbar.png │ │ └── zh-cn │ │ │ └── nested-navbar.png │ ├── _media │ │ ├── example-with-yaml.md │ │ ├── example.html │ │ ├── example.js │ │ ├── example.md │ │ ├── favicon.ico │ │ ├── icon.svg │ │ ├── powered-by-vercel.svg │ │ └── vercel_logo.svg │ ├── _navbar.md │ ├── _sidebar.md │ ├── cdn.md │ ├── configuration.md │ ├── cover.md │ ├── custom-navbar.md │ ├── deploy.md │ ├── embed-files.md │ ├── emoji.md │ ├── helpers.md │ ├── index.html │ ├── language-highlight.md │ ├── markdown.md │ ├── more-pages.md │ ├── plugins.md │ ├── pwa.md │ ├── quickstart.md │ ├── ssr.md │ ├── themes.md │ ├── vue.md │ └── write-a-plugin.md ├── index.html ├── jest.config.js ├── lerna.json ├── lib │ ├── docsify.js │ ├── docsify.min.js │ ├── plugins │ │ ├── disqus.js │ │ ├── disqus.min.js │ │ ├── emoji.js │ │ ├── emoji.min.js │ │ ├── external-script.js │ │ ├── external-script.min.js │ │ ├── front-matter.js │ │ ├── front-matter.min.js │ │ ├── ga.js │ │ ├── ga.min.js │ │ ├── gitalk.js │ │ ├── gitalk.min.js │ │ ├── matomo.js │ │ ├── matomo.min.js │ │ ├── search.js │ │ ├── search.min.js │ │ ├── zoom-image.js │ │ └── zoom-image.min.js │ └── themes │ │ ├── buble.css │ │ ├── dark.css │ │ ├── dolphin.css │ │ ├── pure.css │ │ └── vue.css ├── package-lock.json ├── package.json ├── packages │ └── docsify-server-renderer │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.js │ │ ├── package-lock.json │ │ └── package.json ├── playwright.config.js ├── sandbox.config.json ├── server.js ├── src │ ├── core │ │ ├── Docsify.js │ │ ├── config.js │ │ ├── event │ │ │ ├── index.js │ │ │ ├── scroll.js │ │ │ └── sidebar.js │ │ ├── fetch │ │ │ ├── ajax.js │ │ │ └── index.js │ │ ├── global-api.js │ │ ├── index.js │ │ ├── init │ │ │ └── lifecycle.js │ │ ├── render │ │ │ ├── compiler.js │ │ │ ├── compiler │ │ │ │ ├── code.js │ │ │ │ ├── headline.js │ │ │ │ ├── image.js │ │ │ │ ├── link.js │ │ │ │ ├── paragraph.js │ │ │ │ ├── taskList.js │ │ │ │ └── taskListItem.js │ │ │ ├── embed.js │ │ │ ├── emoji-data.js │ │ │ ├── emojify.js │ │ │ ├── gen-tree.js │ │ │ ├── index.js │ │ │ ├── progressbar.js │ │ │ ├── slugify.js │ │ │ ├── tpl.js │ │ │ └── utils.js │ │ ├── router │ │ │ ├── history │ │ │ │ ├── abstract.js │ │ │ │ ├── base.js │ │ │ │ ├── hash.js │ │ │ │ └── html5.js │ │ │ ├── index.js │ │ │ └── util.js │ │ ├── util │ │ │ ├── core.js │ │ │ ├── dom.js │ │ │ ├── env.js │ │ │ ├── index.js │ │ │ ├── polyfill │ │ │ │ └── css-vars.js │ │ │ └── str.js │ │ └── virtual-routes │ │ │ ├── exact-match.js │ │ │ ├── index.js │ │ │ └── next.js │ ├── plugins │ │ ├── disqus.js │ │ ├── emoji.js │ │ ├── external-script.js │ │ ├── front-matter │ │ │ ├── index.js │ │ │ ├── parser.js │ │ │ └── yaml.js │ │ ├── ga.js │ │ ├── gitalk.js │ │ ├── matomo.js │ │ ├── search │ │ │ ├── component.js │ │ │ ├── index.js │ │ │ └── search.js │ │ └── zoom-image.js │ └── themes │ │ ├── basic │ │ ├── _coverpage.styl │ │ └── _layout.styl │ │ ├── buble.styl │ │ ├── dark.styl │ │ ├── dolphin.styl │ │ ├── pure.styl │ │ └── vue.styl └── test │ ├── README.md │ ├── config │ ├── jest.setup-tests.js │ ├── jest.setup.js │ ├── jest.teardown.js │ ├── playwright.setup.js │ ├── playwright.teardown.js │ └── server.js │ ├── e2e │ ├── .eslintrc.js │ ├── configuration.test.js │ ├── example.test.js │ ├── fixtures │ │ └── docsify-init-fixture.js │ ├── index-file.test.js │ ├── plugins.test.js │ ├── search.test.js │ ├── security.test.js │ ├── sidebar.test.js │ ├── virtual-routes.test.js │ └── vue.test.js │ ├── helpers │ ├── docsify-init.js │ └── wait-for.js │ ├── integration │ ├── .eslintrc.js │ ├── __snapshots__ │ │ ├── docs.test.js.snap │ │ └── emoji.test.js.snap │ ├── docs.test.js │ ├── docsify.test.js │ ├── emoji.test.js │ ├── example.test.js │ ├── global-apis.test.js │ └── render.test.js │ └── unit │ ├── .eslintrc.js │ ├── __snapshots__ │ └── example.test.js.snap │ ├── core-util.test.js │ ├── example.test.js │ ├── fixtures │ ├── get-time-of-day.js │ └── greet.js │ ├── render-util.test.js │ ├── router-history-base.test.js │ └── router-util.test.js ├── favicon.png ├── go-microv5-components.md ├── index.html ├── navbar.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | interview -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2637309949/go-interview/f38194aec9f813133b39c1e915b0b530ad9fae7b/.nojekyll -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "type": "cmake", 5 | "request": "launch", 6 | "name": "Debug portfile(s)", 7 | "cmakeDebugType": "external", 8 | "pipeName": "\\\\.\\pipe\\vcpkg_ext_portfile_dbg", 9 | "preLaunchTask": "Debug vcpkg commands" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /HashMapExample.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2637309949/go-interview/f38194aec9f813133b39c1e915b0b530ad9fae7b/HashMapExample.java -------------------------------------------------------------------------------- /docs/Algorithm/图相关的算法题.md: -------------------------------------------------------------------------------- 1 | 以下是一些与图相关的经典算法题: 2 | 3 | ### 1. 最短路径问题 4 | - **Dijkstra算法**:用于计算从源点到图中所有其他点的最短路径,适用于非负权重的图。 5 | - **Bellman-Ford算法**:可以处理负权重边的图,并能检测负权重环路。 6 | 7 | ### 2. 最小生成树 8 | - **Prim算法**:从一个起始点开始,逐步扩展生成树,选择权重最小的边。 9 | - **Kruskal算法**:将图的所有边按权重排序,逐步选择边并确保不形成环路,直到生成树包含所有顶点。 10 | 11 | ### 3. 图的遍历 12 | - **深度优先搜索(DFS)**:用于图的遍历和搜索,常用于解决连通性问题。 13 | - **广度优先搜索(BFS)**:用于查找最短路径和层次遍历,适合无权重图。 14 | 15 | ### 4. 拓扑排序 16 | - 适用于有向无环图(DAG),用于确定任务的执行顺序。 17 | 18 | ### 5. 强连通分量 19 | - **Kosaraju算法**:用于找出有向图中的所有强连通分量。 20 | - **Tarjan算法**:另一种查找强连通分量的高效方法。 21 | 22 | ### 6. 图的连通性 23 | - 判断图是否连通,可以通过DFS或BFS实现。 24 | 25 | ### 7. 二分图检测 26 | - 检测一个图是否为二分图,通常使用DFS或BFS进行染色。 27 | 28 | ### 8. 图的最长路径 29 | - 在有向无环图中,最长路径可以通过动态规划或拓扑排序解决。 30 | 31 | ### 9. 二叉树的最小距离 32 | - 在二叉树中找到两个节点之间的最小距离,可以通过查找最近公共祖先(LCA)实现。 33 | 34 | ### 10. 网络流 35 | - **Ford-Fulkerson算法**:用于解决最大流问题。 36 | - **Edmonds-Karp算法**:Ford-Fulkerson的具体实现,使用BFS查找增广路径。 37 | 38 | 这些题目涵盖了图论中的许多基本概念和应用,适合在算法面试和竞赛中练习。如果你对某个特定题目或算法需要更详细的解释或示例,请告诉我! -------------------------------------------------------------------------------- /docs/Case/10亿个数中找到最大的一个数以及最大的K个数.md: -------------------------------------------------------------------------------- 1 | ### 1. 如何高效地找到最大的一个数 2 | 3 | #### 方法: 4 | 将10亿个数据分成1000份,每份包含100万个数据,找到每份数据中的最大值。然后在这1000个最大值中再进行一次比较,找到最终的最大值。 5 | 6 | #### 优点: 7 | 这种方法通过分割数据并行处理,能够有效减少单次操作的数据量,提高处理效率。每次处理的数据占用的内存较少,约为4MB,且总共需要进行1000次比较。 8 | 9 | ### 2. 如何高效地找到第K大的数 10 | 11 | #### 方法: 12 | 对于top K问题,常用的方法是结合分治法、哈希、小顶堆等技术: 13 | 14 | 1. **分治法**: 15 | - 首先将数据集按照哈希方法分解成多个较小的数据集。 16 | - 对每个小数据集使用小顶堆找到其中最大的K个数。 17 | - 最后在所有数据集的top K结果中再次使用小顶堆找到全局的top K。 18 | 19 | 2. **小顶堆**: 20 | - 构建一个大小为K的小顶堆,首先读入前K个数建立堆,然后遍历后续的数据,比较每个数与堆顶元素(最小的数)。 21 | - 如果当前数比堆顶元素大,则替换堆顶并调整堆结构,保持堆内始终是当前最大的K个数。 22 | 23 | #### 优点: 24 | - **时间复杂度**:O(N log K)。其中,建堆的时间复杂度是O(K),总的时间复杂度主要取决于遍历N个元素并调整堆的时间。 25 | - **适用场景**:数据量大,数据分布广泛,通过分治可以有效减少内存使用,小顶堆确保了高效的K个最大数的计算。 26 | 27 | ### 3. top K问题的常用方法 28 | 29 | - **快排 + 选择排序**: 30 | - 通过对整个集合进行排序后,直接查找第K个数。 31 | - 时间复杂度:O(N log N)。 32 | - 缺点:需要较大的内存,且整体排序的效率相对较低。 33 | 34 | - **局部淘汰**: 35 | - 先取前K个元素进行排序,然后扫描剩余元素,使用二分查找将新元素插入到已排序的K个元素中,并淘汰最小值。 36 | - 时间复杂度:O(N log K)。 37 | 38 | - **Hash法**: 39 | - 对于重复率较高的数据,使用Hash方法去重,减少内存使用量,然后结合分治法或最小堆法查找top K。 40 | 41 | - **Trie树**: 42 | - 对于词频统计问题,通过Trie树统计词频,再结合小顶堆找出top K。 43 | - 适用范围:数据量大,重复多,但种类小,能被内存容纳。 44 | - 时间复杂度:O(Len * N),其中N为字符串个数,Len为字符串长度。 45 | 46 | ### 4. 实际应用场景 47 | 48 | #### 单机+单核+足够大内存 49 | - 直接顺序遍历或使用HashMap计算词频,适用于内存能够容纳全部数据的情况。 50 | 51 | #### 单机+多核+足够大内存 52 | - 采用分区+多线程的方式,分配每个线程处理一个分区的数据,最后进行归并。 53 | 54 | #### 单机+单核+受限内存 55 | - 采用分治法,将数据分割成多个小文件,再依次加载到内存中处理。 56 | 57 | #### 多机+受限内存 58 | - 使用分布式处理,结合MapReduce框架,通过分发数据到多个机器并行计算。 59 | 60 | ### 总结 61 | 在处理10亿个数时,通过分治法结合小顶堆、Trie树等方法,可以高效地找到最大的数或前K大的数。这些方法既能充分利用内存和计算资源,又能应对大规模数据的挑战。 -------------------------------------------------------------------------------- /docs/Case/假如明天是活动高峰?QPS 预计会翻10倍,你要怎么做?.md: -------------------------------------------------------------------------------- 1 | 如果明天的活动高峰期间预计 QPS(每秒查询次数)会翻10倍,这将对系统的承载能力提出极高的要求。为了应对这种情况,需要采取一系列措施来确保系统的稳定性和性能。以下是一些关键步骤: 2 | 3 | ### 1. **系统容量评估与扩展** 4 | - **负载测试**:提前进行压力测试,模拟高峰期的流量,评估系统的当前容量和瓶颈所在。 5 | - **扩展服务器资源**:根据测试结果,水平扩展应用服务器、数据库服务器和缓存服务器的数量。使用云服务(如 AWS、GCP)的弹性扩展能力,增加计算资源。 6 | - **提升数据库性能**:考虑对数据库进行读写分离、分库分表、优化索引等措施,减轻主库压力。 7 | 8 | ### 2. **缓存优化** 9 | - **增加缓存层**:将常用数据(如商品信息、库存数据)加载到分布式缓存(如 Redis、Memcached)中,减少数据库查询次数。 10 | - **使用热点数据缓存**:识别高频访问的数据,将其单独存储在热点缓存中,进一步提升访问速度。 11 | - **缓存预热**:在活动开始前,将预计会被频繁访问的数据提前加载到缓存中,避免缓存冷启动问题。 12 | 13 | ### 3. **限流与熔断** 14 | - **设置限流机制**:对接口进行限流,防止突发流量超出系统承受能力。可以根据不同用户群体设置不同的限流规则,优先保证重要用户的请求。 15 | - **启用熔断器**:在检测到系统过载时,启用熔断机制,暂时拒绝部分请求或降级处理,防止系统崩溃。 16 | 17 | ### 4. **CDN 加速** 18 | - **内容分发网络(CDN)**:将静态资源(如图片、CSS、JS 文件)通过 CDN 分发到全球各地的节点,减少服务器的静态资源负担,加速用户访问。 19 | - **缓存策略优化**:为静态资源设置合理的缓存过期时间,减少CDN节点向源站的请求次数。 20 | 21 | ### 5. **队列化请求处理** 22 | - **使用消息队列**:将秒杀、下单等高并发请求放入消息队列(如 Kafka、RabbitMQ)中,异步处理请求,平滑流量波动。 23 | - **任务分发与处理**:根据队列中的消息进行有序处理,避免数据库或其他资源同时受到大量请求的冲击。 24 | 25 | ### 6. **数据库优化** 26 | - **数据库分片**:根据业务需求,将数据库进行分片,分散压力。例如,将用户数据按地理区域或用户 ID 进行分片。 27 | - **读写分离**:通过主从复制,将读请求分发到从库,减轻主库压力。 28 | - **索引优化**:检查数据库表的索引情况,删除冗余索引,添加必要的索引,提高查询效率。 29 | 30 | ### 7. **应急预案** 31 | - **监控与告警**:设置完善的监控和告警系统,实时监控系统的各项指标(CPU、内存、磁盘、网络等),提前发现问题。 32 | - **应急方案**:制定详细的应急预案,包括系统降级、流量切换、快速扩容等策略,确保在突发情况下能够迅速响应。 33 | 34 | ### 8. **用户体验优化** 35 | - **排队机制**:在用户请求量超过系统处理能力时,引导用户排队,避免系统直接崩溃。排队页面可以展示倒计时或其他提示,提升用户体验。 36 | - **界面优化**:优化前端页面的加载速度,减少 HTTP 请求,使用懒加载、压缩资源等手段提升用户体验。 37 | 38 | ### 总结 39 | 40 | 面对预计的QPS激增,需要综合考虑系统的各个层面,从资源扩展、缓存优化、限流与熔断、队列化处理、数据库优化、应急预案等方面入手,确保系统在高峰期间的稳定性和可靠性。提前进行模拟测试和性能调优,可以帮助识别潜在问题,做出针对性的调整。 -------------------------------------------------------------------------------- /docs/Case/判断链表是否有回环.md: -------------------------------------------------------------------------------- 1 | 判断链表是否有环,经典的算法是**“快慢指针”(Floyd 判圈算法)**,其基本思想是通过两个指针遍历链表,一个快指针一次走两步,一个慢指针一次走一步。如果链表有环,两个指针最终会相遇;如果没有环,快指针会在链表末尾遇到 `null`。 2 | 3 | ### Golang 实现代码: 4 | 5 | ```go 6 | type ListNode struct { 7 | Val int 8 | Next *ListNode 9 | } 10 | 11 | func hasCycle(head *ListNode) bool { 12 | if head == nil || head.Next == nil { 13 | return false 14 | } 15 | 16 | slow, fast := head, head.Next 17 | 18 | for fast != nil && fast.Next != nil { 19 | if slow == fast { 20 | return true 21 | } 22 | slow = slow.Next // 慢指针走一步 23 | fast = fast.Next.Next // 快指针走两步 24 | } 25 | 26 | return false 27 | } 28 | ``` 29 | 30 | ### 解释: 31 | 1. **初始化指针**: 32 | - `slow` 指针每次走一步,`fast` 指针每次走两步。 33 | 2. **循环条件**: 34 | - 循环条件为 `fast != nil && fast.Next != nil`,保证快指针没有走到链表末尾。若到达链表末尾说明无环。 35 | 3. **相遇判断**: 36 | - 如果 `slow == fast`,说明链表有环,两个指针在环中某处相遇。 37 | 4. **返回值**: 38 | - 如果两个指针相遇,则返回 `true`;否则,当快指针到达链表末尾时,返回 `false`。 39 | 40 | ### 时间复杂度与空间复杂度: 41 | - **时间复杂度**:O(n),因为慢指针最多遍历整个链表一次,快指针也是如此。 42 | - **空间复杂度**:O(1),只使用了常量空间。 43 | 44 | 这是链表环检测的高效方法,既不需要额外的存储空间,又能够在线性时间内完成。 -------------------------------------------------------------------------------- /docs/Case/实现秒杀功能.md: -------------------------------------------------------------------------------- 1 | 在电商系统中,实现秒杀功能(即限时抢购)通常面临几个挑战,尤其是商品超卖的问题。以下是一些常见的实现方法和解决方案: 2 | 3 | ### 实现秒杀功能的步骤 4 | 5 | 1. **系统设计与准备** 6 | - **高可用架构**:确保系统能够承受秒杀期间的大量流量。使用负载均衡、分布式架构等技术来提高系统的可用性。 7 | - **数据库优化**:优化数据库性能,确保能够快速响应高并发的请求。考虑使用缓存(如 Redis)来减轻数据库压力。 8 | 9 | 2. **抢购流程** 10 | - **请求排队**:使用队列系统(如 RabbitMQ、Kafka)来排队处理秒杀请求,避免同时处理大量请求导致的系统崩溃。 11 | - **限流**:对请求进行限流,防止超出系统承受能力。可以使用令牌桶算法或漏桶算法来限制请求速率。 12 | 13 | 3. **库存管理** 14 | - **库存预热**:在秒杀开始前,将商品的库存信息加载到缓存中,减少数据库的访问压力。 15 | - **库存检查**:在用户提交订单时,先检查库存是否充足。如果库存不足,立即返回失败信息。 16 | 17 | ### 解决商品超卖问题 18 | 19 | 1. **乐观锁** 20 | - **原理**:在订单处理时,使用版本号或时间戳等机制进行库存检查和更新。乐观锁假设冲突较少,每次更新时检查数据是否被其他请求修改过。 21 | - **实现**:在库存数据表中添加一个版本号字段。每次更新库存时,检查版本号是否匹配,确保没有其他操作修改过库存。 22 | 23 | 2. **悲观锁** 24 | - **原理**:在处理秒杀请求时,使用数据库的锁机制(如行锁)来防止多个请求同时修改库存。 25 | - **实现**:在秒杀请求处理中,对商品的库存行进行加锁,确保每次只有一个请求能够修改库存。 26 | 27 | 3. **分布式锁** 28 | - **原理**:在分布式系统中,使用分布式锁来确保库存的唯一性。常用的分布式锁工具有 Redis 的 `SETNX` 命令、Zookeeper 等。 29 | - **实现**:在秒杀请求处理时,通过分布式锁来控制对库存的访问。确保只有一个请求能够成功获得锁并更新库存。 30 | 31 | 4. **消息队列** 32 | - **原理**:使用消息队列将秒杀请求异步处理,避免直接对库存进行高并发的操作。通过消息队列来平滑请求压力。 33 | - **实现**:将秒杀请求放入消息队列中,由后台服务逐步处理请求。这样可以控制对库存的访问频率,避免超卖。 34 | 35 | 5. **库存回滚** 36 | - **原理**:在秒杀过程中,如果发现库存不足或其他异常情况,及时回滚库存,避免造成超卖。 37 | - **实现**:在订单处理过程中,设置超时时间或异常处理机制,确保在出现问题时能够及时回滚库存,恢复正常状态。 38 | 39 | 6. **缓存与数据库一致性** 40 | - **原理**:使用缓存来提高查询速度,同时保证缓存与数据库的一致性。常用方法包括缓存预热和异步更新。 41 | - **实现**:将商品库存存储在缓存中,并设置合理的过期时间。秒杀过程中,及时更新缓存和数据库,保持一致性。 42 | 43 | ### 总结 44 | 45 | 秒杀功能的实现涉及高并发处理、库存管理和系统架构等多个方面。通过使用上述方法和技术,可以有效地处理秒杀带来的挑战,减少商品超卖的风险,提高系统的稳定性和用户体验。 -------------------------------------------------------------------------------- /docs/Go/GPM调度模型.md: -------------------------------------------------------------------------------- 1 | Go语言的GPM调度模型是Go运行时中用于处理并发的核心机制之一,它将Goroutine(轻量级线程)有效地映射到系统线程上,以最大化并发性能。GPM模型主要由三个部分组成:G(Goroutine)、P(Processor)、M(Machine)。让我们逐一详细介绍: 2 | 3 | ### 1. **G(Goroutine)** 4 | - **Goroutine** 是Go语言中用于并发执行的轻量级线程,每个Goroutine都有自己的栈和上下文信息。 5 | - Goroutine相对于操作系统的线程更加轻量级,可以在同一时间内运行成千上万的Goroutine。 6 | 7 | ### 2. **P(Processor)** 8 | - **P** 是处理Goroutine的调度器的上下文,每个P包含一个本地运行队列(Local Run Queue),用于存储需要运行的Goroutine。 9 | - P的数量由`GOMAXPROCS`设置决定,它决定了并行执行的最大线程数。 10 | - P不仅管理Goroutine,还负责与M协作,将Goroutine分配给M执行。 11 | 12 | ### 3. **M(Machine)** 13 | - **M** 代表操作系统的线程,负责执行Goroutine。一个M一次只能执行一个Goroutine。 14 | - M是实际执行代码的工作单元,M与P绑定后才能执行Goroutine。 15 | - M可以通过调度器从全局运行队列中拉取新的Goroutine,也可以与其他M协作完成工作。 16 | 17 | ### 4. **GPM模型的调度过程** 18 | - **调度器工作机制**:Goroutine创建后会被放入P的本地队列,P会从该队列中选择Goroutine,并将其分配给M执行。如果本地队列为空,P可以从全局运行队列或其他P的队列中窃取任务。 19 | - **工作窃取机制**:如果一个P的本地队列为空,而另一个P的本地队列中有多个Goroutine,前者可以从后者中窃取任务,从而保持系统的高效利用率。 20 | - **阻塞与调度**:当M执行的Goroutine阻塞(例如I/O操作)时,M会释放当前的P并等待P重新分配任务,从而避免资源浪费。 21 | 22 | ### 5. **模型优点** 23 | - **高效的并发调度**:GPM模型使得Go语言可以高效地管理数百万个Goroutine的并发执行。 24 | - **可伸缩性**:通过P与M的动态调度,GPM模型可以充分利用多核处理器的性能。 25 | - **轻量级**:Goroutine非常轻量,创建和切换的成本比系统线程要低得多。 26 | -------------------------------------------------------------------------------- /docs/Go/Go协程的栈内存管理.md: -------------------------------------------------------------------------------- 1 | Go 协程(Goroutine)的栈内存管理是 Go 语言设计中一个重要的优化点。它通过灵活的栈增长策略,确保 Goroutine 能够高效地使用内存,支持高并发。以下是 Go 协程栈内存管理的详细介绍: 2 | 3 | ### 1. **栈的初始大小** 4 | - Goroutine 的栈在创建时非常小,通常只有 2KB 左右(不同的 Go 版本可能略有不同)。与传统线程的固定大小栈(通常是 1MB 或更多)相比,这种设计使得 Go 程序能够创建大量 Goroutine,而不占用大量内存。 5 | 6 | ### 2. **栈的动态增长** 7 | - Go 协程的栈是动态增长的。当 Goroutine 运行时,如果当前栈空间不足以满足函数调用的需求,Go 运行时会自动将栈扩展到更大的空间。 8 | - 栈增长的过程大致如下: 9 | 1. **检查栈空间**:每次函数调用时,Go 运行时都会检查当前栈空间是否足够。 10 | 2. **栈扩展**:如果不够,运行时会分配一块更大的内存,将原栈中的内容拷贝到新栈,并更新相应的栈指针。 11 | 3. **继续执行**:栈扩展后,程序继续执行,几乎不会受到影响。 12 | 13 | ### 3. **栈的动态缩小** 14 | - 除了动态增长,Go 的栈还可以动态缩小。当 Goroutine 中的栈深度减少时,Go 运行时可能会将栈缩小,以释放不再使用的内存。 15 | - 缩小栈的操作不像增长栈那样频繁,因为缩小操作可能会影响性能。但在一些长生命周期的 Goroutine 中,这种机制有助于释放未使用的内存。 16 | 17 | ### 4. **栈分片和垃圾回收** 18 | - Goroutine 的栈由多个分片(segments)组成,随着栈的增长和缩小,这些分片可能被分配或释放。 19 | - Go 运行时的垃圾回收器(GC)会定期检查这些分片,并回收那些不再需要的内存。这种设计使得 Goroutine 的栈管理更加灵活,能够有效地控制内存使用。 20 | 21 | ### 5. **栈溢出和保护** 22 | - 为了防止栈溢出,Go 运行时在栈的末尾保留了一块保护区(guard page)。如果 Goroutine 访问了这块保护区,运行时会触发栈溢出错误,防止非法内存访问。 23 | - 当发生栈溢出时,程序会触发运行时异常,并输出相关的错误信息,帮助开发者定位问题。 24 | 25 | ### 6. **栈管理的优点** 26 | - **高效内存使用**:通过动态调整栈大小,Go 能够在高并发场景下高效地利用内存,支持成千上万的 Goroutine。 27 | - **自动化管理**:开发者无需手动管理栈内存,Go 运行时会自动处理栈的增长和缩小,简化了并发编程的复杂性。 28 | - **安全性**:栈的动态增长和保护机制降低了栈溢出和内存非法访问的风险,提升了程序的健壮性。 29 | 30 | Go 的栈内存管理机制是其支持高并发的重要基础之一。通过灵活的栈调整策略,Go 语言能够在高并发场景下保持良好的性能和内存效率。 -------------------------------------------------------------------------------- /docs/Go/Stop the World (STW).md: -------------------------------------------------------------------------------- 1 | **Stop-the-World (STW)** 是垃圾回收(GC)过程中的一个重要概念,指的是在垃圾回收期间,程序的所有 Goroutine 会被暂停(或“停止”),以保证在回收内存时的一致性和正确性。在 Go 语言的垃圾回收过程中,STW 主要用于标记和清除阶段。 2 | 3 | ### 1. **STW 的必要性** 4 | 5 | STW 的主要目的是确保在进行垃圾回收时,所有的内存引用(即指针)都是一致的。由于垃圾回收过程中需要遍历堆内存和指针,STW 可以防止以下问题: 6 | - **数据竞争**:在 GC 的过程中,若内存被修改,可能会导致对对象的错误引用或丢失。 7 | - **一致性问题**:垃圾回收需要在程序处于一致状态时进行,STW 可以确保在标记和清除阶段内存的完整性。 8 | 9 | ### 2. **GC 中的 STW 阶段** 10 | 11 | 在 Go 语言的垃圾回收过程中,STW 通常会发生在以下几个阶段: 12 | 1. **标记阶段**:GC 必须遍历所有可达对象的引用,标记这些对象。为了保证遍历的一致性,所有的 Goroutine 必须暂停,直到标记阶段完成。 13 | 2. **清理阶段**:GC 清除那些没有被标记的对象。这一阶段同样需要 STW,以确保删除的内存不再被任何 Goroutine 使用。 14 | 15 | ### 3. **STW 的影响** 16 | 17 | - **暂停时间**:STW 会导致应用程序在垃圾回收期间暂停。虽然 Go 的垃圾回收器在设计时尽量减少 STW 的时间,但在大规模堆内存和高并发场景下,STW 的暂停时间可能会影响应用程序的响应性和吞吐量。 18 | - **用户体验**:对于实时性要求高的应用程序,STW 的暂停可能会导致延迟增加,从而影响用户体验。 19 | 20 | ### 4. **Go 语言中的 STW 优化** 21 | 22 | Go 语言的垃圾回收器在设计时考虑了 STW 的影响,并进行了优化,以减少其对应用程序的干扰: 23 | - **并发垃圾回收**:Go 的垃圾回收器采用并发标记算法,即使在垃圾回收的过程中,应用程序仍然可以运行。标记阶段被分为多个阶段,其中一些可以与应用程序的执行并发进行。 24 | - **增量式回收**:通过将垃圾回收过程分为多个小步骤,Go 语言能够减少每个 STW 期间的时间,从而降低对应用程序的暂停时间。 25 | - **GC 速度调节**:`GOGC` 环境变量允许开发者调节 GC 的频率。增大 `GOGC` 的值可以减少 GC 的频率,从而减少 STW 的总次数,但可能会导致更高的内存使用。 26 | 27 | ### 5. **如何监控和调试 STW** 28 | 29 | - **`runtime.ReadMemStats`**:可以通过 `runtime.ReadMemStats` 函数获取垃圾回收的统计信息,包括 STW 的时间。 30 | - **GC 日志**:使用 `GODEBUG=gctrace=1` 环境变量可以开启 GC 日志,帮助分析 GC 的频率和 STW 的时间。 31 | - **性能分析工具**:Go 提供了 `pprof` 工具,可以用来分析内存分配和 GC 活动,帮助识别和优化 GC 相关的性能问题。 32 | 33 | ### 6. **总结** 34 | 35 | Stop-the-World (STW) 是垃圾回收中不可避免的一部分,用于保证回收过程中的内存一致性。虽然 STW 会导致程序暂停,影响应用程序的实时性,但 Go 语言通过并发标记、增量回收和优化调节等手段来减轻 STW 的影响。理解 STW 及其优化方法可以帮助开发者更好地设计和优化 Go 应用程序,尤其是在高并发和大规模应用中。 -------------------------------------------------------------------------------- /docs/Go/gofunc过程.md: -------------------------------------------------------------------------------- 1 | `go func()` 创建一个 goroutine 的执行流程,具体过程如下: 2 | 3 | 1. **创建 goroutine**:通过 `go func()` 来创建一个新的 goroutine。 4 | 5 | 2. **队列存储**:有两个存储 G(goroutine)的队列,一个是 P 的本地队列,一个是全局 G 队列。新创建的 G 会首先保存在 P 的本地队列中,如果本地队列满了,那么 G 会存入全局队列。 6 | 7 | 3. **G 的执行调度**:G 需要 M(操作系统线程)来执行。每个 M 必须绑定一个 P,M 和 P 是 1:1 的关系。M 会从 P 的本地队列弹出一个可执行状态的 G 来执行。如果 P 的本地队列为空,M 会从其他 M-P 组合中偷取 G,或者从全局队列中获取 G 来执行。 8 | 9 | 4. **M 调度 G 的执行**:M 调度 G 的执行是一个循环机制,M 会不断地调度 G,直到所有 G 被执行。 10 | 11 | 5. **系统调用的处理**:当 M 执行 G 时,如果 G 发生了阻塞操作(例如发起系统调用),M 也会被阻塞。此时,如果其他 G 正在等待执行,runtime 会将这个 M 从 P 上解绑,并将 M 放入空闲线程池中,启动一个新的 M 来继续处理剩余的 G。如果有空闲的 M,则复用已有的 M 来执行 G。 12 | 13 | 6. **M 系统调用结束后的恢复**:当系统调用结束后,runtime 会尝试让原来的 G 和 M 重新获得一个 P 并继续执行。如果成功,M 和 G 重新进入可执行状态。如果失败,G 会被放入全局队列,等待调度。 14 | 15 | 这个过程展示了 Go 语言 runtime 中调度器如何高效地管理并调度 goroutine,最大化 CPU 资源的利用率,并避免阻塞操作影响其他 goroutine 的执行。 -------------------------------------------------------------------------------- /docs/Go/sync.Pool.md: -------------------------------------------------------------------------------- 1 | `sync.Pool` 是 Go 标准库中的一种用于临时对象缓存的并发安全的对象池。它的主要目的是减少内存分配和垃圾回收的开销,通过重用对象来提高性能。以下是 `sync.Pool` 的详细介绍: 2 | 3 | ### 1. **基本概念** 4 | - `sync.Pool` 是一个可以存储任意类型对象的池,用于临时存储和复用这些对象。 5 | - 典型的使用场景是需要频繁分配和释放短生命周期的对象,例如在高频率请求处理中,每次请求都需要分配一些临时的结构体或切片。 6 | 7 | ### 2. **工作机制** 8 | - **Get**:从对象池中获取一个对象。如果池中有可用对象,则直接返回;如果没有可用对象,则调用 `New` 函数(如果提供了)创建一个新对象并返回。如果 `New` 函数未提供且池中无可用对象,则返回 `nil`。 9 | - **Put**:将对象放回对象池中,以便将来复用。放入池中的对象会在后续的 `Get` 操作中被复用。 10 | 11 | ### 3. **主要特点** 12 | - **并发安全**:`sync.Pool` 可以在多个 Goroutine 间安全地共享和复用对象,而不需要额外的锁机制。 13 | - **自动清理**:`sync.Pool` 中的对象不是永久存储的,对象池会随着垃圾回收器的运行而被清空。因此,`sync.Pool` 主要用于缓存短期对象,而非长生命周期的对象。 14 | - **全局和局部存储**:每个 `P`(Processor) 都维护一个本地的 `sync.Pool`,`Get` 操作优先从本地的池中获取对象,这减少了锁争用的可能性。只有在本地池为空时,才会访问全局池。 15 | 16 | ### 4. **使用示例** 17 | ```go 18 | package main 19 | 20 | import ( 21 | "fmt" 22 | "sync" 23 | ) 24 | 25 | func main() { 26 | var pool = sync.Pool{ 27 | New: func() interface{} { 28 | return "default" 29 | }, 30 | } 31 | 32 | pool.Put("Hello") 33 | pool.Put("World") 34 | 35 | fmt.Println(pool.Get()) // 输出:Hello 36 | fmt.Println(pool.Get()) // 输出:World 37 | fmt.Println(pool.Get()) // 输出:default,因为池中已经没有对象,返回New创建的对象 38 | } 39 | ``` 40 | 41 | ### 5. **使用建议** 42 | - **适用场景**:`sync.Pool` 适用于那些创建和销毁成本高但生命周期短的对象。 43 | - **性能提升**:在需要频繁创建小对象的场景下,使用 `sync.Pool` 可以显著减少垃圾回收的负担,提升性能。 44 | - **避免滥用**:因为 `sync.Pool` 的对象在 GC 时会被清理,所以不适用于需要长期保存的对象。如果对象创建和销毁成本很低,使用 `sync.Pool` 可能并不能带来明显的性能提升。 45 | 46 | `sync.Pool` 是一个非常有用的工具,尤其是在高并发场景下。了解如何正确使用它可以帮助你在开发 Go 应用程序时实现更高的性能和更低的内存占用。 -------------------------------------------------------------------------------- /docs/Go/特权 Goroutine g0.md: -------------------------------------------------------------------------------- 1 | 在 Go 语言的运行时系统中,`g0` 是一个特殊的 Goroutine。了解 `g0` 可以帮助你更好地理解 Go 的 Goroutine 调度和管理机制。 2 | 3 | ### **1. `g0` 的定义** 4 | 5 | `g0` 是 Go 运行时内部使用的一个 Goroutine,它不参与普通的用户代码执行,而是负责处理一些运行时的内部任务,比如垃圾回收、调度和一些底层操作。 6 | 7 | ### **2. `g0` 的作用** 8 | 9 | - **调度和管理**:`g0` 处理调度器的内部任务,比如在运行时需要切换上下文时,`g0` 负责这些操作。 10 | - **垃圾回收**:`g0` 可能会在进行垃圾回收时执行一些关键任务,帮助管理和清理内存。 11 | - **系统调用**:`g0` 可能会处理系统调用和其他底层操作,这些操作需要在一个特别的 Goroutine 上执行,以避免干扰用户代码的正常执行。 12 | 13 | ### **3. `g0` 的特点** 14 | 15 | - **特权 Goroutine**:`g0` 是一种特权 Goroutine,它在 Go 的运行时系统中有特殊的地位。它不是用户创建的,而是 Go 运行时在初始化时创建的。 16 | - **不可见**:`g0` 对普通的 Go 代码是不可见的,程序员不能直接操作或控制 `g0`。 17 | - **低级别操作**:`g0` 主要用于低级别的操作,不涉及用户的业务逻辑。 18 | 19 | ### **4. `g0` 与其他 Goroutine 的关系** 20 | 21 | - **调度器 Goroutine**:Go 的调度器会在多个 CPU 核心上调度用户创建的 Goroutine。`g0` 不参与这些调度操作,但它会在需要时介入系统级别的操作。 22 | - **上下文切换**:在进行 Goroutine 的上下文切换时,`g0` 可能会执行一些必要的操作来完成这个切换。 23 | 24 | ### **5. `g0` 的存在背景** 25 | 26 | - **设计原因**:Go 运行时系统设计 `g0` 是为了分离用户代码和系统级操作,从而提供更好的稳定性和性能。通过将系统级任务交给 `g0`,可以避免对用户代码的干扰。 27 | - **运行时优化**:`g0` 的存在也帮助优化 Go 运行时的性能,尤其是在涉及到垃圾回收和调度时。 28 | 29 | ### **总结** 30 | 31 | `g0` 是 Go 运行时系统中的一个特殊 Goroutine,负责处理系统级的任务,如垃圾回收和调度操作。虽然普通的 Go 代码无法直接操作 `g0`,但了解它的存在和作用有助于更好地理解 Go 的调度和内存管理机制。`g0` 的设计旨在提升 Go 语言的性能和稳定性,分离用户代码和底层系统操作。 -------------------------------------------------------------------------------- /docs/Kafka/Kafka高效率原因.md: -------------------------------------------------------------------------------- 1 | Kafka 的高性能主要归功于以下几个因素: 2 | 3 | 1. **顺序写入**:Kafka 使用顺序写入的方式将数据追加到日志文件中。这种方式比随机写入更高效,因为顺序写入最大化了磁盘的顺序读写速度,减少了磁盘寻道时间。 4 | 5 | 2. **日志分段**:Kafka 将日志分成多个段(segment)。每个段都是一个独立的文件,这使得日志文件的管理更加高效,并且可以更容易地进行压缩和删除旧数据。 6 | 7 | 3. **高效的存储结构**:Kafka 使用了压缩和索引技术来优化存储和读取速度。它使用了类似于LSM树的结构来处理写入和读取,减少了存储和读取的开销。 8 | 9 | 4. **分布式架构**:Kafka 的分布式架构使得它可以在多个服务器上分布数据和负载。每个分区的数据存储和处理都是独立的,这样就可以通过水平扩展来增加吞吐量和容错能力。 10 | 11 | 5. **批量处理**:Kafka 支持批量发送和接收消息,这降低了网络和磁盘的开销,提高了吞吐量。 12 | 13 | 6. **内存映射文件**:Kafka 使用内存映射文件(memory-mapped files)来减少磁盘 I/O 开销。通过内存映射文件,Kafka 可以直接在内存中读取数据,而不需要频繁的磁盘 I/O 操作。 14 | 15 | 7. **消费者拉取**:Kafka 采用了消费者拉取的模型而不是服务器推送的模型。这使得消费者可以根据自己的速度来拉取数据,避免了因为生产者的速度问题导致的性能瓶颈。 16 | 17 | 这些设计和技术使得 Kafka 能够处理大规模的数据流,并提供高吞吐量和低延迟的数据处理能力。 -------------------------------------------------------------------------------- /docs/Kafka/消费调优.md: -------------------------------------------------------------------------------- 1 | 消费调优 Kafka 消费者的性能可以通过以下几个方面进行优化: 2 | 3 | 1. **并行消费**:增加消费者实例,利用多个消费者并行处理消息。确保消费者组的数量与分区数相匹配。 4 | 5 | 2. **批量处理**:使用批量消费配置,例如 `max.poll.records`,设置一次从 Kafka 拉取多条消息,减少网络往返次数。 6 | 7 | 3. **合理配置 `fetch` 参数**:调整 `fetch.min.bytes` 和 `fetch.max.wait.ms`,优化消息拉取的效率。 8 | 9 | 4. **调整 `session.timeout.ms` 和 `max.poll.interval.ms`**:确保消费者能够在适当的时间内处理消息,防止因超时导致的重新平衡。 10 | 11 | 5. **高效的消息处理**:优化消息处理逻辑,确保消费者在 `poll()` 后尽快处理消息并提交位移。 12 | 13 | 6. **监控和调试**:使用 Kafka 监控工具(如 Confluent Control Center、Prometheus + Grafana)监控消费者的性能,发现瓶颈并进行调整。 14 | 15 | 7. **调整 `auto.offset.reset`**:设置为 `latest` 或 `earliest`,根据业务需求选择适当的偏移重置策略。 16 | 17 | 通过这些调优策略,可以提高 Kafka 消费者的性能和稳定性。 -------------------------------------------------------------------------------- /docs/Mongo/MongoDB的优势有哪些.md: -------------------------------------------------------------------------------- 1 | MongoDB的优势包括: 2 | 3 | 1. **灵活的数据模型**:MongoDB使用文档存储格式(BSON),支持动态架构,使得可以在不影响现有数据的情况下轻松修改数据结构。 4 | 5 | 2. **水平扩展性**:MongoDB支持分片,可以轻松在多个服务器之间分配数据,允许横向扩展以处理大量数据。 6 | 7 | 3. **高性能**:MongoDB在读取和写入操作上表现良好,适合处理高吞吐量的应用。 8 | 9 | 4. **丰富的查询能力**:支持复杂的查询,包括聚合、索引、地理查询等,提供灵活的数据检索方式。 10 | 11 | 5. **强大的社区支持**:MongoDB拥有活跃的社区和丰富的文档资源,开发者可以轻松找到支持和学习材料。 12 | 13 | 6. **自动化管理**:支持自动故障转移和数据备份,简化了运维工作。 14 | 15 | 7. **适合大数据和实时分析**:MongoDB能够处理大规模数据并支持实时分析,适用于需要快速响应的应用场景。 16 | 17 | 这些优势使得MongoDB在许多现代应用中成为流行的选择,特别是在处理非结构化或半结构化数据时。 -------------------------------------------------------------------------------- /docs/Mysql/MyISAM和InnoDB.md: -------------------------------------------------------------------------------- 1 | MyISAM和InnoDB是MySQL数据库管理系统中两种主要的存储引擎。它们在性能、功能和支持方面有显著的区别。以下是它们的主要特性和区别: 2 | 3 | ### 1. **MyISAM** 4 | 5 | **特性**: 6 | - **表锁**:MyISAM使用表锁来管理并发访问,这意味着在对表进行写操作时,整个表会被锁定,其他的读写操作会被阻塞。 7 | - **不支持事务**:MyISAM不支持事务(ACID特性),这意味着你无法进行事务回滚或提交。 8 | - **不支持外键约束**:MyISAM不支持外键约束,这会影响数据的完整性。 9 | - **压缩**:MyISAM支持表的压缩功能,可以减少存储空间的使用。 10 | - **快速读操作**:MyISAM在读取操作上通常比InnoDB更快,适用于读操作频繁的应用。 11 | - **表级锁**:适合对读操作频繁的环境,但对写操作较多的环境可能性能较差。 12 | - **存储格式**:数据和索引分别存储在不同的文件中,数据文件的扩展名通常为`.MYD`,索引文件为`.MYI`。 13 | 14 | **使用场景**: 15 | - 主要用于读操作较多且对事务性要求不高的应用。 16 | - 适合对数据完整性要求不高但对查询性能有要求的环境。 17 | 18 | **示例**: 19 | ```sql 20 | CREATE TABLE example ( 21 | id INT AUTO_INCREMENT PRIMARY KEY, 22 | name VARCHAR(255) 23 | ) ENGINE=MyISAM; 24 | ``` 25 | 26 | ### 2. **InnoDB** 27 | 28 | **特性**: 29 | - **行锁**:InnoDB使用行锁来管理并发访问,这可以减少锁争用,提高并发性。 30 | - **支持事务**:InnoDB支持事务,包括ACID特性(原子性、一致性、隔离性、持久性),可以进行事务回滚和提交。 31 | - **支持外键约束**:InnoDB支持外键约束,可以确保数据的完整性和一致性。 32 | - **聚集索引**:InnoDB的主键索引是聚集索引,数据是按主键排序存储的,这可以提高基于主键的查询效率。 33 | - **自恢复**:InnoDB具有崩溃恢复功能,能够在系统崩溃后自动恢复数据。 34 | - **表空间**:InnoDB将表数据存储在表空间中,可以在多个文件中进行数据存储,通常是`.ibd`文件。 35 | - **支持多版本并发控制(MVCC)**:提高了并发性,减少了读写操作的冲突。 36 | 37 | **使用场景**: 38 | - 主要用于需要事务支持和数据完整性约束的应用。 39 | - 适合对写操作频繁且需要高并发的环境。 40 | - 适合需要外键约束和数据恢复功能的应用。 41 | 42 | **示例**: 43 | ```sql 44 | CREATE TABLE example ( 45 | id INT AUTO_INCREMENT PRIMARY KEY, 46 | name VARCHAR(255) 47 | ) ENGINE=InnoDB; 48 | ``` 49 | 50 | ### 主要区别总结 51 | 52 | - **锁机制**:MyISAM使用表锁,而InnoDB使用行锁。 53 | - **事务支持**:MyISAM不支持事务,InnoDB支持事务。 54 | - **外键约束**:MyISAM不支持外键约束,InnoDB支持。 55 | - **存储格式**:MyISAM的数据和索引分开存储,InnoDB的数据和索引存储在一起。 56 | - **并发控制**:InnoDB通过行锁和MVCC实现更好的并发控制。 57 | 58 | 选择存储引擎时,需要根据具体应用的需求,如事务处理能力、并发性能、数据完整性等,来决定使用MyISAM还是InnoDB。 -------------------------------------------------------------------------------- /docs/Mysql/Mysql有哪几种索引.md: -------------------------------------------------------------------------------- 1 | MySQL主要有以下几种索引: 2 | 3 | 1. **普通索引**:最基本的索引类型,允许重复值。 4 | 2. **唯一索引**:类似于普通索引,但不允许重复值。 5 | 3. **主键索引**:特殊的唯一索引,主键字段不允许为NULL,且每张表只能有一个主键。 6 | 4. **复合索引**:由多个列组成的索引,可以提高多个列的查询性能。 7 | 5. **全文索引**:用于全文搜索,通常在文本字段上使用。 8 | 6. **空间索引**:用于空间数据类型的索引,支持地理信息查询。 9 | 10 | 你对其中的某种索引想了解更多吗? -------------------------------------------------------------------------------- /docs/Mysql/b树与b+树的区别.md: -------------------------------------------------------------------------------- 1 | B树和B+树都是平衡树的一种,广泛应用于数据库和文件系统中。它们的主要区别在于结构和性能优化上。以下是B树和B+树的主要区别: 2 | 3 | ### 1. 结构差异 4 | - **B树**: 5 | - **节点存储键和值**:B树的每个节点不仅存储键,还存储与键关联的数据(值)。叶子节点和内部节点都可以存储数据。 6 | - **多层次的值存储**:数据可能存储在内节点或叶子节点,因此查找时可能会终止于非叶节点。 7 | 8 | - **B+树**: 9 | - **节点只存储键,值存储在叶子节点**:B+树的内部节点只存储键,数据(值)全部存储在叶子节点中。内部节点用于索引,只有叶子节点存储数据。 10 | - **叶子节点之间有链表连接**:B+树的叶子节点之间通过链表连接,形成一个有序的链表。这使得B+树能够高效地进行范围查询和遍历。 11 | 12 | ### 2. 查找效率 13 | - **B树**: 14 | - 查找可能在非叶子节点结束,因为值可能存储在内部节点。这意味着查找的路径可能比B+树短。 15 | 16 | - **B+树**: 17 | - 查找必须到达叶子节点,因为所有数据都存储在叶子节点中。这通常意味着更多的磁盘I/O,但由于数据集中在叶子节点,可以更容易进行批量操作。 18 | 19 | ### 3. 范围查询性能 20 | - **B树**: 21 | - 范围查询需要对每个可能的节点进行查找,不如B+树高效。 22 | 23 | - **B+树**: 24 | - 由于叶子节点形成了一个有序链表,范围查询非常高效,可以从起点直接沿着链表遍历所有符合条件的叶子节点。 25 | 26 | ### 4. 存储效率 27 | - **B树**: 28 | - 由于内部节点存储了数据,可能导致树的阶数较小,需要更多的层次来容纳同样数量的键值对。 29 | 30 | - **B+树**: 31 | - 由于内部节点只存储键,因此可以容纳更多的键,树的高度相对较低,减少了树的层数,节省了内存空间。 32 | 33 | ### 5. 插入与删除操作 34 | - **B树**: 35 | - 插入和删除可能会改变非叶节点的内容,调整可能会涉及更多的节点。 36 | 37 | - **B+树**: 38 | - 插入和删除只影响叶子节点和可能的内部节点的键,操作较为简单且对性能影响较小。 39 | 40 | ### 6. 应用场景 41 | - **B树**: 42 | - 适用于查找较多的场景,因为查找可能在非叶节点结束,减少了查找路径长度。 43 | 44 | - **B+树**: 45 | - 适用于数据库和文件系统等需要高效范围查询的场景,因为B+树的叶子节点链表结构使得范围查询非常高效。 46 | 47 | ### 总结 48 | - **B树**:键和值都可以存储在任何节点,查找效率较高,但范围查询不如B+树。 49 | - **B+树**:键只存储在内部节点,所有值存储在叶子节点,并且叶子节点之间有序连接,范围查询和遍历非常高效。 50 | 51 | 因此,B+树更适合数据库和文件系统等需要大量范围查询的场景,而B树则可能适合对单个记录查找效率要求更高的场景。 -------------------------------------------------------------------------------- /docs/Mysql/mysql事务是怎么保证最终一致性.md: -------------------------------------------------------------------------------- 1 | MySQL事务通过以下几个机制来保证最终一致性: 2 | 3 | 1. **ACID特性**:事务遵循原子性、一致性、隔离性和持久性,确保操作要么全部成功,要么全部失败。 4 | 2. **隔离级别**:MySQL提供多种隔离级别(如读未提交、读已提交、可重复读、串行化),以控制事务之间的可见性,避免脏读、不可重复读和幻读。 5 | 3. **日志机制**:使用日志(如重做日志和撤销日志)来记录事务操作,以便在发生故障时进行恢复。 6 | 4. **锁机制**:通过行级锁和表级锁来管理并发,确保在同一时间只有一个事务可以修改某些数据。 7 | 8 | 这些机制共同作用,确保即使在系统故障或并发操作的情况下,数据最终也能保持一致。你想深入了解哪个方面吗? -------------------------------------------------------------------------------- /docs/Mysql/为什么MySQL数据库索引选择使用B+树.md: -------------------------------------------------------------------------------- 1 | MySQL数据库选择使用B+树作为索引结构的主要原因包括以下几点: 2 | 3 | ### 1. **高效的磁盘I/O操作** 4 | 5 | **局部性原理**: 6 | - B+树的节点被设计成适合磁盘块的大小。每个节点的大小通常等于一个磁盘页的大小,这样可以最大限度地利用磁盘的预读功能。磁盘预读是指即使只请求一个数据块,系统也会一次性读取整个页面,从而减少磁盘寻道次数。 7 | - 由于B+树的节点和磁盘页大小匹配,整个节点在一次磁盘I/O操作中完全加载到内存中,这减少了访问节点时的磁盘I/O次数。 8 | 9 | ### 2. **较小的树高** 10 | 11 | **树高较低**: 12 | - B+树是一种平衡树,其高度 \( h \) 与节点的出度 \( d \) 成反比。由于B+树的每个节点可以有多个子节点,这样树的高度 \( h \) 保持较低,查找操作的时间复杂度为 \( O(\log_d N) \),其中 \( N \) 是索引中的键的数量,\( d \) 是树的度。 13 | - 高度较低意味着查找、插入、删除操作的效率较高,因为这些操作涉及的磁盘I/O次数较少。 14 | 15 | ### 3. **有效的范围查询** 16 | 17 | **顺序访问指针**: 18 | - 在B+树中,所有的叶子节点都通过顺序访问指针相连,这使得范围查询变得非常高效。进行范围查询时,一旦找到起始点,可以顺序遍历叶子节点以获取所有匹配的记录,从而避免了对整个树的重新遍历。 19 | - 这种顺序访问指针特别适用于范围查询(如找出所有在某个范围内的记录)。 20 | 21 | ### 4. **支持高效的插入和删除** 22 | 23 | **节点分裂与合并**: 24 | - B+树在插入和删除时会进行节点分裂和合并操作,这些操作在保证树的平衡的同时,能较好地处理动态数据集。这些操作能够在O(\log N)时间内完成,保持了树的平衡性并确保查询效率。 25 | - 由于B+树的结构稳定,它能够较好地处理大量的插入和删除操作,而不会显著影响查询性能。 26 | 27 | ### 5. **适合大规模数据存储** 28 | 29 | **优化外部存储**: 30 | - B+树的设计考虑了外存存储的特点,其较大的节点出度能够减少树的高度,从而降低对磁盘I/O的需求。这种设计非常适合处理大规模的数据集,特别是在数据库中需要频繁进行磁盘访问时。 31 | 32 | ### 总结 33 | 34 | B+树在数据库中作为索引的主要优势在于它能够有效地减少磁盘I/O次数,支持快速的范围查询,并且能高效地处理动态数据集。其结构能够优化对外存储的访问,使得它成为MySQL等数据库系统中广泛使用的索引结构。 -------------------------------------------------------------------------------- /docs/Mysql/主键索引和非主键索引的区别.md: -------------------------------------------------------------------------------- 1 | 在MySQL中,主键索引和非主键索引(普通索引)都是用于提高查询效率的机制,但它们有不同的特性和用途。以下是它们的主要区别: 2 | 3 | ### 1. 主键索引(Primary Key Index) 4 | 5 | **定义**:主键索引是一种特殊的索引,它在表中唯一标识每一行数据。每个表只能有一个主键索引。 6 | 7 | **特性**: 8 | - **唯一性**:主键索引要求主键列的值必须唯一,不能为NULL。 9 | - **自动创建**:当你创建表时,如果定义了主键,MySQL会自动创建主键索引。 10 | - **聚集索引**:InnoDB存储引擎中的主键索引通常是聚集索引(Clustered Index)。这意味着表的数据实际上是按主键的顺序存储在磁盘上的。其他索引会包含主键值作为其键的一部分。 11 | - **效率**:由于主键索引是唯一的,因此在查找、插入和删除操作时具有很高的效率。 12 | 13 | **使用场景**: 14 | - 用于唯一标识表中的每一行数据。 15 | - 常用于需要唯一性约束的列,如用户ID、订单号等。 16 | 17 | **示例**: 18 | ```sql 19 | CREATE TABLE users ( 20 | id INT PRIMARY KEY, 21 | name VARCHAR(100) 22 | ); 23 | ``` 24 | 25 | ### 2. 非主键索引(普通索引) 26 | 27 | **定义**:非主键索引是指除了主键索引之外的所有索引。它们用于加速对表的查询操作,但不具有主键索引的唯一性要求。 28 | 29 | **特性**: 30 | - **非唯一性**:非主键索引的列值可以重复,也可以为NULL(除非显式指定UNIQUE约束)。 31 | - **非聚集索引**:在InnoDB存储引擎中,非主键索引是非聚集索引(Secondary Index)。非主键索引存储的是索引列的值和主键值的组合。数据的实际存储是按主键排序的,非主键索引会通过主键来访问数据。 32 | - **灵活性**:可以在表中创建多个非主键索引,用于加速不同的查询条件。 33 | 34 | **使用场景**: 35 | - 用于提高特定查询条件的性能,如频繁用于WHERE子句中的列。 36 | - 当需要根据非主键列快速查找数据时使用。 37 | 38 | **示例**: 39 | ```sql 40 | CREATE INDEX idx_name ON users (name); 41 | ``` 42 | 43 | ### 主要区别总结 44 | 45 | - **唯一性**:主键索引要求唯一性,非主键索引不要求唯一性。 46 | - **数量**:每个表只能有一个主键索引,但可以有多个非主键索引。 47 | - **索引类型**:主键索引通常是聚集索引,而非主键索引通常是非聚集索引。 48 | - **数据存储**:主键索引直接决定了表的数据存储顺序(对于InnoDB),而非主键索引则通过主键值访问数据。 49 | 50 | 主键索引和非主键索引各有其独特的作用和适用场景。在设计数据库表时,需要根据具体的查询需求和数据特性选择合适的索引策略。 -------------------------------------------------------------------------------- /docs/Mysql/事务什么场景下会失效.md: -------------------------------------------------------------------------------- 1 | ### 事务什么场景下会失效 2 | 3 | 在数据库中,事务可能会因为某些特定的场景或操作而失效。以下是常见的导致事务失效的场景: 4 | 5 | --- 6 | 7 | #### 1. **隐式提交(Implicit Commit)** 8 | - 如果在事务执行过程中,某些操作触发了隐式提交,当前事务会被强制提交,后续的操作将无法回滚。 9 | - 常见的隐式提交操作包括: 10 | - 创建或删除数据库对象(如表、索引等)。 11 | - 使用 `CREATE TABLE`, `ALTER TABLE`, `DROP TABLE` 等DDL语句。 12 | - 执行 `LOCK TABLES` 或 `UNLOCK TABLES`。 13 | 14 | --- 15 | 16 | #### 2. **死锁检测与超时** 17 | - 当两个或多个事务相互持有对方需要的资源并等待释放时,会发生死锁。 18 | - 数据库检测到死锁后,会选择一个事务作为牺牲品(通常是最小代价的事务),将其回滚并抛出异常,导致该事务失效。 19 | 20 | --- 21 | 22 | #### 3. **并发冲突** 23 | - 在高并发场景下,如果多个事务同时对同一数据进行修改,可能会导致以下问题: 24 | - **脏读**:读取到未提交的数据。 25 | - **不可重复读**:同一事务中多次读取同一数据,结果不一致。 26 | - **幻读**:事务中插入或删除数据导致查询结果集变化。 27 | - 如果隔离级别设置不当(如READ UNCOMMITTED或READ COMMITTED),可能会导致事务失效。 28 | 29 | --- 30 | 31 | #### 4. **超出锁等待时间** 32 | - 如果事务在获取锁时等待的时间超过了设定的锁等待超时时间(如MySQL的`innodb_lock_wait_timeout`),事务会被终止并抛出异常。 33 | 34 | --- 35 | 36 | #### 5. **存储引擎不支持事务** 37 | - 某些存储引擎(如MySQL的MyISAM)不支持事务功能。如果使用这些存储引擎,即使编写了事务代码,也无法保证事务的一致性和完整性。 38 | 39 | --- 40 | 41 | #### 6. **网络中断或系统故障** 42 | - 如果在事务执行过程中发生网络中断、数据库崩溃或服务器宕机等问题,事务可能无法正常完成,导致部分操作已提交而部分未提交,从而失效。 43 | 44 | --- 45 | 46 | #### 7. **违反约束条件** 47 | - 如果事务中的操作违反了数据库的约束条件(如主键冲突、外键约束、唯一性约束等),事务会自动回滚并抛出异常。 48 | 49 | --- 50 | 51 | #### 8. **手动中断事务** 52 | - 开发者可以通过显式的SQL命令(如`ROLLBACK`)手动中断事务,导致事务失效。 53 | 54 | --- 55 | 56 | ### 总结 57 | 58 | 事务失效的原因主要包括隐式提交、死锁、并发冲突、锁等待超时、存储引擎限制、系统故障、约束条件违反以及手动中断等。为了避免事务失效,开发者需要合理设计事务逻辑,选择合适的隔离级别,并处理好异常情况。 -------------------------------------------------------------------------------- /docs/Mysql/原子性与一致性.md: -------------------------------------------------------------------------------- 1 | 仅仅依靠**原子操作**并不能完全保证系统的一致性,尤其是在复杂的并发场景中。虽然原子操作可以保证单次操作的完整性,但在分布式系统或多步骤事务中,还需要额外的机制来确保数据一致性。下面通过一个具体例子说明: 2 | 3 | ### 场景:银行账户转账 4 | 假设我们有两个银行账户,账户A和账户B,用户从账户A转账100元到账户B。这可以看作两个步骤的操作: 5 | 1. 从账户A中减去100元。 6 | 2. 向账户B中加上100元。 7 | 8 | 如果我们仅依赖**原子操作**,确保每次操作都是原子的,即每次操作是不可分割的,那么单个步骤的操作(如减去100元或加上100元)可以是线程安全的。但是,依然不能保证转账操作的一致性。 9 | 10 | #### 问题: 11 | 1. **一致性缺失**:如果在从账户A扣除100元后,系统发生崩溃,而没有来得及向账户B加上100元,结果是资金会“丢失”。 12 | 13 | 2. **部分失败**:虽然账户A的操作是原子的,账户B的操作也是原子的,但这两个步骤之间的整个操作并不是原子的。如果在A账户扣款成功之后,B账户未能加款,系统仍然处于不一致的状态。 14 | 15 | #### 解决方法: 16 | 为了保证数据的一致性,通常需要引入诸如**事务机制**或**分布式锁**等额外机制。在分布式系统中,可能还需要通过**两阶段提交**(2PC)或**分布式事务**来确保多个节点之间的数据一致性。 17 | 18 | ### 示例代码(Golang 中原子操作): 19 | 20 | ```go 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "sync/atomic" 26 | ) 27 | 28 | func main() { 29 | var accountA int64 = 1000 30 | var accountB int64 = 500 31 | 32 | // 使用原子操作从A账户减去100元 33 | atomic.AddInt64(&accountA, -100) 34 | // 使用原子操作向B账户增加100元 35 | atomic.AddInt64(&accountB, 100) 36 | 37 | fmt.Printf("账户A余额: %d\n", accountA) 38 | fmt.Printf("账户B余额: %d\n", accountB) 39 | } 40 | ``` 41 | 42 | 在这个例子中,原子操作确保了对单个账户余额的加减是线程安全的,但如果在两个原子操作之间发生了系统崩溃或失败,系统仍然可能处于不一致的状态。 43 | 44 | ### 总结: 45 | - **原子性**:确保单个操作的完整性,即操作要么完成要么不执行。 46 | - **一致性**:需要确保整个系统在执行完操作后,数据的状态是一致的,而这往往需要更多的控制机制(如事务)来保证。 47 | -------------------------------------------------------------------------------- /docs/Mysql/如何解决幻读的.md: -------------------------------------------------------------------------------- 1 | 在MySQL中,幻读(Phantom Read)是指在一个事务中读取到的记录集在事务过程中发生了变化,导致后续读取结果不一致的现象。幻读问题主要发生在涉及范围查询的场景下,比如查询一定范围内的记录,但在查询过程中,其他事务插入或删除了这些记录,从而导致事务中的数据读取出现不一致。 2 | 3 | MySQL通过不同的隔离级别来解决幻读问题,主要有以下几种方式: 4 | 5 | ### 1. **读取已提交(Read Committed)隔离级别** 6 | 7 | - **描述**:在读取已提交隔离级别中,事务只能读取已经提交的数据。这个隔离级别通过确保一个事务只能看到其他已提交事务的数据来解决脏读问题,但不能完全解决幻读问题。 8 | - **幻读情况**:由于在读取已提交隔离级别中,查询操作可能会看到其他事务对表的插入或删除操作,因此仍然会存在幻读现象。 9 | 10 | ### 2. **可重复读(Repeatable Read)隔离级别** 11 | 12 | - **描述**:在可重复读隔离级别中,事务在执行过程中读取的数据都是一致的,即事务中的多个读取操作看到的数据是相同的。这种隔离级别通过在事务开始时快照数据的方式来实现。 13 | - **幻读情况**:MySQL的InnoDB存储引擎通过实现多版本并发控制(MVCC)来解决幻读问题。在可重复读隔离级别中,InnoDB会为每个事务维护一个快照(快照隔离),确保在事务执行过程中数据的稳定性。具体来说,InnoDB使用间隙锁(Gap Lock)来防止其他事务在当前事务的范围内插入新记录,从而避免幻读的发生。 14 | 15 | ### 3. **串行化(Serializable)隔离级别** 16 | 17 | - **描述**:在串行化隔离级别中,事务通过对所有涉及的记录进行锁定来确保事务的完全隔离。这是最高级别的隔离,确保事务在执行过程中不会受到其他事务的干扰。 18 | - **幻读情况**:串行化隔离级别完全解决了幻读问题,因为它通过对读取范围内的所有记录加锁,阻止其他事务对这些记录的插入或删除操作。这种隔离级别保证了事务执行的完全隔离性,但会引入较大的性能开销,因为可能导致锁竞争和吞吐量降低。 19 | 20 | ### 4. **InnoDB 的间隙锁(Gap Lock)机制** 21 | 22 | - **描述**:InnoDB通过间隙锁来解决幻读问题。在可重复读隔离级别下,InnoDB会在事务执行期间对查询结果集范围内的间隙进行加锁,防止其他事务在这些间隙中插入新的记录。 23 | - **工作原理**: 24 | - **间隙锁**:锁定的是记录之间的空隙,即某些值的范围内的空位。这样可以防止其他事务在这些空位中插入新的记录,从而避免幻读。 25 | - **范围锁**:范围锁不仅锁定了已存在的记录,还锁定了在查询条件范围内的空隙,以阻止其他事务插入新记录。 26 | 27 | ### 总结 28 | 29 | - **读取已提交**:不能完全解决幻读问题,只能防止脏读。 30 | - **可重复读**:通过MVCC和间隙锁解决幻读问题,适合大多数应用场景。 31 | - **串行化**:完全解决幻读问题,但性能开销较大,适用于对事务隔离性要求极高的场景。 32 | 33 | 选择合适的隔离级别取决于业务需求和性能要求。一般来说,可重复读隔离级别在保证数据一致性的同时,能提供相对较好的性能,适合大多数应用场景。如果应用对事务隔离性要求非常高,串行化隔离级别可以提供更强的数据一致性保障。 -------------------------------------------------------------------------------- /docs/Mysql/悲观锁与乐观锁区别及使用场景.md: -------------------------------------------------------------------------------- 1 | 悲观锁(Pessimistic Locking)和乐观锁(Optimistic Locking)是两种处理并发控制的策略,每种策略都有其特定的应用场景和优缺点。它们在MySQL中用于管理数据的并发访问和更新,确保数据的一致性和完整性。 2 | 3 | ### 1. 悲观锁(Pessimistic Locking) 4 | 5 | **概念**: 6 | - 悲观锁是一种保守的锁策略,它假设会发生冲突,因此在读取或修改数据时会立即锁定数据,以防止其他事务同时修改这些数据。悲观锁的核心思想是“锁住一切”,确保数据在事务执行期间不会被其他事务修改。 7 | 8 | **实现方式**: 9 | - **行级锁**:在MySQL的InnoDB存储引擎中,通过使用 `SELECT ... FOR UPDATE` 或 `SELECT ... LOCK IN SHARE MODE` 语句实现。前者用于在读取时锁定数据以进行更新,后者用于读取数据并允许其他事务读取但不修改数据。 10 | - **表级锁**:通过 `LOCK TABLES` 语句或 `LOCK TABLES ... WRITE` 锁定整个表,从而在事务执行期间防止其他事务对该表进行任何操作。 11 | 12 | **优点**: 13 | - **数据一致性**:由于数据在事务期间被锁定,确保了数据的一致性和完整性,避免了并发更新导致的数据冲突。 14 | - **简单易懂**:锁定机制明确,容易理解和实现。 15 | 16 | **缺点**: 17 | - **性能开销**:锁定的数据会影响并发性能,尤其是在高并发环境下,锁竞争可能导致性能下降。 18 | - **可能导致死锁**:多个事务相互等待对方释放锁,可能导致死锁情况。 19 | 20 | **使用场景**: 21 | - **高冲突环境**:数据更新冲突的可能性较高,如银行账户余额更新。 22 | - **复杂事务**:涉及多个操作的复杂事务,确保操作的原子性和一致性。 23 | 24 | ### 2. 乐观锁(Optimistic Locking) 25 | 26 | **概念**: 27 | - 乐观锁是一种假设冲突不会发生的锁策略,它在事务开始时不立即锁定数据,而是在提交时检查数据是否被其他事务修改。如果数据在事务期间被修改,则提交操作会失败,事务需要重新尝试或进行补救。 28 | 29 | **实现方式**: 30 | - **版本号**:在表中添加一个版本号字段,每次更新数据时,版本号都会递增。事务在提交时检查版本号是否匹配,如果不匹配则回滚或重试。 31 | - **时间戳**:在表中添加一个时间戳字段,每次更新时记录时间戳。事务在提交时检查时间戳是否匹配,确保数据在提交期间没有被修改。 32 | 33 | **优点**: 34 | - **减少锁竞争**:由于数据在事务期间没有被锁定,减少了锁竞争,提高了并发性能。 35 | - **避免死锁**:乐观锁不涉及锁的获取和释放,因此避免了死锁问题。 36 | 37 | **缺点**: 38 | - **处理复杂性**:事务需要检查数据版本或时间戳,如果发生冲突,事务可能需要重新执行或处理失败的情况。 39 | - **不适用于高冲突环境**:在高冲突环境下,重试操作的开销可能较大。 40 | 41 | **使用场景**: 42 | - **低冲突环境**:数据更新冲突的可能性较低,如用户信息的更新。 43 | - **高并发环境**:需要高并发性能的应用,如Web应用的会话管理。 44 | 45 | ### 总结 46 | 47 | - **悲观锁**:在事务执行期间锁定数据,以确保一致性和完整性,适用于高冲突或复杂事务场景。缺点是可能导致性能下降和死锁。 48 | - **乐观锁**:假设数据冲突不会发生,减少了锁的竞争,适用于低冲突环境或需要高并发性能的场景。缺点是处理冲突和重试可能增加复杂性。 49 | 50 | 根据应用场景和业务需求选择合适的锁策略,可以有效地管理并发访问和数据一致性。 -------------------------------------------------------------------------------- /docs/Mysql/查询语句的执行顺序.md: -------------------------------------------------------------------------------- 1 | 在 SQL 中,一条查询语句的执行顺序通常是从内到外的,这意味着它的处理步骤并不完全按照书写的顺序。以下是一个典型 SQL 查询语句的执行顺序: 2 | 3 | ### 1. **FROM** 4 | - 确定要查询的表,并从中提取数据。此阶段可能会涉及到联接操作。 5 | 6 | ### 2. **JOIN** 7 | - 如果查询涉及多个表的联接,则在这一阶段执行。所有联接条件将在此处评估。 8 | 9 | ### 3. **WHERE** 10 | - 过滤结果集,仅保留满足条件的记录。此步骤在数据提取后立即进行。 11 | 12 | ### 4. **GROUP BY** 13 | - 将结果集按指定字段进行分组。这通常与聚合函数(如 COUNT、SUM 等)一起使用。 14 | 15 | ### 5. **HAVING** 16 | - 在分组后进行的过滤操作,用于过滤聚合结果集中的记录。 17 | 18 | ### 6. **SELECT** 19 | - 确定要返回的列,包括计算字段、聚合函数等。在这一阶段执行选择和计算操作。 20 | 21 | ### 7. **DISTINCT** 22 | - 如果指定了 `DISTINCT`,则在这一阶段去除重复的记录。 23 | 24 | ### 8. **ORDER BY** 25 | - 对结果集按指定列进行排序。这是在所有过滤和分组操作之后进行的。 26 | 27 | ### 9. **LIMIT/OFFSET** 28 | - 如果使用了 `LIMIT` 或 `OFFSET`,则在此阶段限制返回的记录数量或指定结果的起始位置。 29 | 30 | ### 示例查询 31 | 32 | ```sql 33 | SELECT DISTINCT name, COUNT(*) 34 | FROM students 35 | WHERE age > 18 36 | GROUP BY name 37 | HAVING COUNT(*) > 1 38 | ORDER BY name 39 | LIMIT 10; 40 | ``` 41 | 42 | ### 执行顺序 43 | 44 | 1. **FROM students** 45 | 2. **WHERE age > 18** 46 | 3. **GROUP BY name** 47 | 4. **HAVING COUNT(*) > 1** 48 | 5. **SELECT DISTINCT name, COUNT(*)** 49 | 6. **ORDER BY name** 50 | 7. **LIMIT 10** 51 | 52 | ### 总结 53 | 54 | SQL 查询的执行顺序遵循特定的逻辑步骤,尽管我们书写时可能不按照这个顺序。理解这个执行顺序有助于优化查询性能和理解查询结果。如果还有其他问题或需要进一步的细节,请告诉我! -------------------------------------------------------------------------------- /docs/Mysql/联合索引.md: -------------------------------------------------------------------------------- 1 | 在MySQL中,联合索引是按照索引中字段的顺序依次存储的。在你提到的联合索引 `(name, age)` 中,`name` 字段在前,`age` 字段在后。这意味着在查询时,MySQL 会优先使用 `name` 字段来过滤数据,然后再使用 `age` 字段进一步筛选。 2 | 3 | ### 查询条件 4 | - `name LIKE 'c%'` 5 | - `age = 10` 6 | 7 | ### 索引的使用情况 8 | 1. **`name` 字段**: 9 | - 查询条件是 `name LIKE 'c%'`。由于`LIKE 'c%'`有一个前缀匹配的条件(即没有使用通配符`%`开头),MySQL可以使用索引中的`name`字段进行范围扫描(range scan)。这个扫描会定位到所有以字母“c”开头的`name`字段。 10 | 11 | 2. **`age` 字段**: 12 | - 因为`name`字段的前缀匹配已经使用了索引,并且`age`是联合索引中的第二个字段,MySQL接着会在`name`匹配到的结果集中使用`age = 10`的条件进行进一步的过滤。 13 | - 因此,在联合索引 `(name, age)` 上,MySQL可以有效地利用两个字段的索引来加速查询。 14 | 15 | ### 索引使用过程 16 | - **第一步**:MySQL首先使用`name`字段的前缀匹配来缩小范围。这会选出所有以“c”开头的`name`字段的记录。 17 | - **第二步**:在范围缩小后的结果集里,MySQL会使用`age = 10`的条件进行精准匹配,进一步过滤出符合条件的记录。 18 | 19 | ### 总结 20 | 对于查询条件 `name LIKE 'c%' AND age = 10`,MySQL会使用联合索引 `(name, age)`,首先根据`name`字段进行前缀匹配,然后根据`age`字段进行精准匹配,从而提高查询的效率。 -------------------------------------------------------------------------------- /docs/Mysql/聚簇索引和非聚簇索引.md: -------------------------------------------------------------------------------- 1 | 聚簇索引(Clustered Index)和非聚簇索引(Non-Clustered Index)是数据库管理系统中两种不同的索引类型,它们在数据的物理存储方式、查询效率、使用场景等方面都有显著的区别。 2 | 3 | ### 聚簇索引(Clustered Index) 4 | 5 | **定义**: 6 | - 聚簇索引是一种将表中的数据行与索引按顺序存储的索引类型。在一个表中,数据的物理存储顺序与聚簇索引的键值顺序相同。 7 | 8 | **特点**: 9 | - **唯一性**:每个表只能有一个聚簇索引,因为表的数据只能以一种物理顺序存储。 10 | - **存储结构**:聚簇索引的叶子节点包含实际的数据行,而不是指向数据行的指针。这意味着通过聚簇索引查找数据时,不需要再进行额外的回表操作。 11 | - **查询性能**:对于范围查询(如 `BETWEEN`、`<`、`>`)、排序操作,以及需要读取连续数据的查询,聚簇索引性能较高,因为数据物理上是连续存储的。 12 | 13 | **应用场景**: 14 | - 适用于频繁进行排序操作或范围查询的列,例如日期字段、自动递增的主键字段等。 15 | - 在 MySQL 的 InnoDB 存储引擎中,主键通常被默认用作聚簇索引。 16 | 17 | ### 非聚簇索引(Non-Clustered Index) 18 | 19 | **定义**: 20 | - 非聚簇索引是一种独立于数据存储顺序的索引类型。非聚簇索引的叶子节点存储的是指向数据行的指针,而不是实际的数据行。 21 | 22 | **特点**: 23 | - **多个索引**:一个表可以有多个非聚簇索引,因为这些索引不会影响数据的物理存储顺序。 24 | - **存储结构**:非聚簇索引的叶子节点包含索引键值和指向对应数据行的指针。这意味着通过非聚簇索引查找数据时,可能需要通过指针进行额外的回表操作来获取实际数据。 25 | - **查询性能**:非聚簇索引在精确查找(如查找某个具体值)时性能较好,但对于需要大量回表的查询,性能可能不如聚簇索引。 26 | 27 | **应用场景**: 28 | - 适用于需要快速查找特定值的列,例如用户名、邮箱、唯一标识符等。 29 | - 可以在需要多个查询条件时使用多个非聚簇索引。 30 | 31 | ### 聚簇索引与非聚簇索引的比较 32 | 33 | - **数据存储**: 34 | - 聚簇索引:数据按索引键的顺序存储,叶子节点包含实际数据。 35 | - 非聚簇索引:数据存储顺序独立于索引,叶子节点包含指向数据的指针。 36 | 37 | - **数量限制**: 38 | - 聚簇索引:一个表只能有一个。 39 | - 非聚簇索引:一个表可以有多个。 40 | 41 | - **查询性能**: 42 | - 聚簇索引:适合范围查询、排序操作,因为数据物理上是连续存储的。 43 | - 非聚簇索引:适合精确查找,但对于范围查询和需要回表的操作性能较差。 44 | 45 | - **空间开销**: 46 | - 聚簇索引:由于数据按索引存储,不需要额外的指针存储,节省空间。 47 | - 非聚簇索引:需要额外存储指针,空间开销更大。 48 | 49 | ### 总结 50 | 51 | - **聚簇索引**:数据和索引是合二为一的,适合那些需要排序和范围查询的场景。 52 | - **非聚簇索引**:数据和索引分离,适合快速精确查找的场景,可以创建多个,提供灵活的查询优化手段。 -------------------------------------------------------------------------------- /docs/Mysql/表锁和行锁机制.md: -------------------------------------------------------------------------------- 1 | 在MySQL中,表锁和行锁是两种主要的锁机制,用于处理多用户并发访问数据库时的竞争条件。它们各自有不同的应用场景和特性。 2 | 3 | ### 表锁(Table Locks) 4 | 5 | **概念**: 6 | - 表锁是指对整个表加锁,这样在锁定期间其他线程(或事务)不能对该表进行任何操作,包括读取和写入。 7 | 8 | **特性**: 9 | - **粒度大**:锁住整个表,这意味着锁的粒度较大,可能会导致较大的锁竞争。 10 | - **开销低**:表锁的开销较低,因为它不需要记录和管理行级的锁信息。 11 | - **锁的范围广**:由于锁住了整个表,所以在锁定期间其他事务无法对表中的任何行进行操作。 12 | - **适用场景**:表锁通常适用于读写操作较少、并发要求较低的场景。适用于需要确保整个表一致性的操作,例如批量更新或删除操作。 13 | 14 | **锁类型**: 15 | - **读锁(共享锁)**:允许其他事务读取表,但不允许写入。多个读锁可以同时存在。 16 | - **写锁(排他锁)**:不允许其他事务读取或写入表。写锁会阻塞所有其他的读锁和写锁。 17 | 18 | ### 行锁(Row Locks) 19 | 20 | **概念**: 21 | - 行锁是指对表中的特定行加锁,这样在锁定期间其他线程(或事务)只能对这些特定的行进行操作,而不会影响其他行。 22 | 23 | **特性**: 24 | - **粒度小**:锁的粒度较小,只锁定需要操作的行,从而提高了并发性和性能。 25 | - **开销高**:管理行锁的开销较大,因为需要记录和管理每个被锁定的行。 26 | - **锁的范围小**:由于只锁定了特定的行,其他行可以被并发访问,从而减少了锁竞争。 27 | - **适用场景**:行锁适用于高并发的场景,特别是当操作的表中只有部分行需要锁定时。适用于需要高并发的在线事务处理(OLTP)系统。 28 | 29 | **锁类型**: 30 | - **共享锁(读锁)**:允许其他事务读取锁定的行,但不允许修改。 31 | - **排他锁(写锁)**:不允许其他事务读取或修改锁定的行。 32 | 33 | ### 锁机制的对比 34 | 35 | | 特性 | 表锁 | 行锁 | 36 | |------------|----------------------------|----------------------------| 37 | | 锁粒度 | 大(锁住整个表) | 小(锁住特定行) | 38 | | 并发性 | 低(锁住整个表会影响其他操作) | 高(只锁住特定行,其他行不受影响) | 39 | | 开销 | 低(不需要记录每行锁信息) | 高(需要记录和管理每行锁信息) | 40 | | 适用场景 | 读写操作较少、低并发场景 | 高并发、高更新频繁的场景 | 41 | 42 | ### 在MySQL中的实现 43 | 44 | - **MyISAM存储引擎**:主要使用表锁。MyISAM存储引擎锁定整个表进行读写操作,不支持行锁。 45 | - **InnoDB存储引擎**:支持行锁。InnoDB存储引擎通过实现行级锁来提高并发性,并且支持事务和多版本并发控制(MVCC)。 46 | 47 | 选择使用表锁还是行锁取决于应用场景的需求,比如数据的访问模式、并发要求以及对性能的期望。 -------------------------------------------------------------------------------- /docs/Mysql/记录锁、间隙锁与临键锁.md: -------------------------------------------------------------------------------- 1 | 在MySQL的InnoDB存储引擎中,行锁是一种关键机制,用于在高并发环境下实现数据的一致性和隔离性。InnoDB支持三种主要的行锁类型:记录锁、间隙锁和临键锁。每种锁有不同的锁定范围和用途,用于处理各种并发控制场景。 2 | 3 | ### 1. 记录锁(Record Locks) 4 | 5 | **概念**: 6 | - 记录锁是锁定单个数据行,以确保在锁定期间其他事务无法修改或删除该行数据。记录锁是最基本的行级锁。 7 | 8 | **特性**: 9 | - **锁定单行数据**:只对特定的行进行锁定,其他行可以被并发访问。 10 | - **精确锁定**:精确到具体的记录,避免了不必要的锁竞争。 11 | - **应用场景**:适用于需要保护特定记录的并发事务,如更新或删除操作。 12 | 13 | **示例**: 14 | - 如果有一个表 `employees`,事务A对某一员工记录加锁,那么在事务A提交或回滚之前,其他事务不能对该员工记录进行修改。 15 | 16 | ### 2. 间隙锁(Gap Locks) 17 | 18 | **概念**: 19 | - 间隙锁是对记录间隙(即数据行之间的空白区间)加锁,以防止在该区间内插入新的记录。间隙锁防止了其他事务在当前事务锁定的区间内插入新的数据行,从而确保数据一致性。 20 | 21 | **特性**: 22 | - **锁定间隙**:锁定记录之间的“间隙”,以防止插入新的记录。 23 | - **不锁定已有记录**:间隙锁不会锁定已有的记录,只锁定记录之间的间隙。 24 | - **应用场景**:用于防止其他事务插入新记录,适用于处理范围查询或范围更新时。 25 | 26 | **示例**: 27 | - 在一个有序的表 `employees` 上,事务A对某一范围的记录进行查询或修改,InnoDB会在该范围内加锁,以防止其他事务插入新的记录到该范围内。 28 | 29 | ### 3. 临键锁(Next-Key Locks) 30 | 31 | **概念**: 32 | - 临键锁是记录锁和间隙锁的组合,锁定一个记录及其前面的间隙。临键锁确保了对某一记录的锁定,并同时锁定该记录之前的间隙,从而防止其他事务插入到该记录的前面或修改该记录。 33 | 34 | **特性**: 35 | - **组合锁定**:同时锁定记录和记录之前的间隙。 36 | - **避免幻读**:防止在事务中插入或修改数据的同时出现幻读问题。 37 | - **应用场景**:适用于需要避免幻读的事务,特别是在有唯一索引的表中,临键锁能够确保事务的隔离性和一致性。 38 | 39 | **示例**: 40 | - 在表 `employees` 中,如果事务A对某一记录加锁,InnoDB会锁定该记录及其前面的间隙,以防止其他事务插入新的记录到该记录前面的间隙中。 41 | 42 | ### 行锁的作用和实现 43 | 44 | - **避免死锁**:行锁机制通过锁定特定记录和间隙,减少了表级锁竞争,从而降低了死锁的可能性。 45 | - **提高并发性**:通过锁定粒度较小的行,而不是整个表,InnoDB能够在高并发环境下提供更好的性能和可伸缩性。 46 | - **保证事务隔离性**:行锁机制通过控制数据的并发访问,确保事务的隔离性和一致性,避免了数据竞争和不一致的问题。 47 | 48 | ### 小结 49 | 50 | - **记录锁**:锁定单个记录,适用于对特定记录的操作。 51 | - **间隙锁**:锁定记录之间的间隙,防止插入新记录。 52 | - **临键锁**:组合了记录锁和间隙锁,锁定记录及其前面的间隙,确保事务隔离性。 53 | 54 | 这三种锁机制在InnoDB中共同工作,以处理复杂的并发操作和数据一致性问题。在设计数据库和事务时,了解这些锁机制有助于优化性能和避免潜在的并发问题。 -------------------------------------------------------------------------------- /docs/Mysql/说说脏读、不可重复读、幻读,怎么解决的.md: -------------------------------------------------------------------------------- 1 | 在数据库事务管理中,脏读、不可重复读和幻读是三种典型的并发问题。每种问题都与事务的隔离级别有关,通过调整事务隔离级别可以解决这些问题。 2 | 3 | ### 1. 脏读(Dirty Read) 4 | **定义**:脏读是指一个事务可以读取另一个事务尚未提交的修改。如果该事务回滚,则第一个事务所读取的数据将无效。 5 | 6 | **场景**: 7 | - 事务 A 修改了一条记录,但尚未提交。 8 | - 事务 B 读取了这条未提交的记录。 9 | - 事务 A 之后回滚,撤销了修改。 10 | - 此时,事务 B 读取的数据是无效的,即脏数据。 11 | 12 | **解决办法**:通过设置 **`READ COMMITTED`** 或更高级别的事务隔离级别可以避免脏读。常见的隔离级别有: 13 | - **`READ COMMITTED`**:只允许读取已提交的修改,从而防止脏读。 14 | 15 | ### 2. 不可重复读(Non-repeatable Read) 16 | **定义**:不可重复读是指在同一个事务中多次读取同一数据时,读到的数据不一致。因为在两次读取之间,另一个事务可能已经修改并提交了该数据。 17 | 18 | **场景**: 19 | - 事务 A 读取一条记录。 20 | - 事务 B 修改了这条记录并提交。 21 | - 事务 A 再次读取这条记录,发现内容已发生变化,无法保证数据的重复读取一致性。 22 | 23 | **解决办法**:通过设置 **`REPEATABLE READ`** 隔离级别可以防止不可重复读。 24 | - **`REPEATABLE READ`**:事务在开始时所读取的数据在整个事务期间保持一致,避免了不可重复读。 25 | 26 | ### 3. 幻读(Phantom Read) 27 | **定义**:幻读是指在一个事务中,两次相同的查询条件可能返回不同的结果集,第一次查询后,另一个事务插入了新记录,导致第二次查询出现“幻影”数据。 28 | 29 | **场景**: 30 | - 事务 A 读取一组符合条件的记录。 31 | - 事务 B 插入了一条新记录,并提交。 32 | - 事务 A 再次读取相同的条件,发现多了一条记录,即幻读。 33 | 34 | **解决办法**:通过设置 **`SERIALIZABLE`** 隔离级别可以防止幻读。 35 | - **`SERIALIZABLE`**:最严格的隔离级别,保证事务之间完全串行执行,避免幻读问题。 36 | 37 | ### 各个事务隔离级别 38 | 39 | 1. **`READ UNCOMMITTED`**(未提交读):允许脏读、不可重复读、幻读。 40 | 2. **`READ COMMITTED`**(提交读):防止脏读,但允许不可重复读和幻读。 41 | 3. **`REPEATABLE READ`**(可重复读):防止脏读和不可重复读,但允许幻读。 42 | 4. **`SERIALIZABLE`**(可串行化):防止脏读、不可重复读和幻读,但性能较差。 43 | 44 | ### 总结 45 | - **脏读**:事务读到了未提交的数据,通过 `READ COMMITTED` 解决。 46 | - **不可重复读**:同一事务多次读取同一数据结果不一致,通过 `REPEATABLE READ` 解决。 47 | - **幻读**:同一事务多次查询结果集不一致,通过 `SERIALIZABLE` 解决。 48 | 49 | 在实际应用中,根据业务需求选择合适的隔离级别,平衡性能与数据一致性。 -------------------------------------------------------------------------------- /docs/Network/ARP协议工作原理.md: -------------------------------------------------------------------------------- 1 | ARP(Address Resolution Protocol,地址解析协议)是一种网络协议,用于将 IP 地址转换为 MAC 地址。在以太网中,数据帧通过 MAC 地址进行传输,而 IP 地址则用于网络层的路由,因此在局域网中发送数据前,需要将目标 IP 地址解析为对应的 MAC 地址。ARP 正是用于完成这一任务的。 2 | 3 | ### ARP 协议的工作原理 4 | 5 | #### 1. **ARP 请求(ARP Request)** 6 | 当一台主机需要向同一局域网内的另一台主机发送数据时,它首先需要知道目标主机的 MAC 地址。如果源主机只知道目标主机的 IP 地址但不知道其 MAC 地址,它会发出一个 ARP 请求。 7 | 8 | - **广播发送**:ARP 请求是以广播方式发送的,数据帧的目标 MAC 地址设置为全 F(即 FF:FF:FF:FF:FF:FF),表示网络中所有设备都能接收到这个请求。 9 | - **请求内容**:ARP 请求帧中包含了源主机的 IP 地址和 MAC 地址,以及目标主机的 IP 地址。此时,目标主机的 MAC 地址是未知的。 10 | 11 | #### 2. **ARP 回复(ARP Reply)** 12 | 局域网内所有的设备都会接收到这个广播请求,但只有目标主机会做出响应。 13 | 14 | - **单播回复**:目标主机在确认该 ARP 请求中包含的 IP 地址与自己的 IP 地址匹配后,会发送一个 ARP 回复。这是一个单播消息,直接发送给发出请求的源主机。 15 | - **回复内容**:ARP 回复中包含了目标主机的 MAC 地址,以及请求中对应的 IP 地址。 16 | 17 | #### 3. **更新 ARP 缓存** 18 | - **源主机更新缓存**:源主机在接收到 ARP 回复后,会将目标主机的 IP 地址与 MAC 地址的对应关系存入本地的 ARP 缓存中。之后,源主机可以直接使用这个 MAC 地址进行数据传输,而无需再次发送 ARP 请求。 19 | - **目标主机更新缓存**:同时,目标主机也可以将源主机的 IP 地址和 MAC 地址对存入它自己的 ARP 缓存中,以备将来使用。 20 | 21 | #### 4. **数据传输** 22 | 在获得目标主机的 MAC 地址后,源主机可以构建数据帧,将其 IP 数据包封装在以太网帧中,并将帧的目标 MAC 地址设置为刚刚解析到的目标主机的 MAC 地址,然后通过物理层将数据发送出去。 23 | 24 | ### ARP 缓存 25 | - **缓存机制**:为了减少频繁的 ARP 请求和提高网络效率,每台主机会维护一个 ARP 缓存,用于存储 IP 地址与 MAC 地址的映射。这个缓存是有时间限制的,通常条目会在几分钟后过期,以保证映射的有效性。 26 | 27 | - **缓存查看**:在大多数操作系统中,可以通过命令(如 `arp -a`)查看当前的 ARP 缓存内容。 28 | 29 | ### ARP 协议中的问题 30 | 1. **ARP 欺骗(ARP Spoofing)**: 31 | - 在局域网中,由于 ARP 请求是以广播方式发送的,恶意主机可以伪装成其他设备,发送伪造的 ARP 回复,导致网络中的其他主机更新错误的 IP-MAC 对应关系。这种攻击方式称为 ARP 欺骗,常被用来执行中间人攻击(MITM)。 32 | 33 | 2. **广播风暴**: 34 | - 如果局域网中的主机数量很多,频繁的 ARP 广播请求可能导致网络拥塞,影响性能。 35 | 36 | ### 总结 37 | ARP 协议通过广播 ARP 请求来解析 IP 地址,并通过 ARP 回复获取目标主机的 MAC 地址。这一过程在局域网内进行,通常是无感知且高效的,但也存在一定的安全风险,如 ARP 欺骗等。ARP 缓存的引入提高了解析效率,但需要注意缓存的有效性和安全性。 -------------------------------------------------------------------------------- /docs/Network/HTTP 1.0,1.1,2.0 的区别.md: -------------------------------------------------------------------------------- 1 | HTTP 1.0、1.1 和 2.0 是三个不同版本的超文本传输协议(HTTP),它们之间有一些关键的区别,主要体现在连接管理、性能优化和功能扩展等方面。 2 | 3 | ### HTTP 1.0 4 | - **单次请求-响应**:HTTP 1.0 是无状态协议,每次请求都会建立一个新的TCP连接,服务器处理完请求后立即关闭连接。 5 | - **请求头简化**:HTTP 1.0 请求头较为简单,不支持Host头部字段,因此不能直接支持虚拟主机。 6 | - **缓存控制**:HTTP 1.0 引入了简单的缓存控制机制,如通过 `Expires` 头部指定资源的过期时间。 7 | 8 | ### HTTP 1.1 9 | - **持久连接**:HTTP 1.1 默认支持持久连接(Persistent Connection),即 `Connection: keep-alive`。多个请求可以复用同一个TCP连接,从而减少连接的建立和断开带来的开销。 10 | - **管道机制**:HTTP 1.1 引入了请求管线化(Pipelining),允许客户端在收到第一个响应前发送多个请求。但由于队头阻塞(Head-of-Line Blocking)问题,这个特性并没有得到广泛应用。 11 | - **Host头**:HTTP 1.1 要求所有请求必须包含Host头部字段,支持同一服务器上多个域名的虚拟主机。 12 | - **缓存控制增强**:引入了 `Cache-Control` 头部字段,提供更细粒度的缓存控制策略。 13 | - **分块传输编码**:支持分块传输编码(Chunked Transfer Encoding),允许服务器在数据尚未全部准备好时开始传输,适用于流式数据传输。 14 | 15 | ### HTTP 2.0 16 | - **二进制协议**:HTTP 2.0 将HTTP消息封装为二进制帧(Binary Framing Layer),而不是文本协议。这种改进使得协议更加紧凑、高效,减少了解析开销。 17 | - **多路复用**:HTTP 2.0 支持多路复用(Multiplexing),允许在一个TCP连接上同时发送多个请求和响应,消除了HTTP 1.x中的队头阻塞问题。 18 | - **头部压缩**:HTTP 2.0 引入了HPACK头部压缩算法,大大减少了请求和响应中的头部大小,提升了传输效率。 19 | - **服务器推送**:HTTP 2.0 支持服务器推送(Server Push),允许服务器在客户端请求前主动推送资源,减少延迟。 20 | - **优先级**:HTTP 2.0 允许客户端为请求设置优先级,服务器可以根据优先级优化资源传输顺序。 21 | 22 | ### 总结 23 | - **HTTP 1.0**:每次请求都需要建立和关闭连接,效率较低,功能简单。 24 | - **HTTP 1.1**:引入了持久连接和请求管线化,优化了连接管理和缓存控制,增加了对虚拟主机的支持。 25 | - **HTTP 2.0**:采用二进制传输,多路复用,头部压缩和服务器推送等新特性,显著提升了传输效率和用户体验。 -------------------------------------------------------------------------------- /docs/Network/HTTP 的方法有哪些.md: -------------------------------------------------------------------------------- 1 | HTTP 协议定义了一组方法(又称为请求方式),用于指示客户端要对资源执行的操作。主要的 HTTP 方法包括: 2 | 3 | ### 常用的 HTTP 方法 4 | 5 | 1. **GET** 6 | - **功能**:请求获取指定资源的表示。GET 方法用于从服务器获取数据,不应该改变服务器上的资源状态。 7 | - **特点**:安全的、幂等的(多次相同的 GET 请求应返回相同的结果)。 8 | 9 | 2. **POST** 10 | - **功能**:向指定资源提交数据,用于创建或更新资源。POST 方法可能会导致服务器上的资源状态发生改变。 11 | - **特点**:非幂等的(多次相同的 POST 请求可能会创建多个资源或重复操作)。 12 | 13 | 3. **PUT** 14 | - **功能**:更新指定资源的表示,或者创建资源(如果资源不存在的话)。PUT 方法会用请求中的数据替换指定资源的现有内容。 15 | - **特点**:幂等的(多次相同的 PUT 请求将产生相同的结果)。 16 | 17 | 4. **DELETE** 18 | - **功能**:请求删除指定的资源。 19 | - **特点**:幂等的(多次相同的 DELETE 请求应产生相同的结果,尽管删除操作可能会导致资源不存在)。 20 | 21 | 5. **HEAD** 22 | - **功能**:获取指定资源的元数据(如头部信息),不获取资源主体。HEAD 方法与 GET 方法类似,但不返回资源主体部分。 23 | - **特点**:安全的、幂等的。 24 | 25 | 6. **OPTIONS** 26 | - **功能**:查询服务器支持的 HTTP 方法。OPTIONS 方法用于描述目标资源的通信选项。 27 | - **特点**:安全的、幂等的。 28 | 29 | 7. **PATCH** 30 | - **功能**:对资源进行部分修改。与 PUT 不同,PATCH 方法只需提交修改的数据,而不是整个资源。 31 | - **特点**:非幂等的(多次相同的 PATCH 请求可能产生不同的结果)。 32 | 33 | 8. **TRACE** 34 | - **功能**:回显服务器收到的请求,用于诊断和调试。TRACE 方法用于将服务器收到的请求报文原封不动地返回给客户端。 35 | - **特点**:安全的、幂等的。 36 | 37 | 9. **CONNECT** 38 | - **功能**:将请求的通道建立为一个透明的 TCP 通道,用于 SSL/TLS 连接等。CONNECT 方法用于创建一个隧道,用于在客户端和服务器之间传递数据。 39 | - **特点**:通常与 HTTPS 相关,用于代理服务器。 40 | 41 | 这些方法提供了不同的操作功能,允许客户端与服务器之间进行各种交互,满足 Web 应用程序的需求。 -------------------------------------------------------------------------------- /docs/Network/HTTP协议中的OPTIONS.md: -------------------------------------------------------------------------------- 1 | HTTP协议中的`OPTIONS`方法用于查询服务器支持的通信选项。它可以用于确认服务器允许哪些HTTP方法、支持哪些特性(如CORS、认证方式等),以及用于特定资源或整个服务器的配置。 2 | 3 | ### 主要用途 4 | 5 | 1. **跨域资源共享(CORS)预检请求**: 6 | - 在浏览器中,当前端发起跨域请求且使用了非简单HTTP方法(如`PUT`、`DELETE`、`PATCH`)或带有自定义请求头时,浏览器会首先发送一个`OPTIONS`请求,称为“预检请求”(Preflight Request)。通过这个预检请求,浏览器可以询问服务器是否允许该跨域请求,并确认实际请求是否安全。 7 | - 服务器通过响应`Access-Control-Allow-Methods`、`Access-Control-Allow-Headers`等HTTP头来告知浏览器允许的请求方法和头信息。 8 | 9 | 2. **探测服务器支持的HTTP方法**: 10 | - 客户端可以发送`OPTIONS`请求来检测服务器支持哪些HTTP方法(如`GET`、`POST`、`PUT`等),从而避免发送不支持的方法导致的错误响应。 11 | - 服务器会通过响应头`Allow`返回支持的HTTP方法列表。 12 | 13 | 3. **服务器或资源的能力探测**: 14 | - `OPTIONS`方法可以用来查询服务器整体或特定资源的能力配置,比如允许的内容类型、是否支持身份验证等。 15 | 16 | ### 示例 17 | 18 | **请求:** 19 | ```http 20 | OPTIONS /resource HTTP/1.1 21 | Host: example.com 22 | ``` 23 | 24 | **响应:** 25 | ```http 26 | HTTP/1.1 204 No Content 27 | Allow: GET, POST, OPTIONS 28 | Access-Control-Allow-Methods: GET, POST, OPTIONS 29 | Access-Control-Allow-Headers: Content-Type, Authorization 30 | ``` 31 | 32 | - **Allow**:列出了服务器支持的HTTP方法。 33 | - **Access-Control-Allow-Methods**:列出了允许的跨域HTTP方法。 34 | - **Access-Control-Allow-Headers**:列出了允许的自定义请求头。 35 | 36 | ### 总结 37 | `OPTIONS`方法主要用于客户端在执行请求前探测服务器支持的能力或设置。它是实现跨域请求处理的重要部分,尤其在现代Web应用中非常常见。 -------------------------------------------------------------------------------- /docs/Network/Linux端口范围.md: -------------------------------------------------------------------------------- 1 | 在 Linux 系统中,端口的使用范围由系统内核设置和网络协议标准决定。以下是有关 Linux 中端口范围的一些关键点: 2 | 3 | ### 1. 端口分类 4 | 5 | - **系统端口(Well-Known Ports)**:0-1023 6 | - 这些端口用于标准服务和协议,例如 HTTP(80)、HTTPS(443)、FTP(21)、SSH(22)等。 7 | - 只有系统进程或需要超级用户权限的程序可以绑定这些端口。 8 | 9 | - **注册端口(Registered Ports)**:1024-49151 10 | - 这些端口由应用程序使用,通常是应用程序的特定服务,如数据库服务(例如 MySQL 的 3306 端口)或其他应用。 11 | - 这些端口不需要超级用户权限,但应当避免与知名应用的端口冲突。 12 | 13 | - **动态端口(Dynamic/Private Ports)**:49152-65535 14 | - 这些端口通常用于临时或动态分配,例如客户端应用程序的端口。 15 | - 这些端口用于从系统端口池中分配给应用程序,通常由操作系统在运行时分配。 16 | 17 | ### 2. 查看和配置端口范围 18 | 19 | #### 查看端口范围 20 | 可以使用以下命令查看系统中的端口范围设置: 21 | 22 | ```bash 23 | cat /proc/sys/net/ipv4/ip_local_port_range 24 | ``` 25 | 26 | 该命令的输出类似于: 27 | 28 | ``` 29 | 32768 60999 30 | ``` 31 | 32 | 这表示系统的动态端口范围是 32768 到 60999。 33 | 34 | #### 配置端口范围 35 | 如果需要更改动态端口范围,可以使用 `sysctl` 命令或直接修改 `/etc/sysctl.conf` 文件: 36 | 37 | 1. **临时更改**: 38 | ```bash 39 | sudo sysctl -w net.ipv4.ip_local_port_range="20000 65000" 40 | ``` 41 | 42 | 2. **永久更改**: 43 | - 编辑 `/etc/sysctl.conf` 文件,添加或修改以下行: 44 | ```bash 45 | net.ipv4.ip_local_port_range = 20000 65000 46 | ``` 47 | - 使更改生效: 48 | ```bash 49 | sudo sysctl -p 50 | ``` 51 | 52 | ### 3. 端口冲突和安全性 53 | 54 | - **端口冲突**:确保应用程序在绑定端口时不会与系统服务或其他应用程序发生冲突。通常,通过检查 `/etc/services` 文件和使用 `netstat` 或 `ss` 命令来避免端口冲突。 55 | - **安全性**:为了提高安全性,建议限制外部访问非必要的端口,并使用防火墙(如 `iptables` 或 `firewalld`)来控制端口访问。 56 | 57 | ### 4. 相关命令 58 | 59 | - **查看正在使用的端口**: 60 | ```bash 61 | netstat -tuln 62 | ``` 63 | 或 64 | ```bash 65 | ss -tuln 66 | ``` 67 | 68 | - **查看端口占用情况**: 69 | ```bash 70 | lsof -i :port_number 71 | ``` 72 | 73 | - **列出端口和协议**: 74 | ```bash 75 | sudo nmap -sT -p- localhost 76 | ``` 77 | 78 | 这些工具和命令可以帮助你查看和管理系统中的端口使用情况。 -------------------------------------------------------------------------------- /docs/Network/TCP 三次握手以及四次挥手的流程.md: -------------------------------------------------------------------------------- 1 | ### TCP 三次握手(Three-Way Handshake) 2 | 3 | TCP 三次握手是建立一个可靠的 TCP 连接的过程,目的是为了确保双方能够正常收发数据,确认彼此的接收能力与发送能力。 4 | 5 | **1. 第一次握手(SYN):** 6 | - 客户端向服务器发送一个 SYN(Synchronize Sequence Number,同步序列号)报文,表示请求建立连接。 7 | - SYN 报文中包含一个初始序列号 `Seq`,表示数据传输的序列号起点。 8 | 9 | **2. 第二次握手(SYN-ACK):** 10 | - 服务器收到 SYN 报文后,回复一个 SYN-ACK 报文,表示同意连接,并同时请求客户端确认连接。 11 | - SYN-ACK 报文中包含服务器的初始序列号 `Seq` 和对客户端的 ACK(Acknowledgment,确认)序列号 `Ack = 客户端的 Seq + 1`。 12 | 13 | **3. 第三次握手(ACK):** 14 | - 客户端收到服务器的 SYN-ACK 报文后,向服务器发送一个 ACK 报文,表示确认连接已经建立。 15 | - ACK 报文中包含 `Ack = 服务器的 Seq + 1`,此时,TCP 连接正式建立,双方可以开始数据传输。 16 | 17 | ### TCP 四次挥手(Four-Way Handshake) 18 | 19 | TCP 四次挥手是终止一个 TCP 连接的过程,确保双方都已完成数据传输并释放资源。 20 | 21 | **1. 第一次挥手(FIN):** 22 | - 客户端发送一个 FIN(Finish)报文,表示结束数据发送,请求关闭连接。 23 | - 发送 FIN 的一方不会再发送数据,但可以接收数据。 24 | 25 | **2. 第二次挥手(ACK):** 26 | - 服务器收到 FIN 报文后,发送一个 ACK 报文,表示确认收到关闭请求。 27 | - 此时,服务器仍可以继续发送数据。 28 | 29 | **3. 第三次挥手(FIN):** 30 | - 服务器发送完所有数据后,向客户端发送 FIN 报文,表示数据发送完毕,请求关闭连接。 31 | 32 | **4. 第四次挥手(ACK):** 33 | - 客户端收到服务器的 FIN 报文后,发送一个 ACK 报文,确认连接关闭。 34 | - ACK 发送后,客户端等待一段时间(TIME_WAIT 状态)后,正式关闭连接。 35 | 36 | ### 为什么需要三次握手和四次挥手? 37 | 38 | **三次握手的原因:** 39 | - **双向确认通信双方的发送与接收能力**:三次握手确保双方都已经接收到对方的 SYN 并回复了 ACK,确认连接的建立是可靠的。 40 | - **防止重复的连接初始化**:三次握手可以避免由于网络延迟导致的旧连接请求影响新连接的建立。 41 | 42 | **四次挥手的原因:** 43 | - **全双工通信的特性**:TCP 是全双工通信,双方都需要单独关闭发送通道。四次挥手允许双方独立地结束各自的数据发送,确保在关闭连接之前,所有的数据都能被完整发送和接收。 44 | - **保证所有数据都被传输和接收**:四次挥手确保了在关闭连接之前,双方都确认了对方已经不再发送数据,从而避免数据丢失。 -------------------------------------------------------------------------------- /docs/Network/TCP 半连接发生场景.md: -------------------------------------------------------------------------------- 1 | TCP 半连接(**half-open connection**)通常发生在 TCP 三次握手过程中。具体来说,在三次握手的第二步和第三步之间,连接处于半连接状态。这意味着服务器已经收到了客户端的 SYN 包,并发送了 SYN-ACK 包,但还未收到客户端的 ACK 包,从而未完全建立连接。 2 | 3 | ### **TCP 半连接的发生场景** 4 | 5 | 1. **正常三次握手过程**: 6 | - 客户端向服务器发送 SYN 包,表示请求建立连接。 7 | - 服务器接收到 SYN 包后,返回一个 SYN-ACK 包,表示同意建立连接。这时,连接进入半连接状态(SYN_RCVD 状态)。 8 | - 客户端收到 SYN-ACK 包后,发送一个 ACK 包,服务器收到 ACK 后,连接正式建立。 9 | 10 | 2. **半连接的典型场景**: 11 | - **客户端未发送 ACK 包**:客户端在接收到 SYN-ACK 包后,可能由于网络问题或恶意攻击等原因,未发送 ACK 包。这时服务器会一直保持在半连接状态,等待 ACK 包的到来,直到超时。 12 | - **SYN flood 攻击**:攻击者伪造大量的源 IP 地址发送 SYN 包,但不发送最终的 ACK 包,导致服务器保持大量半连接状态,耗尽资源。 13 | - **网络故障**:客户端与服务器之间的网络出现故障,导致客户端的 ACK 包无法到达服务器,服务器因此停留在半连接状态。 14 | 15 | ### **TCP 半连接的处理** 16 | 17 | - **服务器处理半连接**:服务器会维护一个半连接队列,用于存储处于半连接状态的请求。TCP 协议允许服务器在等待 ACK 包时进入 SYN_RCVD 状态。如果 ACK 包未在一定时间内收到,服务器会认为连接失败,并释放资源。 18 | 19 | - **SYN Cookies**:为了防止半连接攻击(如 SYN flood),服务器可以使用 SYN cookies 技术。当收到 SYN 包时,服务器不立即分配资源,而是生成一个特殊的 SYN cookie 作为序列号,并发送给客户端。只有在收到合法的 ACK 包后,服务器才会正式建立连接,从而避免了资源浪费。 20 | 21 | ### **总结** 22 | 23 | TCP 半连接是 TCP 三次握手过程中连接未完全建立的状态,通常在客户端未发送 ACK 包时发生。半连接状态是网络攻击(如 SYN flood)的常见目标,因此需要采取措施如使用 SYN Cookies 来防止服务器资源被耗尽。 -------------------------------------------------------------------------------- /docs/Network/TCP 协议的延迟 ACK 和累计应答.md: -------------------------------------------------------------------------------- 1 | ### 延迟 ACK (Delayed Acknowledgment) 2 | 3 | **定义**: 4 | 延迟 ACK 是一种在 TCP 协议中使用的优化技术,它允许接收方在收到数据包后不立即发送 ACK 确认应答,而是延迟一段时间,等待可能会有其他数据到达,以减少网络中的 ACK 包数量。 5 | 6 | **工作原理**: 7 | 1. 当接收方收到数据包时,它会暂时不发送 ACK 确认应答。 8 | 2. 接收方会在一个小的时间窗口(通常是几十毫秒)内等待,看是否会收到更多的数据包。 9 | 3. 如果在这个时间窗口内收到了更多的数据包,接收方会将这些数据包的 ACK 一起发送,从而减少了 ACK 包的数量。 10 | 4. 如果在等待时间内没有收到更多的数据包,接收方会发送一个确认应答。 11 | 12 | **优点**: 13 | - 减少了网络中的 ACK 包数量,从而降低了网络负载。 14 | - 改善了 TCP 的传输效率,尤其是在高延迟网络中。 15 | 16 | **缺点**: 17 | - 延迟 ACK 可能会导致增加了传输延迟,因为接收方可能会延迟发送 ACK。 18 | - 在某些高负载情况下,可能会出现 ACK 延迟过长的问题,影响性能。 19 | 20 | ### 累计应答 (Cumulative Acknowledgment) 21 | 22 | **定义**: 23 | 累计应答是 TCP 协议中一种确认机制,它允许接收方通过一个 ACK 确认应答来确认所有已接收的数据包,即使这些数据包不是最后一个接收到的数据包。 24 | 25 | **工作原理**: 26 | 1. 当接收方收到数据包时,它会将 ACK 确认应答发送给发送方,确认所有从序列号 1 到当前收到的数据包的最后一个序列号的数据包都已成功接收。 27 | 2. 发送方根据接收到的 ACK 确认应答来更新其发送窗口,知道哪些数据已经被成功接收。 28 | 29 | **优点**: 30 | - 简化了接收方的确认逻辑,减少了需要处理的 ACK 数量。 31 | - 改善了 TCP 的吞吐量和性能,因为发送方可以根据累计应答来推测网络状态。 32 | 33 | **缺点**: 34 | - 如果接收方丢失了一个数据包或 ACK 包,则所有后续的数据包的确认也会受到影响。 35 | - 可能会导致不必要的数据重传,因为接收方可能会丢失数据包的 ACK,从而要求重新传输所有未确认的数据包。 -------------------------------------------------------------------------------- /docs/Network/TCP 可靠传输的保证.md: -------------------------------------------------------------------------------- 1 | TCP(传输控制协议)通过多种机制保证数据在网络上的可靠传输。这些机制包括: 2 | 3 | ### 1. **数据包重传** 4 | 5 | - **确认应答(ACK)**: 6 | - 接收方在收到数据包后,发送确认应答(ACK)给发送方,表示数据包已成功接收。如果发送方在超时时间内未收到确认应答,它会重传数据包。 7 | 8 | - **超时重传**: 9 | - 发送方会设置一个超时时间(RTO, Retransmission Timeout),如果在超时时间内没有收到 ACK,发送方会重传数据包。 10 | 11 | ### 2. **序列号和确认号** 12 | 13 | - **序列号**: 14 | - 每个数据包(TCP段)都有一个序列号,用于标识数据包的顺序和帮助接收方重新组装数据包。 15 | 16 | - **确认号**: 17 | - 确认号是接收方在 ACK 中返回的值,表示接收方期望接收到的下一个字节的序列号。这帮助发送方确定哪些数据已经被成功接收。 18 | 19 | ### 3. **流量控制** 20 | 21 | - **滑动窗口机制**: 22 | - TCP 使用滑动窗口来控制流量,防止发送方发送的数据超过接收方的处理能力。接收方通过窗口大小来告知发送方它可以接收多少数据。 23 | 24 | ### 4. **拥塞控制** 25 | 26 | - **慢启动(Slow Start)**: 27 | - 在连接初始阶段,TCP 发送方的拥塞窗口(CWND)逐渐增加,以避免突然的网络拥塞。 28 | 29 | - **拥塞避免(Congestion Avoidance)**: 30 | - 当网络中开始出现拥塞时,TCP 通过减小拥塞窗口的大小来减少数据流量,从而减轻网络拥塞。 31 | 32 | - **快速重传(Fast Retransmit)和快速恢复(Fast Recovery)**: 33 | - 如果发送方收到三个重复的 ACK(表示有数据丢失),它会立即重传丢失的数据包,并调整拥塞窗口以应对网络拥塞。 34 | 35 | ### 5. **数据完整性** 36 | 37 | - **校验和**: 38 | - TCP 在每个数据包中包含校验和字段,用于检测数据在传输过程中是否发生了错误。接收方计算接收到的数据包的校验和,并与发送方的校验和进行比较。如果不匹配,数据包会被丢弃,并可能导致重传。 39 | 40 | ### 6. **数据重组** 41 | 42 | - **顺序重组**: 43 | - 接收方根据数据包的序列号将接收到的数据重新组装成原始数据流。即使数据包乱序到达,TCP 也能确保数据按正确顺序交给应用层。 44 | 45 | ### 总结 46 | 47 | 通过以上机制,TCP 能够确保数据的完整性、顺序和准确性,从而提供可靠的端到端数据传输。这些特性使得 TCP 成为需要高可靠性的应用(如文件传输、电子邮件、网页浏览等)的理想选择。 -------------------------------------------------------------------------------- /docs/Network/TCP 滑动窗口以及重传机制.md: -------------------------------------------------------------------------------- 1 | TCP 的滑动窗口和重传机制是其实现可靠数据传输的核心部分。 2 | 3 | ### 1. **TCP 滑动窗口** 4 | 5 | 滑动窗口(Sliding Window)机制用于控制数据的流动,使得发送方可以在未收到确认(ACK)之前连续发送多个数据包。这种机制不仅提高了网络带宽的利用率,还确保了数据的有序传输。 6 | 7 | - **窗口大小**:窗口大小指发送方在未收到确认之前,可以连续发送的最大字节数。窗口大小由接收方通过 ACK 包中的窗口字段来通知发送方。发送方依据该字段来调整自己的发送速度。 8 | 9 | - **发送窗口**:发送窗口表示发送方可以发送但尚未收到确认的数据范围。窗口的左边界是已经收到确认的数据,右边界则是发送方根据接收方窗口大小计算出的最大可发送字节。 10 | 11 | - **接收窗口**:接收窗口则是接收方能够接受但尚未处理的数据范围。接收方通过窗口大小来告诉发送方它能够接收的数据量,从而防止发送方发送过多数据导致接收方缓存溢出。 12 | 13 | - **窗口滑动**:当接收到发送的数据并确认之后,窗口的左边界就会向前滑动,从而为新的数据发送腾出空间。窗口的滑动是动态的,根据 ACK 的接收情况实时调整。 14 | 15 | ### 2. **TCP 重传机制** 16 | 17 | 为了确保可靠的数据传输,TCP 实现了多种重传机制来处理数据包丢失的情况。 18 | 19 | - **超时重传**:每次发送数据包时,TCP 都会启动一个定时器。如果在指定时间内没有收到对应的 ACK,发送方会认为该数据包可能丢失,并重新发送该数据包。 20 | 21 | - **快速重传**:当接收方收到一个乱序的数据包时,会重复发送最后一个有序数据的 ACK(即 DupACK)。如果发送方连续收到三个相同的 ACK(称为三个 DupACK),它就会立即重传该 ACK 对应的数据包,而不必等待超时。 22 | 23 | - **重传超时时间(RTO)**:RTO 是由 TCP 根据网络状况动态计算的超时时间。RTO 的计算通常基于往返时间(RTT)和 RTT 的波动情况(RTTVAR)。RTO 越准确,超时重传的效率越高,减少不必要的重传。 24 | 25 | - **选择性确认(SACK)**:SACK 是一种可选的 TCP 扩展,用于在接收方告知发送方它已经成功接收的所有非连续数据段。这可以帮助发送方只重传那些确实丢失的数据段,而不是从丢失点开始的所有数据。 26 | 27 | ### **总结** 28 | TCP 滑动窗口机制提高了数据传输效率,而重传机制则确保了数据的可靠性。通过这两种机制的结合,TCP 能够在复杂多变的网络环境下,实现高效且可靠的数据传输。 -------------------------------------------------------------------------------- /docs/Network/TCP 的 TIME_WAIT.md: -------------------------------------------------------------------------------- 1 | TCP 中的 `TIME_WAIT` 状态是四次挥手(连接终止)过程中非常重要的一个阶段。当 TCP 连接的一端主动关闭连接(即发送最后一个 `FIN` 报文段)后,会进入 `TIME_WAIT` 状态。 2 | 3 | ### TIME_WAIT 的作用 4 | 5 | 1. **确保最后的 ACK 能被接收方正确收到**: 6 | - 在四次挥手的最后一步,主动关闭连接的一方发送一个 ACK 报文段,确认接收到了对方的 FIN。如果这个 ACK 报文段因为网络原因丢失,接收方没有收到,会重新发送 FIN 报文。`TIME_WAIT` 状态下的连接确保能够重传 ACK,防止对方以为连接还未成功关闭。 7 | 8 | 2. **允许延迟的报文在网络中消失**: 9 | - TCP 是可靠的,但网络并不总是如此。一些报文可能会在网络中被延迟。如果不等待这些延迟报文在网络中消失,而立即复用相同的 IP 地址和端口号,延迟报文可能会被误认为属于新的连接,导致数据混乱。`TIME_WAIT` 状态为此提供了足够的时间(通常是 2 * MSL,最大报文段生存时间)来确保这些报文不会对新连接造成影响。 10 | 11 | ### TIME_WAIT 的持续时间 12 | 13 | `TIME_WAIT` 状态持续时间通常为 2 * MSL。MSL 是 Maximum Segment Lifetime,即一个 TCP 报文段在网络中的最大存活时间。根据标准,MSL 通常设置为 30 秒或 1 分钟,所以 `TIME_WAIT` 状态可能会持续 1 到 2 分钟。 14 | 15 | ### TIME_WAIT 的影响 16 | 17 | - **资源占用**: 18 | - 当服务器需要处理大量短连接时(如 HTTP 请求),会产生大量的 `TIME_WAIT` 连接,这可能会消耗较多的系统资源(如文件描述符、端口等)。 19 | 20 | - **端口耗尽**: 21 | - 当客户端在短时间内反复建立连接并主动关闭,会导致大量的 `TIME_WAIT` 状态,可能导致端口耗尽,使得新的连接无法建立。 22 | 23 | ### 解决措施 24 | 25 | 1. **端口复用**: 26 | - 通过启用 `SO_REUSEADDR` 选项,允许新的连接在 `TIME_WAIT` 状态下的端口上复用。 27 | 28 | 2. **减少 TIME_WAIT 持续时间**: 29 | - 调整内核参数以缩短 `TIME_WAIT` 状态的持续时间。例如,在 Linux 上可以通过修改 `/proc/sys/net/ipv4/tcp_fin_timeout` 来减少 `TIME_WAIT` 持续时间。 30 | 31 | 3. **负载均衡和代理**: 32 | - 在高并发场景下,通过负载均衡和代理分担流量,减少单个服务器上的连接压力,降低 `TIME_WAIT` 状态的数量。 -------------------------------------------------------------------------------- /docs/Network/TCP 的 keepalive.md: -------------------------------------------------------------------------------- 1 | TCP 和 HTTP 都有各自的 `keepalive` 机制,但它们的目的和实现方式有所不同。 2 | 3 | ### TCP Keepalive 4 | - **目的**:TCP的`keepalive`主要用于检测空闲连接是否仍然活跃,帮助识别那些因网络故障或对端崩溃导致的死连接。 5 | - **实现方式**: 6 | - TCP `keepalive` 是在 TCP 层实现的。 7 | - 如果启用了`keepalive`,在连接保持空闲一段时间(通常是2小时)后,TCP会发送一个探测包(`keepalive probe`)给对方。 8 | - 如果对方没有响应,TCP会继续发送探测包(通常每75秒发送一次),在连续多次(通常是9次)没有收到对方的响应后,TCP会认为连接已经断开,并通知应用程序。 9 | 10 | - **场景**:适用于长时间空闲的TCP连接,例如长期保持的SSH会话或数据库连接。 11 | 12 | ### HTTP Keepalive 13 | - **目的**:HTTP的`keepalive`(也称为持久连接)用于在同一TCP连接上复用多个HTTP请求,减少连接建立和关闭的开销,提高性能。 14 | - **实现方式**: 15 | - HTTP `keepalive` 是在应用层实现的。 16 | - 默认情况下,HTTP/1.1的连接是持久的,除非客户端或服务器明确关闭连接(通过在响应头中设置`Connection: close`)。 17 | - 通过保持连接,客户端可以在同一个TCP连接上发送多个HTTP请求,而不必为每个请求都建立一个新的TCP连接。 18 | - 连接在一段时间后(通常由服务器配置)会自动关闭,或者在请求处理完后明确关闭。 19 | 20 | - **场景**:适用于需要频繁请求资源的HTTP连接,比如浏览器与Web服务器之间的通信。 21 | 22 | ### 区别总结 23 | 1. **层次不同**:TCP `keepalive` 工作在传输层,HTTP `keepalive` 工作在应用层。 24 | 2. **目的不同**: 25 | - TCP `keepalive`用于检测连接的健康状态,防止死连接。 26 | - HTTP `keepalive`用于提高性能,减少连接建立和关闭的开销。 27 | 3. **实现方式**: 28 | - TCP `keepalive`通过发送探测包检测连接是否仍然有效。 29 | - HTTP `keepalive`通过在同一TCP连接上复用多个请求来减少资源消耗。 30 | 31 | TCP `keepalive`更多是用于长时间保持的底层连接的稳定性,而HTTP `keepalive`则是为了优化短时间内多次请求的效率。 -------------------------------------------------------------------------------- /docs/Network/TCP粘包.md: -------------------------------------------------------------------------------- 1 | TCP粘包是指在基于TCP协议的数据传输中,多个数据包在接收端被合并成一个数据包,从而导致接收方无法正确分辨出数据包的边界。这种情况通常发生在发送端发送的数据量较小或传输频率较高时,由于TCP是流式协议,它会尽量高效地利用网络带宽,将多个小的数据包合并为一个较大的数据包发送,从而导致粘包问题。 2 | 3 | ### TCP粘包产生的原因 4 | 5 | 1. **发送端数据量较小**:TCP会将多个小数据包合并成一个较大的数据包进行传输,以提高传输效率。 6 | 2. **接收端读取数据的方式**:接收端在读取数据时,可能会一次性读取多个数据包,从而造成粘包现象。 7 | 8 | ### 解决TCP粘包问题的方法 9 | 10 | 1. **定长消息**: 11 | - 通过固定消息的长度,接收端每次读取固定长度的数据。这样即使发生粘包,接收端也能正确区分每条消息的边界。 12 | - **优点**:简单易实现。 13 | - **缺点**:可能会浪费空间,尤其是消息长度差异较大的情况下。 14 | 15 | 2. **分隔符**: 16 | - 在每个数据包之间添加特定的分隔符(如`\n`、`\r\n`等)。接收端在接收数据时,通过分隔符来区分不同的数据包。 17 | - **优点**:适用于变长消息,易于实现。 18 | - **缺点**:需要确保分隔符不会出现在实际数据中,可能需要进行转义。 19 | 20 | 3. **消息头**: 21 | - 在每个数据包的前面添加一个固定长度的消息头,消息头中包含整个消息的长度。接收端先读取消息头,然后根据长度读取相应长度的数据。 22 | - **优点**:适用于变长消息,能精确定位每条消息的边界。 23 | - **缺点**:实现较为复杂,需要解析消息头。 24 | 25 | 4. **使用高级协议或框架**: 26 | - 例如,使用Google的Protocol Buffers(Protobuf),或其他支持消息序列化的框架,这些框架通常会处理粘包问题。 27 | - **优点**:便于消息的序列化和反序列化,降低开发复杂度。 28 | - **缺点**:引入了第三方库,增加了依赖性。 29 | 30 | ### 实际应用场景 31 | - 对于传输固定长度的数据,可以选择定长消息的方式。 32 | - 对于文本数据,可以使用换行符作为分隔符。 33 | - 对于复杂的通信协议,消息头的方式较为通用和灵活。 34 | -------------------------------------------------------------------------------- /docs/Network/osi网络模型.md: -------------------------------------------------------------------------------- 1 | OSI(开放系统互联)网络模型是一个标准化的网络通信框架,分为七层: 2 | 3 | 1. **物理层**:处理物理连接和信号传输。 4 | 2. **数据链路层**:负责节点间的数据传输,错误检测和纠正。 5 | 3. **网络层**:负责数据包的路由和转发。 6 | 4. **传输层**:提供端到端的通信,确保数据完整性和顺序。 7 | 5. **会话层**:管理会话建立、维护和终止。 8 | 6. **表示层**:处理数据格式转换和加密解密。 9 | 7. **应用层**:为用户提供网络服务,如HTTP、FTP等。 10 | 11 | 这个模型有助于理解和设计网络协议和设备。你想深入哪个层次? -------------------------------------------------------------------------------- /docs/Network/一次 HTTP 的请求过程.md: -------------------------------------------------------------------------------- 1 | 一次完整的 HTTP 请求过程中,会涉及到多个步骤,从用户发起请求到接收响应,通常包括以下过程: 2 | 3 | ### 1. **DNS 解析** 4 | - 当用户在浏览器中输入一个 URL 或点击一个链接时,首先需要将域名解析为 IP 地址。浏览器会先查询本地缓存,如果没有找到,就会向 DNS 服务器发起查询请求,获取目标服务器的 IP 地址。 5 | 6 | ### 2. **建立 TCP 连接** 7 | - 通过 DNS 解析到的 IP 地址,浏览器开始与目标服务器建立 TCP 连接。这个过程涉及 **TCP 三次握手**,以确保客户端和服务器之间的连接已经准备好进行数据传输。 8 | 9 | ### 3. **发送 HTTP 请求** 10 | - 一旦 TCP 连接建立,浏览器会构建一个 HTTP 请求报文,包含请求方法(如 GET、POST)、请求 URL、HTTP 版本、请求头部信息(如 User-Agent、Cookie 等)以及请求体(通常用于 POST 请求)。 11 | - 浏览器将这个请求通过 TCP 连接发送到目标服务器。 12 | 13 | ### 4. **服务器处理请求** 14 | - 服务器接收到 HTTP 请求后,首先解析请求报文,查看请求的资源和请求方法。 15 | - 服务器根据请求的 URL 和请求方法,调用相应的程序或服务来处理请求。这可能涉及查询数据库、调用后台服务、读取文件等操作。 16 | 17 | ### 5. **生成 HTTP 响应** 18 | - 服务器处理完成后,生成一个 HTTP 响应报文,包含响应状态码(如 200 OK、404 Not Found)、响应头部信息(如 Content-Type、Content-Length)、以及响应体(通常是请求的网页、数据等)。 19 | - 服务器将这个响应报文通过 TCP 连接返回给客户端。 20 | 21 | ### 6. **浏览器接收响应** 22 | - 浏览器接收到服务器的响应后,首先解析响应状态码,以确定请求是否成功。 23 | - 然后浏览器会根据响应头部的信息来处理响应体,例如解码压缩内容、处理 cookies 等。 24 | 25 | ### 7. **渲染页面** 26 | - 如果响应体是 HTML 页面,浏览器会解析 HTML 内容,并发起额外的 HTTP 请求来获取页面中的资源(如图片、CSS 文件、JavaScript 文件等)。 27 | - 这些资源获取到后,浏览器会按照 HTML 文档的结构进行页面渲染,生成最终的用户界面。 28 | 29 | ### 8. **关闭连接** 30 | - 在 HTTP/1.0 中,通常在请求完成后立即关闭 TCP 连接。 31 | - 在 HTTP/1.1 中,引入了 **持久连接**(Keep-Alive)机制,允许同一连接复用多个请求,以减少开销。 32 | 33 | ### 9. **后续操作** 34 | - 用户可能会在页面上进行进一步操作,如点击按钮、填写表单等,这些操作会触发新的 HTTP 请求,重复以上过程。 35 | 36 | ### 总结 37 | 在一次 HTTP 请求过程中,从域名解析、建立连接、发送请求、服务器处理、接收响应到最终渲染页面,每一步都在后台进行一系列复杂的操作,确保最终用户能够看到并交互所请求的内容。 -------------------------------------------------------------------------------- /docs/Network/了解网络攻击吗.md: -------------------------------------------------------------------------------- 1 | 是的,网络攻击是网络安全领域的一个重要话题。以下是关于 XSS(跨站脚本攻击)和 CSRF(跨站请求伪造)的详细介绍: 2 | 3 | ### XSS(Cross-Site Scripting) 4 | 5 | **XSS(跨站脚本攻击)** 是一种常见的网络攻击,攻击者通过在网页中注入恶意脚本,影响访问该网页的用户。XSS 攻击主要有三种类型: 6 | 7 | 1. **存储型 XSS**: 8 | - 恶意脚本被存储在目标服务器上(例如,数据库、日志文件、评论系统等)。当用户访问包含恶意脚本的页面时,脚本会被执行。 9 | - **示例**:在一个评论系统中,攻击者发布含有恶意脚本的评论,所有查看该评论的用户都会执行恶意脚本。 10 | 11 | 2. **反射型 XSS**: 12 | - 恶意脚本通过 URL 参数或请求参数被传递到服务器,并在服务器响应中反射回用户的浏览器。通常,这种攻击利用用户点击的恶意链接。 13 | - **示例**:攻击者发送一个含有恶意脚本的 URL,用户点击该链接后,脚本会被执行。 14 | 15 | 3. **基于 DOM 的 XSS**: 16 | - 恶意脚本直接通过修改浏览器中的 DOM(文档对象模型)来执行,而不是通过服务器端的响应。 17 | - **示例**:攻击者利用 JavaScript 操作网页 DOM,注入恶意脚本并执行。 18 | 19 | **防御措施**: 20 | - **输入验证和过滤**:对用户输入进行严格验证,过滤掉恶意代码。 21 | - **输出编码**:对动态生成的内容进行适当的编码,防止脚本被执行。 22 | - **使用安全的库和框架**:利用现代开发框架的安全特性,自动处理 XSS 问题。 23 | - **Content Security Policy (CSP)**:配置 CSP 策略,限制可执行的脚本来源。 24 | 25 | ### CSRF(Cross-Site Request Forgery) 26 | 27 | **CSRF(跨站请求伪造)** 是一种网络攻击,攻击者通过欺骗用户在已认证的网页上执行未授权操作。攻击者诱使用户访问恶意网站,该网站生成伪造的请求,利用用户的权限执行操作。 28 | 29 | **工作原理**: 30 | 1. 用户登录一个网站并获得认证(如 cookies)。 31 | 2. 用户在浏览器中访问攻击者控制的恶意网站。 32 | 3. 恶意网站生成伪造的请求并利用用户的认证信息发送到目标网站。 33 | 4. 目标网站接收并执行伪造请求,因为请求看起来是来自合法用户。 34 | 35 | **示例**: 36 | - 用户在银行网站登录账户并转账。攻击者创建一个恶意页面,当用户访问该页面时,恶意页面通过用户的浏览器向银行网站发起转账请求。 37 | 38 | **防御措施**: 39 | - **使用 CSRF Token**:在每个请求中包含一个唯一的 CSRF Token,服务器验证请求中的 Token 是否有效。 40 | - **验证 Referer 和 Origin 头部**:检查请求的来源,以确保请求是从合法页面发起的。 41 | - **使用 SameSite Cookies 属性**:设置 cookies 的 `SameSite` 属性,防止第三方网站通过跨站请求发送 cookies。 42 | 43 | ### 总结 44 | 45 | - **XSS**:攻击者通过注入恶意脚本影响用户的浏览器和数据,主要防御措施包括输入验证、输出编码和 CSP。 46 | - **CSRF**:攻击者诱使用户在已认证的网页上执行伪造操作,主要防御措施包括使用 CSRF Token 和验证请求来源。 47 | 48 | 了解这些攻击及其防御措施对于提高 web 应用的安全性非常重要。 -------------------------------------------------------------------------------- /docs/Network/什么是 SYN flood.md: -------------------------------------------------------------------------------- 1 | **SYN flood** 是一种常见的 **DDoS(分布式拒绝服务)攻击**,其主要目的是耗尽服务器资源,导致合法用户无法访问服务。它通过滥用 TCP 协议的三次握手过程来实现。 2 | 3 | ### **SYN flood 攻击的原理** 4 | 5 | 1. **三次握手概述**:在正常的 TCP 连接中,客户端与服务器通过三次握手来建立连接: 6 | - 客户端发送一个 SYN 包(同步序列号)给服务器,表示请求建立连接。 7 | - 服务器收到 SYN 包后,回复一个 SYN-ACK 包,表示同意连接。 8 | - 客户端收到 SYN-ACK 包后,再发送一个 ACK 包,确认连接建立。 9 | 10 | 2. **SYN flood 攻击**:攻击者会伪造大量的源 IP 地址,不断向目标服务器发送 SYN 包。这些 SYN 包会让服务器为每一个连接分配资源并进入半连接状态,等待客户端的 ACK 包。然而,由于攻击者并不会回复 ACK,服务器会保持这些连接的半开放状态(称为 SYN_RCVD 状态),直到超时。由于服务器资源有限,过多的半连接会导致服务器无法处理新的连接请求,最终导致拒绝服务。 11 | 12 | ### **防止 SYN flood 攻击的措施** 13 | 14 | 1. **SYN Cookies**:这是一个防御 SYN flood 的有效方法。当服务器接收到 SYN 请求时,不立即为其分配资源,而是通过某种算法生成一个特殊的 SYN cookie(序列号),将其作为 SYN-ACK 包的一部分发送给客户端。如果客户端回复了 ACK 包,服务器再根据这个 SYN cookie 验证客户端的合法性,然后正式分配资源建立连接。这样可以避免在三次握手完成前耗尽服务器资源。 15 | 16 | 2. **缩短 SYN 超时时间**:通过缩短 SYN_RCVD 状态的超时时间,服务器可以更快地释放那些没有完成握手的连接,从而减少资源占用。 17 | 18 | 3. **限制 SYN 半连接队列大小**:可以通过配置服务器的 SYN 半连接队列大小,限制服务器能够处理的未完成握手的连接数,从而防止资源被过度占用。 19 | 20 | 4. **防火墙和 IP 黑名单**:使用防火墙或入侵检测系统(IDS)来检测和过滤可疑的 SYN flood 攻击流量,可以对那些大量发送 SYN 请求的 IP 地址进行封禁或限制其请求频率。 21 | 22 | 5. **负载均衡**:通过负载均衡器将流量分配到多个服务器,减少单台服务器的压力,同时可以防止 SYN flood 攻击对单一目标的影响。 23 | 24 | 6. **增强服务器性能**:通过提升服务器的硬件配置,增加内存和 CPU 处理能力,可以提高服务器对大量 SYN 请求的处理能力,从而减缓 SYN flood 攻击的效果。 25 | 26 | ### **总结** 27 | 28 | SYN flood 利用 TCP 三次握手中的漏洞,试图耗尽服务器资源,使其无法响应合法用户的请求。防御 SYN flood 攻击可以通过多种方法,如 SYN Cookies、缩短超时时间、限制队列大小等来有效减轻攻击影响,确保服务的可用性。 -------------------------------------------------------------------------------- /docs/Network/从系统层面上,UDP如何保证尽量可靠.md: -------------------------------------------------------------------------------- 1 | UDP(User Datagram Protocol)是一种无连接的、面向消息的协议,它不保证数据包的可靠性、顺序性和完整性。因此,从协议本身来说,UDP不会主动采取措施来确保数据的可靠传输。然而,在实际应用中,可以通过一些系统层面的技术和策略来提高UDP通信的可靠性。 2 | 3 | ### 1. **应用层协议的设计** 4 | - **重传机制**:在应用层实现超时重传机制。当发送端没有在指定时间内收到接收端的确认包(ACK)时,自动重传丢失的数据包。 5 | - **序列号**:为每个数据包添加序列号,接收端可以通过序列号来判断数据包的顺序并检测丢包。 6 | - **ACK机制**:实现ACK确认机制,发送端在收到接收端的确认后才认为数据包成功送达。 7 | 8 | ### 2. **使用 FEC(前向纠错码)** 9 | - **前向纠错码(Forward Error Correction, FEC)**:发送端可以为多个数据包生成冗余数据,并在接收端使用这些冗余数据来纠正可能出现的错误或丢失数据包,从而提高数据传输的可靠性。 10 | 11 | ### 3. **调整系统内核参数** 12 | - **增加UDP缓冲区大小**:调整系统的UDP缓冲区大小以减少丢包。通常可以通过调整`/proc/sys/net/core/rmem_max`和`/proc/sys/net/core/wmem_max`来增加接收和发送缓冲区的最大值。 13 | - **优化网络接口卡(NIC)配置**:启用NIC的硬件卸载功能,如TSO(TCP Segmentation Offload)和LRO(Large Receive Offload),减少主机CPU负载,从而减少丢包率。 14 | 15 | ### 4. **网络层策略** 16 | - **QoS(服务质量)**:在网络层配置QoS策略,优先传输重要的UDP数据包,减少网络拥塞对数据传输的影响。 17 | - **MTU(最大传输单元)设置**:调整网络设备的MTU以减少因分片导致的丢包。过大的数据包可能会导致分片,进而增加丢包风险。 18 | - **避免网络拥堵**:通过合理的网络设计和带宽管理,减少网络拥塞对UDP通信的影响。 19 | 20 | ### 5. **利用可靠的中间件** 21 | - **可靠的消息传递中间件**:在某些场景中,可以通过可靠的消息传递中间件来保证消息的传递。例如,使用中间代理服务器或消息队列来缓存和重发未成功传输的数据。 22 | 23 | ### 6. **多路径传输** 24 | - **多路径传输(Multipath Transmission)**:通过同时使用多条网络路径传输数据,可以提高数据包到达的成功率。如果一条路径上出现问题,其他路径上的数据包仍然可以正常到达。 25 | 26 | ### 7. **监控与日志** 27 | - **监控与日志记录**:实时监控UDP传输的性能,分析丢包率、延迟等指标,及时调整相关配置。通过日志记录,可以帮助追踪和解决传输中的问题。 28 | 29 | ### 8. **应用协议层的补充机制** 30 | - 在应用层可以通过构建基于UDP之上的协议(如RTP, Real-time Transport Protocol)来增加可靠性,这些协议通常具备抖动缓冲、错误检测和恢复等机制,适用于流媒体、在线游戏等场景。 31 | 32 | 通过以上策略和技术,可以在系统层面上尽量提高UDP的可靠性,虽然它无法像TCP那样完全保证数据的可靠传输,但这些措施可以大大减少UDP通信中的数据丢失和错误。 -------------------------------------------------------------------------------- /docs/Network/图解HTTPS.md: -------------------------------------------------------------------------------- 1 | 以下是 HTTPS 工作原理的 Markdown 风格图解: 2 | 3 | --- 4 | 5 | ## **HTTPS 工作流程** 6 | 7 | ### **1. 初始请求** 8 | - **客户端(浏览器) → 服务器:** 9 | - 客户端向服务器发送请求,要求建立 HTTPS 连接(如 `https://example.com`)。 10 | - 服务器返回其 SSL/TLS 证书。 11 | 12 | ### **2. SSL/TLS 握手** 13 | - **证书交换:** 14 | - 服务器将其 SSL/TLS 证书发送给客户端,证书中包含服务器的公钥。 15 | 16 | ``` 17 | ┌───────────────┐ 18 | │ 浏览器请求 │ 19 | │ https://example.com │ 20 | └───────────────┘ 21 | │ 22 | ▼ 23 | ┌───────────────┐ 24 | │ 服务器返回 │ 25 | │ SSL/TLS 证书 │ 26 | └───────────────┘ 27 | ``` 28 | 29 | ### **3. 证书验证** 30 | - **客户端验证:** 31 | - 客户端验证服务器的证书是否有效(包括检查证书颁发机构是否可信,证书是否过期等)。 32 | - 如果验证通过,客户端生成一个随机对称密钥,并用服务器的公钥加密。 33 | 34 | ``` 35 | ┌───────────────┐ 36 | │ 客户端验证 │ 37 | │ 服务器证书 │ 38 | └───────────────┘ 39 | │ 40 | ▼ 41 | ┌───────────────┐ 42 | │ 生成对称密钥 │ 43 | │ 并用公钥加密 │ 44 | └───────────────┘ 45 | ``` 46 | 47 | ### **4. 对称密钥交换** 48 | - **客户端 → 服务器:** 49 | - 客户端将加密后的对称密钥发送给服务器。 50 | - 服务器用自己的私钥解密对称密钥。 51 | 52 | ``` 53 | ┌───────────────┐ 54 | │ 加密的对称密钥│ 55 | │ 发送给服务器 │ 56 | └───────────────┘ 57 | │ 58 | ▼ 59 | ┌───────────────┐ 60 | │ 服务器用私钥解密│ 61 | │ 获得对称密钥 │ 62 | └───────────────┘ 63 | ``` 64 | 65 | ### **5. 安全通信** 66 | - **加密通信:** 67 | - 客户端和服务器使用对称加密进行数据通信,此时通信是加密的,确保数据安全。 68 | 69 | ``` 70 | ┌───────────────┐ 71 | │ 加密数据传输 │ 72 | │ 使用对称密钥 │ 73 | └───────────────┘ 74 | │ 75 | ▼ 76 | ┌───────────────┐ 77 | │ 服务器接收 │ 78 | │ 并解密数据 │ 79 | └───────────────┘ 80 | ``` 81 | 82 | ### **6. 终止连接** 83 | - **断开连接:** 84 | - 当通信结束时,客户端和服务器可以终止 SSL/TLS 连接。 85 | 86 | ``` 87 | ┌───────────────┐ 88 | │ 终止 SSL/TLS │ 89 | │ 安全连接 │ 90 | └───────────────┘ 91 | ``` 92 | 93 | --- 94 | 95 | 这个流程说明了 HTTPS 如何通过 SSL/TLS 协议确保客户端与服务器之间的通信是加密的,防止数据被窃取或篡改。 -------------------------------------------------------------------------------- /docs/Network/查看服务器是否被攻击的方法.md: -------------------------------------------------------------------------------- 1 | DDoS(Distributed Denial of Service,分布式拒绝服务)攻击是一种常见的网络攻击方式,通过大量恶意流量涌入目标服务器,使其无法为正常用户提供服务。中小型企业往往缺乏足够的安全防护措施,因此容易成为攻击目标。了解DDoS攻击的类型、检测方法以及防护措施对于保障网站和服务器的安全非常重要。 2 | 3 | ### 一、DDoS攻击的类型 4 | 5 | 1. **CC攻击(Challenge Collapsar攻击)**: 6 | - **特点**:通过模拟多个用户不断发送HTTP请求,使目标服务器资源耗尽,无法处理正常请求。 7 | - **检测方法**:服务器响应时间显著增加,CPU和内存占用率高,网站打开速度变慢或超时。 8 | 9 | 2. **SYN攻击**: 10 | - **特点**:攻击者向目标服务器发送大量SYN请求,导致服务器资源耗尽,无法响应合法请求。 11 | - **检测方法**:使用`netstat`查看系统的半连接状态(SYN_RECV状态的连接数),如果大量存在,则可能受到SYN攻击。 12 | 13 | 3. **UDP攻击**: 14 | - **特点**:攻击者通过发送大量伪造的UDP包到目标服务器,消耗带宽或使服务器无法正常响应。 15 | - **检测方法**:使用`iftop`或`nethogs`查看网络流量,观察是否存在异常大的UDP流量。 16 | 17 | 4. **TCP洪水攻击**: 18 | - **特点**:攻击者发送大量伪造的TCP连接请求,使目标服务器资源耗尽,无法处理正常的TCP连接。 19 | - **检测方法**:通过`tcpdump`或`wireshark`分析网络流量,查看是否存在大量无效的TCP连接请求。 20 | 21 | ### 二、如何查看服务器是否被DDoS攻击 22 | 23 | 1. **监控服务器性能**: 24 | - 使用`top`、`htop`、`vmstat`等工具监控CPU、内存、磁盘IO的使用情况。如果出现资源使用率异常高,且没有明显的业务增长,可能是遭受了DDoS攻击。 25 | 26 | 2. **检查网络流量**: 27 | - 使用`iftop`、`nethogs`等工具监控实时网络流量。如果发现网络带宽占用异常增高,且流量来源不明,可能是DDoS攻击导致的。 28 | 29 | 3. **分析连接状态**: 30 | - 使用`netstat -an`命令查看当前系统的网络连接情况,注意`SYN_RECV`、`ESTABLISHED`状态的连接数是否异常多,尤其是从同一IP段或无规律的IP地址出现大量连接。 31 | 32 | 4. **日志分析**: 33 | - 通过分析服务器日志(如Nginx、Apache日志)查找异常请求,特别是短时间内大量相同或相似的请求,可能是CC攻击的迹象。 34 | 35 | ### 三、如何防护DDoS攻击 36 | 37 | 1. **使用DDoS防护服务**: 38 | - 可以选择专业的DDoS防护服务,如Cloudflare、Akamai、阿里云等,提供针对性的流量清洗和防护措施。 39 | 40 | 2. **配置防火墙和访问控制**: 41 | - 使用硬件防火墙、WAF(Web应用防火墙)以及IP访问控制列表(ACL)限制访问,屏蔽可疑IP地址或限制某些协议的访问。 42 | 43 | 3. **优化服务器配置**: 44 | - 通过调整服务器的`sysctl`配置,优化TCP/IP协议栈的资源分配,减少SYN半连接队列长度,增加处理能力。 45 | 46 | 4. **启用速率限制**: 47 | - 配置Nginx、Apache等Web服务器的请求速率限制模块,防止单个IP地址发送过多请求。 48 | 49 | 5. **分布式部署**: 50 | - 将应用部署到多个服务器或数据中心,使用负载均衡器分散流量,降低单点被攻击的风险。 51 | 52 | 6. **定期备份和应急预案**: 53 | - 及时备份重要数据,并制定应急预案,以便在遭受严重攻击时能够快速恢复服务。 54 | 55 | 通过以上方法,可以有效检测和防护DDoS攻击,保障服务器和网站的稳定运行。 -------------------------------------------------------------------------------- /docs/Network/网络七层协议.md: -------------------------------------------------------------------------------- 1 | 网络七层协议模型,也称为 OSI(Open Systems Interconnection)模型,是一个用于理解和设计网络通信系统的标准模型。这个模型将网络通信过程划分为七个不同的层次,每一层都有其特定的功能和协议。七层协议模型的各层如下: 2 | 3 | 1. **物理层 (Physical Layer)**: 4 | - 负责传输原始的比特流,涉及电气信号、光纤、无线电波等硬件接口和传输介质。 5 | - 示例协议和标准:IEEE 802.3 (以太网)、IEEE 802.11 (无线局域网) 等。 6 | 7 | 2. **数据链路层 (Data Link Layer)**: 8 | - 负责在物理层提供的传输介质上建立、维护和断开连接,处理帧的传输,错误检测和纠正。 9 | - 示例协议:以太网协议 (Ethernet)、PPP (Point-to-Point Protocol) 等。 10 | 11 | 3. **网络层 (Network Layer)**: 12 | - 负责数据包的路由选择和转发,处理逻辑地址(如 IP 地址)和网络层的协议。 13 | - 示例协议:IP (Internet Protocol)、ICMP (Internet Control Message Protocol) 等。 14 | 15 | 4. **传输层 (Transport Layer)**: 16 | - 负责在主机间提供端到端的通信,确保数据的完整性和顺序,处理流量控制和错误恢复。 17 | - 示例协议:TCP (Transmission Control Protocol)、UDP (User Datagram Protocol) 等。 18 | 19 | 5. **会话层 (Session Layer)**: 20 | - 负责管理和控制会话(即数据交换的连接),确保数据交换过程的同步和对话管理。 21 | - 示例协议:NetBIOS、RPC (Remote Procedure Call) 等。 22 | 23 | 6. **表示层 (Presentation Layer)**: 24 | - 负责数据的格式化、加密和解密,确保不同系统间的数据能正确表示和理解。 25 | - 示例协议:MIME (Multipurpose Internet Mail Extensions)、SSL/TLS (Secure Sockets Layer/Transport Layer Security) 等。 26 | 27 | 7. **应用层 (Application Layer)**: 28 | - 负责与用户应用程序进行交互,提供应用服务和接口,处理应用层协议。 29 | - 示例协议:HTTP (Hypertext Transfer Protocol)、FTP (File Transfer Protocol)、SMTP (Simple Mail Transfer Protocol) 等。 30 | 31 | ### TCP 和 UDP 在网络协议中的层次 32 | 33 | - **TCP (Transmission Control Protocol)**: 34 | - TCP 位于 **传输层**。它提供了可靠的、面向连接的通信服务,确保数据包按顺序到达,并进行错误检测和重传。TCP 通过建立连接、流量控制和错误恢复机制来保证数据的完整性和可靠性。 35 | 36 | - **UDP (User Datagram Protocol)**: 37 | - UDP 也位于 **传输层**。它提供了无连接的通信服务,虽然速度较快,但不保证数据包的顺序和完整性。UDP 适用于对实时性要求较高或容错能力较强的应用,如视频流和在线游戏。 -------------------------------------------------------------------------------- /docs/Network/限流策略.md: -------------------------------------------------------------------------------- 1 | 限流是保护系统资源,防止服务过载的重要手段。通过限流,系统能够在高并发的情况下保证稳定性,防止因流量突增导致的性能下降或服务崩溃。以下是几种常见的限流策略: 2 | 3 | ### 1. **固定窗口限流(Fixed Window Rate Limiting)** 4 | - **原理**:将时间划分为固定大小的窗口(如1秒或1分钟),在每个窗口内统计请求的数量。如果超过了规定的限制,则拒绝后续的请求。 5 | - **优点**:实现简单,适合处理突发流量。 6 | - **缺点**:在窗口边界可能出现流量突增(即时间窗口切换时,可能瞬间允许大量请求通过)。 7 | 8 | ### 2. **滑动窗口限流(Sliding Window Rate Limiting)** 9 | - **原理**:与固定窗口不同,滑动窗口将时间划分为多个小区间,并在这些小区间上移动窗口,动态计算一定时间内的请求数量。 10 | - **优点**:更加精细化,能平滑处理流量。 11 | - **缺点**:实现复杂度较高,资源开销大。 12 | 13 | ### 3. **令牌桶算法(Token Bucket)** 14 | - **原理**:系统以固定速率生成令牌并存储在一个“桶”里,每次请求需要消耗一个令牌才能通过。如果桶里的令牌数量不足,请求就会被拒绝或延迟处理。 15 | - **优点**:允许一定的流量突发,适合处理突发流量和平均流量限制。 16 | - **缺点**:在高并发下,可能出现令牌消耗过快的问题。 17 | 18 | ### 4. **漏桶算法(Leaky Bucket)** 19 | - **原理**:请求像水滴一样注入“桶”中,桶以固定速率漏水。如果桶满了,后续的请求将被拒绝或排队等待。 20 | - **优点**:可以平滑地处理请求流量,适用于需要严格控制请求速率的场景。 21 | - **缺点**:不允许突发流量,通过速率是固定的。 22 | 23 | ### 5. **并发数限制** 24 | - **原理**:限制系统中同时处理的请求数量,如果超过这个限制,新的请求要么被拒绝,要么被放入等待队列。 25 | - **优点**:直接控制系统的并发压力,防止系统资源耗尽。 26 | - **缺点**:可能导致高延迟或者请求丢弃。 27 | 28 | ### 6. **基于优先级的限流** 29 | - **原理**:根据请求的优先级进行限流,高优先级的请求可以优先通过限流检查,而低优先级的请求可能会被限制。 30 | - **优点**:适合有不同重要性请求的系统,可以保证关键业务的流畅。 31 | - **缺点**:需要设计合理的优先级策略,可能增加系统复杂性。 32 | 33 | ### 7. **分布式限流** 34 | - **原理**:在分布式系统中,限流策略需要跨多个节点协作进行。可以使用集中式组件(如 Redis、Etcd)来记录全局的限流状态。 35 | - **优点**:适合微服务架构和分布式系统,可以全局控制流量。 36 | - **缺点**:实现较为复杂,可能引入一定的网络延迟。 37 | 38 | ### 8. **动态限流** 39 | - **原理**:根据系统的实时负载和状态(如 CPU 使用率、响应时间等)动态调整限流策略。 40 | - **优点**:适应性强,可以根据实际情况调整限流策略,最大化资源利用率。 41 | - **缺点**:需要复杂的监控和决策机制,可能导致波动和不稳定。 42 | 43 | ### 实践建议 44 | - **监控和告警**:任何限流策略都需要与监控结合,实时监控流量情况,并在达到或接近限流阈值时触发告警。 45 | - **限流策略组合**:可以组合多种限流策略,比如使用令牌桶来处理突发流量,结合固定窗口限流控制整体流量。 46 | - **灰度发布**:在生产环境下使用限流策略时,可以先进行灰度发布,逐步增加限流范围,确保系统稳定性。 -------------------------------------------------------------------------------- /docs/Redis/AOF和RDB的过期删除策略.md: -------------------------------------------------------------------------------- 1 | 在 Redis 中,RDB(Redis 数据库快照)和 AOF(追加文件)持久化策略涉及到的过期删除策略有所不同。这两种策略在处理过期键的删除时有不同的机制和影响。以下是对 RDB 和 AOF 过期删除策略的详细介绍。 2 | 3 | ### RDB(Redis 数据库快照)的过期删除策略 4 | 5 | #### **过期键处理** 6 | 7 | 1. **在快照生成时处理**: 8 | - 在 RDB 快照生成过程中,Redis 会遍历当前数据库中的所有键。对于设置了过期时间的键,如果它们已经过期,Redis 会将这些过期键从快照中排除。 9 | - 换句话说,RDB 快照包含的是快照生成时数据库中有效的键值数据,过期的键不会被包含在 RDB 文件中。 10 | 11 | 2. **过期键的恢复**: 12 | - 在从 RDB 文件中恢复数据时,已经过期的键不会被恢复到 Redis 实例中。因此,RDB 持久化机制在恢复过程中不需要额外处理过期键的问题,因为过期键在 RDB 文件中本身就不存在。 13 | 14 | #### **影响** 15 | 16 | - **延迟删除**:过期键可能在生成 RDB 快照之前存在,因此在生成快照的过程中处理这些过期键会有一定的延迟,可能会对系统性能产生一定影响。 17 | - **恢复一致性**:RDB 文件恢复后的数据状态是快照生成时的状态,不会包含已经过期的键,因此恢复数据时能够保持一致性。 18 | 19 | ### AOF(追加文件)的过期删除策略 20 | 21 | #### **过期键处理** 22 | 23 | 1. **写操作的记录**: 24 | - AOF 文件记录的是 Redis 实例执行的所有写操作,包括设置过期时间的操作。对于设置过期时间的键,AOF 文件中会记录 `EXPIRE` 或 `PEXPIRE` 等命令。 25 | - 当 Redis 执行这些过期时间设置命令时,AOF 文件会将这些操作追加到文件末尾。 26 | 27 | 2. **恢复过程中的过期键处理**: 28 | - 在从 AOF 文件恢复数据时,Redis 会按顺序重放 AOF 文件中的所有命令,包括设置过期时间的命令。因此,过期时间的命令会在恢复过程中被执行,并在内存中设置相应的过期时间。 29 | - 如果 AOF 文件中的某个键已经过期,它会根据 AOF 文件中的命令设置过期时间,但在 Redis 中,该键会被标记为过期并最终被删除。 30 | 31 | 3. **持久化过程中处理**: 32 | - 在 AOF 持久化的过程中,Redis 会按照 `appendfsync` 配置选项进行文件同步,确保每次写操作都被持久化到 AOF 文件中。 33 | - AOF 的重写机制会周期性地创建新的 AOF 文件,压缩旧的文件。在重写过程中,Redis 会重新生成 AOF 文件内容,这可能包括已过期键的处理。 34 | 35 | #### **影响** 36 | 37 | - **持久性和恢复一致性**:AOF 机制可以确保即使在 Redis 重启后,所有的写操作(包括设置过期时间的操作)都能被重放,从而在恢复数据时保留原有的过期设置。 38 | - **性能影响**:由于 AOF 文件的写操作包括所有的过期时间设置命令,频繁的过期操作可能会导致 AOF 文件的增长,从而影响性能。 39 | 40 | ### 结合使用 RDB 和 AOF 41 | 42 | 在使用 RDB 和 AOF 结合的持久化策略时,过期键的处理会依赖于两者的交互: 43 | 44 | - **RDB 生成时**:生成 RDB 文件时会忽略过期的键。 45 | - **AOF 重放**:AOF 文件的重放过程会执行过期设置命令,使得 Redis 恢复时包含过期时间的逻辑。 46 | 47 | 结合使用 RDB 和 AOF 的持久化机制可以综合考虑数据恢复速度和持久性,但也需注意过期键在两者间的处理细节,以确保一致性和性能。 -------------------------------------------------------------------------------- /docs/Redis/Redis 哈希槽.md: -------------------------------------------------------------------------------- 1 | Redis 哈希槽(Hash Slot)是 Redis Cluster(集群模式)中分片和数据分布的核心概念,用于将数据均匀地分布在集群的多个节点上。具体来说,哈希槽的机制如下: 2 | 3 | ### 1. **哈希槽的定义** 4 | - 在 Redis Cluster 中,整个键空间被划分为 16384 个哈希槽,编号从 0 到 16383。 5 | - 每个键通过 CRC16 哈希函数计算得到一个值,然后对 16384 取模(`hash(key) % 16384`),得到该键对应的哈希槽编号。 6 | 7 | ### 2. **哈希槽与节点的映射** 8 | - Redis Cluster 中的每个节点(主节点)都负责管理一部分哈希槽,比如节点 A 负责哈希槽 0-5460,节点 B 负责哈希槽 5461-10922,节点 C 负责哈希槽 10923-16383。 9 | - 当集群启动或发生拓扑变化时,哈希槽会动态地分配或重新分配给不同的节点。 10 | 11 | ### 3. **数据存储与访问** 12 | - 当一个键值对(Key-Value)被存储到 Redis Cluster 中时,首先通过哈希函数计算该键对应的哈希槽编号,然后将数据存储到负责该哈希槽的节点上。 13 | - 当客户端需要访问某个键时,集群通过相同的哈希计算找到对应的哈希槽,从而定位到负责该槽的节点,进而完成数据访问。 14 | 15 | ### 4. **哈希槽的优点** 16 | - **负载均衡**:哈希槽机制确保了键值对在集群中均匀分布,避免某些节点的负载过重。 17 | - **扩展性**:当增加或减少节点时,集群可以通过重新分配哈希槽来调整节点之间的数据分布,实现平滑扩展。 18 | - **容错性**:Redis Cluster 可以通过副本机制(即主从复制)确保哈希槽的数据冗余,当主节点故障时,副本节点可以快速接管相应的哈希槽。 19 | 20 | ### 5. **哈希槽迁移** 21 | - 在集群扩展或缩减时,Redis 支持哈希槽迁移,即将某些哈希槽及其对应的数据从一个节点迁移到另一个节点。迁移过程中,集群依然保持对外可用。 22 | 23 | 哈希槽的设计确保了 Redis Cluster 在大规模分布式场景下的高可用性和高性能,同时提供了灵活的数据分片和管理能力。 -------------------------------------------------------------------------------- /docs/Redis/Redis 的同步机制.md: -------------------------------------------------------------------------------- 1 | Redis 的同步机制涉及数据复制和持久化两方面。这里的同步机制主要包括主从复制、持久化过程中的数据同步、以及哨兵模式的同步。以下是详细的介绍: 2 | 3 | ### 1. 主从复制(Replication) 4 | 5 | Redis 主从复制机制允许将数据从一个 Redis 实例(主节点)复制到一个或多个 Redis 实例(从节点)。这种机制使得数据可以在多个节点间保持一致,提高数据的可用性和容错能力。 6 | 7 | #### **同步过程** 8 | 9 | 1. **初次同步**: 10 | - **全量复制**:当从节点第一次连接到主节点时,主节点会执行全量数据复制。主节点生成当前数据集的快照(RDB 文件),然后将这个快照通过网络传输到从节点。接着,主节点将自身的数据更改日志(增量数据)发送给从节点。 11 | - **创建 RDB 快照**:主节点在进行全量复制时,会创建一个 RDB 快照,并将该快照发送到从节点。 12 | - **传输数据**:从节点接收到快照后,会用该数据覆盖自身的数据集,并开始接收主节点的增量数据。 13 | 14 | 2. **增量同步**: 15 | - **增量数据传输**:在全量同步完成后,主节点会将后续的写操作(增量数据)以 AOF 格式传输到从节点。这个增量同步是基于主节点生成的命令流来进行的。 16 | - **断线重连**:如果从节点与主节点的连接中断,Redis 会尝试恢复连接并进行增量同步。断线期间从节点会缓存未能接收到的增量数据,重新连接后继续接收主节点的增量数据。 17 | 18 | 3. **异步复制**: 19 | - 主从复制机制是异步的,这意味着主节点将写操作发给从节点,但不会等待从节点确认这些写操作的完成。因此,从节点的数据可能会有一定的延迟。 20 | 21 | #### **主从复制的配置** 22 | 23 | - **`replicaof` 命令**:用于设置从节点的主节点。 24 | - **`slaveof` 命令**:在旧版 Redis 中用于设置从节点的主节点(现已被 `replicaof` 替代)。 25 | - **`masterauth` 配置**:用于设置从节点连接主节点时的认证密码。 26 | 27 | ### 2. 持久化过程中的数据同步 28 | 29 | Redis 的持久化机制(RDB 和 AOF)在数据同步时也有重要作用: 30 | 31 | - **RDB 持久化**: 32 | - **保存快照**:主节点在特定时间点创建 RDB 快照,并将该快照持久化到磁盘。RDB 文件可以在从节点重启时加载。 33 | - **数据一致性**:RDB 文件在生成快照时会包含主节点的全部数据,并传输到从节点用于恢复。 34 | 35 | - **AOF 持久化**: 36 | - **追加写操作**:主节点的每个写操作都被追加到 AOF 文件中。从节点通过接收主节点的 AOF 文件来同步写操作。 37 | 38 | ### 3. 哨兵模式(Sentinel) 39 | 40 | Redis 哨兵模式用于监控 Redis 实例,并在主节点出现故障时自动进行主从切换。 41 | 42 | #### **哨兵的同步过程** 43 | 44 | 1. **监控和检测**: 45 | - 哨兵实例持续监控主节点和从节点的状态。若主节点发生故障,哨兵会检测到并发出警报。 46 | 47 | 2. **故障转移**: 48 | - **选举新主节点**:在检测到主节点故障后,哨兵会进行故障转移选举,从现有的从节点中选择一个作为新的主节点。 49 | - **更新配置**:在完成故障转移后,哨兵会更新配置,使所有从节点指向新的主节点,并通知客户端新的主节点地址。 50 | 51 | 3. **通知客户端**: 52 | - 哨兵会向客户端和其他从节点广播主节点的变更,确保整个集群的数据同步和一致性。 53 | 54 | ### 总结 55 | 56 | Redis 的同步机制包括主从复制的全量和增量同步、持久化过程中的数据同步、以及哨兵模式的故障转移同步。这些机制确保了数据在不同节点间的一致性和高可用性,并处理了数据恢复和故障处理的情况。 -------------------------------------------------------------------------------- /docs/Redis/String数据结构sds.md: -------------------------------------------------------------------------------- 1 | 在 Redis 中,`String` 类型的数据结构是最基本的类型,它实际上是一个字节数组(byte array)。但在底层实现中,Redis 并不是直接使用 C 语言的传统字符串(即以 `\0` 结尾的字符数组),而是使用了一种名为 `SDS`(Simple Dynamic String)的抽象数据结构。 2 | 3 | ### 1. **SDS(Simple Dynamic String)的特点** 4 | 5 | SDS 是 Redis 中用来表示字符串的基础数据结构,它相比传统的 C 字符串有以下几个特点: 6 | 7 | - **动态扩容**:SDS 支持动态扩展,当字符串长度需要增加时,SDS 可以自动调整空间大小,无需手动管理内存。 8 | - **二进制安全**:SDS 不会对字符串内容做任何假设,它可以存储任意二进制数据,而不仅仅是以 `\0` 结尾的字符数组。 9 | - **常数时间获取长度**:SDS 会在内部记录字符串的长度,所以获取字符串长度的操作是 O(1) 的,而 C 字符串需要遍历整个字符数组来计算长度(O(n))。 10 | - **预分配空间和惰性空间释放**:SDS 在扩展时会预分配一些额外的空间,以减少频繁的内存分配和复制操作。此外,当 SDS 缩短字符串时,它并不会立即释放多余的内存空间,而是保留起来以备后用。 11 | 12 | ### 2. **SDS 的结构** 13 | 14 | SDS 的数据结构大致如下(简化版): 15 | 16 | ```c 17 | struct sdshdr { 18 | int len; // 当前字符串的长度 19 | int free; // 剩余可用空间 20 | char buf[]; // 字符串数据 21 | }; 22 | ``` 23 | 24 | - **len**:表示当前 SDS 所保存字符串的长度。 25 | - **free**:表示当前 SDS 预分配的、尚未使用的空间大小。 26 | - **buf**:实际保存字符串数据的地方。 27 | 28 | 例如,一个长度为 5 的字符串 `"hello"`,对应的 SDS 结构可能是这样的: 29 | 30 | - `len` = 5 31 | - `free` = 3 (预分配了一些额外的空间) 32 | - `buf` = "hello\0"(注意 `buf` 并不是传统意义上的以 `\0` 结尾的字符串,只是恰好有 `\0` 用来方便处理) 33 | 34 | ### 3. **SDS 的扩容机制** 35 | 36 | 当 SDS 需要扩容时,会采用一种“渐进式扩容”的策略: 37 | 38 | - **空间预分配**:当 SDS 需要扩展时,它不仅仅只分配所需的内存,还会预分配额外的空间。具体的策略是: 39 | - 如果 SDS 当前长度小于 1MB,那么扩容时会分配与当前长度相同的额外空间,即如果扩展后的长度是 `N`,那么实际分配的总空间是 `2N`。 40 | - 如果 SDS 当前长度大于等于 1MB,那么每次扩展只额外分配 1MB 的空间。 41 | 42 | 这种策略能够有效减少频繁的内存分配操作,提升字符串操作的效率。 43 | 44 | - **惰性空间释放**:当字符串缩短时,SDS 并不会立即释放多余的内存空间,而是将这些空间保留起来,以便下次扩展时直接使用。这种策略避免了频繁的内存分配和释放操作,提高了内存使用的效率。 45 | 46 | ### 4. **SDS 的应用场景** 47 | 48 | SDS 被广泛应用于 Redis 的各种模块中,尤其是在 Redis 的 `String` 类型中,SDS 是核心的数据结构。无论是简单的字符串值还是复杂的数据序列化,Redis 都依赖于 SDS 的灵活性和高效性。 49 | 50 | 通过 SDS,Redis 实现了对字符串操作的高效处理,并且避免了传统 C 字符串在安全性和效率上的不足。 -------------------------------------------------------------------------------- /docs/Redis/bgsave时数据拷贝过程.md: -------------------------------------------------------------------------------- 1 | 在 Redis 执行 `RDB` 快照时,`BGSAVE` 命令会创建一个子进程来执行持久化任务。具体来说,Redis 使用了**写时复制(Copy-On-Write, COW)**机制来避免阻塞主进程,并且减少内存开销。 2 | 3 | ### BGSAVE 的工作原理: 4 | 1. 当 Redis 接收到 `BGSAVE` 命令时,Redis 主进程会**创建一个子进程**,子进程负责将当前的内存数据写入到 RDB 文件中。 5 | 2. **写时复制(Copy-On-Write, COW)**:在子进程生成的过程中,Redis 并不会立即复制整个内存数据。相反,子进程与父进程共享相同的内存页。当父进程(Redis 主进程)继续处理请求并修改数据时,只有被修改的部分内存(页面)才会被复制。这就是写时复制机制的关键所在:**只有在写操作发生时,才会复制对应的数据**。 6 | 7 | - **共享内存页**:父进程和子进程在生成时共享所有内存页。 8 | - **写时复制**:当父进程修改某个数据时,这个数据所在的内存页会被复制一份给父进程,子进程仍然可以访问原来的内存页,而父进程访问修改后的内存页。 9 | 10 | 3. 子进程使用共享的内存页将快照数据写入 RDB 文件,写入完成后子进程退出,RDB 文件生成完成。 11 | 12 | ### 写时复制的优势: 13 | - **性能影响最小化**:通过使用 COW,主进程可以继续响应客户端请求,而不需要阻塞整个进程的执行。只有当需要修改数据时,才会触发内存页的复制,因此对性能的影响较小。 14 | - **节省内存**:由于主进程和子进程共享大部分未被修改的数据,系统内存的占用得到有效控制。只有修改的数据页才会额外占用内存。 15 | 16 | ### 例子: 17 | 假设 Redis 当前有 1GB 的数据,当执行 `BGSAVE` 时,主进程创建子进程。如果没有任何修改发生,父子进程共享同一份 1GB 数据,而不需要额外复制。当父进程修改某个 key 时,才会触发这部分数据的复制。 18 | 19 | ### 总结: 20 | 在 `BGSAVE` 执行时,子进程并不会直接拷贝整个内存的数据,而是通过写时复制(COW)机制共享内存,只有当主进程修改数据时才会将被修改的部分内存复制一份给主进程。这种机制确保了持久化操作不会阻塞主进程,并且在大多数情况下节省了内存。 -------------------------------------------------------------------------------- /docs/Redis/pipeline.md: -------------------------------------------------------------------------------- 1 | Redis 的 Pipeline 是一种优化客户端与 Redis 服务器之间通信性能的技术,它允许客户端在不等待服务器响应的情况下发送多个命令,然后一次性接收这些命令的响应。使用 Pipeline 带来了以下好处: 2 | 3 | ### 1. **减少网络延迟** 4 | - 在传统的请求-响应模式下,客户端需要等待服务器对每个命令的响应,然后再发送下一个命令。这种模式会导致每个命令都要经过网络延迟。 5 | - 使用 Pipeline 后,多个命令可以一次性发送,减少了网络往返次数,从而降低了延迟,提高了整体性能。 6 | 7 | ### 2. **提高吞吐量** 8 | - Pipeline 允许客户端批量发送命令,这样可以在同一时间内处理更多请求。由于减少了网络延迟,服务器能够更快地处理这些命令,提高了系统的吞吐量。 9 | 10 | ### 3. **减轻服务器负担** 11 | - 通过减少每次命令的往返次数,服务器可以更有效地使用资源,减少等待时间。这样,服务器可以更专注于处理命令而不是等待网络 I/O,提高了整体资源利用率。 12 | 13 | ### 4. **优化批量操作** 14 | - 对于需要执行大量相似操作的场景(如批量设置键值对、批量获取数据等),Pipeline 尤其有效。它可以大幅减少单个命令处理所需的时间,从而加快整个批量操作的速度。 15 | 16 | ### 5. **简化客户端代码** 17 | - 使用 Pipeline,可以将多个命令组合在一起,简化客户端的逻辑代码。这样,客户端可以更容易地管理和发送批量命令。 18 | 19 | ### 应用场景 20 | 21 | - **数据批量插入**:在大量数据插入的场景下,可以使用 Pipeline 提高插入速度。 22 | - **批量查询**:对大量键进行批量查询时,Pipeline 可以显著降低延迟。 23 | - **高并发请求**:在高并发请求的场景下,Pipeline 可以减少网络 I/O,提升系统的处理能力。 24 | 25 | ### 使用注意事项 26 | 27 | - **内存消耗**:因为所有命令和响应都会暂时存储在客户端内存中,Pipeline 在处理大量数据时可能会消耗较多内存。 28 | - **错误处理**:由于命令是批量发送的,发生错误时,需要更复杂的机制来处理和识别具体哪条命令出现了问题。 29 | 30 | 通过有效利用 Pipeline,可以显著提升 Redis 在高并发场景下的性能,降低网络开销,增加操作的效率。 -------------------------------------------------------------------------------- /docs/Redis/redis中hash扩容过程.md: -------------------------------------------------------------------------------- 1 | 在 Redis 中,哈希表(Hash)的底层实现使用的是字典(dict),其核心是哈希表结构。当 Redis 中的哈希表需要存储更多的键值对时,可能会触发哈希表的扩容过程。扩容过程的目的是为了保持哈希表的操作效率(例如查找、插入、删除等操作的时间复杂度)接近 O(1)。 2 | 3 | ### Redis 哈希表的扩容过程 4 | 5 | Redis 的哈希表扩容过程主要分为以下几个步骤: 6 | 7 | 1. **判断是否需要扩容**: 8 | Redis 在插入新元素时会判断当前哈希表的负载因子(Load Factor)来决定是否需要扩容。负载因子是哈希表中已使用的槽(slot)数量与哈希表总大小的比值。当负载因子超过某个阈值时(通常是 1),Redis 会触发扩容。 9 | 10 | 2. **分配新的哈希表**: 11 | Redis 会先分配一个新的哈希表 `ht[1]`,其大小通常是现有哈希表 `ht[0]` 的两倍。此时,新旧哈希表会同时存在。 12 | 13 | 3. **数据渐进式迁移**: 14 | Redis 采用“渐进式 rehashing”(rehash)的方式将旧哈希表中的数据迁移到新哈希表。具体来说,并不会一次性将所有数据迁移完毕,而是逐步进行: 15 | - 在每次对哈希表进行插入、删除、查询等操作时,Redis 会顺便将旧哈希表中的一部分数据(通常是一小部分槽位的数据)迁移到新哈希表。 16 | - 通过这种方式,扩容过程不会造成单次操作的长时间阻塞,从而提高了系统的响应能力。 17 | 18 | 4. **更新哈希表引用**: 19 | 当所有数据都迁移到新哈希表后,Redis 会将 `ht[1]` 变为 `ht[0]`,并清空原来的 `ht[0]`,从而完成哈希表的扩容。 20 | 21 | ### 具体扩容策略 22 | 23 | - **惰性迁移**:数据迁移是渐进式的,只有在对哈希表进行操作时才会发生。这样做的目的是为了避免一次性迁移造成的系统性能下降。 24 | 25 | - **避免主键竞争**:在渐进式 rehashing 过程中,新旧哈希表会同时存在。因此,在执行插入、删除、查询操作时,Redis 会先在新哈希表中查找,如果未找到,再到旧哈希表中查找。这样可以确保扩容过程中数据的一致性。 26 | 27 | - **扩容与缩容**:Redis 不仅会在需要时对哈希表进行扩容,当哈希表中的键值对数量减少时(例如被删除),Redis 也会根据一定的条件触发哈希表的缩容操作,释放多余的内存资源。 28 | 29 | ### 总结 30 | 31 | Redis 通过渐进式 rehashing 实现了哈希表的扩容,确保了在扩容过程中系统的高效性和低延迟响应。通过这种方式,Redis 能够在处理大量数据时依然保持高性能,避免了扩容过程中可能出现的性能瓶颈。 -------------------------------------------------------------------------------- /docs/Redis/redis主从复制怎么实现.md: -------------------------------------------------------------------------------- 1 | Redis 的主从复制(Replication)机制可以分为全量同步和增量同步。主从复制的实现方式如下: 2 | 3 | ### 1. 全量同步(Full Resynchronization) 4 | 全量同步通常在以下情况下发生: 5 | - 从服务器(Slave)第一次启动并连接到主服务器(Master)。 6 | - 网络中断或其他错误导致主从服务器的复制状态不同步。 7 | 8 | #### 全量同步的过程: 9 | 1. **主服务器生成快照**: 10 | - 当从服务器请求全量同步时,主服务器执行 `BGSAVE` 命令,生成一个 RDB 快照文件,同时将此后的所有写操作记录到缓冲区中。 11 | 12 | 2. **发送快照文件**: 13 | - 主服务器将生成的 RDB 文件发送给从服务器。该文件包含了主服务器的全量数据。 14 | 15 | 3. **从服务器载入快照**: 16 | - 从服务器接收到 RDB 文件后,丢弃原有数据并载入快照文件中的数据。 17 | 18 | 4. **同步缓冲区中的写命令**: 19 | - 主服务器将快照生成过程中缓冲区中的写操作指令逐步发送给从服务器。快照载入完成后,从服务器继续执行这些缓冲区中的写命令。 20 | 21 | 5. **进入增量同步**: 22 | - 全量同步完成后,从服务器和主服务器进入增量同步状态,继续接收和执行新的写命令。 23 | 24 | ### 2. 增量同步(Partial Resynchronization) 25 | 增量同步是在主从服务器处于正常工作状态时,主服务器向从服务器实时同步写命令的过程。 26 | 27 | #### 增量同步的过程: 28 | 1. **实时复制**: 29 | - 当主服务器执行写命令时,主服务器会将这个写命令发送给所有连接的从服务器。 30 | 31 | 2. **从服务器执行写命令**: 32 | - 从服务器接收到主服务器发送的写命令后,立即执行相同的操作,以保持与主服务器数据一致。 33 | 34 | ### 3. Redis 主从同步策略 35 | Redis 会优先尝试进行增量同步。如果增量同步由于某种原因失败(例如网络中断时间过长导致从服务器的复制偏移量失效),则会回退到全量同步,重新同步整个数据库。 36 | 37 | #### 实现细节: 38 | - **偏移量和复制ID**:Redis 使用 **复制偏移量** 和 **复制ID** 来实现增量同步。在正常工作期间,主服务器和从服务器会保持一个复制偏移量,记录同步进度。如果网络中断后重新连接,Redis 会尝试通过偏移量进行增量同步,恢复丢失的数据。 39 | 40 | - **缓冲区大小**:Redis 的主服务器有一个 **复制积压缓冲区**(replication backlog buffer),用来保存最近的写操作命令,缓冲区大小可以通过 `repl-backlog-size` 参数配置。如果从服务器重连时,缓冲区中的数据足以覆盖中断期间的所有操作,增量同步可以继续进行,否则需要进行全量同步。 41 | 42 | ### 4. Redis 主从同步策略的核心逻辑 43 | Redis 通过以下步骤确保数据一致性: 44 | - 主从服务器第一次同步时会执行全量同步。 45 | - 当主从服务器发生中断时,首先会尝试通过复制偏移量进行增量同步。如果增量同步失败,会重新执行全量同步。 46 | -------------------------------------------------------------------------------- /docs/Redis/redis如何判断Key是否存在.md: -------------------------------------------------------------------------------- 1 | Redis 中判断 Key 是否存在的原理依赖于 Redis 的底层数据结构和哈希表的实现。当你使用 `EXISTS` 命令时,Redis 会在其内部的数据结构中查找相应的 key。具体原理如下: 2 | 3 | ### 1. 数据结构 4 | Redis 使用**字典**(哈希表)来存储键值对。每个 key 都映射到 Redis 的全局哈希表中,哈希表的 key 是字符串,值可以是各种数据类型(如字符串、列表、集合等)。 5 | 6 | ### 2. 哈希表查找 7 | 当执行 `EXISTS` 命令时,Redis 首先在哈希表中查找该 key: 8 | 9 | - 如果哈希表中有该 key,说明 key 存在,Redis 返回 `1`。 10 | - 如果哈希表中没有找到该 key,说明 key 不存在,Redis 返回 `0`。 11 | 12 | 由于哈希表的查找时间复杂度是 **O(1)**,即无论哈希表中的数据量有多大,查找某个 key 都是非常快速的。Redis 内部采用了**渐进式 rehashing**(重新散列),确保哈希表操作在大多数情况下都能保持高效。 13 | 14 | ### 3. 过期机制的影响 15 | 值得注意的是,Redis 的键值对可以设置过期时间。如果一个 key 设置了过期时间并且已经过期,那么即使它还在哈希表中,但在查找时 Redis 会自动判断它是否已经过期: 16 | 17 | - 如果 key 已经过期,Redis 会先将其删除,然后返回 `0`,表示该 key 不存在。 18 | - 如果 key 尚未过期,Redis 会继续返回 `1`,表示 key 仍然存在。 19 | 20 | ### 总结 21 | Redis 的 `EXISTS` 命令依赖哈希表的数据结构和查找机制,在绝大多数情况下,查找 key 的效率都非常高(O(1) 复杂度)。同时,Redis 的过期机制确保了过期的 key 不会被误判为存在。 22 | 23 | -------------------------------------------------------------------------------- /docs/Redis/redis缓存为什么要延时双删.md: -------------------------------------------------------------------------------- 1 | 延时双删(Delayed Double Deletion)是一种缓存一致性策略,用于解决在高并发环境下,数据库与缓存数据可能不一致的问题。具体来说,延时双删通常在以下场景中使用: 2 | 3 | ### 场景描述 4 | 在系统中,数据可能同时存在于缓存和数据库中。当某个数据发生更新操作时,为了保证缓存中的数据与数据库中的数据一致,通常会在更新数据库后立即删除缓存中的旧数据。然而,在高并发场景下,可能会出现以下问题: 5 | 6 | 1. **数据更新与缓存读操作并发**: 7 | - 假设在某个请求 A 进行数据更新时,更新了数据库并删除了缓存中的旧数据。 8 | - 但是,在删除缓存之后,数据库事务还未提交成功,此时可能会有另一个请求 B 读取数据。 9 | - 如果请求 B 先于事务提交,B 会发现缓存为空,然后查询数据库,并将旧数据重新写入缓存,导致缓存与数据库中的数据不一致。 10 | 11 | ### 延时双删策略 12 | 为了避免这种数据不一致的情况,可以使用延时双删策略。其步骤如下: 13 | 14 | 1. **第一次删除缓存**: 15 | - 在数据更新操作之前或之后,首先删除缓存中的旧数据。 16 | 17 | 2. **更新数据库**: 18 | - 进行数据库更新操作,并提交事务。 19 | 20 | 3. **延时一定时间后再次删除缓存**: 21 | - 在数据库更新完成并提交事务后,设置一个短暂的延迟(例如 500 毫秒或 1 秒),然后再次删除缓存。这是为了防止在事务提交后但缓存还未更新的时间窗口中,产生不一致的情况。 22 | 23 | ### 优点 24 | - **防止并发读写导致的数据不一致**: 延时双删策略可以有效避免在缓存删除和数据库更新的时间差内产生的不一致问题。 25 | - **增强数据一致性**: 通过延迟删除,确保即使在高并发的情况下,最终缓存和数据库的数据能够保持一致。 26 | 27 | ### 实现示例 28 | ```go 29 | func updateData(key string, newData interface{}) { 30 | // 第一次删除缓存 31 | cache.Delete(key) 32 | 33 | // 更新数据库 34 | db.Update(key, newData) 35 | 36 | // 延时双删 37 | time.Sleep(500 * time.Millisecond) 38 | cache.Delete(key) 39 | } 40 | ``` 41 | 42 | ### 延时双删的适用场景 43 | 延时双删特别适用于读写频繁的场景,以及对数据一致性要求较高的系统。虽然增加了实现复杂度,但可以显著提高系统在高并发环境下的缓存一致性。 -------------------------------------------------------------------------------- /docs/Redis/假设Redis 的 master 节点宕机了,你会怎么进行数据恢复?.md: -------------------------------------------------------------------------------- 1 | 当 Redis 的主节点(Master)宕机后,数据恢复是确保系统持续正常运行的关键步骤。以下是应对 Redis 主节点宕机后的数据恢复方法: 2 | 3 | ### 1. **启用 Redis Sentinel** 4 | - **自动故障转移**:如果系统中配置了 Redis Sentinel,它会自动检测到主节点的宕机,并迅速选择一个从节点(Slave)提升为新的主节点。这种自动化过程可以确保服务的高可用性,尽可能减少宕机时间。 5 | - **客户端重定向**:在 Sentinel 完成主从切换后,客户端应自动或手动重新连接到新的主节点。 6 | 7 | ### 2. **手动故障转移** 8 | - **选择新的主节点**:如果没有使用 Redis Sentinel,可以手动选择一个健康的从节点,停止其他从节点的同步,并将选定的从节点提升为主节点。 9 | - **重定向客户端**:所有的客户端连接需要更新为新的主节点地址,确保业务继续正常运行。 10 | 11 | ### 3. **数据恢复** 12 | - **基于 RDB 文件恢复**:如果 Redis 主节点定期生成 RDB(Redis Database)快照文件,可以使用最近一次的 RDB 文件进行数据恢复。将 RDB 文件复制到新的主节点所在的服务器,并启动 Redis 实例,该实例会自动加载 RDB 文件中的数据。 13 | - **基于 AOF 文件恢复**:如果 Redis 配置了 AOF(Append Only File)持久化,可以通过 AOF 文件恢复数据。将 AOF 文件复制到新的主节点所在的服务器,并启动 Redis 实例,它会重新执行 AOF 文件中的所有命令以恢复数据。 14 | 15 | ### 4. **数据一致性检查** 16 | - **一致性验证**:在故障转移后,确保所有从节点的数据与新的主节点数据一致。如果存在数据不一致的情况,可能需要重新同步这些从节点。 17 | - **手动修复数据**:如果存在数据丢失或损坏,可能需要手动修复数据。这可以通过检查日志、历史记录或其他数据备份来完成。 18 | 19 | ### 5. **防止未来的故障** 20 | - **增加监控与告警**:设置更为严格的监控和告警系统,实时监测 Redis 实例的状态,提前发现潜在的问题。 21 | - **优化主从复制配置**:确保主从复制链路的稳定性,尽量减少主节点宕机的风险。 22 | - **定期备份与测试恢复**:定期进行数据备份,并定期测试备份文件的恢复流程,确保在出现故障时可以快速恢复。 23 | 24 | ### 6. **备份恢复** 25 | - **从备份中恢复**:如果主从节点均不可用,且没有其他方式恢复数据,可以从最近的备份中恢复数据。这可能会导致部分数据丢失,但可以保证系统尽快恢复可用性。 26 | 27 | ### 总结 28 | 29 | Redis 主节点宕机后,数据恢复的关键在于确保系统尽可能快地恢复服务,并尽量减少数据丢失。通过使用 Redis Sentinel 进行自动化故障转移,手动提升从节点,使用 RDB 或 AOF 文件恢复数据,结合完善的监控和备份策略,可以有效应对 Redis 主节点宕机带来的风险。 -------------------------------------------------------------------------------- /docs/Redis/关于影响Redis性能的几点因素.md: -------------------------------------------------------------------------------- 1 | 影响 Redis 性能的因素有很多,主要包括以下几个方面: 2 | 3 | ### 1. **硬件资源** 4 | - **CPU**: Redis 是单线程的,因此 CPU 性能对于处理请求的速度至关重要。高频繁的操作和复杂的查询会占用较多的 CPU 资源。 5 | - **内存**: Redis 是一个内存数据库,所有数据都存储在内存中。内存大小直接决定了 Redis 能够存储的数据量,同时也影响了缓存的命中率。 6 | - **磁盘**: 如果使用持久化功能(如 RDB 快照或 AOF 日志),磁盘的读写速度会影响持久化的性能和效率。 7 | 8 | ### 2. **网络** 9 | - **带宽**: 网络带宽影响 Redis 数据的传输速度。带宽不足会导致 Redis 响应时间变长,特别是在高负载的情况下。 10 | - **延迟**: 网络延迟会增加 Redis 的响应时间。较高的延迟会影响客户端与 Redis 服务器之间的数据交互速度。 11 | 12 | ### 3. **数据结构和操作** 13 | - **数据结构选择**: 不同的数据结构(如字符串、哈希、列表、集合、有序集合等)在 Redis 中有不同的实现方式和性能特征。选择合适的数据结构可以提高性能。 14 | - **操作复杂性**: 执行复杂的操作(如大规模的排序、交集或并集操作)会消耗更多的 CPU 和内存资源,从而影响性能。 15 | 16 | ### 4. **持久化机制** 17 | - **RDB(快照)**: 定期将数据库快照保存到磁盘,如果设置不当,频繁的快照操作可能会影响 Redis 的性能。 18 | - **AOF(追加文件)**: 记录每个写操作到日志文件,可能会导致磁盘 I/O 增加,特别是在写操作频繁的场景下。 19 | - **AOF 重写**: 定期进行 AOF 文件的压缩和重写操作会消耗额外的资源。 20 | 21 | ### 5. **配置** 22 | - **最大内存限制**: 设置 Redis 的最大内存限制可以防止内存溢出。配置不当可能导致 Redis 被杀死(OOM 错误)或性能下降。 23 | - **持久化配置**: 适当的持久化配置(如快照频率、AOF 策略)可以平衡性能和数据持久性需求。 24 | 25 | ### 6. **客户端和请求模式** 26 | - **客户端数量和负载**: 客户端数量的增加和请求负载的增加都会对 Redis 性能产生影响。需要通过合理的连接池和负载均衡策略来优化。 27 | - **请求模式**: 请求的类型和频率也会影响性能。例如,大量的写操作和高并发的读操作会导致 Redis 的性能瓶颈。 28 | 29 | ### 7. **网络分区和故障** 30 | - **网络分区**: 网络分区或故障可能导致 Redis 集群或主从复制的性能问题,影响数据的可用性和一致性。 31 | - **主从复制延迟**: 在主从复制配置中,主节点和从节点之间的延迟可能会影响数据的一致性和读取性能。 32 | 33 | ### 8. **并发和并行** 34 | - **单线程设计**: Redis 是单线程的,虽然它通过事件驱动模型处理请求,但仍然受到 CPU 单线程性能的限制。多核机器中的其他线程无法直接提升 Redis 性能。 35 | - **阻塞操作**: 一些操作(如阻塞的 List 操作)可能会影响 Redis 的整体性能,因为它们会阻塞当前的事件循环。 36 | 37 | ### 9. **缓存策略** 38 | - **缓存失效策略**: Redis 的过期策略和淘汰策略(如 LRU、LFU)会影响缓存的有效性和性能。选择合适的策略可以提高缓存的效率和性能。 39 | 40 | ### 10. **集群架构** 41 | - **Redis 集群**: 在 Redis 集群中,节点之间的数据分片和复制策略会影响性能。合理的分片策略和节点配置可以提升集群的性能和扩展性。 -------------------------------------------------------------------------------- /docs/Redis/如何处理网络延迟和网络异常.md: -------------------------------------------------------------------------------- 1 | 在使用 Redis 搭建分布式系统时,网络延迟和网络异常是两个不可避免的挑战。妥善处理这些问题对于保证系统的高可用性和一致性至关重要。以下是一些常见的策略和实践方法来应对这些问题。 2 | 3 | ### 1. **网络延迟的处理** 4 | 5 | 网络延迟会导致 Redis 操作的响应时间变长,从而影响分布式系统的性能。以下是一些应对网络延迟的策略: 6 | 7 | #### **1.1 使用本地缓存** 8 | - 在 Redis 前增加一层本地缓存(如 Memcached 或者应用内存缓存),可以减少对 Redis 的直接访问次数,从而降低网络延迟的影响。 9 | - 常用数据可以存储在本地缓存中,当本地缓存失效时再访问 Redis。 10 | 11 | #### **1.2 优化 Redis 集群的网络拓扑** 12 | - 尽量将 Redis 部署在靠近应用服务器的网络环境中,减少数据传输的物理距离,从而减少网络延迟。 13 | - 如果采用多数据中心部署,可以在每个数据中心部署 Redis 实例,确保请求尽量在本地数据中心内完成。 14 | 15 | #### **1.3 使用异步操作** 16 | - 在某些场景下,采用异步方式操作 Redis 可以避免阻塞主线程,从而减轻网络延迟对应用程序响应时间的影响。 17 | 18 | ### 2. **网络异常的处理** 19 | 20 | 网络异常包括网络分区、网络丢包、连接超时等,这些问题会影响 Redis 的可用性和数据一致性。以下是一些处理网络异常的策略: 21 | 22 | #### **2.1 使用 Redis 哨兵(Sentinel)** 23 | - Redis Sentinel 可以监控 Redis 主从实例的健康状况,自动处理主从切换,以应对主实例因网络异常导致的不可用。 24 | - 哨兵的心跳机制和故障转移功能可以在网络异常发生时尽量保证 Redis 集群的高可用性。 25 | 26 | #### **2.2 数据冗余与多副本** 27 | - 为了防止网络异常导致的数据丢失,可以使用 Redis 主从复制(Replication),将数据复制到多个节点,确保即使某个节点因网络问题不可用,数据仍然可以从其他节点读取。 28 | - 使用多副本策略时,需要注意副本之间的数据一致性问题,可能需要结合其他一致性协议如 PAXOS 或 Raft。 29 | 30 | #### **2.3 超时与重试机制** 31 | - 对于 Redis 操作,可以设置合理的超时时间。如果某次操作因网络异常超时,可以设计重试机制来重新发起请求。 32 | - 需要注意的是,重试次数和间隔时间需要合理配置,避免过度重试导致的系统负载增加。 33 | 34 | #### **2.4 分布式锁的租约机制** 35 | - 在分布式锁的场景中,网络异常可能导致锁的失效。通过设置锁的租约机制(Lease),即使出现网络异常,锁也会在租约到期后自动释放,避免死锁问题。 36 | 37 | #### **2.5 网络隔离与快速故障检测** 38 | - 对 Redis 集群网络进行隔离和优化,减少不必要的网络层复杂性,从而降低网络异常的概率。 39 | - 结合应用层和 Redis 层的监控与告警机制,快速检测并定位网络异常问题,及时采取相应的应对措施。 40 | 41 | ### 3. **实践中的注意事项** 42 | 43 | - **监控与告警**:持续监控 Redis 的性能指标,如响应时间、连接状态、数据同步状态等。配置告警机制,当出现异常时,能够及时通知运维团队进行处理。 44 | - **合理配置超时时间**:根据实际应用的延迟需求,配置合理的 Redis 客户端超时时间,避免因超时过短导致的频繁重试,也避免超时过长影响系统响应。 45 | - **数据一致性考虑**:在网络异常可能导致的数据不一致场景下,考虑使用 Redis 的 Lua 脚本、事务、或者其他一致性保证机制来尽量降低数据不一致的风险。 46 | 47 | 通过以上方法,可以在使用 Redis 搭建分布式系统时有效应对网络延迟和网络异常,从而提高系统的稳定性和可用性。 -------------------------------------------------------------------------------- /docs/Redis/缓存三兄弟.md: -------------------------------------------------------------------------------- 1 | “缓存三兄弟”,即 **Redis 缓存穿透、缓存击穿、缓存雪崩**。这是在后端开发中与缓存相关的三个经典问题,以下是它们的详细解释: 2 | 3 | 1. **缓存穿透**: 4 | 缓存穿透是指用户请求的数据既不在缓存中,也不存在于数据库中。由于缓存无法命中,所有请求都会直接落到数据库上,可能导致数据库压力过大甚至崩溃。常见的解决方法包括: 5 | - 使用 **布隆过滤器** 拦截非法请求。 6 | - 对查询结果为空的请求也进行缓存(例如缓存一个空值),并设置较短的过期时间。 7 | 8 | 2. **缓存击穿**: 9 | 缓存击穿是指某些热点数据在缓存中失效后,短时间内有大量请求同时访问该数据,导致这些请求直接打到数据库,造成瞬时压力激增。解决方案包括: 10 | - **设置热点数据永不过期**。 11 | - 使用 **互斥锁** 或 **单线程更新缓存**,确保只有一个请求能更新缓存,其余请求等待缓存更新完成。 12 | 13 | 3. **缓存雪崩**: 14 | 缓存雪崩是指大量缓存数据在同一时间失效,导致大量请求直接涌向数据库,可能引发系统崩溃。为避免缓存雪崩,可以采取以下措施: 15 | - **为缓存设置随机过期时间**,避免大量缓存同时失效。 16 | - 在缓存失效时,使用 **降级策略**,如返回默认值或限流保护数据库。 17 | -------------------------------------------------------------------------------- /docs/Redis/缓存穿透、击穿、雪崩、预热、更新、降级.md: -------------------------------------------------------------------------------- 1 | 在缓存系统的设计与运维中,有几个常见的问题和优化策略,这些术语包括缓存穿透、缓存击穿、缓存雪崩、缓存预热、缓存更新和缓存降级。以下是对这些概念的详细解释: 2 | 3 | ### 1. 缓存穿透 4 | 5 | **定义**:缓存穿透指的是请求直接绕过缓存系统,直接访问数据库或其他存储系统。这种情况通常发生在请求的数据既不在缓存中也不在数据库中,导致缓存系统无法拦截请求,所有请求都直接到达数据库,造成数据库负担过重。 6 | 7 | **解决方案**: 8 | - **布隆过滤器**:在缓存前使用布隆过滤器检查请求的数据是否存在。布隆过滤器能有效拦截不存在的数据请求,从而避免对数据库的无效查询。 9 | - **缓存空值**:对数据库查询结果为“空”的请求也进行缓存,并设置合理的过期时间,以减少重复查询。 10 | 11 | ### 2. 缓存击穿 12 | 13 | **定义**:缓存击穿指的是某一热点数据的缓存过期,导致大量请求同时到达数据库。这种情况通常发生在缓存过期的瞬间,大量请求同时到达数据库,造成数据库的瞬时压力增大。 14 | 15 | **解决方案**: 16 | - **互斥锁**:在缓存过期的情况下,使用互斥锁确保只有一个请求去数据库查询数据,其他请求等待数据加载完成后再从缓存中获取数据。 17 | - **加锁机制**:对缓存失效的数据进行加锁,确保同一时刻只有一个请求能查询数据库并更新缓存。 18 | 19 | ### 3. 缓存雪崩 20 | 21 | **定义**:缓存雪崩指的是缓存中的大量数据在同一时间失效,导致大量请求直接访问数据库,造成数据库负担剧增,甚至可能导致数据库崩溃。 22 | 23 | **解决方案**: 24 | - **缓存过期时间随机化**:为缓存数据设置随机的过期时间,避免大量数据同时过期。 25 | - **预热缓存**:在系统启动时或缓存数据更新时,提前加载数据到缓存中,避免缓存大规模失效。 26 | - **多级缓存**:使用多级缓存机制(如本地缓存和分布式缓存结合),减少对单一缓存层的依赖。 27 | 28 | ### 4. 缓存预热 29 | 30 | **定义**:缓存预热是指在系统启动或在系统负载较低时,提前将一些重要的数据加载到缓存中,以提高系统在高负载情况下的响应速度。 31 | 32 | **解决方案**: 33 | - **数据预加载**:在系统启动时,加载常用数据到缓存中。 34 | - **定期刷新**:定期将热点数据重新加载到缓存中,确保缓存中始终有有效的数据。 35 | 36 | ### 5. 缓存更新 37 | 38 | **定义**:缓存更新是指在数据发生变化时,更新缓存中的数据,以确保缓存和数据库中的数据一致。 39 | 40 | **解决方案**: 41 | - **写穿模式**:每次写操作同时更新缓存和数据库,保证缓存中的数据始终与数据库中的数据一致。 42 | - **写回模式**:先更新缓存,异步更新数据库。这样可以减少数据库的写入压力,但可能存在数据不一致的风险。 43 | - **失效策略**:在数据库数据变更时使缓存失效,下次请求时重新加载数据到缓存中。 44 | 45 | ### 6. 缓存降级 46 | 47 | **定义**:缓存降级是指在缓存系统出现故障或不可用时,系统能够自动切换到其他备选方案(如直接访问数据库),以保证系统的可用性和稳定性。 48 | 49 | **解决方案**: 50 | - **降级策略**:设置降级逻辑,当缓存不可用时,自动切换到数据库或其他备用系统处理请求。 51 | - **熔断机制**:当缓存服务出现异常时,快速切换到备用处理流程,以避免缓存故障导致系统整体不可用。 52 | 53 | 通过理解和应用这些缓存优化策略,可以有效提高系统的性能和稳定性。 -------------------------------------------------------------------------------- /docs/Source/go-microv5.md: -------------------------------------------------------------------------------- 1 | # go-microv5 组件关系图案例(完整调用过程) 2 | 3 | 以下是一个基于 `go-microv5` 的完整调用过程关系图示例。 4 | 5 | ```mermaid 6 | sequenceDiagram 7 | participant Client as 客户端 8 | participant Service as 服务 9 | participant Registry as 服务注册中心 10 | participant Selector as 服务选择器 11 | participant Transport as 传输层 12 | participant Broker as 消息代理 13 | participant Queue as 消息队列 14 | participant Protocol as 通信协议 15 | participant ServiceInstance as 服务实例 16 | 17 | Client->>Service: 调用服务 18 | Service->>Registry: 注册服务 19 | Service->>Selector: 选择服务实例 20 | Selector->>Registry: 获取服务实例列表 21 | Selector->>ServiceInstance: 选择最佳实例 22 | Service->>Transport: 发送请求 23 | Transport->>Protocol: 使用 HTTP/GRPC 协议通信 24 | Service->>Broker: 发布消息 25 | Broker->>Queue: 推送消息到队列 26 | Queue->>ServiceInstance: 消费消息 27 | ServiceInstance->>Client: 返回响应 28 | ``` 29 | 30 | ## 组件说明 31 | 32 | - **客户端 (Client)**: 发起服务调用的入口。 33 | - **服务 (Service)**: 核心业务逻辑处理单元,负责处理请求。 34 | - **服务注册中心 (Registry)**: 管理服务的注册与发现。 35 | - **服务选择器 (Selector)**: 选择合适的服务实例,支持负载均衡。 36 | - **传输层 (Transport)**: 提供网络通信能力,支持多种协议。 37 | - **消息代理 (Broker)**: 实现发布/订阅模式的消息传递。 38 | - **消息队列 (Queue)**: 用于异步消息传递。 39 | - **通信协议 (Protocol)**: 支持 HTTP、gRPC 等协议。 40 | - **服务实例 (ServiceInstance)**: 实际运行的服务节点,处理请求并返回响应。 41 | -------------------------------------------------------------------------------- /docs/Theory/Ctrl C发生了什么.md: -------------------------------------------------------------------------------- 1 | `Ctrl + C` 是一个在计算机终端或控制台中广泛使用的键盘快捷键,它的主要作用是中断当前正在运行的程序或命令。具体来说,按下 `Ctrl + C` 会向终端或控制台中的进程发送一个中断信号。这个操作的细节如下: 2 | 3 | ### 作用 4 | 5 | 1. **发送中断信号**: 6 | - `Ctrl + C` 发送一个 `SIGINT`(Signal Interrupt)信号给当前的前台进程。`SIGINT` 信号的默认行为是中断进程,即停止当前的操作并终止程序的执行。 7 | 8 | 2. **终止程序**: 9 | - 当程序收到 `SIGINT` 信号后,它通常会被终止。程序可以通过捕获 `SIGINT` 信号并注册自定义处理函数来执行清理操作或其他自定义行为,而不是直接退出。 10 | 11 | ### 实现细节 12 | 13 | - **在 Unix/Linux 系统中**: 14 | - `Ctrl + C` 发送 `SIGINT` 信号到进程组中的所有前台进程。 15 | - 默认情况下,`SIGINT` 信号会终止正在运行的进程,但程序可以通过设置信号处理程序来改变这一行为。例如,程序可以捕获 `SIGINT` 信号并在接收到信号时执行某些特定操作,而不是直接退出。 16 | 17 | - **在 Windows 系统中**: 18 | - `Ctrl + C` 的作用类似,发送中断信号到当前前台进程,通常会中止进程的执行。 19 | - Windows 命令行窗口的处理机制与 Unix/Linux 系统类似,但具体的实现细节可能会有所不同。 20 | 21 | ### 示例 22 | 23 | 在一个 Unix/Linux 系统的终端中,如果你正在运行一个长时间执行的程序(如 `ping` 命令),按下 `Ctrl + C` 会中断 `ping` 命令的执行,并返回终端提示符。例如: 24 | 25 | ```bash 26 | $ ping google.com 27 | PING google.com (142.250.196.174) 56(84) bytes of data. 28 | 64 bytes from 142.250.196.174: icmp_seq=1 ttl=117 time=12.1 ms 29 | 64 bytes from 142.250.196.174: icmp_seq=2 ttl=117 time=11.7 ms 30 | ^C 31 | --- google.com ping statistics --- 32 | 2 packets transmitted, 2 received, 0% packet loss, time 1001ms 33 | ``` 34 | 35 | 在这个例子中,`^C` 表示 `Ctrl + C` 被按下,`ping` 命令被中断。 36 | 37 | ### 总结 38 | 39 | `Ctrl + C` 是一个常用的快捷键,用于中断当前的命令或程序。它通过发送 `SIGINT` 信号来实现这一功能,允许用户在命令行界面中中止正在运行的任务。 -------------------------------------------------------------------------------- /docs/Theory/LRU 算法及其实现方式.md: -------------------------------------------------------------------------------- 1 | LRU(Least Recently Used,最近最少使用)算法是一种常见的缓存淘汰算法,用于在缓存满时决定哪些数据应该被移除,以腾出空间存放新数据。LRU 基于一个简单的原则:最近最少被使用的数据最可能在未来也不会被使用,因此应当优先淘汰这些数据。 2 | 3 | ### **LRU 算法原理** 4 | - **基本思想**:LRU 通过记录每个数据项的使用时间,将最近最少使用的数据淘汰出缓存。具体来说,每当访问缓存中的一个数据项时,该项的访问时间会更新为最新时间;而当需要淘汰数据时,选择最久未被访问的数据项进行删除。 5 | 6 | ### **实现方式** 7 | LRU 算法可以通过多种方式实现,常见的有两种: 8 | 9 | #### **1. 基于链表和哈希表的实现** 10 | - **双向链表**: 11 | - 使用一个双向链表来维护缓存中的数据,链表的头部保存最近访问的数据,链表的尾部保存最久未被访问的数据。 12 | - 每次访问一个数据项时,将该数据项移动到链表的头部。 13 | - 当缓存满时,将链表尾部的数据项移除。 14 | 15 | - **哈希表**: 16 | - 使用一个哈希表存储缓存中的键值对,以支持 O(1) 时间复杂度的快速查找。 17 | - 哈希表中的每个键指向双向链表中的一个节点。 18 | 19 | - **操作**: 20 | - **访问数据**:通过哈希表找到对应的链表节点,并将其移动到链表头部。 21 | - **新增数据**:将数据插入到链表头部,同时在哈希表中添加对应的键值对。 22 | - **删除数据**:如果缓存满,删除链表尾部节点,并在哈希表中移除对应的键。 23 | 24 | - **优点**:结合双向链表和哈希表的实现,能够在 O(1) 时间复杂度内完成缓存数据的访问、插入和删除操作。 25 | 26 | #### **2. 基于栈的实现** 27 | - **栈的结构**:使用两个栈来实现 LRU,一个用于保存缓存数据的顺序,另一个用于辅助操作。 28 | - 每次访问数据时,将该数据项推入栈中。 29 | - 当缓存满时,删除栈底部的数据项,这些数据是最久未被访问的。 30 | 31 | - **操作**: 32 | - **访问数据**:如果数据已在栈中,找到它并将其弹出,然后重新压入栈顶。 33 | - **新增数据**:如果数据不在栈中,直接压入栈顶。 34 | - **删除数据**:缓存满时,弹出栈底的数据项。 35 | 36 | - **缺点**:栈的实现需要线性时间复杂度(O(n))来查找和删除数据,因此在效率上不如基于链表和哈希表的实现。 37 | 38 | ### **LRU 在实际应用中的优化** 39 | - **Window LRU**:在大规模缓存系统中,可能会采用“窗口”大小限制来优化 LRU 的性能。例如,仅追踪最近的 N 次访问,而不是整个访问历史。 40 | - **LRU-K**:通过记录每个数据项的前 K 次访问时间,选择最近 K 次访问中最远的一次来决定淘汰。这个策略比单一的 LRU 更能反映数据的长期访问模式。 41 | 42 | ### **总结** 43 | LRU 是一种经典的缓存淘汰算法,适用于那些数据访问具有时间局部性(即最近访问的数据很可能会再次访问)的场景。通过使用双向链表和哈希表,LRU 能在 O(1) 时间复杂度内高效管理缓存,但在某些高性能场景下,可能会需要进一步的优化或结合其他算法(如 LFU)来应对特殊需求。 -------------------------------------------------------------------------------- /docs/Theory/Linux中的零拷贝技术.md: -------------------------------------------------------------------------------- 1 | **零拷贝(Zero Copy)**是Linux操作系统中的一种优化技术,它旨在减少数据在内核空间和用户空间之间的复制操作,从而提高I/O操作的效率。零拷贝技术尤其在网络数据传输和文件系统中得到了广泛应用。下面简要介绍零拷贝技术的原理、常见实现方式以及其优势。 2 | 3 | ### 零拷贝的原理 4 | 5 | 在传统的数据传输过程中,数据通常需要在内核空间和用户空间之间多次复制。例如,在网络传输中,数据从磁盘读取后会被复制到内核缓冲区,再从内核缓冲区复制到用户空间的应用程序缓冲区,最后再从用户空间复制回内核缓冲区,通过网络发送出去。 6 | 7 | 零拷贝技术通过减少或消除这些不必要的复制操作,直接在内核空间内处理数据,避免数据在内核和用户空间之间的往返,显著提高了传输效率。 8 | 9 | ### Linux中的零拷贝实现方式 10 | 11 | Linux内核提供了几种常见的零拷贝技术,包括: 12 | 13 | #### 1. `sendfile` 14 | 15 | `sendfile` 系统调用可以将数据直接从文件描述符复制到套接字,而不需要经过用户空间。它的工作原理如下: 16 | 17 | 1. 文件数据从磁盘读取到内核缓冲区。 18 | 2. 数据从内核缓冲区直接复制到网卡的发送缓冲区。 19 | 3. 数据通过网络发送到目标。 20 | 21 | 在这个过程中,数据没有经过用户空间,从而实现了零拷贝。 22 | 23 | #### 2. `mmap` + `write` 24 | 25 | `mmap` 系统调用可以将文件映射到内存地址空间,通过这种方式,应用程序可以像访问内存一样访问文件内容。`write` 系统调用则可以将内存中的数据直接写入文件或发送到网络。使用 `mmap` 和 `write` 的组合可以减少数据复制的次数,实现接近零拷贝的效果。 26 | 27 | 1. 文件内容通过 `mmap` 映射到用户空间内存。 28 | 2. 应用程序直接读取 `mmap` 映射的内存。 29 | 3. 使用 `write` 将数据写入目标文件或通过网络发送。 30 | 31 | #### 3. `splice` 和 `tee` 32 | 33 | `splice` 和 `tee` 是Linux提供的两个系统调用,它们允许在不同的文件描述符之间移动数据,而不经过用户空间。`splice` 可以在两个文件描述符之间移动数据,而 `tee` 可以在两个管道之间复制数据,而不进行实际的数据拷贝。 34 | 35 | - `splice` 示例:用于在套接字和管道之间传递数据,不涉及用户空间。 36 | - `tee` 示例:用于在管道之间复制数据,避免用户空间拷贝。 37 | 38 | ### 零拷贝的优势 39 | 40 | - **提高性能**:减少数据在内核和用户空间之间的复制,减少CPU消耗,降低上下文切换的开销,显著提高I/O操作的性能。 41 | - **节省内存带宽**:通过减少数据复制,降低内存带宽的消耗,从而提高整体系统性能。 42 | - **减轻CPU负载**:零拷贝技术能够将数据传输的负载更多地交给DMA(Direct Memory Access)等硬件完成,从而释放CPU资源用于其他计算任务。 43 | 44 | ### 适用场景 45 | 46 | - **高性能网络传输**:如Web服务器、文件传输服务等场景,通常使用零拷贝技术提高数据传输效率。 47 | - **文件系统操作**:零拷贝在文件拷贝、备份等操作中可以显著提高性能,特别是在处理大文件时。 48 | 49 | 通过零拷贝技术,Linux操作系统在高性能网络和文件系统操作中得以显著优化,有效减少了I/O操作的开销。 -------------------------------------------------------------------------------- /docs/Theory/Linux系统态与用户态.md: -------------------------------------------------------------------------------- 1 | 在 Linux 操作系统中,系统态(Kernel Mode)和用户态(User Mode)是操作系统中两个重要的运行模式。这两种模式主要是为了保护系统资源的安全性和稳定性,并提高操作系统的可靠性。以下是它们的区别和各自的特点: 2 | 3 | ### **用户态(User Mode)** 4 | - **概念**: 5 | - 用户态是用户进程(即用户应用程序)运行的模式。在用户态下,进程只能执行有限的、非特权的指令,并且只能访问自己的虚拟地址空间。 6 | - **特点**: 7 | - **受限访问**:用户态进程只能访问有限的系统资源,无法直接访问硬件设备或内核数据结构,必须通过系统调用来请求内核服务。 8 | - **安全性高**:由于用户态进程的权限受限,即使程序出现错误(例如非法内存访问),也不会直接影响整个系统的稳定性。 9 | - **上下文切换**:当用户进程需要使用系统资源时,会通过系统调用陷入内核态,发生上下文切换。 10 | 11 | ### **系统态(Kernel Mode)** 12 | - **概念**: 13 | - 系统态是内核代码运行的模式。在系统态下,操作系统内核拥有最高的权限,可以访问所有系统资源,包括硬件设备和所有的内存空间。 14 | - **特点**: 15 | - **完全访问权限**:内核在系统态下可以执行任何指令,并可以访问所有硬件和内存资源。 16 | - **高风险操作**:由于内核代码运行在系统态,任何错误都可能导致整个系统崩溃或产生安全漏洞。 17 | - **执行系统调用**:当用户态进程需要使用系统资源时,会通过系统调用进入系统态,内核在系统态处理请求后再切换回用户态。 18 | 19 | ### **用户态与系统态的切换** 20 | - **系统调用**:用户进程通过系统调用请求内核服务,例如文件操作、进程控制等。这时,进程从用户态切换到系统态。 21 | - **中断处理**:当硬件设备产生中断时,CPU 会从当前运行的进程切换到系统态,以执行相应的中断处理程序。 22 | - **上下文切换**:在多任务操作系统中,CPU 可能需要在不同进程之间切换,此时可能涉及从用户态切换到系统态,或从系统态切换回用户态。 23 | 24 | ### **总结** 25 | - 用户态与系统态的划分确保了操作系统的安全性和稳定性。用户态负责运行应用程序,受限于系统资源的访问权限;系统态负责管理硬件和系统资源,具备完全的访问权限。通过这种划分,Linux 系统能够有效地防止用户程序对系统的破坏,同时保证高效的系统资源管理。 -------------------------------------------------------------------------------- /docs/Theory/Md5过程.md: -------------------------------------------------------------------------------- 1 | 当然,以下是MD5(消息摘要算法5)的工作原理的简要说明: 2 | 3 | - **输入**:MD5接受一个可变长度的消息作为输入,并生成一个固定长度的输出。 4 | - **填充**:输入消息会被填充,以确保其长度是512位的倍数。填充方式是在消息末尾添加一个'1'位,然后添加零。 5 | - **附加长度**:原始消息的长度(以位为单位)会被附加到填充后的消息末尾。 6 | - **处理**:填充后的消息会被分成512位的块进行处理,通过一系列的位操作、模加法和常量异或操作。这些操作会重复进行4轮。 7 | - **输出**:最终结果是一个128位(16字节)的哈希值,通常以32位的十六进制数表示。 8 | 9 | MD5广泛用于校验和和数据完整性验证,但由于存在允许碰撞攻击的漏洞,不建议在需要高安全性的加密场景中使用。 -------------------------------------------------------------------------------- /docs/Theory/epoll怎么解决io效率问题.md: -------------------------------------------------------------------------------- 1 | `epoll` 通过以下几个关键机制和设计解决了 I/O 效率问题,特别是在处理大量并发连接时的性能瓶颈: 2 | 3 | ### 1. **事件驱动的通知机制** 4 | `epoll` 基于事件驱动的机制来提高 I/O 操作的效率,而不是像 `select` 和 `poll` 那样每次都扫描整个文件描述符集。具体来说: 5 | 6 | - **事件注册**:在使用 `epoll` 时,用户首先通过 `epoll_ctl` 系统调用将感兴趣的文件描述符(如 socket)注册到 `epoll` 实例中,并指定关注的事件(如可读、可写)。 7 | - **事件等待**:用户通过 `epoll_wait` 系统调用等待事件发生。当有文件描述符上的事件(如数据到达)发生时,`epoll` 内核会将这些事件通知用户态程序。 8 | 9 | 这种方式避免了每次都遍历所有文件描述符,只关注状态变化的文件描述符,因此在大量并发连接下极大地提升了效率。 10 | 11 | ### 2. **O(1) 复杂度** 12 | 传统的 `select` 和 `poll` 是线性复杂度(O(n)),即在每次调用时都要遍历所有注册的文件描述符。这在大量文件描述符下性能会显著下降。而 `epoll` 的事件通知机制使得它的性能接近常数时间复杂度(O(1)),即无论监控多少文件描述符,处理每次 I/O 事件的时间几乎是固定的。 13 | 14 | ### 3. **边缘触发(ET, Edge-Triggered)模式** 15 | `epoll` 支持边缘触发模式,只有在文件描述符的状态从未准备好变为准备好时才通知用户态。这减少了不必要的系统调用和上下文切换,但也要求用户态程序更精确地管理读取和写入操作,避免遗漏事件。 16 | 17 | ### 4. **内核和用户空间之间的消息传递方式** 18 | 在内核和用户空间之间,`epoll` 的消息传递主要通过以下方式进行: 19 | 20 | - **共享内存机制**:`epoll` 的事件列表使用共享内存机制,使得内核将事件列表直接写入共享的用户空间内存区域,用户态程序通过 `epoll_wait` 直接读取这些事件。这减少了在内核态和用户态之间的数据拷贝操作,提升了性能。 21 | 22 | - **减少系统调用次数**:由于 `epoll` 的事件驱动机制,用户程序只在有事件发生时调用 `epoll_wait`,而不需要像 `select` 和 `poll` 那样频繁地调用,减少了系统调用的开销。 23 | 24 | - **批量处理**:`epoll_wait` 允许一次返回多个事件,使得用户态程序可以批量处理事件,而不是每次只处理一个。这进一步减少了内核态和用户态之间的切换,提高了效率。 25 | 26 | ### 5. **异步通知与低延迟** 27 | `epoll` 支持异步通知,当文件描述符的状态发生变化时,内核会立即将这些变化记录在 `epoll` 实例中,这使得用户态程序在调用 `epoll_wait` 时能够迅速得到通知,保持低延迟响应。 28 | 29 | ### 6. **避免重复注册和扫描** 30 | 一旦文件描述符被注册到 `epoll` 实例中,除非用户显式取消注册,否则无需重复注册,这避免了像 `select` 那样每次调用都重新传递整个文件描述符集合的开销。此外,由于 `epoll` 内部使用高效的数据结构(如红黑树和链表),它可以快速地定位和管理这些文件描述符。 31 | 32 | ### 总结 33 | `epoll` 通过事件驱动机制、接近 O(1) 的复杂度、边缘触发模式、共享内存和批量处理机制,极大地提高了多路复用 I/O 的效率,特别是在处理大量并发连接时表现出色。这些机制使得 `epoll` 在高性能服务器、网络代理、数据库中间件等需要处理大量并发 I/O 的场景中得到了广泛应用。 -------------------------------------------------------------------------------- /docs/Theory/hash冲突.md: -------------------------------------------------------------------------------- 1 | 在哈希表中,哈希冲突是指两个或多个不同的键被哈希到同一个位置(即哈希值相同)的情况。解决哈希冲突的方法有以下几种: 2 | 3 | ### 1. **开放寻址法 (Open Addressing)** 4 | - **基本原理**: 当发生哈希冲突时,通过寻找下一个空闲的哈希表位置来存储冲突的键值对。常见的策略包括线性探测、二次探测和双重哈希。 5 | - **查询时间**: 6 | - **平均时间复杂度**: \( O(1) \) 7 | - **最坏时间复杂度**: \( O(n) \),在哈希表接近满时,最坏情况下可能需要探查整个哈希表。 8 | 9 | ### 2. **链地址法 (Chaining)** 10 | - **基本原理**: 每个哈希表的槽位保存一个链表(或其他数据结构),所有哈希到同一位置的键值对都存储在这个链表中。当发生冲突时,将新键值对追加到对应槽位的链表中。 11 | - **查询时间**: 12 | - **平均时间复杂度**: \( O(1) \),假设哈希函数较好,链表长度较短。 13 | - **最坏时间复杂度**: \( O(n) \),在极端情况下,所有键值对可能被哈希到同一槽位,此时需要遍历整个链表。 14 | 15 | ### 3. **再哈希法 (Rehashing)** 16 | - **基本原理**: 在发生冲突时,应用另一个哈希函数对键进行再次哈希,直到找到空闲槽位。 17 | - **查询时间**: 18 | - **平均时间复杂度**: \( O(1) \) 19 | - **最坏时间复杂度**: \( O(n) \),如果使用的哈希函数非常糟糕或者哈希表接近满。 20 | 21 | ### 4. **建立公共溢出区** 22 | - **基本原理**: 哈希表的每个槽位存储一个指针,当发生冲突时,将冲突的键值对放到一个公共的溢出区中,而不是哈希表的槽位中。 23 | - **查询时间**: 24 | - **平均时间复杂度**: \( O(1) \) 25 | - **最坏时间复杂度**: \( O(n) \),当所有键值对都哈希到相同位置,所有元素都存储在溢出区中。 26 | 27 | ### 总结: 28 | - **最常用的方法**: 链地址法(Chaining)是最常用的哈希冲突解决方法,因为它的实现简单,并且在负载因子不高时性能较好。 29 | - **查询时间**: 30 | - **平均查询时间**: \( O(1) \),在大多数情况下,哈希表的查询时间非常高效。 31 | - **最坏查询时间**: \( O(n) \),在极端情况下(例如哈希函数产生大量冲突),查询时间可能退化为线性时间。 -------------------------------------------------------------------------------- /docs/Theory/hash函数.md: -------------------------------------------------------------------------------- 1 | Hash 函数(散列函数)是一种将输入数据(通常是字符串、文件、或其他数据)映射为固定长度的值(通常是一个整数)的函数。这个映射值通常称为“散列值”或“哈希值”。Hash 函数广泛应用于计算机科学的多个领域,如数据存储、密码学、数据检索等。 2 | 3 | ### Hash 函数的基本特点 4 | 1. **输入任意性**:Hash 函数可以接受任意长度的输入数据。 5 | 2. **输出固定性**:Hash 函数的输出是固定长度的,这意味着无论输入数据的大小是多少,输出的散列值长度是相同的。 6 | 3. **确定性**:相同的输入数据经过相同的 Hash 函数处理后,总是产生相同的散列值。 7 | 4. **高效性**:Hash 函数计算应该非常高效,能够快速地处理大量数据并生成散列值。 8 | 5. **碰撞低概率**:不同的输入数据产生相同散列值(称为碰撞)的概率应该尽可能低。 9 | 6. **不可逆性**:给定一个散列值,应该难以推断出原始输入数据(特别是在加密场景中)。 10 | 11 | ### Hash 函数的常见应用 12 | 1. **数据存储与检索**: 13 | - **哈希表(Hash Table)**:哈希表通过 Hash 函数将键映射到数组的索引位置,从而实现快速的数据存储与检索。哈希表的查找时间复杂度接近 O(1),是实现字典、集合等数据结构的基础。 14 | 15 | 2. **密码学**: 16 | - **消息摘要**:在密码学中,Hash 函数用于生成消息摘要(如 MD5、SHA-256),这些摘要用于验证数据的完整性。例如,传输一个文件时,可以生成文件的散列值,并在接收端进行比对,以确保文件没有被篡改。 17 | - **数字签名**:在数字签名中,Hash 函数用于生成待签名数据的散列值,然后使用私钥对该散列值进行加密生成签名。 18 | 19 | 3. **数据完整性验证**: 20 | - **校验和**:在网络传输和存储过程中,Hash 函数可以用于生成数据的校验和,确保数据在传输或存储过程中未被损坏。 21 | 22 | 4. **负载均衡**: 23 | - **一致性哈希(Consistent Hashing)**:在分布式系统中,一致性哈希用于将请求均匀分布到不同的服务器上,保证负载均衡,同时在新增或删除节点时对系统影响最小。 24 | 25 | 5. **文件比较**: 26 | - **去重**:通过对文件内容计算哈希值,可以快速比较两个文件是否相同,广泛用于文件去重、数据备份等场景。 27 | 28 | ### 常见的 Hash 函数 29 | - **MD5(Message-Digest Algorithm 5)**:生成 128 位(16 字节)的散列值,已被认为不安全,但仍在某些场景中使用。 30 | - **SHA-1(Secure Hash Algorithm 1)**:生成 160 位(20 字节)的散列值,也被认为不够安全。 31 | - **SHA-2(包括 SHA-224、SHA-256、SHA-384、SHA-512)**:目前广泛使用的安全哈希算法,其中 SHA-256 是最常用的,生成 256 位(32 字节)的散列值。 32 | - **SHA-3**:是 SHA-2 的后继算法,设计上与前者有很大不同,提供更高的安全性。 33 | 34 | ### Hash 碰撞与处理 35 | 由于 Hash 函数将无限多的输入映射到有限的输出范围内,理论上总是存在不同输入映射到相同散列值的可能性,这称为碰撞。 36 | 37 | - **开放寻址法**:碰撞发生时,在哈希表中寻找下一个空闲位置存储数据。 38 | - **链表法**:每个哈希表的桶(bucket)存储一个链表,所有哈希值相同的数据都存在链表中,解决碰撞问题。 39 | 40 | ### 总结 41 | Hash 函数是计算机科学中的基础工具,其快速、确定性和不可逆性使其成为众多领域的关键技术。从简单的数据存储到复杂的密码学应用,Hash 函数在不同场景下扮演着重要角色。 -------------------------------------------------------------------------------- /docs/Theory/linux进程调度算法.md: -------------------------------------------------------------------------------- 1 | Linux 操作系统中,进程调度是指决定哪个进程在特定时间点被分配到 CPU 上运行的过程。Linux 采用了多种进程调度算法,主要目标是实现公平、高效的资源分配,同时保证实时性和响应速度。常见的调度算法如下: 2 | 3 | ### 1. **完全公平调度器 (CFS)** 4 | 5 | - **概述**:从 Linux 2.6.23 开始,CFS (Completely Fair Scheduler) 成为了默认的调度算法。它的核心思想是实现“完美公平”的调度,每个任务都能获得公平的 CPU 使用时间。 6 | - **关键概念**: 7 | - CFS 使用“虚拟运行时间” (vruntime) 来跟踪每个进程的 CPU 使用时间。CPU 时间少的进程会优先调度。 8 | - CFS 不使用时间片,而是依赖平衡 vruntime 来调度各个进程。 9 | - 每个进程都会根据它的优先级被分配一个时间窗 (time slice),优先级越高的进程,其 vruntime 增长得越慢,因此有更多的机会获得 CPU 时间。 10 | - **适用场景**:适合大多数一般用途的系统,追求公平性和性能的平衡。 11 | 12 | ### 2. **实时调度器 (RT Scheduler)** 13 | 14 | - **概述**:Linux 内核还提供了实时调度算法,主要针对实时任务,确保特定任务在规定的时间内完成。 15 | - **类型**: 16 | - **SCHED_FIFO**:实时优先级队列,优先级高的进程优先执行,除非显式让出 CPU,否则不会被抢占。 17 | - **SCHED_RR**:基于轮转法的实时调度,与 `SCHED_FIFO` 类似,但有时间片轮转机制,在时间片结束后会被调度其他相同优先级的任务。 18 | - **适用场景**:对响应时间要求高的场景,如嵌入式系统、音视频处理和工业控制系统。 19 | 20 | ### 3. **批处理调度器 (SCHED_BATCH)** 21 | 22 | - **概述**:批处理调度器适用于批处理任务,这些任务通常不需要快速的响应时间,如编译任务、数据分析等。 23 | - **特点**:批处理调度器不会与交互进程竞争 CPU 时间,因此适合长时间运行的后台任务,确保前台进程有更好的响应。 24 | 25 | ### 4. **空闲调度器 (SCHED_IDLE)** 26 | 27 | - **概述**:空闲调度器为最低优先级的进程服务。当系统没有其他可运行的进程时,才会执行 `SCHED_IDLE` 进程。 28 | - **适用场景**:适用于对响应速度没有要求,只有在系统空闲时才运行的进程。 29 | 30 | ### 5. **传统调度算法 (O(1) Scheduler)** 31 | 32 | - **概述**:在 CFS 之前,Linux 使用 O(1) 调度器。O(1) 调度器根据每个任务的优先级分配固定的时间片,并通过两条运行队列(活动队列和过期队列)来管理可运行的任务。 33 | - **优点**:调度时间复杂度为 O(1),不受系统中任务数量的影响。 34 | - **缺点**:公平性较差,尤其是在高负载情况下,容易发生优先级反转问题。 35 | 36 | ### 调度策略 37 | 38 | Linux 进程调度还可以分为三种主要策略: 39 | 40 | - **SCHED_NORMAL**:用于普通的非实时进程,是 CFS 的默认调度策略。 41 | - **SCHED_FIFO** 和 **SCHED_RR**:用于实时进程,分别是先进先出和轮转调度。 42 | - **SCHED_BATCH** 和 **SCHED_IDLE**:用于不敏感于交互延迟的批处理任务或空闲任务。 43 | 44 | ### 调度级别和优先级 45 | 46 | - **实时优先级**:实时任务具有更高的优先级,范围为 0-99。 47 | - **普通优先级**:非实时任务的优先级范围为 -20 到 19,越小表示优先级越高。 48 | 49 | ### 总结 50 | 51 | Linux 进程调度算法涵盖了从普通进程到实时任务的各种场景。CFS 提供了基于公平的进程调度,适合大多数工作负载,而实时调度器则为有严格时限要求的任务提供保障。 -------------------------------------------------------------------------------- /docs/Theory/互斥锁.md: -------------------------------------------------------------------------------- 1 | **互斥锁**是面试中常见的考查点之一,尤其是在高并发场景下的资源访问控制中。以下是关于互斥锁的详细解释: 2 | 3 | ### 什么是互斥锁? 4 | 互斥锁(Mutex,Mutual Exclusion)是一种用于多线程或多进程编程中的同步机制,确保同一时间只有一个线程或进程可以访问共享资源。它通过加锁和解锁的方式来实现对资源的独占访问,从而避免数据竞争和不一致的问题。 5 | 6 | ### 互斥锁的应用场景 7 | 1. **多线程环境中的共享资源保护**: 8 | 当多个线程需要同时访问一个共享变量或数据结构时,使用互斥锁可以防止多个线程同时修改数据,导致数据错误。 9 | 10 | 2. **数据库操作**: 11 | 在高并发场景下,互斥锁可以用来控制对数据库记录的访问,避免多个线程同时更新同一条记录。 12 | 13 | 3. **分布式系统中的资源竞争**: 14 | 在分布式环境中,互斥锁可以用来确保多个节点对共享资源的有序访问,例如 Redis 分布式锁。 15 | 16 | ### 互斥锁的实现 17 | 互斥锁的实现方式因语言和平台而异,以下是一些常见的实现方式: 18 | 1. **Java**: 19 | - 使用 `synchronized` 关键字实现互斥锁。 20 | - 使用 `ReentrantLock` 提供更灵活的锁机制。 21 | 22 | 示例代码: 23 | ```java 24 | ReentrantLock lock = new ReentrantLock(); 25 | lock.lock(); 26 | try { 27 | // 访问共享资源 28 | } finally { 29 | lock.unlock(); 30 | } 31 | ``` 32 | 33 | 2. **Go**: 34 | - 使用 `sync.Mutex` 提供互斥锁支持。 35 | 36 | 示例代码: 37 | ```go 38 | var mu sync.Mutex 39 | mu.Lock() 40 | // 访问共享资源 41 | mu.Unlock() 42 | ``` 43 | 44 | 3. **Redis**: 45 | - 使用 Redis 的 `SETNX` 命令实现分布式互斥锁。 46 | - 或者使用开源库 Redisson 提供的分布式锁功能。 47 | 48 | ### 互斥锁的优缺点 49 | **优点**: 50 | - 确保资源的独占访问,避免数据竞争。 51 | - 简单易用,适合单机环境。 52 | 53 | **缺点**: 54 | - 可能导致死锁:如果线程在加锁后未能正确释放锁,可能会导致其他线程永久等待。 55 | - 性能开销:加锁和解锁操作会增加一定的性能开销,尤其是在高并发场景下。 56 | 57 | ### 面试中的常见问题 58 | 1. **互斥锁与读写锁的区别?** 59 | - 互斥锁是独占锁,任何线程都不能同时访问资源。 60 | - 读写锁允许多个线程同时读取,但写操作是独占的。 61 | 62 | 2. **如何避免死锁?** 63 | - 遵循加锁顺序:确保所有线程按照相同的顺序获取锁。 64 | - 使用超时机制:设置锁的超时时间,避免长时间等待。 65 | 66 | 3. **分布式锁如何实现?** 67 | - 使用 Redis 的 `SETNX` 和 `EXPIRE` 命令。 68 | - 使用 Zookeeper 的临时节点。 69 | 70 | 通过理解互斥锁的原理、应用场景和实现方式,可以更好地应对面试中相关的高并发问题。 -------------------------------------------------------------------------------- /docs/Theory/共享内存.md: -------------------------------------------------------------------------------- 1 | **共享内存**是多个进程间的一种通信机制,允许它们通过共享同一块物理内存区域来实现数据的共享和交换。每个进程都有自己的虚拟地址空间,但通过共享内存,不同进程可以在各自的地址空间内映射到相同的物理内存区域。这种方式使得多个进程可以快速地访问和修改共享数据,而无需使用管道、消息队列或其他较慢的进程间通信机制。 2 | 3 | ### 共享内存的实现原理 4 | 5 | 1. **创建共享内存:** 6 | 一个进程可以通过系统调用(如POSIX中的`shmget`或`shm_open`,System V中的`shmat`)创建一个共享内存段。这个共享内存段会分配在物理内存中。 7 | 8 | 2. **映射共享内存:** 9 | 其他进程可以通过映射(`mmap`)或附加(`shmat`)的方式,将这个共享内存段映射到它们的虚拟地址空间。尽管每个进程使用的虚拟地址不同,但它们都指向同一个物理内存区域。 10 | 11 | 3. **访问共享内存:** 12 | 映射后,各进程就可以像访问自己的内存一样访问共享内存中的数据。因为这些虚拟地址映射到相同的物理内存区域,所以对数据的修改是即时可见的。 13 | 14 | ### 虚拟地址空间中的映射位置 15 | 16 | 在现代操作系统中,进程的虚拟地址空间通常划分为几个部分,包括用户空间、堆区、栈区、共享库区域、内核空间等。共享内存映射到的虚拟地址位置通常在以下区域中: 17 | 18 | - **用户空间的高地址部分:** 19 | 在多数操作系统中,映射的共享内存区域往往位于用户空间的高地址部分。这一区域一般用于动态库加载和共享内存映射。具体的映射地址由操作系统决定,可以通过`mmap`等系统调用来指定或让操作系统自动选择。 20 | 21 | - **内存映射区(Memory Mapped Region):** 22 | 在典型的虚拟地址空间布局中,内存映射区是专门用来映射文件和共享内存的。这一块区域通常位于堆区上方、栈区下方的高地址部分。 23 | 24 | - **具体地址:** 25 | 操作系统会为映射的共享内存选择一个适当的虚拟地址,通常会避免和进程的其他内存区域(如堆、栈、共享库等)发生冲突。对于程序员而言,这个地址通常是透明的,不需要关心具体地址的数值,但可以通过一些调试工具或内存管理系统调用来查看。 26 | 27 | ### 共享内存的典型使用场景 28 | 29 | 共享内存因其高效性和低延迟的特性,常用于需要高性能的场景,如: 30 | 31 | - **进程间通信(IPC):** 多个进程之间共享数据,避免频繁的上下文切换和系统调用开销。 32 | - **生产者-消费者模型:** 例如,一个进程生成数据并写入共享内存,另一个进程从共享内存中读取数据进行处理。 33 | - **快速数据交换:** 在同一台机器上的不同进程间交换大量数据时,使用共享内存可以显著提高速度。 34 | 35 | 总结来说,**共享内存**是进程间通信的一种高效方式,允许多个进程共享一块物理内存,尽管它们各自的虚拟地址空间不同。映射到实际物理内存的虚拟地址通常位于进程地址空间的高地址部分,具体位置由操作系统管理和分配。 -------------------------------------------------------------------------------- /docs/Theory/内核态和用户态.md: -------------------------------------------------------------------------------- 1 | ### 内核态和用户态 2 | 3 | 在操作系统中,程序运行时的状态可以分为两种:**内核态**(Kernel Mode)和**用户态**(User Mode)。 4 | 5 | - **内核态**:内核态是指操作系统内核执行程序的状态。在内核态下,程序可以访问硬件资源,如内存、硬盘等,也可以执行所有CPU指令。操作系统的内核(包括设备驱动程序、文件系统等)通常运行在内核态。 6 | 7 | - **用户态**:用户态是指普通应用程序运行时的状态。在用户态下,程序只能执行有限的指令集,无法直接访问硬件资源。所有对硬件资源的访问都必须通过系统调用,由操作系统内核代表应用程序执行相应操作。 8 | 9 | 用户态的程序不能直接访问或操作硬件资源,这是为了保证系统的安全性和稳定性。只有操作系统内核能够直接操作硬件,以防止应用程序因错误或恶意行为而影响整个系统的稳定性。 10 | 11 | ### 内核态与用户态的切换 12 | 13 | **内核态与用户态的切换**通常发生在以下几种情况下: 14 | 15 | 1. **系统调用**:当用户态的应用程序需要访问硬件资源时,会通过系统调用进入内核态,由操作系统内核执行相应操作,然后返回用户态。 16 | 17 | 2. **中断**:当硬件设备发出中断请求(如键盘输入、硬盘数据读写完成等)时,CPU会切换到内核态来处理中断。处理完成后,系统会根据具体情况恢复到用户态继续执行程序。 18 | 19 | 3. **异常处理**:当应用程序发生异常(如除零错误、缺页错误等)时,CPU会切换到内核态,由操作系统内核处理异常。 20 | 21 | **切换过程**大致如下: 22 | 23 | 1. **用户态 -> 内核态**:当需要进行系统调用或处理中断时,CPU将程序当前的上下文(如程序计数器、寄存器值等)保存在内核栈中,然后切换到内核态执行相关的操作。 24 | 25 | 2. **内核态 -> 用户态**:当系统调用或中断处理完成后,操作系统将保存的用户态上下文恢复,并切换回用户态继续执行程序。 26 | 27 | ### 程序在开始运行时,内核态和用户态都发生了什么? 28 | 29 | 当一个程序开始运行时,经历了从**内核态**到**用户态**的切换过程,主要涉及以下步骤: 30 | 31 | 1. **进程创建**(内核态):当用户启动一个程序时,操作系统内核会创建一个新的进程。这个过程涉及分配进程控制块(PCB)、分配内存空间、设置程序的初始状态等操作。 32 | 33 | 2. **加载可执行文件**(内核态):操作系统内核将程序的可执行文件从磁盘加载到内存中。这个过程包括解析可执行文件的格式,分配内存空间并将文件内容加载到内存中相应的位置。 34 | 35 | 3. **设置用户态的初始环境**(内核态):操作系统内核为即将进入用户态的程序设置好初始的栈指针、程序计数器(指向程序的入口点),以及必要的寄存器值。 36 | 37 | 4. **切换到用户态**:当所有的初始化工作完成后,操作系统将CPU的控制权交给程序,切换到用户态开始执行程序的指令。 38 | 39 | 5. **程序运行**(用户态):程序在用户态下开始执行。此时,程序无法直接访问硬件资源,所有的硬件访问请求(如文件读写、网络通信等)都通过系统调用由操作系统内核代理执行。 40 | 41 | 整个过程展示了操作系统如何管理程序的执行,确保系统的安全性和稳定性。 -------------------------------------------------------------------------------- /docs/Theory/分布式事务.md: -------------------------------------------------------------------------------- 1 | 分布式事务通常有以下几种实现方式,各自具有不同的原理和应用场景: 2 | 3 | ### 1. **两阶段提交(2PC)** 4 | 5 | #### 原理: 6 | - **准备阶段(Prepare Phase)**:协调者向所有参与者发送准备请求,询问它们是否能够提交事务。参与者执行事务并锁定资源,然后返回投票(YES 或 NO)给协调者。 7 | - **提交阶段(Commit Phase)**:如果所有参与者都返回 YES,协调者发送提交请求;否则,发送回滚请求。 8 | 9 | #### 特点: 10 | - **一致性强**:确保所有参与者要么全部提交,要么全部回滚。 11 | - **阻塞问题**:在网络故障时,可能会导致参与者长时间处于等待状态。 12 | 13 | ### 2. **三阶段提交(3PC)** 14 | 15 | #### 原理: 16 | - **准备阶段(CanCommit)**:协调者询问参与者是否准备好提交。 17 | - **预提交阶段(PreCommit)**:参与者确认准备好后,进入预提交状态,锁定资源。 18 | - **提交阶段(DoCommit)**:协调者最终通知参与者提交事务。 19 | 20 | #### 特点: 21 | - **非阻塞**:相较于 2PC,3PC 增加了一个阶段,以减少锁定资源的时间,降低阻塞风险。 22 | - **复杂性增加**:实现和处理相对复杂。 23 | 24 | ### 3. **补偿事务** 25 | 26 | #### 原理: 27 | - **业务逻辑**:在多个微服务或数据库中执行操作后,如果某个操作失败,通过执行补偿操作来撤销之前的操作。 28 | - **最终一致性**:系统在某个时间点可能不一致,但最终会达到一致性。 29 | 30 | #### 特点: 31 | - **灵活性高**:适合长事务或复杂业务逻辑的场景。 32 | - **实现复杂**:需要设计补偿逻辑,确保正确性。 33 | 34 | ### 4. **TCC(Try-Confirm-Cancel)模式** 35 | 36 | #### 原理: 37 | - **Try**:尝试执行操作并保留资源。 38 | - **Confirm**:所有参与者确认成功执行。 39 | - **Cancel**:在出现错误时,所有参与者回滚操作。 40 | 41 | #### 特点: 42 | - **高可靠性**:通过精细的控制确保事务的一致性。 43 | - **实现复杂**:需要明确的补偿逻辑,适用于严格的业务场景。 44 | 45 | ### 5. **基于消息队列的最终一致性** 46 | 47 | #### 原理: 48 | - **异步操作**:将操作消息发送到消息队列,消费者处理消息并执行相应操作,确保最终一致性。 49 | - **重试机制**:在失败时进行重试。 50 | 51 | #### 特点: 52 | - **高吞吐量**:支持高并发操作,适合微服务架构。 53 | - **一致性延迟**:在某个时间点不一定一致,但会最终一致。 54 | 55 | ### 总结 56 | 57 | 每种分布式事务的实现方式都有其优缺点,适用于不同的场景。选择合适的方案时,需要考虑系统的可用性、性能需求和一致性要求。如果你需要更详细的说明或特定场景的分析,请告诉我! -------------------------------------------------------------------------------- /docs/Theory/协程和线程的区别.md: -------------------------------------------------------------------------------- 1 | 协程是一种轻量级的用户级线程,允许在单个线程中执行多个任务。与线程的主要区别包括: 2 | 3 | ### 1. **调度**: 4 | - **线程**:由操作系统进行调度,可能涉及上下文切换,消耗更多资源。 5 | - **协程**:由程序控制调度,通常在用户空间执行,切换开销小。 6 | 7 | ### 2. **资源消耗**: 8 | - **线程**:每个线程都有独立的堆栈和上下文,创建和销毁的开销较大。 9 | - **协程**:共享线程的堆栈,创建和销毁更快,消耗更少资源。 10 | 11 | ### 3. **并发**: 12 | - **线程**:支持真正的并行处理,适合多核处理器。 13 | - **协程**:在单线程中实现并发,适合I/O密集型任务。 14 | 15 | ### 4. **编程模型**: 16 | - **线程**:通常使用锁和同步机制来管理共享状态。 17 | - **协程**:通过非阻塞调用和异步编程模型简化状态管理。 18 | 19 | 协程通常在高并发和I/O操作中表现优异。你想更深入了解某个特定方面吗? -------------------------------------------------------------------------------- /docs/Theory/堆和栈访问效率哪个更高.md: -------------------------------------------------------------------------------- 1 | 在一般情况下,**栈(stack)**的访问效率比**堆(heap)**要高。这主要是由于以下几个原因: 2 | 3 | ### 1. 内存分配方式 4 | 5 | - **栈**:栈内存的分配是由系统自动完成的,通常是一次性分配一块内存,用来存储局部变量、函数参数和返回地址。栈的分配和释放速度非常快,因为它遵循**后进先出(LIFO)**的原则,只需要简单地移动栈指针即可完成分配和释放。由于栈的分配是在编译时已经确定,因此栈中的内存是连续的,访问速度很快。 6 | 7 | - **堆**:堆内存的分配是由程序员手动控制的,通常使用 `malloc`、`new` 等函数进行分配,使用 `free`、`delete` 等函数进行释放。堆内存的分配和释放相对复杂,因为堆中的内存不是连续的,分配器需要找到足够大小的空闲内存块,这会导致内存碎片化,并且访问堆中的数据时通常需要通过指针进行间接访问,增加了开销。 8 | 9 | ### 2. 缓存局部性 10 | 11 | - **栈**:由于栈内存是连续的,数据在内存中的布局紧凑,因此在访问栈中的数据时,CPU 缓存命中率较高,有利于提高访问速度。 12 | 13 | - **堆**:堆内存中的数据不一定是连续的,分配的内存块可能分布在内存的各个区域,这种分散性会降低缓存命中率,影响访问速度。 14 | 15 | ### 3. 管理开销 16 | 17 | - **栈**:由于栈的分配和释放是自动管理的,且只涉及到栈指针的简单移动,因此管理开销很低。 18 | 19 | - **堆**:堆内存的分配和释放需要更多的管理开销,包括内存分配算法的执行、内存碎片化的处理、垃圾回收(在某些编程语言中),这些都会增加额外的时间和资源消耗。 20 | 21 | ### 总结 22 | 23 | 综上所述,**栈的访问效率通常要高于堆**。栈适合用于临时性、生命周期短的数据(如局部变量),而堆适合用于需要动态分配内存且生命周期较长的数据(如对象、数组)。在性能敏感的场景下,合理选择栈和堆进行内存管理对于优化程序效率非常重要。 -------------------------------------------------------------------------------- /docs/Theory/孤儿进程和僵尸进程以及僵死进程的解决方案.md: -------------------------------------------------------------------------------- 1 | 孤儿进程和僵尸进程都是进程管理中的特殊情况,它们在系统资源的管理和释放方面有重要影响。以下是它们的定义和解决方案: 2 | 3 | ### 1. 孤儿进程 4 | #### 定义: 5 | - **孤儿进程** 是指其父进程已经结束,但它仍在运行的子进程。由于子进程失去了父进程的监护,系统会将这些孤儿进程的父进程重定向为 `init` 进程(PID为1的进程)。`init` 进程会自动接管这些孤儿进程,并在它们结束时回收其资源。 6 | 7 | #### 解决方案: 8 | - 系统会自动处理孤儿进程,不需要额外干预。`init` 进程会接管并正确回收这些孤儿进程的资源。 9 | 10 | ### 2. 僵尸进程 11 | #### 定义: 12 | - **僵尸进程** 是指一个已经终止的进程,但其父进程尚未读取它的退出状态信息(通过 `wait()` 系列系统调用)。这种进程虽然已经停止执行,但仍在进程表中占有一个条目,因而称为僵尸进程。 13 | 14 | #### 解决方案: 15 | - **父进程处理**:父进程应当及时使用 `wait()` 或 `waitpid()` 系列函数来读取子进程的退出状态,这样系统可以释放与僵尸进程相关的资源。 16 | - **父进程退出**:如果父进程没有处理僵尸进程且一直运行,导致出现多个僵尸进程,最简单的处理方式是让父进程退出,这样系统的 `init` 进程会接管这些僵尸进程并清理它们。 17 | - **信号处理**:在父进程中设置 `SIGCHLD` 信号的处理函数,在子进程退出时自动调用 `wait()` 函数,避免产生僵尸进程。 18 | 19 | ### 3. 僵死进程 20 | #### 定义: 21 | - **僵死进程** 并不是一个正式的术语,它通常指的是进程由于某种原因(例如进入死循环、占用大量资源或不响应信号)而无法正常终止,导致系统资源被占用但无法释放的状态。 22 | 23 | #### 解决方案: 24 | - **手动终止**:使用 `kill` 命令手动发送信号,如 `SIGKILL`,强制终止该进程。 25 | - **资源监控**:通过监控工具(如 `top`, `htop`, `ps`)检测异常的进程,并在发现问题时进行处理。 26 | - **系统资源管理**:配置系统资源限制(如 `ulimit`)防止单个进程过度消耗资源。 27 | 28 | ### 总结: 29 | - **孤儿进程**:由 `init` 进程自动接管,不需要手动干预。 30 | - **僵尸进程**:应通过父进程及时读取子进程的退出状态来避免或清理。 31 | - **僵死进程**:应通过手动信号或系统资源管理工具进行监控和终止。 -------------------------------------------------------------------------------- /docs/Theory/抢占分布式锁时.md: -------------------------------------------------------------------------------- 1 | 为解决进程A在持有分布式锁时被抢占的问题,可以采用以下几种策略: 2 | 3 | 1. **锁续期机制**:在锁的持有期间,定期续期(例如,通过心跳机制)以防止锁过期。这样,即使A的执行时间较长,也能保持锁的有效性。 4 | 5 | 2. **乐观锁**:在操作开始时记录数据版本,结束时检查版本是否一致,避免长时间持锁而被抢占。 6 | 7 | 3. **重试机制**:当进程B抢占锁后,进程A可以在适当的时间间隔后尝试重新获取锁。 8 | 9 | 4. **补偿机制**:在锁被抢占时,A可以记录执行状态,并在重新获取锁后继续执行未完成的操作。 10 | 11 | 结合这些策略,可以提高分布式锁的稳定性,减少因长时间执行而导致的锁过期问题。你想深入了解某个策略吗? -------------------------------------------------------------------------------- /docs/Theory/指针和引用的区别.md: -------------------------------------------------------------------------------- 1 | 指针和引用是两种用于间接访问变量或对象的机制,尽管它们在功能上有些相似,但它们在实现和使用上有显著的区别。 2 | 3 | ### 1. 定义与概念 4 | 5 | - **指针**: 6 | - 指针是一个变量,用来存储另一个变量的内存地址。 7 | - 在使用指针时,你可以通过它间接地访问或修改其指向的变量。 8 | - 在大多数编程语言中(如C/C++/Go),指针可以进行算术运算(如加减法),可以改变指向的地址。 9 | 10 | - **引用**: 11 | - 引用是一个别名,它是某个已经存在的变量的另一个名字。 12 | - 一旦引用被初始化,它就始终指向原始变量,不能改变指向其他变量。 13 | - 引用的概念在C++、Java等语言中比较常见,Go语言本身并不直接支持引用概念,但可以通过指针和别名来实现类似的效果。 14 | 15 | ### 2. 声明与使用 16 | 17 | - **指针**: 18 | - 在C/C++中,指针通过`*`来声明。 19 | - 例如:`int *ptr = &var;`表示声明一个指向`var`的指针`ptr`。 20 | - 通过解引用运算符`*`来访问指针所指向的变量的值。 21 | 22 | - **引用**: 23 | - 在C++中,引用通过`&`来声明。 24 | - 例如:`int &ref = var;`表示声明一个`var`的引用`ref`。 25 | - 使用引用时,不需要特殊的符号,直接使用引用名即可。 26 | 27 | ### 3. 内存管理 28 | 29 | - **指针**: 30 | - 指针本身占用内存,它保存着另一个变量的地址。 31 | - 需要小心管理指针的生命周期,避免悬空指针和内存泄漏等问题。 32 | - 指针可以指向`NULL`或`nil`,表示它不指向任何有效的内存。 33 | 34 | - **引用**: 35 | - 引用本身不占用内存,仅作为已有变量的别名存在。 36 | - 一旦创建引用,必须立即绑定到一个对象或变量,不能为`NULL`。 37 | 38 | ### 4. 可修改性 39 | 40 | - **指针**: 41 | - 指针可以在程序中动态改变它所指向的变量或对象。 42 | - 可以通过指针来改变指针指向的对象,也可以让指针指向其他对象。 43 | 44 | - **引用**: 45 | - 引用一旦绑定某个对象或变量后,就不能再改变其指向。 46 | - 引用本身不可变,但通过引用可以修改其指向的变量的值。 47 | 48 | ### 5. 安全性与灵活性 49 | 50 | - **指针**: 51 | - 指针提供了更大的灵活性,可以指向任何地址,包括动态分配的内存。 52 | - 但这也带来了更多的风险,例如指向无效地址导致程序崩溃或安全漏洞。 53 | 54 | - **引用**: 55 | - 引用在一定程度上是更安全的,因为它总是绑定到一个有效的变量。 56 | - 引用的灵活性不如指针,因为它无法重新绑定,也不能执行指针的算术运算。 57 | 58 | ### 6. 在不同语言中的使用 59 | 60 | - **C/C++**: 61 | - C语言主要使用指针进行各种操作,尤其是在动态内存分配、数组和字符串操作中。 62 | - C++同时支持指针和引用,引用更多用于传递函数参数和返回值,提供更高的抽象和安全性。 63 | 64 | - **Java**: 65 | - Java中没有指针的概念,但引用在Java中非常普遍,Java中的对象变量本质上都是引用。 66 | 67 | - **Go**: 68 | - Go语言没有传统意义上的引用,但使用指针来间接访问内存,指针可以传递到函数中,允许函数修改传入的参数。 69 | 70 | ### 总结 71 | - **指针** 适用于需要更灵活、低级别内存操作的场景。 72 | - **引用** 更安全且易于使用,适合用于简化代码和提高代码的可维护性。 -------------------------------------------------------------------------------- /docs/Theory/操作系统中的中断.md: -------------------------------------------------------------------------------- 1 | 操作系统中的中断(Interrupt)是一种重要的机制,用于处理计算机硬件或软件事件的及时响应。它允许计算机在执行当前任务的同时,能够及时响应外部或内部的事件,从而实现多任务处理和高效的资源管理。 2 | 3 | ### 中断的基本概念 4 | 5 | 1. **中断的定义**: 6 | 中断是指在计算机系统运行过程中,CPU在执行一条指令的过程中,由于某些事件的发生,使得CPU暂时中断当前的指令执行,转而去处理这些事件。处理完中断事件后,CPU再返回中断前的状态继续执行原来的程序。 7 | 8 | 2. **中断源**: 9 | 中断可以来源于多种途径,主要包括: 10 | - **硬件中断**:由硬件设备(如键盘、鼠标、网卡、硬盘等)发出的中断信号。例如,当用户按下键盘上的某个键时,键盘控制器会向CPU发送中断信号,要求处理输入。 11 | - **软件中断**:由软件通过特定指令(如`INT`指令)触发的中断。例如,操作系统调用系统功能时,可能会触发软件中断。 12 | - **异常**:这是由CPU在执行程序过程中检测到的错误或特殊条件(如除零错误、非法指令、缺页等)引发的。 13 | 14 | 3. **中断处理**: 15 | 当中断发生时,操作系统会执行以下步骤: 16 | - **保存上下文**:当前正在执行的程序的状态(如寄存器内容、程序计数器)会被保存,以便中断处理结束后恢复。 17 | - **确定中断源**:通过查询中断控制器(如PIC、APIC)来识别是哪一个设备或哪一个事件触发了中断。 18 | - **中断处理程序**:操作系统会调用对应的中断处理程序(Interrupt Service Routine, ISR),处理特定的中断事件。 19 | - **恢复上下文**:中断处理完成后,操作系统恢复被中断前保存的程序状态,继续执行被中断的程序。 20 | 21 | 4. **中断优先级**: 22 | 中断可以有优先级。当多个中断几乎同时发生时,CPU会按照中断优先级进行处理。优先级高的中断会优先得到处理,而低优先级的中断则可能会被延迟。 23 | 24 | 5. **中断屏蔽**: 25 | 在某些情况下,操作系统或硬件可能会临时屏蔽(禁用)某些中断,以确保关键代码段的正常执行,不被打断。 26 | 27 | ### 中断的作用 28 | 29 | - **提高系统响应速度**:中断机制允许系统及时响应硬件设备的请求,而无需轮询检查设备状态,从而提高了系统的响应速度和效率。 30 | - **实现并发处理**:通过中断,操作系统可以处理多个并发事件,如处理用户输入、网络数据接收等。 31 | - **错误处理**:中断机制还用于处理系统运行时的各种异常情况,如非法操作或硬件故障。 32 | 33 | ### 中断与异常的区别 34 | 35 | - **中断**:通常是由外部设备引发的,并且是异步发生的。例如,用户按下键盘或收到网络数据。 36 | - **异常**:通常是由当前指令执行过程中发生的事件引发的,是同步发生的。例如,除以零错误或非法指令执行。 37 | 38 | 通过中断机制,操作系统能够高效管理系统资源,及时响应各种事件,确保计算机系统的稳定和高效运行。 -------------------------------------------------------------------------------- /docs/Theory/死锁概念,死锁产生的四个必要条件,如何避免和预防死锁.md: -------------------------------------------------------------------------------- 1 | ### 一、死锁的概念 2 | 3 | **死锁**是在多线程或多进程系统中,一组进程或线程彼此等待对方释放资源,导致所有进程或线程都无法继续执行的状态。换句话说,死锁是系统中两个或多个进程在互相等待对方释放资源,导致所有进程都无法继续运行的情况。 4 | 5 | ### 二、死锁产生的四个必要条件 6 | 7 | 死锁的产生必须满足以下四个条件,称为 **死锁的必要条件**,这些条件必须同时成立: 8 | 9 | 1. **互斥条件**(Mutual Exclusion) 10 | - 资源不能被共享,某个资源在某个时刻只能由一个进程使用。如果另一个进程请求该资源,则该进程只能等待。 11 | 12 | 2. **占有且等待**(Hold and Wait) 13 | - 一个进程已经持有了至少一个资源,同时又提出新的资源请求,但该资源被其他进程占用,此时该进程处于等待状态。 14 | 15 | 3. **不剥夺条件**(No Preemption) 16 | - 资源不能被强制剥夺,即进程已经获得的资源在未使用完之前,不能被强行剥夺,只能在使用完后主动释放。 17 | 18 | 4. **循环等待**(Circular Wait) 19 | - 存在一个进程等待环路,例如进程 A 等待进程 B 持有的资源,进程 B 等待进程 C 持有的资源,而进程 C 又等待进程 A 持有的资源,形成一个环路,导致死锁。 20 | 21 | ### 三、如何避免和预防死锁 22 | 23 | 要避免和预防死锁,可以采取以下几种策略: 24 | 25 | #### 1. **破坏互斥条件** 26 | - 让资源尽可能实现共享,例如读操作可以共享文件锁,从而减少对独占资源的需求。但这并不适用于所有资源,某些资源本身就需要互斥访问(如打印机)。 27 | 28 | #### 2. **破坏占有且等待条件** 29 | - **预防措施**:要求进程在开始执行时一次性申请所有所需资源,这样可以避免在持有部分资源的情况下再去申请新的资源。 30 | - **代价**:可能导致资源的低效利用,因为进程可能持有一些暂时不需要的资源。 31 | 32 | #### 3. **破坏不剥夺条件** 33 | - **预防措施**:如果一个进程已经占有某些资源,但又申请其他资源而被阻塞,那么该进程必须释放已经占有的资源,并稍后重新申请所有资源。 34 | - **代价**:可能会增加系统的开销,因为进程可能要多次释放和重新申请资源。 35 | 36 | #### 4. **破坏循环等待条件** 37 | - **预防措施**:对资源进行全局编号,并要求进程按编号顺序申请资源,从而避免形成环路。例如,如果进程已经持有了编号较小的资源,那么它只能申请比该编号大的资源。 38 | - **代价**:需要对所有资源进行编号,并且进程申请资源的顺序必须严格遵循编号顺序。 39 | 40 | ### 四、死锁的其他处理方法 41 | 42 | 除了避免和预防死锁之外,还可以通过以下方法处理死锁: 43 | 44 | #### 1. **死锁检测和恢复** 45 | - **死锁检测**:系统可以通过检测来识别是否发生了死锁。检测算法通常基于资源分配图或其他数据结构来检查循环等待条件。 46 | - **死锁恢复**:一旦检测到死锁,系统可以通过强制撤销或终止某些进程来打破死锁循环,释放资源。 47 | 48 | #### 2. **死锁避免算法** 49 | - **银行家算法**:银行家算法是一种经典的死锁避免算法,它根据进程的最大资源需求来判断资源分配是否会导致死锁。只有当分配资源后系统仍然处于安全状态时,资源才会被分配给进程。 50 | 51 | #### 3. **资源有序分配** 52 | - 对资源进行排序,并且要求进程按照规定的顺序申请资源,可以有效避免死锁的发生。 53 | 54 | ### 总结 55 | 56 | 死锁是多进程系统中可能出现的严重问题,预防和避免死锁是系统设计中的关键任务。通过破坏死锁的四个必要条件,使用死锁检测与恢复机制,或者采用死锁避免算法,可以有效减少或避免死锁的发生,确保系统的稳定性和高效性。 -------------------------------------------------------------------------------- /docs/Theory/物理内存、虚拟内存和共享内存.md: -------------------------------------------------------------------------------- 1 | 在 `top` 命令的输出中,物理内存、虚拟内存和共享内存是指系统中不同类型的内存使用情况。它们的定义和区别如下: 2 | 3 | ### 1. 物理内存(Physical Memory) 4 | 5 | - **定义**:物理内存是计算机实际安装的 RAM(随机存取内存)。它是系统可以直接使用的内存,处理器可以直接访问它。 6 | - **作用**:物理内存用于存储当前运行的程序和数据。所有程序运行时需要的内存都会分配在物理内存中。 7 | - **在 `top` 中的表示**:`top` 命令中通常以 `RES`(Resident Memory)表示实际占用的物理内存量。 8 | 9 | ### 2. 虚拟内存(Virtual Memory) 10 | 11 | - **定义**:虚拟内存是操作系统创建的一种内存抽象,允许程序使用的地址空间比实际物理内存大。虚拟内存包括物理内存和磁盘上的交换空间(如交换文件或页面文件)的结合。 12 | - **作用**:虚拟内存使得程序可以使用比实际物理内存更多的内存空间,同时提供了内存保护和隔离。虚拟内存也使得程序可以运行在连续的内存空间中,即使物理内存碎片化。 13 | - **在 `top` 中的表示**:`top` 命令中以 `VIRT`(Virtual Memory)表示程序使用的虚拟内存总量。 14 | 15 | ### 3. 共享内存(Shared Memory) 16 | 17 | - **定义**:共享内存是一种内存区域,可以被多个进程共享。共享内存允许进程之间高效地交换数据,因为多个进程可以访问相同的内存区域。 18 | - **作用**:共享内存常用于需要进程间通信(IPC)的场景,减少数据拷贝和提高性能。系统中的共享内存区域通常被用于实现高效的进程间通信。 19 | - **在 `top` 中的表示**:`top` 命令中以 `SHR`(Shared Memory)表示一个进程使用的共享内存量。 20 | 21 | ### 示例解释 22 | 23 | 在 `top` 输出中,你可能会看到类似以下的行: 24 | 25 | ``` 26 | PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 27 | 1234 user 20 0 150000 30000 5000 S 0.0 1.5 0:01.23 myapp 28 | ``` 29 | 30 | - **VIRT (Virtual Memory)**:150,000 KB,表示进程使用的虚拟内存总量,包括物理内存和所有映射的文件、共享内存等。 31 | - **RES (Resident Memory)**:30,000 KB,表示实际占用的物理内存量,不包括交换空间中的内容。 32 | - **SHR (Shared Memory)**:5,000 KB,表示进程所使用的共享内存量,这部分内存可能会被其他进程共享。 33 | 34 | ### 总结 35 | 36 | - **物理内存** 是系统实际的 RAM,直接用于存储正在运行的数据和程序。 37 | - **虚拟内存** 是操作系统管理的虚拟地址空间,包括物理内存和交换空间的总和。 38 | - **共享内存** 是多个进程可以共享的内存区域,用于进程间通信和数据共享。 39 | 40 | 这些信息帮助我们理解系统资源的使用情况,并有助于进行性能分析和优化。 -------------------------------------------------------------------------------- /docs/Theory/红黑树.md: -------------------------------------------------------------------------------- 1 | 红黑树是一种自平衡二叉搜索树,其特点是能够在最坏情况下保持相对均衡,从而确保基本的动态操作(插入、删除、查找)时间复杂度为 \(O(\log n)\)。它广泛用于实现许多计算机系统中的关联数组、符号表、集合和映射等数据结构,如 Java 的 TreeMap 和 C++ 的 STL 中的 `map` 和 `set`。 2 | 3 | 红黑树的主要特点有以下几点: 4 | 5 | ### 1. **节点颜色** 6 | - 红黑树中的每个节点不是红色就是黑色,节点的颜色是红黑树平衡性质的关键。 7 | 8 | ### 2. **根节点是黑色** 9 | - 红黑树的根节点总是黑色的。这个性质确保根节点平衡。 10 | 11 | ### 3. **叶子节点是黑色** 12 | - 这里的叶子节点是指树中所有的空节点(`NIL`),它们不包含数据,表示子树的结束。 13 | 14 | ### 4. **红色节点的子节点是黑色** 15 | - 红色节点不能有两个红色子节点。换句话说,红色节点的子节点必须是黑色(即不能有连续的红色节点)。这叫做“红色节点的约束”。 16 | 17 | ### 5. **从任意节点到其所有子叶的路径包含相同数量的黑色节点** 18 | - 对于任意节点,从该节点到其后代叶子节点的所有路径,必须包含相同数量的黑色节点(称为“黑色高度”相同)。 19 | 20 | ### 6. **自平衡性** 21 | - 虽然红黑树的结构不像 AVL 树那样严格平衡,但它通过颜色规则,确保了树的高度在 \(2 \log n\) 之内,保证了操作的高效性。 22 | 23 | ### 7. **时间复杂度** 24 | - 在红黑树上,常见操作(如插入、删除、查找)的时间复杂度为 \(O(\log n)\),因为树的高度最多为 \(2 \log n\)。 25 | 26 | ### 红黑树的优势: 27 | - 相较于其他平衡树(如 AVL 树),红黑树通过牺牲部分平衡性,减少了调整操作的复杂度。它适用于插入和删除操作频繁的场景,因为插入和删除操作后的重新平衡操作(颜色翻转、旋转)相对较少。 28 | 29 | ### 红黑树的旋转和颜色调整: 30 | - 当插入或删除节点后破坏了红黑树的性质,必须通过**旋转(左旋、右旋)**和**颜色变换**来恢复红黑树的平衡。 -------------------------------------------------------------------------------- /docs/Theory/自旋锁.md: -------------------------------------------------------------------------------- 1 | 自旋锁是一种用于多线程编程的同步机制,它在处理器上通过忙等待的方式实现互斥锁。自旋锁的主要特点是,当一个线程试图获取锁时,它会不断地循环检查锁是否被释放,而不是进入睡眠状态。自旋锁通常适用于以下场景: 2 | 3 | ### 主要特点 4 | 5 | 1. **忙等待**: 6 | 自旋锁的线程在等待获取锁时不会进入睡眠状态,而是不断地检查锁的状态。这种方式称为忙等待(busy-waiting)。如果锁已被其他线程占用,线程会不断地循环尝试获取锁,直到锁被释放。 7 | 8 | 2. **开销较低**: 9 | 自旋锁避免了线程上下文切换的开销,因为它不会将线程挂起。不过,这也意味着自旋锁在锁竞争激烈时可能会导致较高的CPU利用率和资源浪费。 10 | 11 | 3. **适用于短时间锁定**: 12 | 自旋锁适用于锁持有时间很短的场景。因为在短时间内自旋等待的开销通常低于线程上下文切换的开销。然而,对于锁持有时间较长的场景,自旋锁的性能可能会很差。 13 | 14 | ### 实现方式 15 | 16 | 自旋锁的基本实现通常包括一个原子变量(比如一个布尔值),表示锁的状态。线程通过原子操作(如比较和交换)来尝试获取和释放锁。以下是一个简单的自旋锁的实现示例(用Go语言表示): 17 | 18 | ```go 19 | package main 20 | 21 | import ( 22 | "sync" 23 | "time" 24 | ) 25 | 26 | // SpinLock is a simple spinlock implementation 27 | type SpinLock struct { 28 | locked int32 29 | } 30 | 31 | func (sl *SpinLock) Lock() { 32 | for !atomic.CompareAndSwapInt32(&sl.locked, 0, 1) { 33 | // Busy-waiting 34 | time.Sleep(0) // Yielding to prevent CPU overuse 35 | } 36 | } 37 | 38 | func (sl *SpinLock) Unlock() { 39 | atomic.StoreInt32(&sl.locked, 0) 40 | } 41 | 42 | func main() { 43 | var lock SpinLock 44 | var wg sync.WaitGroup 45 | 46 | for i := 0; i < 10; i++ { 47 | wg.Add(1) 48 | go func(i int) { 49 | defer wg.Done() 50 | lock.Lock() 51 | // Critical section 52 | time.Sleep(time.Millisecond * 10) 53 | lock.Unlock() 54 | }(i) 55 | } 56 | 57 | wg.Wait() 58 | } 59 | ``` 60 | 61 | ### 优缺点 62 | 63 | **优点**: 64 | - 避免了线程上下文切换的开销。 65 | - 实现简单,开销小(对于短时间锁定的场景)。 66 | 67 | **缺点**: 68 | - 不适用于锁持有时间较长的情况,因为它会浪费CPU资源。 69 | - 在高竞争情况下,可能导致高CPU利用率和性能下降。 70 | - 需要在实现时考虑公平性,避免某些线程长时间得不到锁。 71 | 72 | 总的来说,自旋锁是一种轻量级的锁机制,适用于对锁持有时间较短的场景。对于锁持有时间较长或高竞争的场景,通常建议使用其他类型的锁(如互斥锁)来提高效率。 -------------------------------------------------------------------------------- /docs/Theory/进程和线程之间有什么区别.md: -------------------------------------------------------------------------------- 1 | 进程和线程是操作系统中的两个基本概念,用于管理和执行程序中的任务。它们在执行方式、资源使用、以及彼此之间的关系上存在一些重要的区别。 2 | 3 | ### 1. **定义** 4 | - **进程(Process)**:是操作系统分配资源和调度任务的基本单位。一个进程代表一个程序的运行实例,它拥有独立的内存空间和系统资源。 5 | - **线程(Thread)**:是进程中的一个执行单元。一个进程可以包含多个线程,这些线程共享进程的资源,如内存、文件句柄等,但每个线程有自己的栈和寄存器。 6 | 7 | ### 2. **资源分配** 8 | - **进程**:每个进程有自己独立的地址空间(内存空间),它们之间的数据是相互隔离的。进程间的通信需要通过进程间通信机制(如管道、消息队列、共享内存等)。 9 | - **线程**:同一进程内的多个线程共享进程的地址空间和资源。由于共享相同的内存区域,线程间的通信更加轻量级和高效,但也带来了线程间同步和数据竞争的问题。 10 | 11 | ### 3. **开销** 12 | - **进程**:由于进程是独立的运行单元,创建和销毁进程的开销较大(需要分配独立的内存空间、加载独立的程序、创建独立的系统资源等)。进程切换涉及到上下文切换,需要保存和恢复进程的状态。 13 | - **线程**:线程的创建、销毁、和切换相对轻量级,因为线程共享进程的资源。线程切换只需要保存和恢复较少的状态信息。 14 | 15 | ### 4. **执行控制** 16 | - **进程**:每个进程有独立的执行流,进程之间互不干扰。一个进程中的崩溃或错误通常不会影响其他进程。 17 | - **线程**:多个线程在同一进程中并发执行,线程之间可以直接通信和共享数据。如果一个线程崩溃,可能会导致整个进程崩溃,从而影响其他线程。 18 | 19 | ### 5. **并发性** 20 | - **进程**:进程之间可以并发执行。多核处理器可以通过多进程并行执行来提高程序的并行性和性能。 21 | - **线程**:线程之间也可以并发执行。同一进程内的多个线程可以在多核处理器上并行执行,从而充分利用多核系统的性能。 22 | 23 | ### 6. **使用场景** 24 | - **进程**:适用于需要隔离的任务或独立运行的程序,适合用于多程序并发处理,如 Web 服务器中的多个独立的用户请求处理。 25 | - **线程**:适用于需要频繁通信和共享数据的任务,适合用于高并发的程序,如多线程的并行计算、实时数据处理等。 26 | 27 | ### 总结 28 | - **进程**:独立的资源和内存空间,更适合隔离和独立运行的任务。 29 | - **线程**:共享资源和内存空间,适合高并发和共享数据的任务。 30 | 31 | 理解这两者的区别有助于在开发中选择合适的并发模型,提高程序的性能和可靠性。 -------------------------------------------------------------------------------- /docs/Theory/进程间通信方式.md: -------------------------------------------------------------------------------- 1 | 进程间通信(Inter-Process Communication, IPC)是指不同进程之间进行数据交换和信号传递的机制。由于进程之间是相互独立的,它们不能直接访问彼此的内存空间,因此需要通过特定的通信机制来进行交互。以下是几种常见的进程间通信方式: 2 | 3 | ### 1. **管道(Pipe)** 4 | - **匿名管道**:用于有亲缘关系的进程之间通信,数据是单向的(即从写端到读端)。管道是基于文件描述符的,数据是按顺序流动的,类似于生产者-消费者模型。 5 | - **命名管道(FIFO)**:与匿名管道不同,命名管道可以用于无亲缘关系的进程之间的通信。命名管道在文件系统中有一个路径名,进程通过路径名访问管道。 6 | 7 | ### 2. **消息队列(Message Queue)** 8 | - 消息队列允许进程通过消息队列发送和接收消息。消息队列是内核对象,每个消息都有一个消息类型,进程可以根据消息类型进行选择性接收。消息队列的优点是可以保证消息的有序性,并且支持非阻塞操作。 9 | 10 | ### 3. **共享内存(Shared Memory)** 11 | - 共享内存是最快的进程间通信方式之一,因为多个进程可以直接访问同一块内存区域。由于多个进程同时访问共享内存可能导致数据竞争,因此通常需要配合使用信号量或互斥锁来进行同步。 12 | 13 | ### 4. **信号量(Semaphore)** 14 | - 信号量用于控制多个进程对共享资源的访问,通常配合共享内存使用。信号量通过一个计数器来管理资源的访问权限,进程可以通过 `wait()` 和 `signal()` 操作对信号量进行操作,从而实现对资源的互斥访问。 15 | 16 | ### 5. **信号(Signal)** 17 | - 信号是一种进程间的异步通知机制。一个进程可以向另一个进程发送信号,用于通知接收进程发生了某个事件。信号可以用于处理简单的事件通知,比如终止进程、定时器等。 18 | 19 | ### 6. **套接字(Socket)** 20 | - 套接字是一种更通用的进程间通信机制,可以用于同一主机上的进程通信,也可以用于不同主机之间的进程通信。套接字支持面向连接的(TCP)和无连接的(UDP)通信方式。 21 | 22 | ### 7. **信任区域(Trusted Region, Windows特性)** 23 | - 信任区域是一种内存共享的形式,但它只在 Windows 系统中可用。它允许进程共享内存,访问受保护的区域,但与常规共享内存相比,它有更严格的访问控制。 24 | 25 | ### 8. **文件映射(Memory-Mapped File)** 26 | - 文件映射可以将一个文件映射到进程的内存地址空间中,允许多个进程共享同一文件的内容。文件映射在 Windows 和 UNIX 系统中都有实现。 27 | 28 | ### 9. **远程过程调用(RPC)** 29 | - 远程过程调用允许进程通过网络请求调用另一进程的过程(或方法),就像调用本地函数一样。RPC 是一种高级的通信方式,通常用于分布式系统中。 30 | 31 | ### 10. **消息传递接口(MPI)** 32 | - MPI 是一种标准化的消息传递库,广泛应用于高性能计算(HPC)领域。它允许多个进程在并行计算环境下进行通信,支持多种通信模式,如点对点通信和集体通信。 33 | 34 | ### 11. **DBus** 35 | - DBus 是一种为 Linux 系统设计的消息总线系统,它允许应用程序之间进行通信。DBus 支持广播和单播消息,是桌面环境和系统服务之间常用的通信机制。 36 | 37 | ### 12. **内核事件通知机制(epoll/kqueue)** 38 | - 这些机制主要用于高效处理大量文件描述符的 IO 事件,常用于服务器处理客户端的请求。它们在通信中的应用更多是间接的,通过事件驱动模型来实现高效的进程间通信。 39 | 40 | 每种通信方式都有其适用的场景和优缺点。选择哪种方式取决于进程间通信的需求,例如数据量、通信频率、实时性要求、以及进程间是否有亲缘关系等。 -------------------------------------------------------------------------------- /docs/Theory/雪花算法.md: -------------------------------------------------------------------------------- 1 | ### 雪花算法(Snowflake Algorithm) 2 | 3 | 雪花算法是一种生成唯一ID的方法,广泛应用于分布式系统中。它的设计目的是为了生成高并发的唯一ID,通常由Twitter提出。生成的ID是64位的整数,结构如下: 4 | 5 | ``` 6 | | 1位 | 41位 | 5位 | 17位 | 7 | |------|--------------|-------|---------------| 8 | | sign | 时间戳(毫秒)| 数据中心ID | 序列号(每毫秒内自增) | 9 | ``` 10 | 11 | - **1位**:符号位,通常为0。 12 | - **41位**:时间戳部分,表示从某个时间点(例如,2022年1月1日)开始的毫秒数。 13 | - **5位**:数据中心ID,标识不同的数据中心或节点。 14 | - **17位**:序列号,用于在同一毫秒内生成多个ID。 15 | 16 | ### 时钟回拨问题 17 | 18 | 时钟回拨是指系统时间因某种原因(如网络同步、手动修改时间等)向后调整的情况。这在使用雪花算法生成ID时可能导致冲突或重复ID。主要问题如下: 19 | 20 | 1. **ID冲突**:如果时钟回拨到一个已经使用过的时间戳,生成的ID可能与之前生成的ID冲突。 21 | 2. **系统不可用**:在时钟回拨后,如果生成ID的时间戳小于最后生成ID的时间戳,可能会导致系统停止生成新ID,造成系统不可用。 22 | 23 | ### 解决方案 24 | 25 | 1. **时间戳检查**: 26 | - 在生成ID时,记录最后使用的时间戳,检查当前时间是否小于最后的时间戳。 27 | - 如果小于,可能需要阻塞或等待,直到系统时间回到正常状态。 28 | 29 | 2. **回滚策略**: 30 | - 在检测到时钟回拨后,可以增加序列号的位数,或者将序列号重置,以避免冲突。 31 | 32 | 3. **使用物理时钟**: 33 | - 尽量依赖网络时间协议(NTP)等物理时钟来同步系统时间,降低时钟回拨的概率。 34 | 35 | 4. **添加随机因子**: 36 | - 在生成ID时加入随机因子,虽然这会降低ID的有序性,但可以降低ID冲突的风险。 37 | 38 | ### 总结 39 | 40 | 雪花算法是一种高效生成唯一ID的方法,但时钟回拨可能会带来潜在问题。通过合理的时间戳检查和补救策略,可以有效地降低时钟回拨带来的影响。如果你还有其他问题或需要更详细的解释,请告诉我! -------------------------------------------------------------------------------- /docsify/.codesandbox/ci.json: -------------------------------------------------------------------------------- 1 | { 2 | "sandboxes": ["2d17z"], 3 | "packages": [".", "packages/docsify-server-renderer"], 4 | "node": "16" 5 | } 6 | -------------------------------------------------------------------------------- /docsify/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /docsify/.eslintignore: -------------------------------------------------------------------------------- 1 | .git 2 | **/*.md 3 | build 4 | docs 5 | lib 6 | node_modules 7 | packages/docsify-server-renderer/build.js 8 | server.js 9 | themes 10 | -------------------------------------------------------------------------------- /docsify/.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: docsify 2 | 3 | -------------------------------------------------------------------------------- /docsify/.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | 12 | ## Bug Report 13 | 14 | #### Steps to reproduce 15 | 16 | 17 | 18 | #### What is current behaviour 19 | 20 | 21 | 22 | #### What is the expected behaviour 23 | 24 | 25 | 26 | #### Other relevant information 27 | 28 | 29 | - [ ] Bug does still occur when all/other plugins are disabled? 30 | 31 | - Your OS: 32 | - Node.js version: 33 | - npm/yarn version: 34 | - Browser version: 35 | - Docsify version: 36 | - Docsify plugins: 37 | 38 | 40 | 41 | #### Please create a reproducible sandbox 42 | 43 | [![Edit 307qqv236](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/307qqv236) 44 | 45 | #### Mention the docsify version in which this bug was not present (if any) 46 | -------------------------------------------------------------------------------- /docsify/.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord - the community chat 4 | url: https://discord.gg/3NwKFyR 5 | about: Join Discord community and chat about Docsify 6 | -------------------------------------------------------------------------------- /docsify/.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | ## Feature request 12 | 13 | #### What problem does this feature solve? 14 | 15 | 16 | 17 | #### What does the proposed API look like? 18 | 19 | 20 | 21 | #### How should this be implemented in your opinion? 22 | 23 | 24 | 25 | #### Are you willing to work on this yourself? 26 | -------------------------------------------------------------------------------- /docsify/.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | ## **Summary** 13 | 14 | 17 | 18 | 21 | 22 | ## **What kind of change does this PR introduce?** 23 | 24 | 27 | 28 | 38 | 39 | 42 | 43 | ## **For any code change,** 44 | 45 | - [ ] Related documentation has been updated if needed 46 | - [ ] Related tests have been updated or tests have been added 47 | 48 | ## **Does this PR introduce a breaking change?** (check one) 49 | 50 | - [ ] Yes 51 | - [ ] No 52 | 53 | If yes, please describe the impact and migration path for existing applications: 54 | 55 | ## **Related issue, if any:** 56 | 57 | 58 | 59 | ## **Tested in the following browsers:** 60 | 61 | - [ ] Chrome 62 | - [ ] Firefox 63 | - [ ] Safari 64 | - [ ] Edge 65 | - [ ] IE 66 | -------------------------------------------------------------------------------- /docsify/.github/semantic.yml: -------------------------------------------------------------------------------- 1 | titleAndCommits: true 2 | allowMergeCommits: true 3 | allowRevertCommits: true 4 | anyCommit: true 5 | -------------------------------------------------------------------------------- /docsify/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *.log 4 | /_playwright-report 5 | /_playwright-results 6 | /node_modules 7 | /themes 8 | 9 | # exceptions 10 | !.gitkeep 11 | -------------------------------------------------------------------------------- /docsify/.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - command: gp await-port 3000 && sleep 3 && gp preview $(gp url 3000) 3 | - init: npm install 4 | command: npm run dev 5 | ports: 6 | - port: 3000 7 | onOpen: ignore 8 | vscode: 9 | extensions: 10 | - sysoev.language-stylus@1.11.0:xX39oruAJ5UQzTNVRdbBaQ== -------------------------------------------------------------------------------- /docsify/.npmignore: -------------------------------------------------------------------------------- 1 | .eslintignore 2 | .eslintrc 3 | .github 4 | .gitignore 5 | .travis.yml 6 | -------------------------------------------------------------------------------- /docsify/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | singleQuote: true, 4 | }; 5 | -------------------------------------------------------------------------------- /docsify/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "cSpell.words": ["coverpage"] 4 | } 5 | -------------------------------------------------------------------------------- /docsify/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/playwright:focal 2 | WORKDIR /app 3 | COPY . . 4 | RUN rm package-lock.json 5 | RUN npm install 6 | RUN npx playwright install 7 | RUN npm run build 8 | ENTRYPOINT ["npm", "run"] 9 | CMD ["test"] -------------------------------------------------------------------------------- /docsify/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 - present Docsify Contributors (https://github.com/docsifyjs/docsify/graphs/contributors) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docsify/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you believe you have found a security vulnerability in docsify, please report it to us asap. 4 | 5 | ## Reporting a Vulnerability 6 | 7 | **Please do not report security vulnerabilities through our public GitHub issues.** 8 | 9 | Send email via :email: maintainers@docsifyjs.org to us. 10 | 11 | Please include as much of the following information as possible to help us better understand the possible issue: 12 | 13 | - Type of issue (e.g. cross-site scripting) 14 | - Full paths of source file(s) related to the manifestation of the issue 15 | - The location of the affected source code (tag/branch/commit or direct URL) 16 | - Any special configuration required to reproduce the issue 17 | - Step-by-step instructions to reproduce the issue 18 | - Proof-of-concept or exploit code 19 | - Impact of the issue, including how an attacker might exploit the issue 20 | 21 | This information will help us triage your report more quickly. 22 | 23 | Thank you in advance. 24 | -------------------------------------------------------------------------------- /docsify/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /docsify/build/cover.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var read = fs.readFileSync 3 | var write = fs.writeFileSync 4 | var version = process.env.VERSION || require('../package.json').version 5 | 6 | var file = __dirname + '/../docs/_coverpage.md' 7 | var cover = read(file, 'utf8').toString() 8 | 9 | console.log('Replace version number in cover page...') 10 | cover = cover.replace( 11 | /(\S+)?<\/small>/g, 12 | '' + version + '' 13 | ) 14 | write(file, cover) 15 | -------------------------------------------------------------------------------- /docsify/build/css.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const {spawn} = require('child_process') 4 | 5 | const args = process.argv.slice(2) 6 | fs.readdir(path.join(__dirname, '../src/themes'), (err, files) => { 7 | if (err) { 8 | console.error('err', err) 9 | process.exit(1) 10 | } 11 | files.map(async (file) => { 12 | if (/\.styl/g.test(file)) { 13 | var stylusCMD; 14 | const stylusBin = ['node_modules', 'stylus', 'bin', 'stylus'].join(path.sep) 15 | var cmdargs = [ 16 | stylusBin, 17 | `src/themes/${file}`, 18 | '-u', 19 | 'autoprefixer-stylus' 20 | ] 21 | cmdargs = cmdargs.concat(args) 22 | 23 | stylusCMD = spawn('node', cmdargs, { shell: true }) 24 | 25 | stylusCMD.stdout.on('data', (data) => { 26 | console.log(`[Stylus Build ] stdout: ${data}`); 27 | }); 28 | 29 | stylusCMD.stderr.on('data', (data) => { 30 | console.error(`[Stylus Build ] stderr: ${data}`); 31 | }); 32 | 33 | stylusCMD.on('close', (code) => { 34 | const message = `[Stylus Build ] child process exited with code ${code}` 35 | 36 | if (code !== 0) { 37 | console.error(message); 38 | process.exit(code) 39 | } 40 | console.log(message); 41 | }); 42 | } else { 43 | return 44 | } 45 | 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /docsify/build/mincss.js: -------------------------------------------------------------------------------- 1 | const cssnano = require('cssnano').process 2 | const path = require('path') 3 | const fs = require('fs') 4 | 5 | files = fs.readdirSync(path.resolve('lib/themes')) 6 | 7 | files.forEach(file => { 8 | file = path.resolve('lib/themes', file) 9 | cssnano(fs.readFileSync(file)).then(result => { 10 | fs.writeFileSync(file, result.css) 11 | }).catch(e => { 12 | console.error(e) 13 | process.exit(1) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /docsify/build/release.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | if [[ -z $1 ]]; then 4 | echo "Enter new version: " 5 | read VERSION 6 | else 7 | VERSION=$1 8 | fi 9 | 10 | read -p "Releasing $VERSION $RELEASE_TAG - are you sure? (y/n) " -n 1 -r 11 | echo 12 | if [[ $REPLY =~ ^[Yy]$ ]]; then 13 | echo "Releasing $VERSION ..." 14 | 15 | # Removing test script as non - availibity of tests. Will Add it once tests are completed 16 | 17 | # npm run test 18 | 19 | # build 20 | VERSION=$VERSION npm run build 21 | 22 | # update packages 23 | cd packages/docsify-server-renderer 24 | npm version $VERSION 25 | if [[ -z $RELEASE_TAG ]]; then 26 | npm publish 27 | else 28 | npm publish --tag $RELEASE_TAG 29 | fi 30 | cd - 31 | 32 | # commit 33 | git add -A 34 | git commit -m "[build] $VERSION $RELEASE_TAG" 35 | npm --no-git-tag-version version $VERSION --message "[release] $VERSION $RELEASE_TAG" 36 | 37 | # changelog 38 | node_modules/.bin/conventional-changelog -p angular -i CHANGELOG.md -s 39 | 40 | git add . 41 | git commit -m "chore: add changelog $VERSION" 42 | 43 | # publish 44 | git tag v$VERSION 45 | git push origin refs/tags/v$VERSION 46 | git push 47 | if [[ -z $RELEASE_TAG ]]; then 48 | npm publish 49 | else 50 | npm publish --tag $RELEASE_TAG 51 | fi 52 | fi 53 | -------------------------------------------------------------------------------- /docsify/build/ssr.js: -------------------------------------------------------------------------------- 1 | var rollup = require('rollup') 2 | var buble = require('rollup-plugin-buble') 3 | var async = require('rollup-plugin-async') 4 | var replace = require('rollup-plugin-replace') 5 | 6 | rollup 7 | .rollup({ 8 | input: 'packages/docsify-server-renderer/index.js', 9 | plugins: [ 10 | async(), 11 | replace({ 12 | __VERSION__: process.env.VERSION || require('../package.json').version, 13 | 'process.env.SSR': true 14 | }), 15 | buble({ 16 | transforms: { 17 | generator: false 18 | } 19 | }) 20 | ], 21 | onwarn: function () {} 22 | }) 23 | .then(function (bundle) { 24 | var dest = 'packages/docsify-server-renderer/build.js' 25 | 26 | console.log(dest) 27 | return bundle.write({ 28 | format: 'cjs', 29 | file: dest 30 | }) 31 | }) 32 | .catch(function (err) { 33 | console.error(err) 34 | process.exit(1) 35 | }) 36 | -------------------------------------------------------------------------------- /docsify/docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2637309949/go-interview/f38194aec9f813133b39c1e915b0b530ad9fae7b/docsify/docs/.nojekyll -------------------------------------------------------------------------------- /docsify/docs/CNAME: -------------------------------------------------------------------------------- 1 | docsify.js.org -------------------------------------------------------------------------------- /docsify/docs/README.md: -------------------------------------------------------------------------------- 1 | ## docsify 2 | 3 | > A magical documentation site generator. 4 | 5 | ## What it is 6 | 7 | Docsify generates your documentation website on the fly. Unlike GitBook, it does not generate static html files. Instead, it smartly loads and parses your Markdown files and displays them as a website. To start using it, all you need to do is create an `index.html` and [deploy it on GitHub Pages](deploy.md). 8 | 9 | See the [Quick start](quickstart.md) guide for more details. 10 | 11 | ## Features 12 | 13 | - No statically built html files 14 | - Simple and lightweight 15 | - Smart full-text search plugin 16 | - Multiple themes 17 | - Useful plugin API 18 | - Emoji support 19 | - Compatible with IE11 20 | - Support server-side rendering ([example](https://github.com/docsifyjs/docsify-ssr-demo)) 21 | 22 | ## Examples 23 | 24 | Check out the [Showcase](https://github.com/docsifyjs/awesome-docsify#showcase) to see docsify in use. 25 | 26 | ## Donate 27 | 28 | Please consider donating if you think docsify is helpful to you or that my work is valuable. I am happy if you can help me [buy a cup of coffee](https://github.com/QingWei-Li/donate). :heart: 29 | 30 | ## Community 31 | 32 | Users and the development team are usually in the [Discord server](https://discord.gg/3NwKFyR). 33 | -------------------------------------------------------------------------------- /docsify/docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](_media/icon.svg) 2 | 3 | # docsify 4.13.1 4 | 5 | > A magical documentation site generator. 6 | 7 | - Simple and lightweight 8 | - No statically built html files 9 | - Multiple themes 10 | 11 | [GitHub](https://github.com/docsifyjs/docsify/) 12 | [Getting Started](#docsify) 13 | -------------------------------------------------------------------------------- /docsify/docs/_images/deploy-github-pages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2637309949/go-interview/f38194aec9f813133b39c1e915b0b530ad9fae7b/docsify/docs/_images/deploy-github-pages.png -------------------------------------------------------------------------------- /docsify/docs/_images/nested-navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2637309949/go-interview/f38194aec9f813133b39c1e915b0b530ad9fae7b/docsify/docs/_images/nested-navbar.png -------------------------------------------------------------------------------- /docsify/docs/_images/zh-cn/nested-navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2637309949/go-interview/f38194aec9f813133b39c1e915b0b530ad9fae7b/docsify/docs/_images/zh-cn/nested-navbar.png -------------------------------------------------------------------------------- /docsify/docs/_media/example-with-yaml.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: John Smith 3 | date: 2020-1-1 4 | --- 5 | 6 | > This is from the `example.md` 7 | -------------------------------------------------------------------------------- /docsify/docs/_media/example.html: -------------------------------------------------------------------------------- 1 |

To infinity and Beyond!

-------------------------------------------------------------------------------- /docsify/docs/_media/example.js: -------------------------------------------------------------------------------- 1 | import fetch from 'fetch' 2 | 3 | const URL = 'https://example.com' 4 | const PORT = 8080 5 | 6 | /// [demo] 7 | const result = fetch(`${URL}:${PORT}`) 8 | .then(function (response) { 9 | return response.json() 10 | }) 11 | .then(function (myJson) { 12 | console.log(JSON.stringify(myJson)) 13 | }) 14 | /// [demo] 15 | 16 | result.then(console.log).catch(console.error) 17 | -------------------------------------------------------------------------------- /docsify/docs/_media/example.md: -------------------------------------------------------------------------------- 1 | > This is from the `example.md` 2 | -------------------------------------------------------------------------------- /docsify/docs/_media/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2637309949/go-interview/f38194aec9f813133b39c1e915b0b530ad9fae7b/docsify/docs/_media/favicon.ico -------------------------------------------------------------------------------- /docsify/docs/_media/vercel_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docsify/docs/_navbar.md: -------------------------------------------------------------------------------- 1 | - Translations 2 | - [:uk: English](/) 3 | - [:cn: 简体中文](/zh-cn/) 4 | - [:de: Deutsch](/de-de/) 5 | - [:es: Español](/es/) 6 | - [:ru: Русский](/ru-ru/) 7 | -------------------------------------------------------------------------------- /docsify/docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | - Getting started 2 | 3 | - [Quick start](quickstart.md) 4 | - [Writing more pages](more-pages.md) 5 | - [Custom navbar](custom-navbar.md) 6 | - [Cover page](cover.md) 7 | 8 | - Customization 9 | 10 | - [Configuration](configuration.md) 11 | - [Themes](themes.md) 12 | - [List of Plugins](plugins.md) 13 | - [Write a Plugin](write-a-plugin.md) 14 | - [Markdown configuration](markdown.md) 15 | - [Language highlighting](language-highlight.md) 16 | - [Emoji](emoji.md) 17 | 18 | - Guide 19 | 20 | - [Deploy](deploy.md) 21 | - [Helpers](helpers.md) 22 | - [Vue compatibility](vue.md) 23 | - [CDN](cdn.md) 24 | - [Offline Mode (PWA)](pwa.md) 25 | - [Server-Side Rendering (SSR)](ssr.md) 26 | - [Embed Files](embed-files.md) 27 | 28 | - [Awesome docsify](awesome.md) 29 | - [Changelog](changelog.md) 30 | -------------------------------------------------------------------------------- /docsify/docs/cdn.md: -------------------------------------------------------------------------------- 1 | # CDN 2 | 3 | Recommended: [jsDelivr](//cdn.jsdelivr.net), which will reflect the latest version as soon as it is published to npm. You can also browse the source of the npm package at [cdn.jsdelivr.net/npm/docsify/](//cdn.jsdelivr.net/npm/docsify/). 4 | 5 | ## Latest version 6 | 7 | ```html 8 | 9 | 10 | 11 | 12 | 13 | ``` 14 | 15 | Alternatively, use [compressed files](#compressed-file). 16 | 17 | ## Specific version 18 | 19 | ```html 20 | 21 | 22 | 23 | 24 | 25 | ``` 26 | 27 | ## Compressed file 28 | 29 | ```html 30 | 31 | 32 | 33 | 34 | 35 | ``` 36 | 37 | ```html 38 | 39 | 40 | 41 | 42 | 43 | ``` 44 | 45 | ## Other CDN 46 | 47 | - https://www.bootcdn.cn/docsify/ 48 | - https://cdn.jsdelivr.net/npm/docsify/ 49 | - https://cdnjs.com/libraries/docsify 50 | - https://unpkg.com/browse/docsify/ 51 | -------------------------------------------------------------------------------- /docsify/docs/markdown.md: -------------------------------------------------------------------------------- 1 | # Markdown configuration 2 | 3 | **docsify** uses [marked](https://github.com/markedjs/marked) as its Markdown parser. You can customize how it renders your Markdown content to HTML by customizing `renderer`: 4 | 5 | ```js 6 | window.$docsify = { 7 | markdown: { 8 | smartypants: true, 9 | renderer: { 10 | link: function() { 11 | // ... 12 | } 13 | } 14 | } 15 | } 16 | ``` 17 | 18 | ?> Configuration Options Reference: [marked documentation](https://marked.js.org/#/USING_ADVANCED.md) 19 | 20 | You can completely customize the parsing rules. 21 | 22 | ```js 23 | window.$docsify = { 24 | markdown: function(marked, renderer) { 25 | // ... 26 | 27 | return marked 28 | } 29 | } 30 | ``` 31 | 32 | ## Supports mermaid 33 | 34 | ```js 35 | // Import mermaid 36 | // 37 | // 38 | 39 | var num = 0; 40 | mermaid.initialize({ startOnLoad: false }); 41 | 42 | window.$docsify = { 43 | markdown: { 44 | renderer: { 45 | code: function(code, lang) { 46 | if (lang === "mermaid") { 47 | return ( 48 | '
' + mermaid.render('mermaid-svg-' + num++, code) + "
" 49 | ); 50 | } 51 | return this.origin.code.apply(this, arguments); 52 | } 53 | } 54 | } 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /docsify/jest.config.js: -------------------------------------------------------------------------------- 1 | const { TEST_HOST } = require('./test/config/server.js'); 2 | 3 | const sharedConfig = { 4 | errorOnDeprecated: true, 5 | globalSetup: './test/config/jest.setup.js', 6 | globalTeardown: './test/config/jest.teardown.js', 7 | resetModules: true, 8 | restoreMocks: true, 9 | setupFilesAfterEnv: ['/test/config/jest.setup-tests.js'], 10 | testEnvironment: 'jsdom', 11 | testURL: `${TEST_HOST}/_blank.html`, 12 | }; 13 | 14 | module.exports = { 15 | projects: [ 16 | // Unit Tests 17 | { 18 | displayName: 'unit', 19 | ...sharedConfig, 20 | testMatch: ['/test/unit/*.test.js'], 21 | }, 22 | // Integration Tests 23 | { 24 | displayName: 'integration', 25 | ...sharedConfig, 26 | testMatch: ['/test/integration/*.test.js'], 27 | }, 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /docsify/lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.0.0-rc.5", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "0.0.0" 7 | } 8 | -------------------------------------------------------------------------------- /docsify/lib/plugins/disqus.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /* eslint-disable no-unused-vars */ 3 | var fixedPath = location.href.replace('/-/', '/#/'); 4 | if (fixedPath !== location.href) { 5 | location.href = fixedPath; 6 | } 7 | 8 | function install(hook, vm) { 9 | var dom = Docsify.dom; 10 | var disqus = vm.config.disqus; 11 | if (!disqus) { 12 | throw Error('$docsify.disqus is required'); 13 | } 14 | 15 | hook.init(function (_) { 16 | var script = dom.create('script'); 17 | 18 | script.async = true; 19 | script.src = "https://" + disqus + ".disqus.com/embed.js"; 20 | script.setAttribute('data-timestamp', Number(new Date())); 21 | dom.appendTo(dom.body, script); 22 | }); 23 | 24 | hook.mounted(function (_) { 25 | var div = dom.create('div'); 26 | div.id = 'disqus_thread'; 27 | var main = dom.getNode('#main'); 28 | div.style = "width: " + (main.clientWidth) + "px; margin: 0 auto 20px;"; 29 | dom.appendTo(dom.find('.content'), div); 30 | 31 | // eslint-disable-next-line 32 | window.disqus_config = function() { 33 | this.page.url = location.origin + '/-' + vm.route.path; 34 | this.page.identifier = vm.route.path; 35 | this.page.title = document.title; 36 | }; 37 | }); 38 | 39 | hook.doneEach(function (_) { 40 | if (typeof window.DISQUS !== 'undefined') { 41 | window.DISQUS.reset({ 42 | reload: true, 43 | config: function () { 44 | this.page.url = location.origin + '/-' + vm.route.path; 45 | this.page.identifier = vm.route.path; 46 | this.page.title = document.title; 47 | }, 48 | }); 49 | } 50 | }); 51 | } 52 | 53 | $docsify.plugins = [].concat(install, $docsify.plugins); 54 | 55 | }()); 56 | -------------------------------------------------------------------------------- /docsify/lib/plugins/disqus.min.js: -------------------------------------------------------------------------------- 1 | !function(){var i=location.href.replace("/-/","/#/");i!==location.href&&(location.href=i),$docsify.plugins=[].concat(function(i,o){var n=Docsify.dom,e=o.config.disqus;if(!e)throw Error("$docsify.disqus is required");i.init(function(i){var t=n.create("script");t.async=!0,t.src="https://"+e+".disqus.com/embed.js",t.setAttribute("data-timestamp",Number(new Date)),n.appendTo(n.body,t)}),i.mounted(function(i){var t=n.create("div");t.id="disqus_thread";var e=n.getNode("#main");t.style="width: "+e.clientWidth+"px; margin: 0 auto 20px;",n.appendTo(n.find(".content"),t),window.disqus_config=function(){this.page.url=location.origin+"/-"+o.route.path,this.page.identifier=o.route.path,this.page.title=document.title}}),i.doneEach(function(i){void 0!==window.DISQUS&&window.DISQUS.reset({reload:!0,config:function(){this.page.url=location.origin+"/-"+o.route.path,this.page.identifier=o.route.path,this.page.title=document.title}})})},$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /docsify/lib/plugins/external-script.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | function handleExternalScript() { 3 | var container = Docsify.dom.getNode('#main'); 4 | var scripts = Docsify.dom.findAll(container, 'script'); 5 | 6 | for (var i = scripts.length; i--; ) { 7 | var script = scripts[i]; 8 | 9 | if (script && script.src) { 10 | var newScript = document.createElement('script'); 11 | 12 | Array.prototype.slice.call(script.attributes).forEach(function (attribute) { 13 | newScript[attribute.name] = attribute.value; 14 | }); 15 | 16 | script.parentNode.insertBefore(newScript, script); 17 | script.parentNode.removeChild(script); 18 | } 19 | } 20 | } 21 | 22 | var install = function (hook) { 23 | hook.doneEach(handleExternalScript); 24 | }; 25 | 26 | window.$docsify.plugins = [].concat(install, window.$docsify.plugins); 27 | 28 | }()); 29 | -------------------------------------------------------------------------------- /docsify/lib/plugins/external-script.min.js: -------------------------------------------------------------------------------- 1 | !function(){function e(){for(var o=Docsify.dom.getNode("#main"),e=Docsify.dom.findAll(o,"script"),n=e.length;n--;){var i,t=e[n];t&&t.src&&(i=document.createElement("script"),Array.prototype.slice.call(t.attributes).forEach(function(o){i[o.name]=o.value}),t.parentNode.insertBefore(i,t),t.parentNode.removeChild(t))}}window.$docsify.plugins=[].concat(function(o){o.doneEach(e)},window.$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /docsify/lib/plugins/ga.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /* eslint-disable no-console */ 3 | // From https://github.com/egoist/vue-ga/blob/master/src/index.js 4 | function appendScript() { 5 | var script = document.createElement('script'); 6 | script.async = true; 7 | script.src = 'https://www.google-analytics.com/analytics.js'; 8 | document.body.appendChild(script); 9 | } 10 | 11 | function init(id) { 12 | appendScript(); 13 | window.ga = 14 | window.ga || 15 | function () { 16 | (window.ga.q = window.ga.q || []).push(arguments); 17 | }; 18 | 19 | window.ga.l = Number(new Date()); 20 | window.ga('create', id, 'auto'); 21 | } 22 | 23 | function collect() { 24 | if (!window.ga) { 25 | init($docsify.ga); 26 | } 27 | 28 | window.ga('set', 'page', location.hash); 29 | window.ga('send', 'pageview'); 30 | } 31 | 32 | var install = function (hook) { 33 | if (!$docsify.ga) { 34 | console.error('[Docsify] ga is required.'); 35 | return; 36 | } 37 | 38 | hook.beforeEach(collect); 39 | }; 40 | 41 | $docsify.plugins = [].concat(install, $docsify.plugins); 42 | 43 | }()); 44 | -------------------------------------------------------------------------------- /docsify/lib/plugins/ga.min.js: -------------------------------------------------------------------------------- 1 | !function(){function n(n){var o;(o=document.createElement("script")).async=!0,o.src="https://www.google-analytics.com/analytics.js",document.body.appendChild(o),window.ga=window.ga||function(){(window.ga.q=window.ga.q||[]).push(arguments)},window.ga.l=Number(new Date),window.ga("create",n,"auto")}function o(){window.ga||n($docsify.ga),window.ga("set","page",location.hash),window.ga("send","pageview")}$docsify.plugins=[].concat(function(n){$docsify.ga?n.beforeEach(o):console.error("[Docsify] ga is required.")},$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /docsify/lib/plugins/gitalk.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /* eslint-disable no-unused-vars */ 3 | function install(hook) { 4 | var dom = Docsify.dom; 5 | 6 | hook.mounted(function (_) { 7 | var div = dom.create('div'); 8 | div.id = 'gitalk-container'; 9 | var main = dom.getNode('#main'); 10 | div.style = "width: " + (main.clientWidth) + "px; margin: 0 auto 20px;"; 11 | dom.appendTo(dom.find('.content'), div); 12 | }); 13 | 14 | hook.doneEach(function (_) { 15 | var el = document.getElementById('gitalk-container'); 16 | while (el.hasChildNodes()) { 17 | el.removeChild(el.firstChild); 18 | } 19 | 20 | // eslint-disable-next-line 21 | gitalk.render('gitalk-container'); 22 | }); 23 | } 24 | 25 | $docsify.plugins = [].concat(install, $docsify.plugins); 26 | 27 | }()); 28 | -------------------------------------------------------------------------------- /docsify/lib/plugins/gitalk.min.js: -------------------------------------------------------------------------------- 1 | $docsify.plugins=[].concat(function(i){var e=Docsify.dom;i.mounted(function(i){var n=e.create("div");n.id="gitalk-container";var t=e.getNode("#main");n.style="width: "+t.clientWidth+"px; margin: 0 auto 20px;",e.appendTo(e.find(".content"),n)}),i.doneEach(function(i){for(var n=document.getElementById("gitalk-container");n.hasChildNodes();)n.removeChild(n.firstChild);gitalk.render("gitalk-container")})},$docsify.plugins); 2 | -------------------------------------------------------------------------------- /docsify/lib/plugins/matomo.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | function appendScript(options) { 3 | var script = document.createElement('script'); 4 | script.async = true; 5 | script.src = options.host + '/matomo.js'; 6 | document.body.appendChild(script); 7 | } 8 | 9 | function init(options) { 10 | window._paq = window._paq || []; 11 | window._paq.push(['trackPageView']); 12 | window._paq.push(['enableLinkTracking']); 13 | setTimeout(function () { 14 | appendScript(options); 15 | window._paq.push(['setTrackerUrl', options.host + '/matomo.php']); 16 | window._paq.push(['setSiteId', String(options.id)]); 17 | }, 0); 18 | } 19 | 20 | function collect() { 21 | if (!window._paq) { 22 | init($docsify.matomo); 23 | } 24 | 25 | window._paq.push(['setCustomUrl', window.location.hash.substr(1)]); 26 | window._paq.push(['setDocumentTitle', document.title]); 27 | window._paq.push(['trackPageView']); 28 | } 29 | 30 | var install = function (hook) { 31 | if (!$docsify.matomo) { 32 | // eslint-disable-next-line no-console 33 | console.error('[Docsify] matomo is required.'); 34 | return; 35 | } 36 | 37 | hook.beforeEach(collect); 38 | }; 39 | 40 | $docsify.plugins = [].concat(install, $docsify.plugins); 41 | 42 | }()); 43 | -------------------------------------------------------------------------------- /docsify/lib/plugins/matomo.min.js: -------------------------------------------------------------------------------- 1 | !function(){function o(n){window._paq=window._paq||[],window._paq.push(["trackPageView"]),window._paq.push(["enableLinkTracking"]),setTimeout(function(){var o,i;o=n,(i=document.createElement("script")).async=!0,i.src=o.host+"/matomo.js",document.body.appendChild(i),window._paq.push(["setTrackerUrl",n.host+"/matomo.php"]),window._paq.push(["setSiteId",String(n.id)])},0)}function i(){window._paq||o($docsify.matomo),window._paq.push(["setCustomUrl",window.location.hash.substr(1)]),window._paq.push(["setDocumentTitle",document.title]),window._paq.push(["trackPageView"])}$docsify.plugins=[].concat(function(o){$docsify.matomo?o.beforeEach(i):console.error("[Docsify] matomo is required.")},$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /docsify/packages/docsify-server-renderer/.gitignore: -------------------------------------------------------------------------------- 1 | build.js 2 | node_modules 3 | *.log 4 | .git 5 | -------------------------------------------------------------------------------- /docsify/packages/docsify-server-renderer/README.md: -------------------------------------------------------------------------------- 1 | # docsify-server-renderer 2 | 3 | ## Install 4 | 5 | ```bash 6 | yarn add docsify-server-renderer 7 | ``` 8 | 9 | ## Usage 10 | 11 | ```js 12 | var Renderer = require('docsify-server-renderer') 13 | var readFileSync = require('fs').readFileSync 14 | 15 | // init 16 | var renderer = new Renderer({ 17 | template: readFileSync('./docs/index.template.html', 'utf-8'), 18 | config: { 19 | name: 'docsify', 20 | repo: 'docsifyjs/docsify' 21 | } 22 | }) 23 | 24 | renderer.renderToString(url) 25 | .then(html => {}) 26 | .catch(err => {}) 27 | ``` 28 | 29 | *index.template.html* 30 | 31 | ```html 32 | 33 | 34 | 35 | 36 | docsify 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ``` 47 | -------------------------------------------------------------------------------- /docsify/packages/docsify-server-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docsify-server-renderer", 3 | "version": "4.13.1", 4 | "description": "docsify server renderer", 5 | "author": { 6 | "name": "qingwei-li", 7 | "email": "cinwell.li@gmail.com", 8 | "url": "https://github.com/QingWei-Li" 9 | }, 10 | "homepage": "https://docsify.js.org", 11 | "license": "MIT", 12 | "repository": "docsifyjs/docsify", 13 | "main": "build.js", 14 | "scripts": { 15 | "test": "echo 'hello'" 16 | }, 17 | "dependencies": { 18 | "debug": "^4.3.3", 19 | "docsify": "^4.12.4", 20 | "node-fetch": "^2.6.6", 21 | "resolve-pathname": "^3.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docsify/playwright.config.js: -------------------------------------------------------------------------------- 1 | const { devices } = require('@playwright/test'); 2 | 3 | /** 4 | * @see https://playwright.dev/docs/test-configuration 5 | * @type {import('@playwright/test').PlaywrightTestConfig} 6 | */ 7 | const config = { 8 | // Setup / Teardown 9 | globalSetup: './test/config/playwright.setup.js', 10 | globalTeardown: './test/config/playwright.teardown.js', 11 | 12 | // Test Execution 13 | expect: { 14 | timeout: 5000, 15 | }, 16 | retries: process.env.CI ? 2 : 0, // Retry on CI only 17 | testDir: './test/e2e', 18 | timeout: 30 * 1000, 19 | workers: process.env.CI ? 1 : undefined, // No parallel tests on CI 20 | forbidOnly: !!process.env.CI, // Fail on CI if test.only in source 21 | 22 | // Output 23 | outputDir: './_playwright-results/', // screenshots, videos, traces, etc. 24 | reporter: [ 25 | [ 26 | 'html', 27 | { 28 | open: 'never', 29 | outputFolder: '_playwright-report', 30 | }, 31 | ], 32 | ], 33 | snapshotDir: './test/e2e/__snapshots__', 34 | 35 | // Config - Shared 36 | // See https://playwright.dev/docs/api/class-testoptions 37 | use: { 38 | actionTimeout: 0, 39 | baseURL: `${process.env.TEST_HOST}`, // Allow relative page.goto() (e.g. `await page.goto('/')`). 40 | trace: 'on-first-retry', 41 | }, 42 | 43 | // Projects 44 | projects: [ 45 | { 46 | name: 'chromium', 47 | use: { ...devices['Desktop Chrome'] }, 48 | }, 49 | { 50 | name: 'firefox', 51 | use: { ...devices['Desktop Firefox'] }, 52 | }, 53 | { 54 | name: 'webkit', 55 | use: { ...devices['Desktop Safari'] }, 56 | }, 57 | // { 58 | // name: 'Mobile Safari', 59 | // use: { ...devices['iPhone 12'] } 60 | // }, 61 | ], 62 | }; 63 | 64 | module.exports = config; 65 | -------------------------------------------------------------------------------- /docsify/sandbox.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "node", 3 | "node": "16" 4 | } 5 | -------------------------------------------------------------------------------- /docsify/server.js: -------------------------------------------------------------------------------- 1 | const liveServer = require('live-server') 2 | const isSSR = !!process.env.SSR 3 | const middleware = [] 4 | 5 | if (isSSR) { 6 | const Renderer = require('./packages/docsify-server-renderer/build.js') 7 | const renderer = new Renderer({ 8 | template: ` 9 | 10 | 11 | 12 | 13 | docsify 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | `, 23 | config: { 24 | name: 'docsify', 25 | repo: 'docsifyjs/docsify', 26 | basePath: 'https://docsify.js.org/', 27 | loadNavbar: true, 28 | loadSidebar: true, 29 | subMaxLevel: 3, 30 | auto2top: true, 31 | alias: { 32 | '/de-de/changelog': '/changelog', 33 | '/zh-cn/changelog': '/changelog', 34 | '/changelog': 35 | 'https://raw.githubusercontent.com/docsifyjs/docsify/master/CHANGELOG' 36 | } 37 | }, 38 | path: './' 39 | }) 40 | 41 | middleware.push(function(req, res, next) { 42 | if (/\.(css|js)$/.test(req.url)) { 43 | return next() 44 | } 45 | renderer.renderToString(req.url).then(html => res.end(html)) 46 | }) 47 | } 48 | 49 | const params = { 50 | port: 3000, 51 | watch: ['lib', 'docs', 'themes'], 52 | middleware 53 | } 54 | 55 | liveServer.start(params) 56 | -------------------------------------------------------------------------------- /docsify/src/core/Docsify.js: -------------------------------------------------------------------------------- 1 | import { Router } from './router/index.js'; 2 | import { Render } from './render/index.js'; 3 | import { Fetch } from './fetch/index.js'; 4 | import { Events } from './event/index.js'; 5 | import { VirtualRoutes } from './virtual-routes/index.js'; 6 | import initGlobalAPI from './global-api.js'; 7 | 8 | import config from './config.js'; 9 | import { isFn } from './util/core'; 10 | import { Lifecycle } from './init/lifecycle'; 11 | 12 | /** @typedef {new (...args: any[]) => any} Constructor */ 13 | 14 | // eslint-disable-next-line new-cap 15 | export class Docsify extends Fetch( 16 | // eslint-disable-next-line new-cap 17 | Events(Render(VirtualRoutes(Router(Lifecycle(Object))))) 18 | ) { 19 | constructor() { 20 | super(); 21 | 22 | this.config = config(this); 23 | 24 | this.initLifecycle(); // Init hooks 25 | this.initPlugin(); // Install plugins 26 | this.callHook('init'); 27 | this.initRouter(); // Add router 28 | this.initRender(); // Render base DOM 29 | this.initEvent(); // Bind events 30 | this.initFetch(); // Fetch data 31 | this.callHook('mounted'); 32 | } 33 | 34 | initPlugin() { 35 | [].concat(this.config.plugins).forEach(fn => { 36 | try { 37 | isFn(fn) && fn(this._lifecycle, this); 38 | } catch (err) { 39 | if (this.config.catchPluginErrors) { 40 | const errTitle = 'Docsify plugin error'; 41 | console.error(errTitle, err); 42 | } else { 43 | throw err; 44 | } 45 | } 46 | }); 47 | } 48 | } 49 | 50 | /** 51 | * Global API 52 | */ 53 | initGlobalAPI(); 54 | -------------------------------------------------------------------------------- /docsify/src/core/event/index.js: -------------------------------------------------------------------------------- 1 | import { isMobile } from '../util/env'; 2 | import { body, on } from '../util/dom'; 3 | import * as sidebar from './sidebar'; 4 | import { scrollIntoView, scroll2Top } from './scroll'; 5 | 6 | /** @typedef {import('../Docsify').Constructor} Constructor */ 7 | 8 | /** 9 | * @template {!Constructor} T 10 | * @param {T} Base - The class to extend 11 | */ 12 | export function Events(Base) { 13 | return class Events extends Base { 14 | $resetEvents(source) { 15 | const { auto2top } = this.config; 16 | 17 | (() => { 18 | // Rely on the browser's scroll auto-restoration when going back or forward 19 | if (source === 'history') { 20 | return; 21 | } 22 | // Scroll to ID if specified 23 | if (this.route.query.id) { 24 | scrollIntoView(this.route.path, this.route.query.id); 25 | } 26 | // Scroll to top if a link was clicked and auto2top is enabled 27 | if (source === 'navigate') { 28 | auto2top && scroll2Top(auto2top); 29 | } 30 | })(); 31 | 32 | if (this.config.loadNavbar) { 33 | sidebar.getAndActive(this.router, 'nav'); 34 | } 35 | } 36 | 37 | initEvent() { 38 | // Bind toggle button 39 | sidebar.btn('button.sidebar-toggle', this.router); 40 | sidebar.collapse('.sidebar', this.router); 41 | // Bind sticky effect 42 | if (this.config.coverpage) { 43 | !isMobile && on('scroll', sidebar.sticky); 44 | } else { 45 | body.classList.add('sticky'); 46 | } 47 | } 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /docsify/src/core/global-api.js: -------------------------------------------------------------------------------- 1 | import prism from 'prismjs'; 2 | import marked from 'marked'; 3 | import * as util from './util'; 4 | import * as dom from './util/dom'; 5 | import { Compiler } from './render/compiler'; 6 | import { slugify } from './render/slugify'; 7 | import { get } from './fetch/ajax'; 8 | 9 | // TODO This is deprecated, kept for backwards compatibility. Remove in next 10 | // major release. We'll tell people to get everything from the DOCSIFY global 11 | // when using the global build, but we'll highly recommend for them to import 12 | // from the ESM build (f.e. lib/docsify.esm.js and lib/docsify.min.esm.js). 13 | export default function () { 14 | window.Docsify = { 15 | util, 16 | dom, 17 | get, 18 | slugify, 19 | version: '__VERSION__', 20 | }; 21 | window.DocsifyCompiler = Compiler; 22 | window.marked = marked; 23 | window.Prism = prism; 24 | } 25 | -------------------------------------------------------------------------------- /docsify/src/core/index.js: -------------------------------------------------------------------------------- 1 | import { documentReady } from './util/dom'; 2 | import { Docsify } from './Docsify'; 3 | 4 | /** 5 | * Run Docsify 6 | */ 7 | // eslint-disable-next-line no-unused-vars 8 | documentReady(_ => new Docsify()); 9 | -------------------------------------------------------------------------------- /docsify/src/core/render/compiler/code.js: -------------------------------------------------------------------------------- 1 | import Prism from 'prismjs'; 2 | // See https://github.com/PrismJS/prism/pull/1367 3 | import 'prismjs/components/prism-markup-templating'; 4 | 5 | export const highlightCodeCompiler = ({ renderer }) => 6 | (renderer.code = function (code, lang = 'markup') { 7 | const langOrMarkup = Prism.languages[lang] || Prism.languages.markup; 8 | const text = Prism.highlight( 9 | code.replace(/@DOCSIFY_QM@/g, '`'), 10 | langOrMarkup, 11 | lang 12 | ); 13 | 14 | return `
${text}
`; 15 | }); 16 | -------------------------------------------------------------------------------- /docsify/src/core/render/compiler/headline.js: -------------------------------------------------------------------------------- 1 | import { getAndRemoveConfig, removeAtag } from '../utils'; 2 | import { slugify } from './slugify'; 3 | 4 | export const headingCompiler = ({ renderer, router, _self }) => 5 | (renderer.code = (text, level) => { 6 | let { str, config } = getAndRemoveConfig(text); 7 | const nextToc = { level, title: removeAtag(str) }; 8 | 9 | if (//g.test(str)) { 10 | str = str.replace('', ''); 11 | nextToc.title = removeAtag(str); 12 | nextToc.ignoreSubHeading = true; 13 | } 14 | 15 | if (/{docsify-ignore}/g.test(str)) { 16 | str = str.replace('{docsify-ignore}', ''); 17 | nextToc.title = removeAtag(str); 18 | nextToc.ignoreSubHeading = true; 19 | } 20 | 21 | if (//g.test(str)) { 22 | str = str.replace('', ''); 23 | nextToc.title = removeAtag(str); 24 | nextToc.ignoreAllSubs = true; 25 | } 26 | 27 | if (/{docsify-ignore-all}/g.test(str)) { 28 | str = str.replace('{docsify-ignore-all}', ''); 29 | nextToc.title = removeAtag(str); 30 | nextToc.ignoreAllSubs = true; 31 | } 32 | 33 | const slug = slugify(config.id || str); 34 | const url = router.toURL(router.getCurrentPath(), { id: slug }); 35 | nextToc.slug = url; 36 | _self.toc.push(nextToc); 37 | 38 | return `${str}`; 39 | }); 40 | -------------------------------------------------------------------------------- /docsify/src/core/render/compiler/image.js: -------------------------------------------------------------------------------- 1 | import { getAndRemoveConfig } from '../utils'; 2 | import { isAbsolutePath, getPath, getParentPath } from '../../router/util'; 3 | 4 | export const imageCompiler = ({ renderer, contentBase, router }) => 5 | (renderer.image = (href, title, text) => { 6 | let url = href; 7 | let attrs = []; 8 | 9 | const { str, config } = getAndRemoveConfig(title); 10 | title = str; 11 | 12 | if (config['no-zoom']) { 13 | attrs.push('data-no-zoom'); 14 | } 15 | 16 | if (title) { 17 | attrs.push(`title="${title}"`); 18 | } 19 | 20 | if (config.size) { 21 | const [width, height] = config.size.split('x'); 22 | if (height) { 23 | attrs.push(`width="${width}" height="${height}"`); 24 | } else { 25 | attrs.push(`width="${width}"`); 26 | } 27 | } 28 | 29 | if (config.class) { 30 | attrs.push(`class="${config.class}"`); 31 | } 32 | 33 | if (config.id) { 34 | attrs.push(`id="${config.id}"`); 35 | } 36 | 37 | if (!isAbsolutePath(href)) { 38 | url = getPath(contentBase, getParentPath(router.getCurrentPath()), href); 39 | } 40 | 41 | if (attrs.length > 0) { 42 | return `${text}`; 45 | } 46 | 47 | return `${text}`; 48 | }); 49 | -------------------------------------------------------------------------------- /docsify/src/core/render/compiler/paragraph.js: -------------------------------------------------------------------------------- 1 | import { helper as helperTpl } from '../tpl'; 2 | 3 | export const paragraphCompiler = ({ renderer }) => 4 | (renderer.paragraph = text => { 5 | let result; 6 | if (/^!>/.test(text)) { 7 | result = helperTpl('tip', text); 8 | } else if (/^\?>/.test(text)) { 9 | result = helperTpl('warn', text); 10 | } else { 11 | result = `

${text}

`; 12 | } 13 | 14 | return result; 15 | }); 16 | -------------------------------------------------------------------------------- /docsify/src/core/render/compiler/taskList.js: -------------------------------------------------------------------------------- 1 | export const taskListCompiler = ({ renderer }) => 2 | (renderer.list = (body, ordered, start) => { 3 | const isTaskList = /
  • /.test( 4 | body.split('class="task-list"')[0] 5 | ); 6 | const isStartReq = start && start > 1; 7 | const tag = ordered ? 'ol' : 'ul'; 8 | const tagAttrs = [ 9 | isTaskList ? 'class="task-list"' : '', 10 | isStartReq ? `start="${start}"` : '', 11 | ] 12 | .join(' ') 13 | .trim(); 14 | 15 | return `<${tag} ${tagAttrs}>${body}`; 16 | }); 17 | -------------------------------------------------------------------------------- /docsify/src/core/render/compiler/taskListItem.js: -------------------------------------------------------------------------------- 1 | export const taskListItemCompiler = ({ renderer }) => 2 | (renderer.listitem = text => { 3 | const isTaskItem = /^(]*>)/.test(text); 4 | const html = isTaskItem 5 | ? `
  • ` 6 | : `
  • ${text}
  • `; 7 | 8 | return html; 9 | }); 10 | -------------------------------------------------------------------------------- /docsify/src/core/render/emojify.js: -------------------------------------------------------------------------------- 1 | import emojiData from './emoji-data.js'; 2 | 3 | function replaceEmojiShorthand(m, $1, useNativeEmoji) { 4 | const emojiMatch = emojiData.data[$1]; 5 | 6 | let result = m; 7 | 8 | if (emojiMatch) { 9 | if (useNativeEmoji && /unicode/.test(emojiMatch)) { 10 | const emojiUnicode = emojiMatch 11 | .replace('unicode/', '') 12 | .replace(/\.png.*/, '') 13 | .split('-') 14 | .map(u => `&#x${u};`) 15 | // Separate multi-character emoji with zero width joiner sequence (ZWJ) 16 | // Hat tip: https://about.gitlab.com/blog/2018/05/30/journey-in-native-unicode-emoji/#emoji-made-up-of-multiple-characters 17 | .join('‍') 18 | .concat('︎'); 19 | result = `${emojiUnicode}`; 20 | } else { 21 | result = `${$1}`; 22 | } 23 | } 24 | 25 | return result; 26 | } 27 | 28 | export function emojify(text, useNativeEmoji) { 29 | return ( 30 | text 31 | // Mark colons in tags 32 | .replace( 33 | /<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g, 34 | m => m.replace(/:/g, '__colon__') 35 | ) 36 | // Mark colons in comments 37 | .replace(//g, m => m.replace(/:/g, '__colon__')) 38 | // Mark colons in URIs 39 | .replace(/([a-z]{2,}:)?\/\/[^\s'">)]+/gi, m => 40 | m.replace(/:/g, '__colon__') 41 | ) 42 | // Replace emoji shorthand codes 43 | .replace(/:([a-z0-9_\-+]+?):/g, (m, $1) => 44 | replaceEmojiShorthand(m, $1, useNativeEmoji) 45 | ) 46 | // Restore colons in tags and comments 47 | .replace(/__colon__/g, ':') 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /docsify/src/core/render/gen-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gen toc tree 3 | * @link https://github.com/killercup/grock/blob/5280ae63e16c5739e9233d9009bc235ed7d79a50/styles/solarized/assets/js/behavior.coffee#L54-L81 4 | * @param {Array} toc List of TOC elements 5 | * @param {Number} maxLevel Deep level 6 | * @return {Array} Headlines 7 | */ 8 | export function genTree(toc, maxLevel) { 9 | const headlines = []; 10 | const last = {}; 11 | 12 | toc.forEach(headline => { 13 | const level = headline.level || 1; 14 | const len = level - 1; 15 | 16 | if (level > maxLevel) { 17 | return; 18 | } 19 | 20 | if (last[len]) { 21 | last[len].children = (last[len].children || []).concat(headline); 22 | } else { 23 | headlines.push(headline); 24 | } 25 | 26 | last[level] = headline; 27 | }); 28 | 29 | return headlines; 30 | } 31 | -------------------------------------------------------------------------------- /docsify/src/core/render/progressbar.js: -------------------------------------------------------------------------------- 1 | import * as dom from '../util/dom'; 2 | 3 | let barEl; 4 | let timeId; 5 | 6 | /** 7 | * Init progress component 8 | */ 9 | function init() { 10 | const div = dom.create('div'); 11 | 12 | div.classList.add('progress'); 13 | dom.appendTo(dom.body, div); 14 | barEl = div; 15 | } 16 | 17 | /** 18 | * Render progress bar 19 | */ 20 | export default function ({ loaded, total, step }) { 21 | let num; 22 | 23 | !barEl && init(); 24 | 25 | if (step) { 26 | num = parseInt(barEl.style.width || 0, 10) + step; 27 | num = num > 80 ? 80 : num; 28 | } else { 29 | num = Math.floor((loaded / total) * 100); 30 | } 31 | 32 | barEl.style.opacity = 1; 33 | barEl.style.width = num >= 95 ? '100%' : num + '%'; 34 | 35 | if (num >= 95) { 36 | clearTimeout(timeId); 37 | // eslint-disable-next-line no-unused-vars 38 | timeId = setTimeout(_ => { 39 | barEl.style.opacity = 0; 40 | barEl.style.width = '0%'; 41 | }, 200); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docsify/src/core/render/slugify.js: -------------------------------------------------------------------------------- 1 | import { hasOwn } from '../util/core'; 2 | 3 | let cache = {}; 4 | const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g; 5 | 6 | function lower(string) { 7 | return string.toLowerCase(); 8 | } 9 | 10 | export function slugify(str) { 11 | if (typeof str !== 'string') { 12 | return ''; 13 | } 14 | 15 | let slug = str 16 | .trim() 17 | .replace(/[A-Z]+/g, lower) 18 | .replace(/<[^>]+>/g, '') 19 | .replace(re, '') 20 | .replace(/\s/g, '-') 21 | .replace(/-+/g, '-') 22 | .replace(/^(\d)/, '_$1'); 23 | let count = cache[slug]; 24 | 25 | count = hasOwn.call(cache, slug) ? count + 1 : 0; 26 | cache[slug] = count; 27 | 28 | if (count) { 29 | slug = slug + '-' + count; 30 | } 31 | 32 | return slug; 33 | } 34 | 35 | slugify.clear = function () { 36 | cache = {}; 37 | }; 38 | -------------------------------------------------------------------------------- /docsify/src/core/render/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a colon formatted string to a object with properties. 3 | * 4 | * This is process a provided string and look for any tokens in the format 5 | * of `:name[=value]` and then convert it to a object and return. 6 | * An example of this is ':include :type=code :fragment=demo' is taken and 7 | * then converted to: 8 | * 9 | * ``` 10 | * { 11 | * include: '', 12 | * type: 'code', 13 | * fragment: 'demo' 14 | * } 15 | * ``` 16 | * 17 | * @param {string} str The string to parse. 18 | * 19 | * @return {object} The original string and parsed object, { str, config }. 20 | */ 21 | export function getAndRemoveConfig(str = '') { 22 | const config = {}; 23 | 24 | if (str) { 25 | str = str 26 | .replace(/^('|")/, '') 27 | .replace(/('|")$/, '') 28 | .replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g, (m, key, value) => { 29 | if (key.indexOf(':') === -1) { 30 | config[key] = (value && value.replace(/"/g, '')) || true; 31 | return ''; 32 | } 33 | 34 | return m; 35 | }) 36 | .trim(); 37 | } 38 | 39 | return { str, config }; 40 | } 41 | 42 | /** 43 | * Remove the tag from sidebar when the header with link, details see issue 1069 44 | * @param {string} str The string to deal with. 45 | * 46 | * @return {string} str The string after delete the element. 47 | */ 48 | export function removeAtag(str = '') { 49 | return str.replace(/(<\/?a.*?>)/gi, ''); 50 | } 51 | -------------------------------------------------------------------------------- /docsify/src/core/router/history/abstract.js: -------------------------------------------------------------------------------- 1 | import { parseQuery } from '../util'; 2 | import { History } from './base'; 3 | 4 | export class AbstractHistory extends History { 5 | constructor(config) { 6 | super(config); 7 | this.mode = 'abstract'; 8 | } 9 | 10 | parse(path) { 11 | let query = ''; 12 | 13 | const queryIndex = path.indexOf('?'); 14 | if (queryIndex >= 0) { 15 | query = path.slice(queryIndex + 1); 16 | path = path.slice(0, queryIndex); 17 | } 18 | 19 | return { 20 | path, 21 | file: this.getFile(path), 22 | query: parseQuery(query), 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /docsify/src/core/util/env.js: -------------------------------------------------------------------------------- 1 | export const inBrowser = !process.env.SSR; 2 | 3 | export const isMobile = inBrowser && document.body.clientWidth <= 600; 4 | 5 | /** 6 | * @see https://github.com/MoOx/pjax/blob/master/lib/is-supported.js 7 | */ 8 | export const supportsPushState = 9 | inBrowser && 10 | (function () { 11 | // Borrowed wholesale from https://github.com/defunkt/jquery-pjax 12 | return ( 13 | window.history && 14 | window.history.pushState && 15 | window.history.replaceState && 16 | // PushState isn’t reliable on iOS until 5. 17 | !navigator.userAgent.match( 18 | /((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/ 19 | ) 20 | ); 21 | })(); 22 | -------------------------------------------------------------------------------- /docsify/src/core/util/index.js: -------------------------------------------------------------------------------- 1 | export * from './core'; 2 | export * from './env'; 3 | export * from '../router/util'; 4 | -------------------------------------------------------------------------------- /docsify/src/core/util/polyfill/css-vars.js: -------------------------------------------------------------------------------- 1 | import * as dom from '../dom'; 2 | import { get } from '../../fetch/ajax'; 3 | 4 | function replaceVar(block, color) { 5 | block.innerHTML = block.innerHTML.replace( 6 | /var\(\s*--theme-color.*?\)/g, 7 | color 8 | ); 9 | } 10 | 11 | export default function (color) { 12 | // Variable support 13 | if (window.CSS && window.CSS.supports && window.CSS.supports('(--v:red)')) { 14 | return; 15 | } 16 | 17 | const styleBlocks = dom.findAll('style:not(.inserted),link'); 18 | [].forEach.call(styleBlocks, block => { 19 | if (block.nodeName === 'STYLE') { 20 | replaceVar(block, color); 21 | } else if (block.nodeName === 'LINK') { 22 | const href = block.getAttribute('href'); 23 | 24 | if (!/\.css$/.test(href)) { 25 | return; 26 | } 27 | 28 | get(href).then(res => { 29 | const style = dom.create('style', res); 30 | 31 | dom.head.appendChild(style); 32 | replaceVar(style, color); 33 | }); 34 | } 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /docsify/src/core/util/str.js: -------------------------------------------------------------------------------- 1 | export function startsWith(str, prefix) { 2 | return str.indexOf(prefix) === 0; 3 | } 4 | 5 | export function endsWith(str, suffix) { 6 | return str.indexOf(suffix, str.length - suffix.length) !== -1; 7 | } 8 | 9 | export function removeDocsifyIgnoreTag(str) { 10 | return str 11 | .replace(//, '') 12 | .replace(/{docsify-ignore}/, '') 13 | .replace(//, '') 14 | .replace(/{docsify-ignore-all}/, '') 15 | .trim(); 16 | } 17 | -------------------------------------------------------------------------------- /docsify/src/core/virtual-routes/exact-match.js: -------------------------------------------------------------------------------- 1 | import { startsWith, endsWith } from '../util/str'; 2 | 3 | /** 4 | * Adds beginning of input (^) and end of input ($) assertions if needed into a regex string 5 | * @param {string} matcher the string to match 6 | * @returns {string} 7 | */ 8 | export function makeExactMatcher(matcher) { 9 | const matcherWithBeginningOfInput = startsWith(matcher, '^') 10 | ? matcher 11 | : `^${matcher}`; 12 | 13 | const matcherWithBeginningAndEndOfInput = endsWith( 14 | matcherWithBeginningOfInput, 15 | '$' 16 | ) 17 | ? matcherWithBeginningOfInput 18 | : `${matcherWithBeginningOfInput}$`; 19 | 20 | return matcherWithBeginningAndEndOfInput; 21 | } 22 | -------------------------------------------------------------------------------- /docsify/src/core/virtual-routes/next.js: -------------------------------------------------------------------------------- 1 | /** @typedef {((value: any) => void) => void} OnNext */ 2 | /** @typedef {(value: any) => void} NextFunction */ 3 | 4 | /** 5 | * Creates a pair of a function and an event emitter. 6 | * When the function is called, the event emitter calls the given callback with the value that was passed to the function. 7 | * @returns {[NextFunction, OnNext]} 8 | */ 9 | export function createNextFunction() { 10 | let storedCb = () => null; 11 | 12 | function next(value) { 13 | storedCb(value); 14 | } 15 | 16 | function onNext(cb) { 17 | storedCb = cb; 18 | } 19 | 20 | return [next, onNext]; 21 | } 22 | -------------------------------------------------------------------------------- /docsify/src/plugins/disqus.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | const fixedPath = location.href.replace('/-/', '/#/'); 3 | if (fixedPath !== location.href) { 4 | location.href = fixedPath; 5 | } 6 | 7 | function install(hook, vm) { 8 | const dom = Docsify.dom; 9 | const disqus = vm.config.disqus; 10 | if (!disqus) { 11 | throw Error('$docsify.disqus is required'); 12 | } 13 | 14 | hook.init(_ => { 15 | const script = dom.create('script'); 16 | 17 | script.async = true; 18 | script.src = `https://${disqus}.disqus.com/embed.js`; 19 | script.setAttribute('data-timestamp', Number(new Date())); 20 | dom.appendTo(dom.body, script); 21 | }); 22 | 23 | hook.mounted(_ => { 24 | const div = dom.create('div'); 25 | div.id = 'disqus_thread'; 26 | const main = dom.getNode('#main'); 27 | div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;`; 28 | dom.appendTo(dom.find('.content'), div); 29 | 30 | // eslint-disable-next-line 31 | window.disqus_config = function() { 32 | this.page.url = location.origin + '/-' + vm.route.path; 33 | this.page.identifier = vm.route.path; 34 | this.page.title = document.title; 35 | }; 36 | }); 37 | 38 | hook.doneEach(_ => { 39 | if (typeof window.DISQUS !== 'undefined') { 40 | window.DISQUS.reset({ 41 | reload: true, 42 | config: function () { 43 | this.page.url = location.origin + '/-' + vm.route.path; 44 | this.page.identifier = vm.route.path; 45 | this.page.title = document.title; 46 | }, 47 | }); 48 | } 49 | }); 50 | } 51 | 52 | $docsify.plugins = [].concat(install, $docsify.plugins); 53 | -------------------------------------------------------------------------------- /docsify/src/plugins/emoji.js: -------------------------------------------------------------------------------- 1 | import emojiData from '../core/render/emoji-data.js'; 2 | 3 | // Deprecation notice 4 | if (window && window.console) { 5 | console.info('Docsify emoji plugin has been deprecated as of v4.13'); 6 | } 7 | 8 | // Emoji from GitHub API 9 | window.emojify = function (match, $1) { 10 | return Object.prototype.hasOwnProperty.call(emojiData.data, $1) === false 11 | ? match 12 | : `${$1}`; 13 | }; 14 | -------------------------------------------------------------------------------- /docsify/src/plugins/external-script.js: -------------------------------------------------------------------------------- 1 | function handleExternalScript() { 2 | const container = Docsify.dom.getNode('#main'); 3 | const scripts = Docsify.dom.findAll(container, 'script'); 4 | 5 | for (let i = scripts.length; i--; ) { 6 | const script = scripts[i]; 7 | 8 | if (script && script.src) { 9 | const newScript = document.createElement('script'); 10 | 11 | Array.prototype.slice.call(script.attributes).forEach(attribute => { 12 | newScript[attribute.name] = attribute.value; 13 | }); 14 | 15 | script.parentNode.insertBefore(newScript, script); 16 | script.parentNode.removeChild(script); 17 | } 18 | } 19 | } 20 | 21 | const install = function (hook) { 22 | hook.doneEach(handleExternalScript); 23 | }; 24 | 25 | window.$docsify.plugins = [].concat(install, window.$docsify.plugins); 26 | -------------------------------------------------------------------------------- /docsify/src/plugins/front-matter/index.js: -------------------------------------------------------------------------------- 1 | import parser from './parser'; 2 | 3 | const install = function (hook, vm) { 4 | // Used to remove front matter from embedded pages if installed. 5 | vm.config.frontMatter = {}; 6 | vm.config.frontMatter.installed = true; 7 | vm.config.frontMatter.parseMarkdown = function (content) { 8 | const { body } = parser(content); 9 | return body; 10 | }; 11 | 12 | hook.beforeEach(content => { 13 | const { attributes, body } = parser(content); 14 | 15 | vm.frontmatter = attributes; 16 | 17 | return body; 18 | }); 19 | }; 20 | 21 | $docsify.plugins = [].concat(install, $docsify.plugins); 22 | -------------------------------------------------------------------------------- /docsify/src/plugins/front-matter/parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Fork https://github.com/egoist/docute/blob/master/src/utils/front-matter.js 3 | */ 4 | /* eslint-disable */ 5 | import parser from './yaml' 6 | 7 | var optionalByteOrderMark = '\\ufeff?' 8 | var pattern = 9 | '^(' + 10 | optionalByteOrderMark + 11 | '(= yaml =|---)' + 12 | '$([\\s\\S]*?)' + 13 | '(?:\\2|\\.\\.\\.)' + 14 | '$' + 15 | '' + 16 | '(?:\\n)?)' 17 | // NOTE: If this pattern uses the 'g' flag the `regex` variable definition will 18 | // need to be moved down into the functions that use it. 19 | var regex = new RegExp(pattern, 'm') 20 | 21 | function extractor(string) { 22 | string = string || '' 23 | 24 | var lines = string.split(/(\r?\n)/) 25 | if (lines[0] && /= yaml =|---/.test(lines[0])) { 26 | return parse(string) 27 | } else { 28 | return { attributes: {}, body: string } 29 | } 30 | } 31 | 32 | function parse(string) { 33 | var match = regex.exec(string) 34 | 35 | if (!match) { 36 | return { 37 | attributes: {}, 38 | body: string 39 | } 40 | } 41 | 42 | var yaml = match[match.length - 1].replace(/^\s+|\s+$/g, '') 43 | var attributes = parser(yaml) || {} 44 | var body = string.replace(match[0], '') 45 | 46 | return { attributes: attributes, body: body, frontmatter: yaml } 47 | } 48 | 49 | function test(string) { 50 | string = string || '' 51 | 52 | return regex.test(string) 53 | } 54 | 55 | export default extractor 56 | -------------------------------------------------------------------------------- /docsify/src/plugins/ga.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // From https://github.com/egoist/vue-ga/blob/master/src/index.js 3 | function appendScript() { 4 | const script = document.createElement('script'); 5 | script.async = true; 6 | script.src = 'https://www.google-analytics.com/analytics.js'; 7 | document.body.appendChild(script); 8 | } 9 | 10 | function init(id) { 11 | appendScript(); 12 | window.ga = 13 | window.ga || 14 | function () { 15 | (window.ga.q = window.ga.q || []).push(arguments); 16 | }; 17 | 18 | window.ga.l = Number(new Date()); 19 | window.ga('create', id, 'auto'); 20 | } 21 | 22 | function collect() { 23 | if (!window.ga) { 24 | init($docsify.ga); 25 | } 26 | 27 | window.ga('set', 'page', location.hash); 28 | window.ga('send', 'pageview'); 29 | } 30 | 31 | const install = function (hook) { 32 | if (!$docsify.ga) { 33 | console.error('[Docsify] ga is required.'); 34 | return; 35 | } 36 | 37 | hook.beforeEach(collect); 38 | }; 39 | 40 | $docsify.plugins = [].concat(install, $docsify.plugins); 41 | -------------------------------------------------------------------------------- /docsify/src/plugins/gitalk.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | function install(hook) { 3 | const dom = Docsify.dom; 4 | 5 | hook.mounted(_ => { 6 | const div = dom.create('div'); 7 | div.id = 'gitalk-container'; 8 | const main = dom.getNode('#main'); 9 | div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;`; 10 | dom.appendTo(dom.find('.content'), div); 11 | }); 12 | 13 | hook.doneEach(_ => { 14 | const el = document.getElementById('gitalk-container'); 15 | while (el.hasChildNodes()) { 16 | el.removeChild(el.firstChild); 17 | } 18 | 19 | // eslint-disable-next-line 20 | gitalk.render('gitalk-container'); 21 | }); 22 | } 23 | 24 | $docsify.plugins = [].concat(install, $docsify.plugins); 25 | -------------------------------------------------------------------------------- /docsify/src/plugins/matomo.js: -------------------------------------------------------------------------------- 1 | function appendScript(options) { 2 | const script = document.createElement('script'); 3 | script.async = true; 4 | script.src = options.host + '/matomo.js'; 5 | document.body.appendChild(script); 6 | } 7 | 8 | function init(options) { 9 | window._paq = window._paq || []; 10 | window._paq.push(['trackPageView']); 11 | window._paq.push(['enableLinkTracking']); 12 | setTimeout(function () { 13 | appendScript(options); 14 | window._paq.push(['setTrackerUrl', options.host + '/matomo.php']); 15 | window._paq.push(['setSiteId', String(options.id)]); 16 | }, 0); 17 | } 18 | 19 | function collect() { 20 | if (!window._paq) { 21 | init($docsify.matomo); 22 | } 23 | 24 | window._paq.push(['setCustomUrl', window.location.hash.substr(1)]); 25 | window._paq.push(['setDocumentTitle', document.title]); 26 | window._paq.push(['trackPageView']); 27 | } 28 | 29 | const install = function (hook) { 30 | if (!$docsify.matomo) { 31 | // eslint-disable-next-line no-console 32 | console.error('[Docsify] matomo is required.'); 33 | return; 34 | } 35 | 36 | hook.beforeEach(collect); 37 | }; 38 | 39 | $docsify.plugins = [].concat(install, $docsify.plugins); 40 | -------------------------------------------------------------------------------- /docsify/src/plugins/search/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import { init as initComponent, update as updateComponent } from './component'; 3 | import { init as initSearch } from './search'; 4 | 5 | const CONFIG = { 6 | placeholder: 'Type to search', 7 | noData: 'No Results!', 8 | paths: 'auto', 9 | depth: 2, 10 | maxAge: 86400000, // 1 day 11 | hideOtherSidebarContent: false, 12 | namespace: undefined, 13 | pathNamespaces: undefined, 14 | }; 15 | 16 | const install = function (hook, vm) { 17 | const { util } = Docsify; 18 | const opts = vm.config.search || CONFIG; 19 | 20 | if (Array.isArray(opts)) { 21 | CONFIG.paths = opts; 22 | } else if (typeof opts === 'object') { 23 | CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto'; 24 | CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge; 25 | CONFIG.placeholder = opts.placeholder || CONFIG.placeholder; 26 | CONFIG.noData = opts.noData || CONFIG.noData; 27 | CONFIG.depth = opts.depth || CONFIG.depth; 28 | CONFIG.hideOtherSidebarContent = 29 | opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent; 30 | CONFIG.namespace = opts.namespace || CONFIG.namespace; 31 | CONFIG.pathNamespaces = opts.pathNamespaces || CONFIG.pathNamespaces; 32 | } 33 | 34 | const isAuto = CONFIG.paths === 'auto'; 35 | 36 | hook.mounted(_ => { 37 | initComponent(CONFIG, vm); 38 | !isAuto && initSearch(CONFIG, vm); 39 | }); 40 | hook.doneEach(_ => { 41 | updateComponent(CONFIG, vm); 42 | isAuto && initSearch(CONFIG, vm); 43 | }); 44 | }; 45 | 46 | $docsify.plugins = [].concat(install, $docsify.plugins); 47 | -------------------------------------------------------------------------------- /docsify/src/plugins/zoom-image.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import mediumZoom from 'medium-zoom'; 3 | 4 | const matchesSelector = 5 | Element.prototype.matches || 6 | Element.prototype.webkitMatchesSelector || 7 | Element.prototype.msMatchesSelector; 8 | 9 | function install(hook) { 10 | let zoom; 11 | 12 | hook.doneEach(_ => { 13 | let elms = Array.apply( 14 | null, 15 | document.querySelectorAll( 16 | '.markdown-section img:not(.emoji):not([data-no-zoom])' 17 | ) 18 | ); 19 | 20 | elms = elms.filter(elm => matchesSelector.call(elm, 'a img') === false); 21 | 22 | if (zoom) { 23 | zoom.detach(); 24 | } 25 | 26 | zoom = mediumZoom(elms); 27 | }); 28 | } 29 | 30 | $docsify.plugins = [].concat(install, $docsify.plugins); 31 | -------------------------------------------------------------------------------- /docsify/src/themes/pure.styl: -------------------------------------------------------------------------------- 1 | $color-primary = #000 2 | $color-bg = #fff 3 | $color-text = #000 4 | $sidebar-width = 300px 5 | 6 | @import 'basic/_layout' 7 | @import 'basic/_coverpage' 8 | -------------------------------------------------------------------------------- /docsify/test/config/jest.setup.js: -------------------------------------------------------------------------------- 1 | const server = require('./server.js'); 2 | 3 | module.exports = async () => { 4 | await server.startAsync(); 5 | }; 6 | -------------------------------------------------------------------------------- /docsify/test/config/jest.teardown.js: -------------------------------------------------------------------------------- 1 | const server = require('./server.js'); 2 | 3 | module.exports = async () => { 4 | server.stop(); 5 | }; 6 | -------------------------------------------------------------------------------- /docsify/test/config/playwright.setup.js: -------------------------------------------------------------------------------- 1 | const server = require('./server.js'); 2 | 3 | module.exports = async config => { 4 | await server.startAsync(); 5 | }; 6 | -------------------------------------------------------------------------------- /docsify/test/config/playwright.teardown.js: -------------------------------------------------------------------------------- 1 | const server = require('./server.js'); 2 | 3 | module.exports = async config => { 4 | server.stop(); 5 | }; 6 | -------------------------------------------------------------------------------- /docsify/test/e2e/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['plugin:playwright/playwright-test'], 3 | }; 4 | -------------------------------------------------------------------------------- /docsify/test/e2e/fixtures/docsify-init-fixture.js: -------------------------------------------------------------------------------- 1 | const base = require('@playwright/test'); 2 | 3 | exports.test = base.test.extend({ 4 | page: async ({ page }, use) => { 5 | global.page = page; 6 | 7 | // Navigate to a real URL by default 8 | // Playwright tests are executed on "about:blank" by default, which will 9 | // cause operations that require the window location to be a valid URL to 10 | // fail (e.g. AJAX requests). Navigating to a blank document with a real 11 | // URL solved this problem. 12 | await page.goto('/_blank.html'); 13 | await use(page); 14 | }, 15 | }); 16 | exports.expect = base.expect; 17 | -------------------------------------------------------------------------------- /docsify/test/e2e/index-file.test.js: -------------------------------------------------------------------------------- 1 | const docsifyInit = require('../helpers/docsify-init'); 2 | const { test, expect } = require('./fixtures/docsify-init-fixture'); 3 | 4 | test.describe('Index file hosting', () => { 5 | const sharedOptions = { 6 | config: { 7 | basePath: '/docs/index.html#/', 8 | }, 9 | testURL: '/docs/index.html#/', 10 | }; 11 | 12 | test('should serve from index file', async ({ page }) => { 13 | await docsifyInit(sharedOptions); 14 | await expect(page.locator('#main')).toContainText( 15 | 'A magical documentation site generator' 16 | ); 17 | expect(page.url()).toMatch(/index\.html#\/$/); 18 | }); 19 | 20 | test('should use index file links in sidebar from index file hosting', async ({ 21 | page, 22 | }) => { 23 | await docsifyInit(sharedOptions); 24 | await page.click('a[href="#/quickstart"]'); 25 | await expect(page.locator('#main')).toContainText('Quick start'); 26 | expect(page.url()).toMatch(/index\.html#\/quickstart$/); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /docsify/test/e2e/security.test.js: -------------------------------------------------------------------------------- 1 | const docsifyInit = require('../helpers/docsify-init'); 2 | const { test, expect } = require('./fixtures/docsify-init-fixture'); 3 | 4 | test.describe('Security - Cross Site Scripting (XSS)', () => { 5 | const sharedOptions = { 6 | markdown: { 7 | homepage: '# Hello World', 8 | }, 9 | routes: { 10 | 'test.md': '# Test Page', 11 | }, 12 | }; 13 | const slashStrings = ['//', '///']; 14 | 15 | for (let slashString of slashStrings) { 16 | const hash = `#${slashString}domain.com/file.md`; 17 | 18 | test(`should not load remote content from hash (${hash})`, async ({ 19 | page, 20 | }) => { 21 | const mainElm = page.locator('#main'); 22 | 23 | await docsifyInit(sharedOptions); 24 | await expect(mainElm).toContainText('Hello World'); 25 | await page.evaluate(() => (location.hash = '#/test')); 26 | await expect(mainElm).toContainText('Test Page'); 27 | await page.evaluate(newHash => { 28 | location.hash = newHash; 29 | }, hash); 30 | await expect(mainElm).toContainText('Hello World'); 31 | expect(page.url()).toMatch(/#\/$/); 32 | }); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /docsify/test/integration/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../unit/.eslintrc'); 2 | -------------------------------------------------------------------------------- /docsify/test/integration/docsify.test.js: -------------------------------------------------------------------------------- 1 | const docsifyInit = require('../helpers/docsify-init'); 2 | 3 | // Suite 4 | // ----------------------------------------------------------------------------- 5 | describe('Docsify', function () { 6 | // Tests 7 | // --------------------------------------------------------------------------- 8 | test('allows $docsify configuration to be a function', async () => { 9 | const testConfig = jest.fn(vm => { 10 | expect(vm).toBeInstanceOf(Object); 11 | expect(vm.constructor.name).toBe('Docsify'); 12 | expect(vm.$fetch).toBeInstanceOf(Function); 13 | expect(vm.$resetEvents).toBeInstanceOf(Function); 14 | expect(vm.route).toBeInstanceOf(Object); 15 | }); 16 | 17 | await docsifyInit({ 18 | config: testConfig, 19 | }); 20 | 21 | expect(typeof Docsify).toBe('object'); 22 | expect(testConfig).toHaveBeenCalled(); 23 | }); 24 | 25 | test('provides the hooks and vm API to plugins', async () => { 26 | const testConfig = jest.fn(vm => { 27 | const vm1 = vm; 28 | 29 | return { 30 | plugins: [ 31 | function (hook, vm2) { 32 | expect(vm1).toEqual(vm2); 33 | 34 | expect(hook.init).toBeInstanceOf(Function); 35 | expect(hook.beforeEach).toBeInstanceOf(Function); 36 | expect(hook.afterEach).toBeInstanceOf(Function); 37 | expect(hook.doneEach).toBeInstanceOf(Function); 38 | expect(hook.mounted).toBeInstanceOf(Function); 39 | expect(hook.ready).toBeInstanceOf(Function); 40 | }, 41 | ], 42 | }; 43 | }); 44 | 45 | await docsifyInit({ 46 | config: testConfig, 47 | }); 48 | 49 | expect(typeof Docsify).toBe('object'); 50 | expect(testConfig).toHaveBeenCalled(); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /docsify/test/integration/global-apis.test.js: -------------------------------------------------------------------------------- 1 | import initGlobalAPI from '../../src/core/global-api.js'; 2 | 3 | // Suite 4 | // ----------------------------------------------------------------------------- 5 | describe('Global APIs', function () { 6 | // Tests 7 | // --------------------------------------------------------------------------- 8 | test('APIs are available', () => { 9 | initGlobalAPI(); 10 | 11 | expect(typeof window.Docsify).toBe('object'); 12 | expect(typeof window.Docsify.util).toBe('object'); 13 | expect(typeof window.Docsify.dom).toBe('object'); 14 | expect(typeof window.Docsify.get).toBe('function'); 15 | expect(typeof window.Docsify.slugify).toBe('function'); 16 | expect(typeof window.Docsify.version).toBe('string'); 17 | expect(typeof window.DocsifyCompiler).toBe('function'); 18 | expect(typeof window.marked).toBe('function'); 19 | expect(typeof window.Prism).toBe('object'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /docsify/test/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | 'jest/globals': true, 4 | }, 5 | extends: ['plugin:jest/recommended', 'plugin:jest/style'], 6 | plugins: ['jest'], 7 | }; 8 | -------------------------------------------------------------------------------- /docsify/test/unit/__snapshots__/example.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Example Tests Jest & JSDOM basics snapshot (jsdom) 1`] = `"

    Test

    This is a test

    "`; 4 | -------------------------------------------------------------------------------- /docsify/test/unit/fixtures/get-time-of-day.js: -------------------------------------------------------------------------------- 1 | export function getTimeOfDay(hours = new Date().getHours()) { 2 | const timeOfDay = 3 | hours < 12 ? 'morning' : hours < 17 ? 'afternoon' : 'evening'; 4 | 5 | return timeOfDay; 6 | } 7 | -------------------------------------------------------------------------------- /docsify/test/unit/fixtures/greet.js: -------------------------------------------------------------------------------- 1 | import { getTimeOfDay } from './get-time-of-day'; 2 | 3 | export function greet(name = 'friend') { 4 | const timeOfDay = getTimeOfDay(); 5 | 6 | return `Good ${timeOfDay}, ${name}!`; 7 | } 8 | -------------------------------------------------------------------------------- /docsify/test/unit/router-util.test.js: -------------------------------------------------------------------------------- 1 | const { resolvePath } = require('../../src/core/util'); 2 | 3 | // Suite 4 | // ----------------------------------------------------------------------------- 5 | describe('router/util', () => { 6 | // resolvePath() 7 | // --------------------------------------------------------------------------- 8 | describe('resolvePath()', () => { 9 | test('resolvePath with filename', () => { 10 | const result = resolvePath('hello.md'); 11 | 12 | expect(result).toBe('/hello.md'); 13 | }); 14 | 15 | test('resolvePath with ./', () => { 16 | const result = resolvePath('./hello.md'); 17 | 18 | expect(result).toBe('/hello.md'); 19 | }); 20 | 21 | test('resolvePath with ../', () => { 22 | const result = resolvePath('test/../hello.md'); 23 | 24 | expect(result).toBe('/hello.md'); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2637309949/go-interview/f38194aec9f813133b39c1e915b0b530ad9fae7b/favicon.png -------------------------------------------------------------------------------- /go-microv5-components.md: -------------------------------------------------------------------------------- 1 | # go-microv5 组件关系图案例(完整调用过程) 2 | 3 | 以下是一个基于 `go-microv5` 的完整调用过程关系图示例。 4 | 5 | ```mermaid 6 | sequenceDiagram 7 | participant Client as 客户端 8 | participant Service as 服务 9 | participant Registry as 服务注册中心 10 | participant Selector as 服务选择器 11 | participant Transport as 传输层 12 | participant Broker as 消息代理 13 | participant Queue as 消息队列 14 | participant Protocol as 通信协议 15 | participant ServiceInstance as 服务实例 16 | 17 | Client->>Service: 调用服务 18 | Service->>Registry: 注册服务 19 | Service->>Selector: 选择服务实例 20 | Selector->>Registry: 获取服务实例列表 21 | Selector->>ServiceInstance: 选择最佳实例 22 | Service->>Transport: 发送请求 23 | Transport->>Protocol: 使用 HTTP/GRPC 协议通信 24 | Service->>Broker: 发布消息 25 | Broker->>Queue: 推送消息到队列 26 | Queue->>ServiceInstance: 消费消息 27 | ServiceInstance->>Client: 返回响应 28 | ``` 29 | 30 | ## 组件说明 31 | 32 | - **客户端 (Client)**: 发起服务调用的入口。 33 | - **服务 (Service)**: 核心业务逻辑处理单元,负责处理请求。 34 | - **服务注册中心 (Registry)**: 管理服务的注册与发现。 35 | - **服务选择器 (Selector)**: 选择合适的服务实例,支持负载均衡。 36 | - **传输层 (Transport)**: 提供网络通信能力,支持多种协议。 37 | - **消息代理 (Broker)**: 实现发布/订阅模式的消息传递。 38 | - **消息队列 (Queue)**: 用于异步消息传递。 39 | - **通信协议 (Protocol)**: 支持 HTTP、gRPC 等协议。 40 | - **服务实例 (ServiceInstance)**: 实际运行的服务节点,处理请求并返回响应。 41 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Golang Doc 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /navbar.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | let navbarContent = ` 5 | [![](https://docsify.js.org/_media/icon.svg)](/) 6 | 7 | 8 | Golang Doc\n\n` 9 | 10 | function walkDir(dir, baseDir = '', currentLevel = 0) { 11 | const files = fs.readdirSync(dir); 12 | let content = ''; 13 | 14 | files.forEach(file => { 15 | const filePath = path.join(dir, file); 16 | const stat = fs.statSync(filePath); 17 | 18 | if (stat.isDirectory()) { 19 | const subDirContent = walkDir(filePath, path.join(baseDir, file), currentLevel + 1); 20 | if (subDirContent) { 21 | const dirName = file; 22 | content += `${' '.repeat(currentLevel)}* ${dirName}\n${subDirContent}`; 23 | } 24 | } else if (path.extname(file) === '.md') { 25 | const relativePath = encodeURIComponent(path.join(baseDir, file).replace(/\\/g, '/')); 26 | const displayName = file.slice(0, -3); // 去掉 `.md` 扩展名 27 | content += `${' '.repeat(currentLevel)}* [${displayName}](${relativePath})\n`; 28 | } 29 | }); 30 | 31 | return content; 32 | } 33 | 34 | function generateNavbar(dirPath, outputFile) { 35 | navbarContent += walkDir(dirPath, dirPath); 36 | fs.writeFileSync(outputFile, navbarContent); 37 | console.log(`_navbar.md generated at ${outputFile}`); 38 | } 39 | 40 | const directoryToScan = './docs'; 41 | const outputFilePath = '_navbar.md'; 42 | 43 | generateNavbar(directoryToScan, outputFilePath); 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "node navbar.js", 4 | "dev": "docsify serve ." 5 | } 6 | } 7 | --------------------------------------------------------------------------------