写给初学者的 Git 极简教程

Git 是一个强大的版本控制系统,它允许多个开发者在同一个项目上协作,跟踪文件的变化,并且可以在不同的时间点恢复到之前的状态。对于初学者来说,理解 Git 的基本概念和操作是非常重要的。在这篇博客中,我们将详细介绍 Git 的使用,并通过具体的例子来演示常用的 Git 命令。

Git 的三个位置

在使用 Git 时,我们需要了解三个主要的位置:本地目录、缓存区(有时也称为暂存区)和远程仓库。

1
2
3
4
5
6
7

# 记住 git 的三个位置
本地目录 <--(cache)----> .git <----> remote
add #将代码添加到缓冲区
commit --> #将代码提交到仓库
push -> 推送到远程
pull <- 从远程拉取

本地目录

这是你在计算机上存放项目文件的地方。当你开始一个新项目或者克隆一个远程仓库时,Git 会在项目文件夹中创建一个隐藏的 .git 文件夹。这个文件夹包含了所有的版本控制信息。

缓存区(.git

当你对本地目录中的文件进行修改后,这些改动并不会立即提交到远程仓库。首先,你需要使用 git add 命令将这些修改添加到缓存区。缓存区是一个临时存放改动文件的地方,你可以在这里预览将要提交的改动。

远程仓库

远程仓库是存储你的项目代码的在线平台,例如 GitHub、GitLab 或 Bitbucket。通过 git pushgit pull 命令,你可以将本地的改动推送到远程仓库,或者从远程仓库拉取其他人的改动到本地。

基本操作流程

添加(add)和提交(commit

  1. 添加改动到缓存区

    1
    git add <文件名>

    如果你想添加所有改动的文件到缓存区,可以使用:

    1
    git add .

    这里的点(.)代表了当前目录下的所有改动文件。

  2. 提交改动到本地仓库

    1
    git commit -m "你的提交信息"

    -m 选项后面跟着的是你对这次提交的描述信息,它有助于你和你的团队成员理解这次提交的目的和内容。

推送(push)和拉取(pull

  1. 推送本地提交到远程仓库

    1
    git push 远程仓库名 分支名

    例如,如果你想推送到名为 origin 的远程仓库的 master 分支,你可以使用:

    1
    git push origin master
  2. 从远程仓库拉取最新改动

    1
    git pull 远程仓库名 分支名

    这个命令会将远程仓库的最新改动拉取到你的本地目录,并自动尝试合并。

查看 Git 状态

要查看当前 Git 的状态,包括哪些文件被修改但还没有暂存或提交,可以使用 git status 命令。这个命令会列出所有的改动文件,并标明它们的状态。

理解 origin

origin 是远程仓库的默认别名。每个远程仓库都可以有一个或多个别名。你可以使用 git remote -v 命令来查看远程仓库的地址和它们的别名。

理解分支

分支是 Git 中一个非常重要的概念。它允许你在不同的线路上进行开发,而不会相互干扰。master 通常是默认的主分支,所有的开发工作都是基于这个分支进行的。

创建和使用分支

创建新分支

1
git checkout -b 新分支名

这个命令会创建一个新的分支,并自动切换到这个分支。例如,如果你想创建一个名为 feature-x 的新分支,你可以使用:

1
git checkout -b feature-x

切换分支

1
git checkout 分支名

如果你想从 feature-x 分支切换回 master 分支,你可以使用:

1
git checkout master

合并分支

当你在一个新分支上完成开发后,你可能需要将这些改动合并回 master 分支。首先,确保你已经切换到 master 分支,然后使用 git merge 命令将其他分支合并进来:

1
git merge 要合并的分支名

例如,如果你想将 feature-x 分支的改动合并到 master 分支,你可以使用:

1
git merge feature-x

通过以上步骤,你可以有效地管理你的代码变更,并且与团队成员协作开发项目。希望这篇博客能帮助你更好地理解和使用 Git!

均衡负载里的算法

在均衡负载重,轮询算法是最常用的一个算法。 通常会使用带权重的轮询(wrr).

轮询

轮询的实现非常简单,假设我们有一组节点 [a,b,c,d] ,在不带有权重的时候,只需要一个 next 变量就可以完成。
使用 next 变量记录当前的位置,下一个就是当前位置加一后与节点的长度求余。

1
2
3
4
5
6
7
next := 0 // 当前选择
.....

func rr(){
next = (next+1)%len(servers)
return next
}

带权重的轮询

通常情况下,我们都会选择带权重的轮询来作为均衡算法,带权重有几个好处:

  • 将某个机器的权重调整为 0 来进行上下线的操作
  • 上线新功能或者新机器,可以逐步调大权重,来灰度部分流量来验证功能。

假设我们有这样一组节点 [ a:10 ,b:20,c:30] , 用来表示 3 个节点和其对应的权重。
我们在做计算的时候,需要对所有的权重求出他们的最小公倍数,让他们的表示成[a:1,b:2,c:3] 这样的值。

这里涉及到一个 gcd 的算法,用来求最小公倍数

gcd

gcd 的算法非常简单,叫做辗转求余,代码如下

1
2
3
4
5
6
func gcd(a int, b int) int {
if b == 0 {
return a
}
return gcd(b, a%b)
}

具体可以参看 gcd

wrr

通过 gcd 计算得到 [a:1,b:2,c:3] 后,我们需要按照权重来选择节点。这个时候,可以将前面的节点变成这样的数组 [a,b,b,c,c,c] ,数组中的每个节点都出现的权重指定的次数,这样我们就可以通过前面 rr 里面的算法 next = (next+1)%len(servers) 来进行计算。
但是这里会出现一个问题,a,b,c 的访问在时间片上面是不均衡的,可能会导致某个时间片对某个区间的直接访问。 这就需要对数组里的内容进行打散。
在 nginx 的 wrr 算法: https://github.com/phusion/nginx/commit/27e94984486058d73157038f7950a0a36ecc6e35 保证节点选择的平滑。

算法如下: 对于节点权重 {5,1,1} ,当前一个节点被选中后,权重减去所有权重和,这里是 7 ,当开始选择时,每个节点加上自身的权重。

这里稍微有一些理论,可以想象一下,这些节点在跑道上排成一队,在选择之前,每个人向前走自己的权重步,如果谁在最前面的话,就能够被选中,被选中的人需要向后退权重和的步数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 a  b  c
0 0 0 (initial state)

5 1 1 (a selected) 第一次选择了 a ,那么 a 的权重 - 7
-2 1 1 第一次选择后的结果

对第一次选择结果加 5 1 1 得到 3 2 2
3 2 2 (a selected) 第二次选择 a ,那么 a 的权重 -7 得到 -4 2 2
-4 2 2 如此反复整过过程。

1 3 3 (b selected)
1 -4 3

6 -3 4 (a selected)
-1 -3 4

4 -2 5 (c selected)
4 -2 -2

9 -1 -1 (a selected)
2 -1 -1

7 0 0 (a selected)
0 0 0

这个算法能够很好的使算法能够均衡。

hash

使用 hash 作为均衡负载算法,会应用到很多地方,通常情况下我们会根据请求的一些值来作为均衡的 key。

特别是一致性的 hash 算法,处在网络请求的很多位置都会用到,可能所用到的 hash 算法不同,但是本质都是为了保证同一个数据包能够发送到一个位置。
通常会根据不同的需求,对 源 ip、源端口 、目标 ip、源端口、目标端口、协议号 这五元组来做 hash 算法。 比如以下的场景

  • 数据包到达网口,网卡多队列
  • cpu 支持 DUMA 的时,需要让相同的数据包进来在一个核上处理
  • 四层均衡负载中保证 tcp/udp 是到同一个后端
  • 七层均衡负载中保证同一个客户端到同一个后端处理

meglev

Maglev算法是Google开发的一种网络负载均衡器。在很多新的四层均衡负载中喜欢使用,比如 facebook 的 katran。
maglev 使用的一个大的槽位,通过一致性 hash 来将计算分布到槽位上。
使用时固定了槽位,查询效率为 O(1).

https://github.com/zhangweidev/meglevgo