GO·NOTE

一份 Go 开发工程师的学习笔记

0%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {
array := []int{80, 60, 20, 10, 1}
fmt.Println(array[:4])
fmt.Println(InsertData(array, 2))
}

func InsertData(array []int, num int) []int {
for i := 0; i < len(array); i++ {
if num > array[i] {
arrayTmp := make([]int, len(array))
copy(arrayTmp, array)
array = append(append(arrayTmp[:i], num), array[i:]...)
break
}
if i == len(array)-1 {
array = append(array, num)
}
}
return array
}

1. git config

配置 Git 的相关参数。

Git 一共有3个配置文件:

  1. 仓库级的配置文件:在仓库的 .git/.gitconfig,该配置文件只对所在的仓库有效。
  2. 全局配置文件:Mac 系统在 ~/.gitconfig,Windows 系统在 C:\Users<用户名>.gitconfig。
  3. 系统级的配置文件:在 Git 的安装目录下(Mac 系统下安装目录在 /usr/local/git)的 etc 文件夹中的 gitconfig。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
# 查看配置信息
# --local:仓库级,--global:全局级,--system:系统级
$ git config <--local | --global | --system> -l

# 查看当前生效的配置信息
$ git config -l

# 编辑配置文件
# --local:仓库级,--global:全局级,--system:系统级
$ git config <--local | --global | --system> -e

# 添加配置项
# --local:仓库级,--global:全局级,--system:系统级
$ git config <--local | --global | --system> --add <name> <value>

# 获取配置项
$ git config <--local | --global | --system> --get <name>

# 删除配置项
$ git config <--local | --global | --system> --unset <name>

# 配置提交记录中的用户信息
$ git config --global user.name <用户名>
$ git config --global user.email <邮箱地址>

# 更改Git缓存区的大小
# 如果提交的内容较大,默认缓存较小,提交会失败
# 缓存大小单位:B,例如:524288000(500MB)
$ git config --global http.postBuffer <缓存大小>

# 调用 git status/git diff 命令时以高亮或彩色方式显示改动状态
$ git config --global color.ui true

# 配置可以缓存密码,默认缓存时间15分钟
$ git config --global credential.helper cache

# 配置密码的缓存时间
# 缓存时间单位:秒
$ git config --global credential.helper 'cache --timeout=<缓存时间>'

# 配置长期存储密码
$ git config --global credential.helper store

2. git clone

从远程仓库克隆一个版本库到本地。

1
2
3
4
5
6
7
8
# 默认在当前目录下创建和版本库名相同的文件夹并下载版本到该文件夹下
$ git clone <远程仓库的网址>

# 指定本地仓库的目录
$ git clone <远程仓库的网址> <本地目录>

# -b 指定要克隆的分支,默认是master分支
$ git clone <远程仓库的网址> -b <分支名称> <本地目录>

3. git init

初始化项目所在目录,初始化后会在当前目录下出现一个名为 .git 的目录。

1
2
3

# 初始化本地仓库,在当前目录下生成 .git 文件夹
$ git init

4. git status

查看本地仓库的状态。

1
2
3
4
5
6
7
8

# 查看本地仓库的状态
$ git status

# 以简短模式查看本地仓库的状态
# 会显示两列,第一列是文件的状态,第二列是对应的文件
# 文件状态:A 新增,M 修改,D 删除,?? 未添加到Git中
$ git status -s

5. git remote

操作远程库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 列出已经存在的远程仓库
$ git remote

# 列出远程仓库的详细信息,在别名后面列出URL地址
$ git remote -v
$ git remote --verbose

# 添加远程仓库
$ git remote add <远程仓库的别名> <远程仓库的URL地址>

# 修改远程仓库的别名
$ git remote rename <原远程仓库的别名> <新的别名>

# 删除指定名称的远程仓库
$ git remote remove <远程仓库的别名>

# 修改远程仓库的 URL 地址
$ git remote set-url <远程仓库的别名> <新的远程仓库URL地址>

6. git branch

操作 Git 的分支命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 列出本地的所有分支,当前所在分支以 "*" 标出
$ git branch

# 列出本地的所有分支并显示最后一次提交,当前所在分支以 "*" 标出
$ git branch -v

# 创建新分支,新的分支基于上一次提交建立
$ git branch <分支名>

# 修改分支名称
# 如果不指定原分支名称则为当前所在分支
$ git branch -m [<原分支名称>] <新的分支名称>
# 强制修改分支名称
$ git branch -M [<原分支名称>] <新的分支名称>

# 删除指定的本地分支
$ git branch -d <分支名称>

# 强制删除指定的本地分支
$ git branch -D <分支名称>

7. git checkout

检出命令,用于创建、切换分支等。

1
2
3
4
5
6
7
8
9
10
11
12
13

# 切换到已存在的指定分支
$ git checkout <分支名称>

# 创建并切换到指定的分支,保留所有的提交记录
# 等同于 "git branch""git checkout" 两个命令合并
$ git checkout -b <分支名称>

# 创建并切换到指定的分支,删除所有的提交记录
$ git checkout --orphan <分支名称>

# 替换掉本地的改动,新增的文件和已经添加到暂存区的内容不受影响
$ git checkout <文件路径>

8. git cherry-pick

1
2
3

# 把已经提交的记录合并到当前分支
$ git cherry-pick <commit ID>

9. git add

把要提交的文件的信息添加到暂存区中。当使用 git commit 时,将依据暂存区中的内容来进行文件的提交。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 把指定的文件添加到暂存区中
$ git add <文件路径>

# 添加所有修改、已删除的文件到暂存区中
$ git add -u [<文件路径>]
$ git add --update [<文件路径>]

# 添加所有修改、已删除、新增的文件到暂存区中,省略 <文件路径> 即为当前目录
$ git add -A [<文件路径>]
$ git add --all [<文件路径>]

# 查看所有修改、已删除但没有提交的文件,进入一个子命令系统
$ git add -i [<文件路径>]
$ git add --interactive [<文件路径>]

10. git commit

1
2
3
4
5
6
7
8
9
10
11
12
# 把暂存区中的文件提交到本地仓库,调用文本编辑器输入该次提交的描述信息
$ git commit

# 把暂存区中的文件提交到本地仓库中并添加描述信息
$ git commit -m "<提交的描述信息>"

# 把所有修改、已删除的文件提交到本地仓库中
# 不包括未被版本库跟踪的文件,等同于先调用了 "git add -u"
$ git commit -a -m "<提交的描述信息>"

# 修改上次提交的描述信息
$ git commit --amend

11. get fetch

从远程仓库获取最新的版本到本地的 tmp 分支上。

1
2
3
4
5
6

# 将远程仓库所有分支的最新版本全部取回到本地
$ git fetch <远程仓库的别名>

# 将远程仓库指定分支的最新版本取回到本地
$ git fetch <远程主机名> <分支名>

12. get merge

合并分支。

1
2
3

# 把指定的分支合并到当前所在的分支下
$ git merge <分支名称>

13. git diff

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 比较当前文件和暂存区中文件的差异,显示没有暂存起来的更改
$ git diff

# 比较暂存区中的文件和上次提交时的差异
$ git diff --cached
$ git diff --staged

# 比较当前文件和上次提交时的差异
$ git diff HEAD

# 查看从指定的版本之后改动的内容
$ git diff <commit ID>

# 比较两个分支之间的差异
$ git diff <分支名称> <分支名称>

# 查看两个分支分开后各自的改动内容
$ git diff <分支名称>...<分支名称>

14. git pull

从远程仓库获取最新版本并合并到本地。
首先会执行 git fetch,然后执行 git merge,把获取的分支的 HEAD 合并到当前分支

1
2
# 从远程仓库获取最新版本。
$ git pull

15. git push

把本地仓库的提交推送到远程仓库。

1
2
3
4
5
6
# 把本地仓库的分支推送到远程仓库的指定分支
$ git push <远程仓库的别名> <本地分支名>:<远程分支名>

# 删除指定的远程仓库的分支
$ git push <远程仓库的别名> :<远程分支名>
$ git push <远程仓库的别名> --delete <远程分支名>

16. get reset

还原提交记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 重置暂存区,但文件不受影响
# 相当于将用 "git add" 命令更新到暂存区的内容撤出暂存区,可以指定文件
# 没有指定 commit ID 则默认为当前 HEAD
$ git reset [<文件路径>]
$ git reset --mixed [<文件路径>]

# 将 HEAD 的指向改变,撤销到指定的提交记录,文件未修改
$ git reset <commit ID>
$ git reset --mixed <commit ID>

# 将 HEAD 的指向改变,撤销到指定的提交记录,文件未修改
# 相当于调用 "git reset --mixed" 命令后又做了一次 "git add"
$ git reset --soft <commit ID>

# 将 HEAD 的指向改变,撤销到指定的提交记录,文件也修改了
$ git reset --hard <commit ID>

17. git revert

生成一个新的提交来撤销某次提交,此次提交之前的所有提交都会被保留。

1
2
# 生成一个新的提交来撤销某次提交
$ git revert <commit ID>

18. git tag

操作标签的命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 打印所有的标签
$ git tag

# 添加轻量标签,指向提交对象的引用,可以指定之前的提交记录
$ git tag <标签名称> [<commit ID>]

# 添加带有描述信息的附注标签,可以指定之前的提交记录
$ git tag -a <标签名称> -m <标签描述信息> [<commit ID>]

# 切换到指定的标签
$ git checkout <标签名称>

# 查看标签的信息
$ git show <标签名称>

# 删除指定的标签
$ git tag -d <标签名称>

# 将指定的标签提交到远程仓库
$ git push <远程仓库的别名> <标签名称>

# 将本地所有的标签全部提交到远程仓库
$ git push <远程仓库的别名> –tags

19. git mv

重命名文件或者文件夹。

1
2
3

# 重命名指定的文件或者文件夹
$ git mv <源文件/文件夹> <目标文件/文件夹>

20 git rm

删除文件或者文件夹。

1
2
3
4
5
6
7
8
9

# 移除跟踪指定的文件,并从本地仓库的文件夹中删除
$ git rm <文件路径>

# 移除跟踪指定的文件夹,并从本地仓库的文件夹中删除
$ git rm -r <文件夹路径>

# 移除跟踪指定的文件,在本地仓库的文件夹中保留该文件
$ git rm --cached

Git操作场景示例

  1. 删除掉本地不存在的远程分支
    多人合作开发时,如果远程的分支被其他开发删除掉,在本地执行 git branch –all 依然会显示该远程分支,可使用下列的命令进行删除:
1
2
3
4
5
6
# 使用 pull 命令,添加 -p 参数
$ git pull -p

# 等同于下面的命令
$ git fetch -p
$ git fetch --prune origin
  1. 推送本地仓库到远程仓库
1
2
3
4
5
6
7
git init
git add README.md
git commit -m "first commit"
git branch -M master
# 如果本地仓库已存在,只需执行下面的代码即可
git remote add origin git@xxxx.git
git push -u origin master

git 文件的三种状态

https://yq.aliyun.com/articles/707703

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {

m := map[string]bool{"a": true, "b": false}
for k := range m {
fmt.Println(k)
}

for k, v := range m {
fmt.Println(k, v)
}

}

//a
//b
//a true
//b false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"fmt"
"math/big"
"strconv"
)

func main() {
x := 2.1
y := 0.8
fmt.Println(x+y)
fmt.Println(float64Add(x,y))
}

func float64Add(x float64, y float64, more ...float64) float64 {
floatX := new(big.Float).SetFloat64(x)
floatY := new(big.Float).SetFloat64(y)
result := new(big.Float).Add(floatX, floatY)
if len(more) > 0 {
for _, m := range more {
floatM := new(big.Float).SetFloat64(m)
result = new(big.Float).Add(result, floatM)
}
}
f, _ := strconv.ParseFloat(result.String(), 64) // 忽略 error
return f
}

由于调用 http 接口返回的数据格式类型由 string 变成了 int64,导致在转 json 时出错,凑巧 转 json 没有处理 error,而是 _ = fastjson.Unmarshal([]byte(resp), bpR,于是给定位问题增加了难度,通过这次调查 bug,总结到以下几点:

  1. 在转 json 的过程中,如果数据类型不一致会报错。
  2. fastjson 在转的过程中,报错的话,会导致剩下可以处理的字段无法处理,而 json 可以处理剩下没有问题的数据。
  3. 在项目中,几乎所有的 error 都要处理,否则问题不好定位。不处理的 error 要注释说明为什么不处理。(defer f.close 不用)
  4. 对比之下,官方包的兼容性还是更强的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
53
package main

import (
"encoding/json"
"fmt"
"github.com/intel-go/fastjson"
)

type BpResponse struct {
User resUser `json:"user"`
}

type resUser struct {
Uid string `json:"uid"`
Name string `json:"name"`
SocialAccounts []map[string]string `json:"social_accounts"`
}

func main() {

resp := `{
"user":{
"name":"",
"phone":{
"phone_number":""
},
"social_accounts":[
{
"access_token":"sxXxxxxxxxxxxxxxxxxxxxx-sdfsfsf-",
"expiration_date":1596509376000,
"id":"oydXy061Ux9aeYRJg1LUJoOP4kYQ",
"openid":"orabwwjJaGHi-ShsuqX4Pxk3Uz9Q",
"platform":"wechat"
}
],
"uid":"UUASRHAHJ5PH3"
}
}`

bpR := &BpResponse{}
fmt.Println(fastjson.Unmarshal([]byte(resp), bpR))
fmt.Println(bpR)

fmt.Println(json.Unmarshal([]byte(resp), bpR))
fmt.Println(bpR)

}

//json: cannot unmarshal number into Go value of type string
//&{{ [map[access_token:xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxx-bt0ZXUq1eqOb23cM6A5q7MjJgOXaxAbyKFUn847xVnRoQlp3IQJ7_s67rMTp93oY]]}}
//
//json: cannot unmarshal number into Go struct field resUser.user.social_accounts of type string
//&{{UUASRHAHJ5PH3 [map[access_token:35_Dnloy3E9cbRM76y9IX_2tyLeaVtHTTJa-bt0ZXUq1eqOb23cM6A5q7MjJXXXXXXXXXXXXXXXXXXJ7_s67rMTp93oY expiration_date: id:oydXy061Ux9aeXXXXXXXXXXXXXXOP4kYQ openid:orabwwjJaGxxxxxxxxxxxxxxxxX4Pxk3Uz9Q platform:wechat]]}}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package main

import (
"context"
"fmt"

"github.com/olivere/elastic/v7"
)

var client *elastic.Client


func init() {
var err error
client, err = elastic.NewClient(elastic.SetURL("http://xmge.top:9200/"),elastic.SetSniff(false))
if err != nil {
panic(err)
}
fmt.Println("connect to es success")
}


type Person struct {
Id int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Married bool `json:"married"`
CompanyId int `json:"company_id"`
}

type Company struct {
Id int `json:"id"`
Name string `json:"name"`
Location string `json:"location"`
}

func main() {
persons := []Person{
{1, "小明", 30, true,1},
{2, "小白", 30, true,1},
{3, "小黑", 28, true,2},
{4, "小红", 27, true,3},
{5, "小蓝", 26, true,3},
}
insertPersons("users",persons...)

companys := []Company{
{1, "A公司", "西二旗"},
{2, "B公司", "上地"},
{3, "C公司", "望京"},
}
insertCompany("company",companys...)
}


func insertPersons(index string,items ...Person) {
for _, item := range items {
put1,err :=client.Index().Index(index).BodyJson(item).Do(context.Background())
if err != nil {
panic(err)
}
fmt.Printf("Indexed %s %s to index %s, type %s\n", index,put1.Id, put1.Index, put1.Type)
}
}

func insertCompany(index string,items ...Company) {
for _, item := range items {
put1,err :=client.Index().Index(index).BodyJson(item).Do(context.Background())
if err != nil {
panic(err)
}
fmt.Printf("Indexed %s %s to index %s, type %s\n", index,put1.Id, put1.Index, put1.Type)
}
}

当网络不通的情况下,通常会想到ping命令,ping一下,但是ping命令内部如何执行的,可能并不清楚,其实ping是基于ICMP协议进行工作的。

ip 和 icpm文章: https://www.cnblogs.com/ssh-html/p/10436168.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"log"
"net"
)

func main() {
address := "xmge.top"
conn, err := net.Dial("ip:icmp", "xmge.top")
if err != nil {
log.Fatal(err)
}
add := conn.RemoteAddr()
fmt.Printf("%s's ip is %s:", address, add.String())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
"fmt"
"net"
)

func main() {
//查找DNS A记录
iprecords, _ := net.LookupIP("xmge.top")
for _, ip := range iprecords {
fmt.Println(ip)
}
//查找DNS CNAME记录
canme, _ := net.LookupCNAME("www.baidu.com")
fmt.Println(canme)
//查找DNS PTR记录
ptr, e := net.LookupAddr("118.24.10.218")
if e != nil {
fmt.Println(e)
}
for _, ptrval := range ptr {
fmt.Println(ptrval)
}
//查找DNS NS记录
nameserver, _ := net.LookupNS("baidu.com")
for _, ns := range nameserver {
fmt.Println("ns记录", ns)
}
//查找DNS MX记录
mxrecods, _ := net.LookupMX("google.com")
for _, mx := range mxrecods {
fmt.Println("mx:", mx)
}
//查找DNS TXT记录
txtrecords, _ := net.LookupTXT("baidu.com")

for _, txt := range txtrecords {
fmt.Println("txt:", txt)
}
}

ping 和 nslookup 的区别

ping出来的是一台服务器的IP,而nslookup是查看该域名有哪几台服务器提供服务,很多公司都使用了负载均衡技术,将用户的访问随机定到某一台服务器上,所以你ping的可能有时候不一样,而nslookup则能看到所有提供服务的服务器。是因为权威是说你ping出来是本地区的服务器的地址,而nslookup是所有的地址,还有就是ping主要是测试服务器跟本机的连通性,所以ping就显得准确了!

1
2
3
4
5
6
7
8
9
10
11
12

func (um *UModel) InsertOrUpdate(session *xorm.Session) (int64, error) {

sql := fmt.Sprintf(`INSERT INTO %s (id,user_id,mine_id,status) VALUES(?,?,?,?)
ON CONFLICT (user_id,mine_id) DO UPDATE SET
status = ?;`, um.TableName())
result, err := session.SQL(sql, um.Id, um.UserId, um.MineId, um.Status).Execute()
if err != nil {
return -1, err
}
return result.RowsAffected()
}

1、结果输出在代码下面

1
2
3
4
5
6
7
8
9
func ExampleHasPrefix() {
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("Go")))
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("C")))
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("")))
// Output:
// true
// false
// true
}

2、方法、参数、错误都打印出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func TestNonpollableDeadline(t *testing.T) {
// On BSD systems regular files seem to be pollable,
// so just run this test on Linux.
if runtime.GOOS != "linux" {
t.Skipf("skipping on %s", runtime.GOOS)
}

f, err := ioutil.TempFile("", "ostest")
if err != nil {
t.Fatal(err)
}
defer os.Remove(f.Name())
defer f.Close()
deadline := time.Now().Add(10 * time.Second)
if err := f.SetDeadline(deadline); err != os.ErrNoDeadline {
t.Errorf("SetDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
}
if err := f.SetReadDeadline(deadline); err != os.ErrNoDeadline {
t.Errorf("SetReadDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
}
if err := f.SetWriteDeadline(deadline); err != os.ErrNoDeadline {
t.Errorf("SetWriteDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
}
}

WaitGroup() 的wait 可以直接用defer实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

// TestRacyRead tests that it is safe to mutate the input Read buffer
// immediately after cancellation has occurred.
func TestRacyRead(t *testing.T) {
t.Parallel()

r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
defer r.Close()
defer w.Close()

var wg sync.WaitGroup
defer wg.Wait()

go io.Copy(w, rand.New(rand.NewSource(0)))

r.SetReadDeadline(time.Now().Add(time.Millisecond))
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()

b1 := make([]byte, 1024)
b2 := make([]byte, 1024)
for j := 0; j < 100; j++ {
_, err := r.Read(b1)
copy(b1, b2) // Mutate b1 to trigger potential race
if err != nil {
if !os.IsTimeout(err) {
t.Error(err)
}
r.SetReadDeadline(time.Now().Add(time.Millisecond))
}
}
}()
}
}

阻塞时现象:cpu 利用率降低,内存使用率上升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"time"
)

// goroutine 阻塞时的排查方法
// 项目中首先要引进 pprof
// 通过访问 host/debug/pprof/goroutine?debug=1 即可查看出哪里的 goroutine 阻塞了
// 通过访问 host/debug/pprof/goroutine?debug=2 可以更详细的查看每个 goroutine 的信息

var c = make(chan int)

func main() {

// 模拟 goroutine 阻塞
go func() {
for range time.Tick(time.Microsecond*100){
c <- time.Now().Second()
}
}()

for i := 0; i < 100; i++ {
go func() {
for {
fmt.Println(<-c)
}
}()
}

// 多次访问 /ping 模拟阻塞的 goroutine
http.HandleFunc("/ping", func(writer http.ResponseWriter, request *http.Request) {
select {}
})

log.Fatal(http.ListenAndServe(":8080",nil))
}