diff --git "a/Git\345\267\245\345\205\267/image-1.png" "b/Git\345\267\245\345\205\267/image-1.png" new file mode 100644 index 0000000..24e9ac8 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-1.png" differ diff --git "a/Git\345\267\245\345\205\267/image-10.png" "b/Git\345\267\245\345\205\267/image-10.png" new file mode 100644 index 0000000..7ddd2fd Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-10.png" differ diff --git "a/Git\345\267\245\345\205\267/image-11.png" "b/Git\345\267\245\345\205\267/image-11.png" new file mode 100644 index 0000000..6254d27 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-11.png" differ diff --git "a/Git\345\267\245\345\205\267/image-12.png" "b/Git\345\267\245\345\205\267/image-12.png" new file mode 100644 index 0000000..35406d1 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-12.png" differ diff --git "a/Git\345\267\245\345\205\267/image-13.png" "b/Git\345\267\245\345\205\267/image-13.png" new file mode 100644 index 0000000..23bdfea Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-13.png" differ diff --git "a/Git\345\267\245\345\205\267/image-14.png" "b/Git\345\267\245\345\205\267/image-14.png" new file mode 100644 index 0000000..3cae55a Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-14.png" differ diff --git "a/Git\345\267\245\345\205\267/image-15.png" "b/Git\345\267\245\345\205\267/image-15.png" new file mode 100644 index 0000000..03cb224 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-15.png" differ diff --git "a/Git\345\267\245\345\205\267/image-16.png" "b/Git\345\267\245\345\205\267/image-16.png" new file mode 100644 index 0000000..86cbcd0 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-16.png" differ diff --git "a/Git\345\267\245\345\205\267/image-17.png" "b/Git\345\267\245\345\205\267/image-17.png" new file mode 100644 index 0000000..9acc799 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-17.png" differ diff --git "a/Git\345\267\245\345\205\267/image-18.png" "b/Git\345\267\245\345\205\267/image-18.png" new file mode 100644 index 0000000..6c6bdbd Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-18.png" differ diff --git "a/Git\345\267\245\345\205\267/image-19.png" "b/Git\345\267\245\345\205\267/image-19.png" new file mode 100644 index 0000000..337e184 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-19.png" differ diff --git "a/Git\345\267\245\345\205\267/image-2.png" "b/Git\345\267\245\345\205\267/image-2.png" new file mode 100644 index 0000000..a7fe983 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-2.png" differ diff --git "a/Git\345\267\245\345\205\267/image-20.png" "b/Git\345\267\245\345\205\267/image-20.png" new file mode 100644 index 0000000..8bce0e3 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-20.png" differ diff --git "a/Git\345\267\245\345\205\267/image-21.png" "b/Git\345\267\245\345\205\267/image-21.png" new file mode 100644 index 0000000..5df283c Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-21.png" differ diff --git "a/Git\345\267\245\345\205\267/image-22.png" "b/Git\345\267\245\345\205\267/image-22.png" new file mode 100644 index 0000000..5975c2d Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-22.png" differ diff --git "a/Git\345\267\245\345\205\267/image-23.png" "b/Git\345\267\245\345\205\267/image-23.png" new file mode 100644 index 0000000..ae5f873 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-23.png" differ diff --git "a/Git\345\267\245\345\205\267/image-24.png" "b/Git\345\267\245\345\205\267/image-24.png" new file mode 100644 index 0000000..cb245d0 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-24.png" differ diff --git "a/Git\345\267\245\345\205\267/image-25.png" "b/Git\345\267\245\345\205\267/image-25.png" new file mode 100644 index 0000000..92d195f Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-25.png" differ diff --git "a/Git\345\267\245\345\205\267/image-26.png" "b/Git\345\267\245\345\205\267/image-26.png" new file mode 100644 index 0000000..fb18f11 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-26.png" differ diff --git "a/Git\345\267\245\345\205\267/image-27.png" "b/Git\345\267\245\345\205\267/image-27.png" new file mode 100644 index 0000000..e01ea84 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-27.png" differ diff --git "a/Git\345\267\245\345\205\267/image-28.png" "b/Git\345\267\245\345\205\267/image-28.png" new file mode 100644 index 0000000..010dfdf Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-28.png" differ diff --git "a/Git\345\267\245\345\205\267/image-29.png" "b/Git\345\267\245\345\205\267/image-29.png" new file mode 100644 index 0000000..b7580c9 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-29.png" differ diff --git "a/Git\345\267\245\345\205\267/image-3.png" "b/Git\345\267\245\345\205\267/image-3.png" new file mode 100644 index 0000000..e46fba0 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-3.png" differ diff --git "a/Git\345\267\245\345\205\267/image-30.png" "b/Git\345\267\245\345\205\267/image-30.png" new file mode 100644 index 0000000..68e7979 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-30.png" differ diff --git "a/Git\345\267\245\345\205\267/image-31.png" "b/Git\345\267\245\345\205\267/image-31.png" new file mode 100644 index 0000000..f15252c Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-31.png" differ diff --git "a/Git\345\267\245\345\205\267/image-32.png" "b/Git\345\267\245\345\205\267/image-32.png" new file mode 100644 index 0000000..9094be4 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-32.png" differ diff --git "a/Git\345\267\245\345\205\267/image-33.png" "b/Git\345\267\245\345\205\267/image-33.png" new file mode 100644 index 0000000..d21ec6e Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-33.png" differ diff --git "a/Git\345\267\245\345\205\267/image-34.png" "b/Git\345\267\245\345\205\267/image-34.png" new file mode 100644 index 0000000..fe91e2c Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-34.png" differ diff --git "a/Git\345\267\245\345\205\267/image-35.png" "b/Git\345\267\245\345\205\267/image-35.png" new file mode 100644 index 0000000..060ff57 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-35.png" differ diff --git "a/Git\345\267\245\345\205\267/image-36.png" "b/Git\345\267\245\345\205\267/image-36.png" new file mode 100644 index 0000000..0a26e03 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-36.png" differ diff --git "a/Git\345\267\245\345\205\267/image-37.png" "b/Git\345\267\245\345\205\267/image-37.png" new file mode 100644 index 0000000..bc471ce Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-37.png" differ diff --git "a/Git\345\267\245\345\205\267/image-38.png" "b/Git\345\267\245\345\205\267/image-38.png" new file mode 100644 index 0000000..55f42ed Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-38.png" differ diff --git "a/Git\345\267\245\345\205\267/image-39.png" "b/Git\345\267\245\345\205\267/image-39.png" new file mode 100644 index 0000000..165b1f0 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-39.png" differ diff --git "a/Git\345\267\245\345\205\267/image-4.png" "b/Git\345\267\245\345\205\267/image-4.png" new file mode 100644 index 0000000..4f84595 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-4.png" differ diff --git "a/Git\345\267\245\345\205\267/image-40.png" "b/Git\345\267\245\345\205\267/image-40.png" new file mode 100644 index 0000000..725b0a9 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-40.png" differ diff --git "a/Git\345\267\245\345\205\267/image-41.png" "b/Git\345\267\245\345\205\267/image-41.png" new file mode 100644 index 0000000..04e5cb4 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-41.png" differ diff --git "a/Git\345\267\245\345\205\267/image-42.png" "b/Git\345\267\245\345\205\267/image-42.png" new file mode 100644 index 0000000..0b53064 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-42.png" differ diff --git "a/Git\345\267\245\345\205\267/image-5.png" "b/Git\345\267\245\345\205\267/image-5.png" new file mode 100644 index 0000000..300d27e Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-5.png" differ diff --git "a/Git\345\267\245\345\205\267/image-6.png" "b/Git\345\267\245\345\205\267/image-6.png" new file mode 100644 index 0000000..54fce7c Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-6.png" differ diff --git "a/Git\345\267\245\345\205\267/image-7.png" "b/Git\345\267\245\345\205\267/image-7.png" new file mode 100644 index 0000000..69b3eb2 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-7.png" differ diff --git "a/Git\345\267\245\345\205\267/image-8.png" "b/Git\345\267\245\345\205\267/image-8.png" new file mode 100644 index 0000000..266ac78 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-8.png" differ diff --git "a/Git\345\267\245\345\205\267/image-9.png" "b/Git\345\267\245\345\205\267/image-9.png" new file mode 100644 index 0000000..eaefe7e Binary files /dev/null and "b/Git\345\267\245\345\205\267/image-9.png" differ diff --git "a/Git\345\267\245\345\205\267/image.png" "b/Git\345\267\245\345\205\267/image.png" new file mode 100644 index 0000000..090b774 Binary files /dev/null and "b/Git\345\267\245\345\205\267/image.png" differ diff --git "a/Git\345\267\245\345\205\267/readme.md" "b/Git\345\267\245\345\205\267/readme.md" new file mode 100644 index 0000000..140c44a --- /dev/null +++ "b/Git\345\267\245\345\205\267/readme.md" @@ -0,0 +1,389 @@ +一:版本控制 + +1.1 What and why? + +—什么是版本控制? + +版本控制是一种记录一个或若干个文件内容变化,以便将来查阅特定版本修订情况的系统 + +—为什么需要版本控制? + +更好的关注变更,了解到每个版本的改动是什么,方便对改动的代码进行检查,预防事故发生;也能随时切换到不同的版本,回滚误删误改的问题代码 + +1.2 版本控制类型 + +● 本地版本控制:本地代码的版本控制。代表性工具:RCS + +● 集中式版本控制:提供一个远端服务器来维护代码版本,本地不保存代码版本,解决多人协作问题。代表性工具:SVN + +● 分布式版本控制:每个仓库都能记录版本历史,解决只有一个服务器保存版本的问题。代表性工具:Git + +二:Git 的基本操作 + +2.1 Git 管理代码的机制(图源网络) + +![alt text](image.png) + +● Remote 表示远端 + +● Repository 表示存储库 + +● Workspace 表示工作区 + +2.2 项目初始化 + +● mkdir study(创建文件夹) + +● cd study(进入文件夹) + +● git init(初始化) + +![alt text](image-1.png) + +如上图,C:/Users/admin/study/.git/是文件夹的路径,根据路径进入文件夹发现里面什么也没有 + +2.3 Git 目录 + +● 为 Window 系统配置 tree 命令环境详见下面这篇文章,其他系统不需要 +http://t.csdn.cn/Wfqs4 + +![alt text](image-2.png) + +● Tree .git 命令(.git 前面有一空格) + +● Tree 命令 + +![alt text](image-3.png) + +2.4 Git Config(Git 配置) + +——每个级别的配置可能重复,但低级别的配置会被高级别的配置覆盖 + +——用户名配置 + +● 使用以下命令分别配置用户名和邮箱 + +![alt text](image-4.png) + +——InsteadOf 配置 + +● 使用以下命令可以把"git://"替换成"https://"方便使用 https 协议 + +——查询命令 + +![alt text](image-5.png) + +● 可以通过"git config"命令查看更改 Git 配置的所有方法 + +● 可以通过"git config --list"命令查看 Git 当前的所有配置 + +● 可以通过"git config --global --list"命令查看当前用户的 global 配置 + +● 可以通过"git config --system --list"命令查看 Git 的系统配置 + +● 可以通过"git config --local --list"命令查看当前仓库的配置,需要到仓库目录执行 + +如果不到仓库目录执行,就会报错: + +![alt text](image-6.png) + +● 可以通过"git config user.name"获取用户名,获取邮箱同理 + +2.5 Git Remote(远端) + +还有一类配置是 Remote 配置,表示本地和远端仓库的关联关系。分为 SSH 和 HTTP 两种 + +● 使用"git remote -h"命令查询 git remote 所有用法 + +● 使用"git remote -v"命令查询当前 remote 配置,会发现什么也没有 + +![alt text](image-7.png) + +● 现在通过以下两个命令分别添加 SSH 和 HTTP 配置 + +![alt text](image-8.png) + +![alt text](image-9.png) + +● 现在再次使用"git remote -v"命令 + +![alt text](image-10.png) + +● 或者通过以下命令查询 + +![alt text](image-11.png) + +——免密配置 + +本地与 Remote 进行通信,一般会通过 SSH 和 HTTP 两种协议,且这两种协议均需要对身份进行认证,因此需要配置一下免密的认证方式 + +● HTTP Remote + +![alt text](image-12.png) + +● SSH Remote + +SSH 可以通过公私钥的机制,将生成的公钥存放在服务端,从而实现免密访问,目前的 Key 的类型有四种,分别是 dsa、rsa、ecdsa、ed25519,默认使用的是 rsa,由于一些安全问题,现在已经不推荐使用 dsa 和 rsa,优先推荐使用 ed25519 + +可以通过"ssh-keygen -t ed25519 -C "邮箱""的命令来配置 SSH Remote + +——可以在 Github 上进行 SSH 的设置,点击 github 的个人头像进入 Settings,再进入 SSH and GPG keys 即可修改 + +2.6 Git Add + +Git Add 是将 workspace 中的代码提交到 index 空间 + +● 首先使用"touch 文件名.文件后缀"命令创建一份文件,代码如下 + +● 使用"git status"命令发现已创建但尚未上传到 index 空间的文件 + +![alt text](image-13.png) + +● 使用"git add ."命令将文件上传到 index 空间 + +● 再次使用"git status"命令发现从红变绿了,说明文件已提交到 index 空间 + +● 使用"tree"命令查看此时已经有一份文件 + +![alt text](image-14.png) + +2.7 Git Commit + +Git Commit 是将 index 空间的文件提交到 repositary 存储库 + +● 使用"git-commit -m "标注(标注一般是文件名.文件后缀)""命令提交到存储库 + +● 提交后再次使用"git status"命令发现已没有文件可以 commit + +![alt text](image-15.png) + +2.8 Objects + +commit/tree/blob 在 git 里面都统一称为 object,除此之外还有个 tag 的 object + +● blob:存储文件的内容 + +● tree:存储文件的目录信息 + +● commit:存储提交信息,一个 commit 可以对应唯一版本的代码 + +——现在我们要获取刚刚 Commit 到 repositary 区的"readme.md"文件的内容,我们需要这样做: + +方法一:直接通过"cat 文件名.文件后缀"命令获取 + +方法二:一共需要四步 + +● 使用"tree .git"命令获取 Git 目录(注意,没有 commit 到 repositary 区的文件不会出现在目录中) + +![alt text](image-16.png) + +● 每个 commit 都会存储对应的 Tree ID,使用"git cat-file -p CommitID"获取 TreeID + +● 再使用"git cat-file -p TreeID"获取 blobID + +● 再使用"git cat-file -p blobID"获取到文件内容 + +额......,会发现啥也没有 +因为刚刚创建文件后并没有写入内容,所以现在来写 +写文件的方法有三种,第一种是直接通过路径找到文件打开写; +第二种是通过"vim 文件名.文件后缀"的命令来修改; + +输入命令"vim readme.md",打开后是这样的,我们先按"i"键(代表 insert),然后随便写点内容进去。写完之后按一下 Esc 退出键,再输入":wq",即可退出 +第三种是通过"echo "内容" >(>为覆盖,>>为追加)文件名.文件后缀"方式修改(不推荐使用,容易把追加误写为覆盖,什么时候少打了一个 ">" 就真的可以 gg 了) + +以上操作完成之后,还需要把刚刚修改过内容的文件再 commit 到 repositary 区,否则再次执行上面的四步后,会发现没有任何变化 +文件 commit 之后,blobID 也会改变,因此需要重新查找,不可以直接使用先前的 blobID,也就是四个步骤要重新来一遍 + +2.9 Refs + +![alt text](image-17.png) + +目录部分的 refs 的内容就是对应的 commit ID,因此把 ref 当作指针,指向对应的 commit 来表示当前 ref 对应的版本。refs/heads 前缀表示的是分支,除此之外还有其他种类的分支,比如 refs/tags 前缀表示的是标签 + +——输入"cat .git/refs/heads/master"获取最新的 commit ID + +——Branch(分支) + +![alt text](image-18.png) + +使用"git checkout -b test"可以创建 test 分支。分支一般用于开发阶段,是可以不断添加 Commit 进行迭代的。 + +——Tag(标签) + +标签一般表示的是有一个稳定版本,指向的 commit 一般不会变更,通过"git tag bbb"命令生成 bbb 标签 + +![alt text](image-19.png) + +这里创建了一些分支和标签 + +——使用"git branch"命令获取当前已创建的所有分支,带\*号标绿的为当前所处分支 + +——使用"git checkout 分支名"命令切换分支 + +![alt text](image-20.png) + +2.10 Annotation Tag(附注标签) + +——附注标签是一类特殊的标签,可以给标签提供一些额外信息 + +● 可以通过"git tag -a 标签名 -m "附注内容""命令来创建附注标签 + +![alt text](image-21.png) + +标签是 bbc,标签内容是 bbc0.2.0 + +● 获取标签的内容,里面包含标签内容的 ID,获取 ID 对应的内容,里面包含当前版本的 commit ID 和标签的附注内容 + +![alt text](image-22.png) + +● 如果不是附注标签,只是普通的标签,则只会显示当前版本的 commit ID + +2.11 追溯版本历史 + +● 为了有前后对照,先用命令修改文件内容 + +● git add 和 git commit + +● 再获取当前版本代码(当前版本的 commit ID) + +● commit 里面会有 parent commit 字段,通过 commit 的串联获取历史版本代码,一直获取 parent commit 可以追溯更久远的代码版本 + +完整代码如下,这里获取了两次历史代码 + +![alt text](image-23.png) + +2.12 修改版本历史 + +——修改 commit message + +● 可以通过"git commit --amend"命令来修改最近一次 commit 的 commit message + +这里将"我又更新啦"替换为"哈哈哈我又更新啦" + +![alt text](image-24.png) + +![alt text](image-25.png) + +修改完毕后,再用老方法获取 commit message,发现不仅 commit message 已更改,commit ID 也更改了 + +——对多个 commit 修改 commit message + +![alt text](image-26.png) + +可以使用"git rebase -i HEAD~3"命令对最近的三个 commit 进行修改,这里给每个 commit message 加五个感叹号! + +![alt text](image-27.png) + +![alt text](image-28.png) + +2.13 Git GC + +在进行 git 指令操作的过程中,会产生很多没有用的垃圾 object,叫做悬空的 object(即没有 ref 指向的 object) + +● 通过"git fsck --lost-found"命令可以发现所有悬空的 object + +![alt text](image-29.png) + +● 通过"git gc"命令可以将悬空的 object 全部回收 + +![alt text](image-30.png) + +回收之后的"tree.git"就变成了这样:(发现 objects 里的东西都被打包了) + +![alt text](image-31.png) + +2.14 Git reset(误删回滚,针对 git gc 误删了某些 commit) + +可以通过 git reset 回滚 commit 版本,每次 reset 的记录都会被记录到 reflog 中 +reflog 是用于记录操作日志,防止误操作后数据丢失的,通过 reflog 可以找到丢失的数据,手动将日志设置为过期 + +● 首先使用"git log --oneline"获取 Git 的历史记录,可以查看每一次的 commit 记录 + +![alt text](image-32.png) + +● 再使用"git reset --hard xxxxxxx"命令来将代码回滚到"xxxxxxx"版本 + +![alt text](image-33.png) + +● 可以使用"git reflog"查看之前做过的 reset 命令 + +![alt text](image-34.png) + +2.15 Git Clone & Pull & Fetch + +● Clone:拉取完整的仓库到本地目录,可以指定分支和深度 + +● Fetch:将远端某些分支的最新代码拉取到本地,不会执行 merge 操作,会修改 refs/remote 内的分支信息,如果需要和本地代码合并需要手动操作 + +● Pull:拉取远端某分支,并和本地代码进行合并,操作等同于"git fetch + git merge",也可以通过"git pull --rebase"完成"git fetch + git rebase"操作 + +● 前两个是从远端拉取到存储库,而 Pull 是从远端直接拉取到工作区 + +最常用的是 clone,然而在 git clone 时会发现一堆问题,这里将几个常见的问题汇总 +随便在 github 上找一个代码库拿去 git clone + +1. fatal: unable to look up github.com (port 9418) () + +![alt text](image-35.png) + +解决方案:将 https 改为 git 即可(或者把本文 2.4 的 insteadOf 改一下) + +2. Could not resolve host: gihub.com(无法 ping 通 github.com) + +解决方案:修改 hosts 文件中的域名配置 + +![alt text](image-36.png) + +操作步骤: + +● 用管理员模式打开记事本 + +● 打开 hosts 文件(路径为 C:\Windows\System32\drivers\etc) + +● 在图示位置添加"140.82.114.4 github.com",切记,不能加"#" + +● 保存即可,打开 gcmd 后发现可以 ping 通 github.com 了 + +3. OpenSSL SSL_read: Connection was aborted, errno 10053(在 github 上没有配置 SSH 公钥) + +![alt text](image-37.png) + +解决方案:获取 SSH 公钥并在 github 上进行配置 + +操作步骤: + +● 在 Git 中输入图示命令获取 SSH 公钥(前提是完成本文 2.5 的 SSH 配置) + +● 打开 github,点击头像,进入 Setting,点击"SSH and GPG keys",再点击"New SSH key",将 SSH 公钥复制进去,保存即可 + +![alt text](image-38.png) + +以上三种错误最为常见,一般依次处理完后,"git clone"就没啥问题了 + +2.16 Git Push + +Git Push 是将本地代码同步到远端 + +——把写的代码上传到 github 上该如何操作? + +1. 首先登录 Github,找到这个界面 + +![alt text](image-39.png) + +2. 点击右上角那个绿色,New 一个 Repositary + +![alt text](image-40.png) + +3. New 完之后会出现下面这个界面(教操作的) + +![alt text](image-41.png) + +![alt text](image-42.png) + +大概就是教我们怎么在 Git 上进行代码的 push 操作,具体的看文档跟着操作就可以了。画红框的部分是,是刚刚在 github 上创建的 repositary 的 URL + +这里随便操作演示一下: + +上 github 查询一下,就会发现代码已经 Push 上来了~~~ + +以上关于 Git 的基本操作就介绍完了,剩下的其实也不太常用,遇到了查文档就行~~~ diff --git "a/HTTP\346\241\206\346\236\266/image-1.png" "b/HTTP\346\241\206\346\236\266/image-1.png" new file mode 100644 index 0000000..535030b Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-1.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image-2.png" "b/HTTP\346\241\206\346\236\266/image-2.png" new file mode 100644 index 0000000..b415fec Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-2.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image-3.png" "b/HTTP\346\241\206\346\236\266/image-3.png" new file mode 100644 index 0000000..0f001c8 Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-3.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image-4.png" "b/HTTP\346\241\206\346\236\266/image-4.png" new file mode 100644 index 0000000..5e3de83 Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-4.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image-5.png" "b/HTTP\346\241\206\346\236\266/image-5.png" new file mode 100644 index 0000000..d342b47 Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-5.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image-6.png" "b/HTTP\346\241\206\346\236\266/image-6.png" new file mode 100644 index 0000000..4e4c532 Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-6.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image-7.png" "b/HTTP\346\241\206\346\236\266/image-7.png" new file mode 100644 index 0000000..89de6eb Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-7.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image-8.png" "b/HTTP\346\241\206\346\236\266/image-8.png" new file mode 100644 index 0000000..a2462c5 Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-8.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image-9.png" "b/HTTP\346\241\206\346\236\266/image-9.png" new file mode 100644 index 0000000..bdf604e Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image-9.png" differ diff --git "a/HTTP\346\241\206\346\236\266/image.png" "b/HTTP\346\241\206\346\236\266/image.png" new file mode 100644 index 0000000..cbe4e1f Binary files /dev/null and "b/HTTP\346\241\206\346\236\266/image.png" differ diff --git "a/HTTP\346\241\206\346\236\266/readme.md" "b/HTTP\346\241\206\346\236\266/readme.md" new file mode 100644 index 0000000..dea2e08 --- /dev/null +++ "b/HTTP\346\241\206\346\236\266/readme.md" @@ -0,0 +1,208 @@ +一:相关基础概念解释 + +1.1:什么是 HTTP? + +HTTP 是超文本传输协议,拆开来看,就是超文本+传输+协议。归根到底 Http 就是一个应用层的协议,只不过是用来传输的,传输的内容是超文本。 + +——什么是超文本? + +一开始,互联网并不叫互联网,因为它不能将两台服务器串联起来,数据无法在两台服务器之间传输,输入的信息只能保存在本地。保存的信息通常以“文本”这种简单的形式存在(文本是一种能够被计算机解析的二进制数据包文件)。后来随着互联网的发展,两台服务器之间能够进行数据传输后,人们也不再满足于只能写入文字和传输文字。于是超文本诞生了,超文本包括图片、视频、跳转链接等。 + +所以,HTTP 经典的解释是:HTTP 是一份在计算机网络中专门在两台服务器之间传输文字、图片、视频、音频等超文本数据的规范协议。 + +1.2:引入——计算机网络模型 + +![alt text](image.png) + +1.3:HTTP 相关协议 + +● IP 协议、TCP 协议、DNS 服务 + +关于上述三个协议的具体解释以及与 HTTP 协议的关系详见: +http://t.csdn.cn/RH7ws + +以下是上述文章内容的部分摘取 + +● IP 协议 +IP 网际协议位于网络层,几乎所有使用网络的系统都会用到 IP 协议。TCP/IP 协议族中的 IP 指的就是网际协议,"IP"和"IP 地址"是不同的,"IP"其实是一种协议的名称。 +IP 协议的作用是把各种数据包传送给对方,而要保证确实传送到对方那里,则需要两个重要的条件, IP 地址和 MAC 地址。IP 地址指明了节点被分配到的地址,MAC 地址是指网卡所属的固定地址。IP 地址可以和 MAC 地址进行配对。IP 地址可变换,但 MAC 地址基本上不会更改。 +ip 间的通信依赖于 mac 地址,在信息传输中,有时候由于中转太多,因此会采用 ARP 协议(解析地址的协议),通过 ip 地址就可以反查出对应的 mac 地址。 + +● TCP 协议 +TCP 位于传输层,提供可靠的字节流服务。TCP 协议为了更容易传送大数据,把大块数据分割成以报文段为单位的数据包进行管理,并且能够确认数据最终是否送达到对方。 +为了保证传输的可靠性,TCP 协议采用了三次握手的策略。TCP 协议把数据包发送出去后,会向对方确认是否成功送达,握手过程中使用了 TCP 的标志——SYN 和 ACK。发送端首先发送一个带 SYN 标志的数据包给对方。接收端收到后,回传一个带有 SYN/ACK 标志的数据包以示传达确认信息。最后,发送端再回传一个带 ACK 标志的数据包,代表“握手”结束,这就是三次握手。若在握手过程中某个阶段莫名中断,TCP 协议会再次以相同的顺序发送相同的数据包。 + +● DNS 服务 +DNS 服务是和 HTTP 协议一样位于应用层的协议。它提供域名到 IP 地址之间的解析服务。 +计算机可以被赋予 IP 地址,也可以被赋予主机名或域名。由于主机名或域名比 IP 更容易记忆,用户通常使用主机名或者域名来访问对方的计算机,而不是直接使用 IP 地址访问。DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务,从而实现通过域名或主机名访问的效果。 + +二:HTTP 请求 + +2.1:HTTP 协议的请求报文 + +先上两张图,在www.baidu.com中搜索“233”的请求和响应报文如下 + +![alt text](image-1.png) + +![alt text](image-2.png) + +Http 协议主要由三大部分组成: + +● 请求行/状态行(Header)——描述 request/response 的基本信息 + +● 请求头/响应头(Header)——使用 key-value 的形式更详细地说明报文 + +● 请求体/响应体(Body)——实际传输的数据,即消息正文,不一定是文本,可以是超文本 + +一份 Http 报文中,可以没有 Body,但一定要有 Body +上面两张图中,第一行均为请求行/状态行。在 Request(请求)中,格式为(方法名+URL+协议版本);在 Response 中,格式为(协议版本+状态码+状态码描述)。第二行开始均为请求头/响应头,使用 k-v 对的形式对请求/响应进行描述。 + +2.2:HTTP 常用请求方法汇总 + +1. GET:获取资源 +2. POST:传输实体主体 +3. HEAD:获取报文首部 +4. PUT:传输文件 +5. DELETE:删除文件 +6. CONNECT:要求用隧道协议连接代理 +7. OPTIONS:询问支持的方法 +8. TRACE:追踪路径 + +2.3:HTTP 请求 URL + +![alt text](image-3.png) + +Http 协议使用 URL 定位互联网上的资源,URL 的格式如上图,一共分为六个部分 + +1. 协议:告诉浏览器使用何种协议。对于大多数的 Web 资源,都是使用 Http/Https 协议 +2. 主机:即域名,指示需要向网络中的哪一台主机发起请求。当然也可以使用 IP 地址发起请求(比如向本地 127.0.0.1) +3. 端口:两台主机发起 TCP 连接需要“主机+端口”两个条件,缺一不可。但如果访问的 Web 服务器使用的是 Http 协议的标准端口(80 或 443),则 URL 的端口部分可省略不写 +4. 路径:跟文件路径的写法类似,表示的是请求主机中的文件路径 +5. 查询:提供给 Web 服务器的额外参数,每个 k—v 对中间由&分隔 +6. 片段:定位标识符。如果是 HTML 文档,浏览器会滚动到片段所描述的位置;如果是音频文件,会定位到片段描述的时间 + +2.4:补充——状态码汇总 + +状态码是一个三位数 + +● 1 开头:信息类 + +● 2 开头:表成功 + +200:成功响应 + +204:请求处理成功,但是没有资源可以返回 + +206:对资源的某一部分进行响应,由 Content-Range 指定范围的实体内容 + +● 3 开头:重定向 + +301:永久重定向,表示请求的资源已重新分配 URI,以后应当使用资源现有的 URI 对其进行访问 + +302:临时重定向,表示请求的资源临时分配了新的 URI,用户本次需要使用新的 URI 进行访问 + +303:请求的资源存在着另一个 URI,应使用 GET 方法定向获取资源 + +304:表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但是未满足既定条件 + +307:与 302 含义相同 + +● 4 开头:客户端错误 + +400:请求的报文存在语法错误 + +401:发送的请求需要有通过 HTTP 认证的认证信息 + +403:表明请求被服务器拒绝了 + +404:表明服务器上无法找到该请求对应的资源 + +● 5 开头:服务端错误 + +500:表明服务器在执行请求时发生了错误 + +503:表明服务器暂时处于超负载或正在进行停机维护,暂时无法处理请求 + +2.5:HTTP 整个请求响应过程 + +用户在浏览器输入网址--->DNS 服务器对域名进行映射,找到被访问网址所对应的 IP 地址--->Http 进程在 80 端口发起一个到目标服务器的 TCP 连接--->Http 客户端通过套接字向服务器发送一个 Http 请求(Request)报文--->Http 服务器通过套接字接收 Request 报文,并对其进行解析--->服务器从存储器/磁盘检索对象--->服务器将检索到的对象封装进 Response 报文中,并通过套接字向客户端进行发送--->Http 服务器通知 TCP 断开 TCP 连接(客户端接收到 Response 报文后才会断开)--->Http 客户端把对应资源通过显示器呈现给用户 + +2.6:HTTPS + +Https=Http+SSL,就是 Http 协议之上加了一个 SSL(安全套接字)协议 +Http 一般是明文传输,易被攻击者窃取到重要信息。而 Https 是在 Http 基础之上通过“传输加密和身份认证”保证了传输的安全性。 + +三:HTTP 框架的分层设计 + +3.1:分层设计图 + +![alt text](image-4.png) + +自上而下分别是: +应用层、中间件层、路由层、协议层(编解码层)、网络层 + +3.2:应用层设计 + +——设计规范 + +应用层需要保证“易用性”,因此需要提供一些合理的 API + +1. 可理解性:使用主流的概念,如 ctx.Body()、ctx.GetBody()、避免使用 ctx.BodyA()类似的语句 +2. 简单性:常用的 API 要放在上层,易误用/低频的 API 放下层。比如 ctx.Request.Header.Peek(key)/ctx.GetHeader(key) +3. 可见性:遵循最小暴露原则,不需要暴露的 API 尽量不暴露,可以抽象为接口 +4. 兼容性:尽量避免 break、change,做好版本管理 +5. 冗余性:要避免冗余(不要出现能通过其他 API 得到的 API) + +3.3:中间件层设计 + +——洋葱模型(中间件典型模型) + +![alt text](image-5.png) + +简述:客户端发送请求后,首先要先经过日志中间件的预处理,再经过 Metrics 中间件的预处理。处理完之后再进行真正的业务逻辑。在这之后,还会有一个后处理的过程。执行完业务逻辑后会先进行 Metrics 中间件的后处理,再进行日志中间件的后处理 +(即日志中间件——>Metrics 中间件——>业务逻辑——>Metrics 中间件——>日志中间件) + +——为什么要中间件? + +假设现在需要打印每个请求的 request 和 response +假如没有中间件: +会发现非常繁琐,每写一个业务逻辑都要重新写一遍打印日志的代码 + +![alt text](image-6.png) + +假如有中间件: + +可以只写一遍打印日志的代码 +这里只有一个中间件,但在现实中,其实是存在多个中间件联合运作的。而多个中间件之间由 ctx.Next()进行连接。 +——那么问题来了,下图中出现的 ctx.Next(),它是如何在多个中间件和真正的业务逻辑代码之间运作的? +ctx.Next(),只能在中间件之间使用,它会挂起当前中间件(也就是 ctx.Next()后面的代码先不执行),而先执行后面的中间件,当没有下一个中间件时,就开始执行真正的业务逻辑代码。执行完毕后,再执行每一个中间件 ctx.Next()后面的代码(由洋葱模型知,先挂起的后执行) + +![alt text](image-7.png) + +3.4:路由层设计 + +● 静态路由:/a/b/c 、/a/b/d ....... + +● 参数路由:/a/:id/c(/a/b/c 或/a/d/c 或......) + +● 路由修复:/a/b <——>/a/b/(即如果是只注册了/a/b,但是访问的 URI 是 /a/b/,那可以提供自动重定向到 /a/b 能力;同样,如果只注册了 /a/b/,但是访问的 URI 是 /a/b,那可以提供自动重定向到 /a/b/ 能力) + +● 冲突路由:同时注册 /a/b 和 /:id/b,并设定优先级。比如:当请求 URI 为 /a/b 时,优先匹配静态路由 /a/b + +参数路由的设计方法: + +——map[string]handlers(一个参数对应一个 handers) +——前缀匹配树(如下图) + +![alt text](image-8.png) + +3.5:协议层(编解码层)设计 + +![alt text](image-9.png) + +任何协议,只要实现了上图中的 Server 接口,就可以被注册到框架中来 +包括 http1、http2、QUIC 等协议都是实现了上述的 Server 接口 + +3.6:网络层设计 +有关网络层 IO 的介绍,请详见这篇文章: +http://t.csdn.cn/gDup7 diff --git "a/RPC\346\241\206\346\236\266/image-1.png" "b/RPC\346\241\206\346\236\266/image-1.png" new file mode 100644 index 0000000..d3a9c79 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-1.png" differ diff --git "a/RPC\346\241\206\346\236\266/image-2.png" "b/RPC\346\241\206\346\236\266/image-2.png" new file mode 100644 index 0000000..12b5091 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-2.png" differ diff --git "a/RPC\346\241\206\346\236\266/image-3.png" "b/RPC\346\241\206\346\236\266/image-3.png" new file mode 100644 index 0000000..c8d9857 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-3.png" differ diff --git "a/RPC\346\241\206\346\236\266/image-4.png" "b/RPC\346\241\206\346\236\266/image-4.png" new file mode 100644 index 0000000..85de230 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-4.png" differ diff --git "a/RPC\346\241\206\346\236\266/image-5.png" "b/RPC\346\241\206\346\236\266/image-5.png" new file mode 100644 index 0000000..f54ee99 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-5.png" differ diff --git "a/RPC\346\241\206\346\236\266/image-6.png" "b/RPC\346\241\206\346\236\266/image-6.png" new file mode 100644 index 0000000..68b7d1d Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-6.png" differ diff --git "a/RPC\346\241\206\346\236\266/image-7.png" "b/RPC\346\241\206\346\236\266/image-7.png" new file mode 100644 index 0000000..1308708 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-7.png" differ diff --git "a/RPC\346\241\206\346\236\266/image-8.png" "b/RPC\346\241\206\346\236\266/image-8.png" new file mode 100644 index 0000000..015b034 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-8.png" differ diff --git "a/RPC\346\241\206\346\236\266/image-9.png" "b/RPC\346\241\206\346\236\266/image-9.png" new file mode 100644 index 0000000..6b98ff2 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image-9.png" differ diff --git "a/RPC\346\241\206\346\236\266/image.png" "b/RPC\346\241\206\346\236\266/image.png" new file mode 100644 index 0000000..f3bb493 Binary files /dev/null and "b/RPC\346\241\206\346\236\266/image.png" differ diff --git "a/RPC\346\241\206\346\236\266/readme.md" "b/RPC\346\241\206\346\236\266/readme.md" new file mode 100644 index 0000000..0e18052 --- /dev/null +++ "b/RPC\346\241\206\346\236\266/readme.md" @@ -0,0 +1,210 @@ +一:RPC 框架简介 + +1.1—为什么? + +在了解什么是 RPC 框架之前,先来看下面这段代码 + +![alt text](image.png) + +这一段代码表示的是本地调用函数的过程。在业务实现中,常常把整个业务中多次出现的业务逻辑从主函数(不一定是主函数,也可以是其他函数)中抽离出来,单独写一个函数或包来给其他函数进行调用。以此来让整个业务逻辑更加清晰,在后续的代码优化中,也更加方便,也让代码有更强大的扩展性。 + +而 RPC(Remote Procedure Call),即远程服务调用,目的亦是如此。但为什么要使用 RPC 而不采取本地调用呢?原因很简单,如果一个项目的访问量不大,一台服务器上部署一个应用+数据库就足够了。但如果一个项目市场很大,意味着访问量很大,那么一台服务器就很难满足需求,因为用户量增多,必然带来卡顿。由此就产生了 RPC 服务框架(emmm,其实中间省略了很多过程,真实的演进过程其实没那么快) + +1.2—是什么? + +RPC 是指远程过程调用协议,简单起见,这里只涉及两台服务器。两台服务器 A、B,一个应用部署在 A 服务器上,想要调用 B 服务器上的函数,但由于不在一个内存空间,是不能直接调用的,需要通过网络来表达调用的语义和传输调用的参数。 +RPC 还具有以下几大优点: +● 单一职责,有利于分工协作和运维开发 +● 可扩展性强,资源使用率更优 +● 故障隔离,服务的整体可靠性更高 + +1.3—常用的 RPC 框架 + +1.3.1—单语言支持 + +Dubbo:国内最早开源的 RPC 框架,由阿里巴巴公司开发并于 2011 年末对外开源,仅支持 Java +Spring Cloud:国外 Pivotal 公司 2014 年对外开源的 RPC 框架,仅支持 Java +Tars:腾讯内部使用的 RPC 框架,于 2017 年对外开源,仅支持 C++ + +1.3.2—多语言支持 + +gRPC:Google 于 2015 年对外开源的跨语言 RPC 框架,支持多种语言 +Thrift:最初是由 Facebook 开发的内部系统跨语言的 RPC 框架,2007 年贡献给了 Apache 基金,成为 Apache 开源项目之一,支持多种语言 + +二:RPC 框架的分层设计 + +2.1—重要名词解释释 + +①IDL 文件: +接口定义语言,是跨平台开发的必备文件。IDL 通过一种中立的方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序之间可以互相通信。 +● 常见的 Thrift 和 Protobuf 都是接口定义语言 + +②Client——Server: +Client 是服务的调用端(需求端),Server 是服务的提供端。举两个之前看到的例子: +操作系统——硬件系统,就是一组 Client—Server。操作系统作为 Client 来调用硬件系统(Server)所提供的服务 +应用软件——操作系统,也是一组 Client—Server。 +同样放到这里,两台服务器,A 服务器作为 Client 来调用 B 服务器(Server) + +③TCP +TCP 即传输控制协议(Transmission Control Protocol),是一种面向连接的、可靠的、基于字节流的传输层通讯协议。 + +④Socket +Socket 是应用层和传输层之间的一个抽象层,它把 TCP/IP 层复杂的操作抽象为了几个简单的接口来供应用层调用已实现进程在网络中通信。socket 是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。 + + +2.2—分层设计结构 + +2.2.1—分层设计图解 + +下图是 Apache Thrift 的分层设计结构图 + +![alt text](image-1.png) + +由上图,可以概述 RPC 服务的大致流程: +Client 以本地调用的方式发起服务调用--->Client 持续把请求参数和方法序列化成二进制数据(打包成能进行网络传输的消息体)--->Client 将消息体通过网络发送给 Server--->Server 从 TCP 通道里面接收到消息体--->根据 RPC 协议,Server 方将二进制数据的消息体分割出不同的请求数据,经过反序列化逆向还原出请求对象--->Server 方找到本地对应的参数和方法--->Server 方将执行结果序列化成二进制数据(打包成能进行网络传输的消息体)--->Server 方将消息体回写到 TCP 通道--->Client 方从 TCP 通道获取消息体--->Client 方将消息体进行反序列化得到最终的调用结果 + +2.2.2—编解码层 + +—生成代码: + +![alt text](image-2.png) + +—数据格式: +● 语言特定的格式:许多编程语言都内建了将内存对象编码为字节序列的支持,如 Java 的 Java.io.Serializable + +● 文本格式:具有人类可读性,如 Json、XML、CSV 等 + +● 二进制编码:具有跨语言和高性能的优点,如 Thrift 的 BinaryProtocol 和 Protobuf 等 + +—编解码协议如何选择? +两大方面考虑:通用性和兼容性 + +通用性:分为技术层面和流行程度。技术层面上,要考虑是否支持跨平台,跨语言,如果不支持,通用性大大降低。流行程度上,编解码过程需要多方参与,要尽可能减少学习成本,另外流行度低的协议往往支持的平台和语言也少。 + +兼容性:移动互联网时代,业务需求更新周期很快,但老的系统依然需要维护。如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,而不影响老的服务,将大大提高系统的灵活度 + +2.2.3—协议层 + +—消息切分: +● 特殊结束符: +过于简单,对于一个协议单元必须要全部读入才能进行处理,除此之外必须防止用户传输的数据不能同结束符相同,否则会出现紊乱,因此使用频率低。 + +![alt text](image-3.png) + +● 变长协议: +一般都是自定义协议,由 header 和 payload 组成,由定长加不定长两部分组成,其中定长部分要描述不定长的内容长度,使用比较广泛。 + +![alt text](image-4.png) + +—协议构造 + +![alt text](image-5.png) + +从上到下: + +1. Length(32bits):数据包大小,不包含自身 + +2. Header Magic(16bits):标识版本信息,协议解析时快速校验,如 0x1000 + +3. Flags(16bits):预留字段,可以不被使用,默认值为 0x0000 + +4. Sequence Number(32bits):表示数据包的 seqID,可用于多路复用,最好确保单个连接中 ID 递增 + +5. Header Size(16bits):等于头部长度字节数/4,(头部长度从第 14 个字节开始计算,一直到 Payload 部分前) + +6. Protocol ID(uint8 编码):表示编解码的方式,有 Binary 和 Compact 两种 + +7. Num Transforms(uint8 编码):表示需要 Transform 的个数 + +8. Transform ID (uint8 编码):表示压缩的方式,如 zilb 和 snappy + +9. Info ID(uint8 编码):用于传递一些定制的 Meta (元)信息 + +10. Payload:消息内容 + +—协议解析 + +![alt text](image-6.png) + +2.2.4—网络通信层 +—socket API 建立网络通讯 + +![alt text](image-7.png) + +—接口详解 + +● socket():创建 socket + +● bind():绑定 socket 到本地地址和端口,通常由 Server 端调用 + +● listen():TCP 专用,开启监听模式 + +● accept():TCP 专用,服务器等待客户端连接,一般是阻塞状态 + +● connect():TCP 专用,客户端主动连接服务器 + +● send():TCP 专用,用于发送数据 + +● recv():TCP 专用,用于接收数据 + +● sendto():UDP 专用,发送数据到指定的 IP 地址和端口 + +● recvfrom():UDP 专用,接收数据,返回数据远端的 IP 地址和端口 + +● close():关闭 socket + +● read():读取数据,socket 默认是阻塞模式的,如果对方没有写数据,read 会一直处于阻塞状态 + +● write():写数据,socket 默认是阻塞模式的,如果对方没有写数据,write 会一直处于阻塞状态 + + +—网络库 +通常来说,主流操作系统都有网络支持,并且提供网络 API,但这些网络 API 非常底层,并不是很好用。而引入网络库可以: +● 提供易用的 API:封装底层 Socket API,实现连接管理和事件分发 +● 更强大的功能:支持 TCP、UDP、UDS 多协议;并能优雅退出、进行异常处理等 +● 更强大的性能:使应用层 buffer 减少 copy,提供高性能定时器、对象池等 + +三:RPC 框架的重要指标 + +3.1—稳定性 + +3.1.1—保障策略 + +● 熔断:一个服务 A 调用服务 B 时,服务 B 的业务逻辑又调用服务 C,若此时服务 C 响应超时,由于服务 B 依赖服务 C,C 超时直接导致 B 的业务一直等待,而这个时候服务 A 继续频繁地调用服务 B,B 就可能因为堆积大量的请求而导致服务宕机。因此需要引入熔断机制,保护调用方,防止被调用的服务出现问题而影响整条链路。 +● 限流:当调用端发送请求过来时,服务端在执行业务逻辑之前应当先检查限流逻辑。如果发现访问量过大,就让服务端直接讲解处理或返回给调用端一个限流异常。 +● 超时:当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,避免造成资源浪费 + +3.1.2—请求成功率 + +好的 RPC 服务框架,应该能限制单点重试和限制链路重试 +因为重试有放大故障的风险,重试会加大下游业务的负载。重试越多,下游服务负载越高,就越容易导致调用不成功,这是一个正反馈过程。 + +3.1.3—长尾请求 + +![alt text](image-8.png) + +![alt text](image-9.png) + +右图设计了一个优化的请求过程,是基于长尾请求设计的。 +长尾请求是指明显高于均值的那部分占比比较小的请求。右图预先设定了一个 t3,保证大部分请求在这个时间间隔内可以返回请求。而不能在 t3 内返回请求的(一般是产生了问题),就会再发送一个请求(即重试)。这样,两次请求只要有一个返回,就会结束掉这段业务代码。这种机制可以大大减少整体延时。 + +3.2—易用性 + +● 生成服务代码脚手架 +● 支持 protobuf 和 thrift +● 内置功能丰富的选项 +● 支持自定义的生成代码插件 + +3.3—扩展性 + +通过中间件、Option(可选参数)、编解码层的数据格式的可扩展性、协议层的变长协议、网络传输层优化、代码生成工具插件等方式来实现高扩展性 + +3.4—观测性 + +传统上会采用 Log、Metric、Tracing 三件套实现请求的观测性。但可能还不够,有些框架的自身状态需要暴露出来,比如当前的环境变量、配置、Client/Server 初始化参数、缓存信息等 + +3.5—高性能 + +高性能=高吞吐+低延迟 +手段:连接池、多路复用、高性能编解码协议、高性能网络库 + diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-1.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-1.png" new file mode 100644 index 0000000..997283b Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-1.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-2.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-2.png" new file mode 100644 index 0000000..91c752c Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-2.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-3.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-3.png" new file mode 100644 index 0000000..5064f34 Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-3.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-4.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-4.png" new file mode 100644 index 0000000..821f9b0 Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-4.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-5.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-5.png" new file mode 100644 index 0000000..8849b1a Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-5.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-6.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-6.png" new file mode 100644 index 0000000..fa5df19 Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-6.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-7.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-7.png" new file mode 100644 index 0000000..f6f3a4d Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-7.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-8.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-8.png" new file mode 100644 index 0000000..417e486 Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-8.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-9.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-9.png" new file mode 100644 index 0000000..3e37d5a Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image-9.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image.png" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image.png" new file mode 100644 index 0000000..0728dfd Binary files /dev/null and "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/image.png" differ diff --git "a/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/readme.md" "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/readme.md" new file mode 100644 index 0000000..d49c17b --- /dev/null +++ "b/\345\255\230\345\202\250\344\270\216\346\225\260\346\215\256\345\272\223/readme.md" @@ -0,0 +1,260 @@ +一:存储系统 + +1.1 存储系统基础 + +What——存储系统是一个提供了读写、控制类接口,能够安全有效地把数据持久化的软件 + +How——① 作为后端软件的底座,性能敏感;② 存储系统软件架构,容易受硬件影响;③ 存储系统的代码,既简单又复杂 + +1.2 数据如何从应用到存储介质? + +![alt text](image.png) + +数据的缓存很重要,几乎贯穿整个存储体系 + +而在存储中要尽量少使用昂贵的拷贝操作 + +存储的硬件设备五花八门,需要有抽象统一的接入层 + +1.3 存储技术:RAID 磁盘阵列(独立冗余磁盘阵列) + +——技术背景: + +● 性能:单块磁盘写入性能 < 多块磁盘的并发写入性能 + +● 成本:单块大容量磁盘价格 < 多块小容量磁盘 + +● 可靠性 :单块磁盘的容错能力有限,不够安全 + +单机存储系统如何做到高性能/高性价比/高可靠性? + +——RAID: + +● 把多块独立的物理硬盘按照不同的方式组合起来形成一个硬盘组(逻辑硬盘)。从而提供比单个硬盘更高的存储性能和提供数据备份技术。 + +——RAID 0 // RAID 1 // RAID 1+0 + +● RAID0(条带化存储)——提性能 + +连续以位或字节为单位分割数据,并行读/写于多个磁盘上,因此具有很高的传输效率。但 RAID0 只是单纯地提高性能,并没有为数据的可靠性提供保证,而且其中一个磁盘失效将影响到所有的数据。因此无法应用到对数据安全性很高的场合 + +● RAID1(镜像存储)——提可靠性 + +通过磁盘数据镜像实现数据冗余,在成对的独立磁盘上产生互为备份的数据,因此写入效率较低。当原始的数据繁忙时,可直接从镜像拷贝中读取数据,因此 RAID1 可以提高读取性能。RAID1 是所有磁盘阵列中单位成本最高的。但提供了很高的数据安全性和可用性。当一个磁盘失效时,系统可以自动切换到镜像磁盘上读写,而不需要重组失效的数据 + +● RAID 1+0(先做镜像,再做条带)——提性能和可靠性 + +将 RAID0 和 RAID1 结合,既满足了存储的可靠性,也大大提高了性能 + +——更多对 RAID 技术的介绍,详见这篇文章:http://t.csdn.cn/R9QKA + +二:数据库系统 + +2.1 数据库分类 + +数据库分为两类:关系型数据库和非关系型数据库 + +——关系是啥? + +关系 = 集合 = 任意元素组成的若干有序偶对 + +——关系代数是啥? + +关系代数 = 对关系作运算的抽象查询语言(交、并、补、笛卡尔积等) + +——关系型数据库是啥? + +本质就是存储系统,但是在存储之外,又发展出了其他能力。比如:① 对结构化数据很友好,处理高效;② 支持事务;③ 支持复杂的查询语言(如 SQL) + +——非关系型数据库是啥? + +本质也是存储系统,但是和关系型数据库不同,非关系型数据库一般不要求严格的结构化数据,它也可能会支持事务,甚至可能支持复杂的查询语言。 + +2.2 数据库 VS 经典存储 + +1. 结构化数据管理 + +比如一条用户的注册数据,现在要将其进行存储: + +![alt text](image-1.png) + +如果用经典存储: + +![alt text](image-2.png) + +如果用数据库: + +![alt text](image-3.png) + +一般存储的意义,就是要实现对数据的查询。而数据在实际的应用中,不可能是一成不变的,因此还需要实现增删改。而判断一个存储系统的性能好坏,一个重要标准就是增删改查是否方便。对比经典存储和数据库两种存储方式可发现:经典存储是要求对每个数据进行元信息(比如长度、格式等)和数据本身的存储,这时候对数据的增删改是个极大的麻烦 + +比如我要删除"xiaoming"这个数据,为了不浪费存储空间,就需要对后面的数据进行移位操作,而且删除"xiaoming"这个信息,后面的"helloworld"和"coding"两个数据也要一并删除,逻辑之多处理也不方便,对程序员是一个极大的痛苦 + +而使用数据库则方便得多,因为数据存储的结构是十分清晰的 + +2. 事务能力 + +经典存储很难或根本无法对事务进行处理,但数据库支持。 + +事务具有以下四个特性: + +● A(原子性):事务内的操作要么全做,要么不做 + +● C(一致性):事务执行前后,数据状态要是一致的 + +● I(隔离性):可以隔离多个并发事务,避免互相影响 + +● D(持久性):事务一旦提交成功,要保证持久性 + +3. 复杂查询能力 + +经典存储如果要做很复杂的查询,实现的代码也是相当复杂的,需要不断地对数据进行遍历。而数据库提供了 SQL 语言,对复杂的查询十分友好。 + +![alt text](image-4.png) + +三:主流产品介绍 + +3.1 单机存储 + +单机存储 = 单个计算机节点上的存储软件系统,一般不涉及网络交互 + +1. 本地文件系统 + +● 文件系统的管理单元:文件 + +● 文件系统接口:如 Ext2/3/4,sysfs、rootfs 等 + +● Linux 文件系统的两大数据结构:Index Node & Directory Entry + +Index Node:记录文件的元数据,如 id、大小、权限、磁盘位置等。inode 是一个文件的唯一标识,会被存储到磁盘上。 + +Directory Entry:记录文件名、inode 指针、层级(parent)关系等。dentry 是内存结构,与 inode 关系是 N:1 + +2. key - value 存储 + +——key 相当于是数据/文件的身份证,value 相当于是数据本身 + +● 常用数据结构:LSM-Tree,是一种牺牲读性能,追求写入性能的数据结构 + +![alt text](image-5.png) + +3.2 分布式存储 + +1. 分布式文件系统 + +● HDFS(堪称大数据时代的基石) + +HDFS 的核心特点: + +● 高吞吐量:支持海量数据存储 + +● 高容错性:数据自动保存多个副本,某一副本丢失可以自动恢复 + +● 弱 POSIX 语义:牺牲了一些 POSIX 的需求来补偿性能,有些操作会和传统的文件系统不同 + +● 性价比高:使用普通 x86 服务器 + +![alt text](image-6.png) + +2. 分布式对象存储 + +● Ceph(开源分布式存储系统里的万金油) + +Ceph 的核心特点: + +● 一切皆为对象:一套系统支持对象接口、块接口、文件接口 + +● 数据写入采用主备复制模型 + +● 数据分布模型采用 CRUSH 算法(HASH+权重+随机抽签) + +![alt text](image-7.png) + +3.3 单机关系型数据库 + +单机数据库 = 单个计算机节点上的数据库系统。事务在单机内执行,但也可能通过网络交互实现分布式事务 + +——关系型数据库通用组件 + +● Query Engine —— 负责解析 Query,生成查询计划 + +● Txn Manager——负责事务并发管理 + +● Lock Manager——负责锁相关的策略 + +● Storage Engine——负责组织内存/磁盘数据结构 + +● Replication——负责主备同步 + +——关键内存数据结构:B-Tree、LRU List + +——关键磁盘数据结构:WriteAheadLog(RedoLog)、Page + +——常用的关系型数据库:Oracle、MySQL、SQL Server + +3.4 单机非关系型数据库 + +● 关系型数据库一般直接使用 SQL 交互,而非关系型数据库交互方式则各不相同,且数据结构千奇百怪。但不管是否为关系型数据库,两者都在尝试支持 SQL 和事务 + +——常用的非关系型数据库 + +1. Elasticsearch: + +● 面向文档存储 + +● 文档可序列化为 JSON、并支持嵌套 + +● 存在 index,index = 文档的集合 + +● 存储和构建索引能力依赖 Lucene 引擎 + +● 实现了大量搜索数据结构和算法 + +● 支持 RESTFUL API、也支持弱 SQL 交互 + +● 跟 RDBMS(关系型数据库管理系统)相比,ES 天然能做模糊搜索,还能自动算出关联程度 + +2. MongoDB: + +● 面向文档存储 + +● 文档可序列化为 JSON/BSON,并支持嵌套 + +● 存在 collection、collection = 文档的集合 + +● 存储和构建索引能力依赖 wiredTiger 引擎 + +● 4.0 后开始支持事务(多文档、跨分片多文档等) + +● 常用 client/SDK 交互,可通过插件转译支持弱 SQL + +3. Redis: + +● 数据结构丰富(hash 表、set、zset、list) + +● C 语言实现,性能超高 + +● 主要基于内存,但支持 AOF、RDB 持久化 + +● 常用 redis-cli/多语言 SDK 交互 + +3.5 分布式数据库 + +——单机数据库遇到了哪些问题和挑战,需要我们引入分布式架构来解决? + +1. 容量: + +单机数据库,一个数据库只能对应一个节点下的硬盘,而一个节点所能挂载的硬盘数量是有限的。在数据爆炸的大数据时代,一套业务逻辑所需要存储的数据量很大,一个节点根本无法实现存储。因此引入了分布式的数据库。而目前主流的分布式数据库架构——"池化节点技术"则很好地解决了上述痛点。 +这一技术是通过动态地扩张/紧缩存储节点实现海量数据存储的。当一套业务逻辑所产生的数据量逐渐上升时,会自动地添加存储节点,数据库是不需要感知存储空间是否足够的。 + +2. 弹性: + +![alt text](image-8.png) + +一个 APP 产品,是有生命周期的,业务从逐渐火爆到逐渐被市场淘汰。这一整个过程中,用户的数据量刚开始会逐步上升,因此需要动态对存储数据进行搬迁,数据量少的时候为了提高性价比,通常选用内存和磁盘容量较小的服务器进行数据存储,当数据量大了之后才会换成更大的。而且随着业务的逐渐火爆,对服务器的请求次数也是逐渐上升,因此对于 CPU 的性能会有更高的要求。但是当 APP 进入衰退期后,对 CPU 性能的要求就不是太高了。因此会选用核数较少的 CPU,但一个性能较低的 CPU 通常对应的硬盘和内存空间也比较低,然而即使 APP 进入了衰退期,数据量也是在上升的,因此更换 CPU 的方式不可行。这时候就需要用到上面的"池化节点技术"了,有了池化节点,当业务进入衰退期时,就可以换成以下的架构方式:换成更多台 CPU 核数更低的节点进行存储 + +3. 性价比: + +![alt text](image-9.png) + +业务上,会存在以下这种情况:APP 数据非常多,但是对服务器的请求次数少(即日活低但是数据量高),这种情况对 CPU 是极大的浪费,可能一个节点的内存和硬盘用光了,CPU 的利用率还不到 20%。这时候随着数据量的再度增大,就需要更多的节点进行数据存储,然而 CPU 的问题不但没有解决,反而更严重了。因此,就需要进行架构上的优化以提升 CPU 使用率。解决方法其实跟上面同理,还是使用池化技术,换成更多 CPU 核数低的节点来提升性价比。 diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-1.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-1.png" new file mode 100644 index 0000000..b083199 Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-1.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-10.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-10.png" new file mode 100644 index 0000000..8249e66 Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-10.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-11.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-11.png" new file mode 100644 index 0000000..7b48eab Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-11.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-2.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-2.png" new file mode 100644 index 0000000..abc4a06 Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-2.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-3.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-3.png" new file mode 100644 index 0000000..606d710 Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-3.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-4.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-4.png" new file mode 100644 index 0000000..395797e Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-4.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-5.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-5.png" new file mode 100644 index 0000000..0d12127 Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-5.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-6.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-6.png" new file mode 100644 index 0000000..2871146 Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-6.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-7.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-7.png" new file mode 100644 index 0000000..1453f18 Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-7.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-8.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-8.png" new file mode 100644 index 0000000..0efc70c Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-8.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image-9.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-9.png" new file mode 100644 index 0000000..93de350 Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image-9.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/image.png" "b/\346\266\210\346\201\257\351\230\237\345\210\227/image.png" new file mode 100644 index 0000000..3a9c8fc Binary files /dev/null and "b/\346\266\210\346\201\257\351\230\237\345\210\227/image.png" differ diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/readme.md" "b/\346\266\210\346\201\257\351\230\237\345\210\227/readme.md" new file mode 100644 index 0000000..c0e097e --- /dev/null +++ "b/\346\266\210\346\201\257\351\230\237\345\210\227/readme.md" @@ -0,0 +1,159 @@ +一:消息队列介绍? + +1.1 消息队列使用场景 + +当系统中出现"数据生产"与"数据消费"的速度不一致;或者"数据生产"与"数据消费"两个环节的性能存在差异;或者在从数据生产到消费过程中遭遇到系统崩溃、链路耗时长尾、服务日志丢失等问题,就需要用到消息队列。 + +这样说比较抽象,举个在现实中存在的例子: + +● 小明今天 10 个达布溜工资到账,打算购买 Apple Watch 提升生活档次(用来装逼),这时候他在某搜索页面发现某个之前没见过的电商平台推出了限时秒杀活动,Apple Watch 只需要官方价格的十分之一。激动的小明立马下载了 APP,并下了订单。但是由于此款新 APP 秒杀活动过于火爆并且设计过于垃圾,面对大量的数据请求时系统处理非常缓慢,以致于付了钱之后半天没有弹出下单成功的消息,这让小明怀疑自己是不是误下了一个杀猪 APP。经过了 180s 的苦等,下单成功的消息终于弹出来了。小明觉得消费体验十分差劲,发誓等 Apple Watch 送到家之后就把 APP 删了。 + +● 这怎么行?小明还没被这款 APP 宰,薅了个羊毛就跑了?APP 的老板十分生气,并把他们基础架构团队的张三直接炒鱿鱼了。 + +● 在以上的案例中,小明下单 Apple Watch,存在以下几个业务逻辑(不全):发起订单、库存记录-1、订单记录+1、通知商家、消息反馈用户。小明等了 180s,可能是上面的任何一个或多个服务挂了。这里我们就假设"通知商家"这一服务挂掉了,张三处理了 180s 这项服务才重启。 + +● 这个架构肯定是有问题的,面对高量请求时就会服务瘫痪。而这种业务环境下状态是不能回滚的,毕竟小明付了钱,被举报了是有概率上 CCTV315 晚会的。 + +● 临危受命的李四接手了张三的工作,发现张三这小子居然把上述的五项服务设计成线型的,也就是说上一服务不完成就没办法进行下一服务,只要中间有一项服务宕机,后面的所有服务都会中断。 + +● 重点来了,李四重构时,引入了消息队列。直接将发起订单且付钱成功之后所将要处理的服务,通过消息的形式告诉消息队列,只要告诉完了,就可以直接执行"消息反馈服务"这项服务了。对于"库存-1"、"订单+1"和"通知商家"三项服务,后面只需慢慢消费消息队列中的消息就行了(当然怎么可能慢慢消费呢?库存没了要及时通知用户的,不然付了钱再退款可太麻烦了,因此要么把"库存-1"这项服务设计成不用消息队列的形式;要么保证其在消费消息队列信息的过程中能够保持高性能) + +● 简单来说就是降低用户下单后看到反馈信息的时间,也是为了防止出事故而导致用户迟迟看不到反馈信息。用户至上,用户的问题先解决,APP 后面才能开始宰人(bushi)。如果中间的"通知商家"服务宕机了,由于存在消息队列,数据被保存在消息队列中,后面只要"通知商家"服务重启,就可以直接消费消息队列中的数据信息,整个服务流程照样完整。额...无非是商家发货慢了点。 + +1.2 消息队列的优点 + +1. 提高系统响应速度(这个很明显,1.1 提到的例子就是) + +——小明下单付钱成功后,把数据消息直接扔进消息队列里,无需等待后续对数据的其他处理,直接把响应结果返回给小明。 + +2. 提高系统稳定性(这个也很明显,同样也是 1.1 的例子) + +——小明下单后响应直接返回,不用怕后面的服务宕机,使用消息队列等服务重启后从消息队列中拿出数据消费就行了 + +3. 异步化、解耦、消除峰值(还是用 1.1 的例子 emmm) + +——异步化、解耦、消除峰值可以一起讲,就是把总的业务逻辑拆分。像张三设计的架构是总线型,也就是所有逻辑全是主逻辑,一步出问题就全部出问题。使用消息队列对整个服务进行解耦,拆分为多个副逻辑,实现服务的异步化。由于有多个副逻辑对主逻辑进行分担,于是就可以降低主逻辑的流量峰值,保证主逻辑的运行速度,降低主逻辑崩溃概率(因为很多跟 C 端用户相关的业务逻辑都是在主逻辑层实现的) + +二:案例——Kafka 实践 + +没错,首先要进入无聊的概念解释 + +2.1 kafka 架构 + +● Topic(逻辑队列): + +每一个不同的业务场景就对应一个 Topic,跟这项业务有关的所有数据都存储在这个 Topic 中 + +● Cluster(物理集群): + +每个集群中包含多个 Topic,即多个业务逻辑的组合 + +● Producer(生产者): + +消息的生产端,负责将业务消息发送到对应的 Topic + +● Consumer(消费者): + +消息的消费端,负责将对应 Topic 中的消息取出消费 + +● ConsumerGroup(消费者组): + +多个消费者组成的集合,不同的消费者组要保证互不干涉 + +● Partition(消息分片): + +在 Topic 中存在多个 Partition,其实就是将消息拆分成多组进行存储,提高消息写入到 Topic 中的速度,以及加快被消费者读取的速度 + +![alt text](image.png) + +上图中:存在两个生产者,将消息写入到 Topic 中,由于 Topic 中存在两个 Partition(分片),写入效率提升了一倍。存在两个消费者组,且都可以读取 Topic1 中的消息,而消费者组 1 存在两个消费者,所以读取速率是消费者组 2 的两倍。两个消费者组设计时实现了互不干涉,即不可能存在一条消息同时被两个消费者组读取到的情况 + +——接下来深入拆解 partition 分片 + +![alt text](image-1.png) + +● Offset: + +每一个分片中储存着多条消息,每条消息都有着一个唯一的 Offset,可以理解为消息的 ID,且这串 ID 在分片内部是递增的 + +● Replica: + +可以理解为分片的副本,一般来说,数据要有备份,否则如果出现某台服务器的消息队列崩溃了,数据就不存在了。因此要有很好的容灾性,就必须将消息队列里的消息做多个副本。Replica 分为 Leader 和 Follower,Follower 负责同步 Leader 队列的数据。当 Leader 出故障之后,某个 Follower 就晋升为 Leader + +● ISR: + +一个分片中存在一个 ISR,与 Leader 信息大致同步的 Follower 就会存放在 ISR 中,如果 Follower 与 Leader 差距较大,就会被踢出 ISR。上述提到的 Leader 故障了,选择 Follower 晋升为新 Leader 也不是乱选的,它一定是 ISR 中最优的那一个。ISR 中的 Replica 排名,以前是根据与 Leader 的 Offset 差距来排的,现在变成了与 Leader 的时间差距 + +——谁来负责分配 Leader 和 Follower?(Broker) + +![alt text](image-2.png) + +上面这幅图代表着 Kafka 中副本的分布图。途中的 Broker 代表每一个 Kafka 节点,所有的 Broker 节点最终组成了一个集群。整个图表示:整个集群(Cluster)包含了四个 Broker 机器节点,集群中有两个 Topic,分别是 Topic1 和 Topic2,Topic1 中存在两个分片,Topic2 中只有一个分片。每个分片都是三副本状态。中间有一个 Broker 同时充当 Controller 的角色,是整个集群的大脑,负责对副本和其他 Broker 进行分配 + +——元数据存储(Zookeeper) + +在集群的基础上,还有一个模块是 Zookeeper,负责储存集群的元数据信息,比如副本的分配信息和 Controller 计算好的方案 + +——最后上一张整体架构图 + +![alt text](image-3.png) + +2.2 kafka 对业务如何做优化处理? + +——Producer 方面的优化 + +1. 批量发送 + +![alt text](image-4.png) + +上图就理解了,批量发送可以减少 IO 次数,加强发送能力 + +2. 数据压缩 + +![alt text](image-5.png) + +还可以对数据进行压缩,通过减少消息大小的方式提高消息存入 Broker 中的速度 + +——Broker 方面的优化 + +1. 如何存储?——顺序写入 + +![alt text](image-6.png) + +消息的存储是要存进磁盘里的,消息写入磁盘的过程中,需要通过移动磁头来找到对应磁道,磁盘转动找到对应扇区,最后写入。寻找磁道的成本是比较高的,因此采用顺序写入(新消息直接在 Partition 末尾追加),以减少时间成本 + +2. 如何迅速找消息返回给 Consumer? + +Consumer 通过发送 FetchRequest 请求消息数据,Broker 会将指定的 Offset 处的消息,按照时间窗口和消息大小窗口发送给 Consumer,具体寻找消息的细节有以下三种方式 + +比如现在要寻找 Offset = 28 的消息 + +● 偏移量索引文件:(二分找到小于目标 Offset 的最大索引值) + +![alt text](image-7.png) + +● 时间戳索引文件:(二分找到小于目标时间戳最大的索引位置) + +![alt text](image-8.png) + +● 零拷贝: + +传统拷贝要经历五步才能将数据从硬盘读到消费者进程,从硬盘空间到内核空间,再到应用空间,最后又经过内核空间到消费者进程。而零拷贝则直接略去了应用空间,直接从硬盘到内核空间到消费者进程。 + +![alt text](image-9.png) + +——Consumer 方面的优化 + +其实就是如何解决 Partition 在 Consumer Group 中的分配方式? + +● 手动分配(Low Level) + +手动分配的一个好处就是启动比较快,因为对于每一个消费者来说,启动的时候就已经知道了自己应该去消费哪个分片了。就好比对下图中的 Consumer Group 1 来说,Consumer 1 去消费分片 123,Consumer 2 去消费分片 456,Consumer 3 去消费 78。这些 Consumer 再启动时就已经知道分配方案了。但这种分配方式却是危险的,假如 Consumer 3 挂掉了,78 分片就停止消费了。又假如现在新增了一个 Consumer 4 ,就要去停掉整个集群,重新修改配置再上线,以保证 Consumer 4 也可以消费数据。这样特别麻烦。 + +![alt text](image-10.png) + +● 自动分配(High Level) + +所以 Kafka 也提供了自动分配的方式,简单来说就是在 Broker 集群中,对于不同的 Consumer Group 来讲,都会选择一台 Broker 当作 Coordinator,而 Coordinator 作用就是帮助 Consumer Group 进行分片的分配,也叫做分片的 Rebalance。使用这种方式,如果 Consumer Group 中有发生宕机,或者有新的 Consumer 加入,整个分片和 Consumer 都会重新分配来达到一个稳定的消费状态 + +![alt text](image-11.png)