02-适用于Go项目的Makefile指南

适用于Go项目的Makefile指南
Table of Contents =================

1: Premise

  • 会使用 go 写项目
  • 会使用 Makefile

2: Makefile 语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
PROJECT="example"

default:
    echo ${PROJECT}

install:
    @govendor sync -v

test: install
    @go test ./...

.PHONY: default install test
  • makefile 格式介绍
    1
    2
    
    <target> : <prerequisites>
    [tab] <command>
    
  • target : 即自定义的想要执行的命令;
  • prerequisites: 前置条件,即执行 target 命令之前执行的命令;
  • commands : 具体的执行的命令;
  • .PHONY 伪指令,内置的关键字;
  • 不带参数,默认执行第一个 target;
  • @ 表示禁止回声,即终端不会打印真实的执行命令;
  • # 表示注释;
  • ${val}表示变量,和 shell 脚本中的变量的声明和使用一致;
  • 允许使用 通配符;

3: Go 项目

  • Go 中支持内置的 go 命令,可以用来执行:测试、编译、运行、语法检查等命令
  • 一个完善的 Go 项目经常会执行哪些命令?
    • go vet 静态检查
    • go test 运行单元测试
    • go fmt 格式化
    • go build 编译
    • go run 运行 …
  • 所以一个适用于 Go 项目的 Makefile 也应该支持这些命令。
    • make default : 编译
    • make fmt: 格式化
    • make vet: 静态检查
    • make test: 运行测试
    • make install: 下载依赖库
    • make clean: 移除编译的二进制文件
  • 所以整体可以如下安排:
 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
BINARY="example"
VERSION=1.0.0
BUILD=`date +%FT%T%z`

PACKAGES=`go list ./... | grep -v /vendor/`
VETPACKAGES=`go list ./... | grep -v /vendor/ | grep -v /examples/`
GOFILES=`find . -name "*.go" -type f -not -path "./vendor/*"`

default:
    @go build -o ${BINARY} -tags=jsoniter

list:
    @echo ${PACKAGES}
    @echo ${VETPACKAGES}
    @echo ${GOFILES}

fmt:
    @gofmt -s -w ${GOFILES}

fmt-check:
    @diff=?(gofmt -s -d $(GOFILES)); \
    if [ -n "$$diff" ]; then \
        echo "Please run 'make fmt' and commit the result:"; \
        echo "$${diff}"; \
        exit 1; \
    fi;

install:
    @govendor sync -v

test:
    @go test -cpu=8 -v -tags integration ./...

vet:
    @go vet $(VETPACKAGES)

docker:
    @docker build -t wuxiaoxiaoshen/example:latest .

clean:
    @if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi

.PHONY: default fmt fmt-check install test vet docker clean

4: supplementary instruction

Makefile 构建工具,大大的简化了构建项目的难度。 真实的生产环境下,需要使用到 CI/CD(持续集成和持续部署), 所以 Makefile 也通常用来和 CI 工具配合使用。 比如新合并的代码,先触发单元测试,静态检查等,在执行 CI 脚本,成功之后,再构建镜像,推送镜像到服务器上,完成持续集成和持续部署一整套流程。 Makefile 通常配合 travis 使用。如下:

01-Go-transaction事务几种方式

Go-transaction事务几种方式

1:方式一

  • 这种写法非常朴实,程序流程也非常明确,但是事务处理与程序流程嵌入太深,容易遗漏,造成严重的问题
 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
func DoSomething() (err error) {
    tx, err := db.Begin()
    if err != nil {
        return
    }


    defer func() {
        if p := recover(); p != nil {
            tx.Rollback()
            panic(p)  // re-throw panic after Rollback
        }
    }()


    if _, err = tx.Exec(...); err != nil {
        tx.Rollback()
        return
    }
    if _, err = tx.Exec(...); err != nil {
        tx.Rollback()
        return
    }
    // ...


    err = tx.Commit()
    return
}

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
25
26
27
28
func DoSomething() (err error) {
    tx, err := db.Begin()
    if err != nil {
        return
    }


    defer func() {
        if p := recover(); p != nil {
            tx.Rollback()
            panic(p) // re-throw panic after Rollback
        } else if err != nil {
            tx.Rollback()
        } else {
            err = tx.Commit()
        }
    }()


    if _, err = tx.Exec(...); err != nil {
        return
    }
    if _, err = tx.Exec(...); err != nil {
        return
    }
    // ...
    return
}

3: 方式三

  • 写法三是对写法二的进一步封装,写法高级一点,缺点同上
 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
func Transact(db *sql.DB, txFunc func(*sql.Tx) error) (err error) {
    tx, err := db.Begin()
    if err != nil {
        return
    }


    defer func() {
        if p := recover(); p != nil {
            tx.Rollback()
            panic(p) // re-throw panic after Rollback
        } else if err != nil {
            tx.Rollback()
        } else {
            err = tx.Commit()
        }
    }()


    err = txFunc(tx)
    return err
}


func DoSomething() error {
    return Transact(db, func (tx *sql.Tx) error {
        if _, err := tx.Exec(...); err != nil {
            return err
        }
        if _, err := tx.Exec(...); err != nil {
            return err
        }
    })
}

01-ElasticSearch7-Restful APIs

ElasticSearch7-Restful APIs
  * [1: /ES-API/说明](#1-es-api说明)
  * [2: Cluster - APIs](#2-cluster---apis)
     * [2.1 查询集群状态](#21-查询集群状态)
     * [2.2 查询集群索引信息](#22-查询集群索引信息)
     * [2.3 使用 help 参数查询](#23-使用-help-参数查询)
     * [2.4 查询集群中的节点信息](#24-查询集群中的节点信息)
  * [3: Index - APIs](#3-index---apis)
     * [3.1 创建索引](#31-创建索引)
     * [3.2 获取索引](#32-获取索引)
     * [3.3 获取全部索引](#33-获取全部索引)
  * [4: Doc - APIs](#4-doc---apis)
     * [4.1 创建文档](#41-创建文档)
     * [4.2 删除文档](#42-删除文档)
     * [4.3 查看文档](#43-查看文档)
     * [4.4 查看该索引下的全部文档](#44-查看该索引下的全部文档)
     * [4.5 覆盖数据](#45-覆盖数据)
     * [4.6 更新数据](#46-更新数据)
  * [5: Mapping - APIs](#5-mapping---apis)
     * [5.1 创建 Mapping](#51-创建-mapping)
     * [5.2 查询 Mapping](#52-查询-mapping)
     * [5.3 查询 template](#53-查询-template)
  * [6: Alias - APIs](#6-alias---apis)
     * [6.1 添加 Alias](#61-添加-alias)
     * [6.2 查询 Alias](#62-查询-alias)
  * [7:查询 - APIs](#7查询---apis)
     * [7.1 条件查询](#71-条件查询)
     * [7.2 匹配查询](#72-匹配查询)
     * [7.3 全量查询](#73-全量查询)
     * [7.4 分页查询](#74-分页查询)
     * [7.5 分页过滤](#75-分页过滤)
     * [7.6 排序查询](#76-排序查询)
     * [7.7 组合查询](#77-组合查询)
     * [7.3 分词查询](#73-分词查询)
     * [7.9 完全匹配查询](#79-完全匹配查询)

1: /ES-API/说明

 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
# 基本查询模板

​```json
{
  "query": {
    "bool": {
      "filter": [
        //filter 这后面是过滤条件
        { "term": { "uri": "111111" } }, //客户号
        {
          "range": { "rundata_date": { "gte": "20190108", "lte": "20190110" } }
        } //时间过滤,注意此字段类型,string不能过滤
      ]
    }
  },
  "aggregations": {
    //这里我主要关注一个指标,handleTime字段,标示执行时间,主要对它进行监控
    "avg_handleTime": {
      //可以自己命名
      "avg": {
        "field": "handleTime" //平均执行时间
      }
    },
    "percent_handleTime": {
      "percentiles": {
        "field": "handleTime",
        "percents": [50, 95, 99] //这个是现实 50、95、99的线,从小到大,例如到95%执行时间为1.5s,可以看满足预期的比例
      }
    },
    "min_handleTime": {
      "min": {
        "field": "handleTime" //最小执行时间
      }
    },
    "max_handleTime": {
      "max": {
        "field": "handleTime" //最大执行时间
      }
    }
  },
  "size": 0 //显示几条数据,我这里不需要显示,可以根据需要修改
}
1
2
3
## /ES-API/Cluster - APIs
​```text
集群相关APIS
  • 公共 Header 参数
参数名示例值参数描述

暂无参数

01-Go Test笔记

Go Test笔记

Go Test

  • 一个完整的单测指令可以是 go test -v -cover -gcflags=all=-l -coverprofile=coverage.out
  • -gcflags=all=-l 防止编译器内联优化导致单测出现问题

1.1 go test

go test -run=^TestDo -v ./ 这里介绍几个常用的参数:

  • -bench regexp 执行相应的 benchmarks,例如 -bench=.;
  • -cover 开启测试覆盖率;
  • -trace=copy_trace.out 生成 trace.out 文件(go tool trace copy_trace.out)
  • -run regexp 只运行 regexp 匹配的函数,例如 -run=Array 那么就执行包含有 Array 开头的函数;
  • -count 执行次数。
  • -v 显示测试的详细命令。

1.2 go test cover 生成测试覆盖度报告

go tool cover -html=coverage.out

01-docker log管理和时区设置

docker log管理和时区设置

1:docker log

1.1 docker log Description

  • docker 的日志分为两种,第一种是 docker 引擎的日志,第二种是 docker 容器运行时的服务日志,默认为 json-file 格式;
  • json-file 日志驱动 记录从容器的 STOUT/STDERR 的输出 ,用 JSON 的格式写到文件中,日志中不仅包含着 输出日志,还有时间戳和 输出格式.
  • docker 所有的日志驱动如下:
    日志驱动descriptionothers
    none运行的容器没有日志,docker logs 也不返回任何输出
    local日志以自定义格式存储,旨在实现最小开销
    json-file日志格式为 JSON,Docker 的默认日志记录驱动程序
    syslog将日志消息写入 syslog。该 syslog 守护程序必须在主机上运行
    journald将日志消息写入 journald。该 journald 守护程序必须在主机上运行
    gelf将日志消息写入 Graylog 扩展日志格式(GELF)端点,例如 Graylog 或 Logstash
    fluentd将日志消息写入 fluentd(转发输入)。该 fluentd 守护程序必须在主机上运行
    awslogs将日志消息写入 Amazon CloudWatch Logs
    splunk使用 HTTP 事件收集器将日志消息写入 splunk
    etwlogs将日志消息写为 Windows 事件跟踪(ETW)事件。仅适用于 Windows 平台
    gcplogs将日志消息写入 Google Cloud Platform(GCP)Logging
    logentries将日志消息写入 Rapid7 Logentries
  • 常用的驱动是 json-filesyslog ,journald 这三种,json-file 是默认的日志驱动;
  • docker 的日志通常默认是保存在 /var/lib/docker/containers/容器 ID/ 目录下,日志文件名称为 容器 ID-json-log,当然,docker 日志也有级别,通常级别为 info。

1.2 磁盘占用分析

1.2.1 df -ah 查看系统中文件的使用情况

  • df (disk free) 检查文件系统占用磁盘情况;
1
2
3
4
5
Size 分割区总容量
Used 已使用的大小
Avail 剩下的大小
Use% 使用的百分比
Mounted on 路径地址

1.2.2 du -sh 查看当前目录下各个文件及目录占用空间大小

  • du (disk usage) 检查目录占用磁盘情况;
1
2
du -h --max-depth=1 /root/* 查看目录下的所有文件大小
du -h --max-depth=1 /root 列出root目录下面所有的一级目录文件大小;

1.2.3 df -h 和 du -sh 显示的磁盘大小不一致原因及解决办法

  • 原因 du 是根据文件名进行的空间统计,使用 rm 时该文件对系统来说已经不可见,所以不会统计这个文件。 df 则是磁盘实际占用的数量
  • du -h /var/lib/docker/
  • df -h /var/lib/docker/
  • 解决办法
    • (1/2) lsof 查看使用文件的进程,结束进程。
      • lsof | grep test.log
      • ps aux | grep 28600
      • kill -9 28600
    • (2/2) 或使用清空的方式代替 rm,如 echo > /tmp/test.log
      • 日志文件清空一般不是删除 rm 命令,而是 > 文件名这样的清空方式.

1.3 docker log 管理

  • 日志文件清空一般不是删除 rm 命令,而是 > 文件名这样的清空方式;

01-go使用Linux内核端口复用进行LoadBalance

1 查看 Linux Kernal

  • socket 五元组 {<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}
  • uname -a
  • Linux 3.9 内核引入了 SO_REUSEPORT选项,可支持多个进程或者线程绑定到同一个端口,用于提高服务器的性能。他的特性包含一下几点:
    • 允许多个 socket 套接字 bind 同一个 TCP/UDP 端口;
      • 每一个线程拥有自己的服务器套接字;
      • 在服务器套接字上没有了锁的竞争;
    • 内核层面实现负载均衡;
    • 安全层面,listen 同一个端口的 socket 套接字只能位于同一个 user 下。
  • 有了SO_RESUEPORT后,每个进程可以 bind 相同的地址和端口,各自是独立平等的;
  • 让多进程监听同一个端口,各个进程中 accept socket fd 不一样,有新连接建立时,内核只会调度一个进程来 accept,并且保证调度的均衡性。

端口复用图示

01-grpc安装(cpp和go)

1 c++ 配置 GRPC

https://grpc.io/docs/languages/cpp/quickstart/

1.1 setup dir

1
2
3
4
5
export MY_INSTALL_DIR=$HOME/.local

mkdir -p $MY_INSTALL_DIR

export PATH="$MY_INSTALL_DIR/bin:$PATH"

1.2 install cmake

1
sudo apt install -y cmake

1.3 Install other required tools

1
sudo apt install -y build-essential autoconf libtool pkg-config

1.4 git clone grpc repo

1
git clone --recurse-submodules -b v1.41.0 https://github.com/grpc/grpc

1.5 Build and install gRPC and Protocol Buffers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cd grpc
mkdir -p cmake/build
pushd cmake/build
cmake -DgRPC_INSTALL=ON \
    -DgRPC_BUILD_TESTS=OFF \
    -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \
    ../..
make -j
make install
popd
1
2
3
4
5
# cmake
mkdir -p cmake/build
cd cmake/build
cmake ../..
make

1.6 编写 proto 文件

1.7 编写 server/client 文件

1.8 执行

2 golang grpc 环境搭建

https://grpc.io/docs/languages/go/quickstart/

011-docker部署gitlab

docker部署gitlab

1:gitlab docker 镜像 pull

  • docker pull gitlab/gitlab-ce:latest
  • 新建目录~/gitlab

2:启动脚本

~/gitlab/start.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash
GITLAB_DIR=/home/lucas/gitlab/data

docker run -d \
    -p 5443:443 \
    -p 9998:9998 \
    -p 2222:22 \
    --name gitlab \
    -v ${GITLAB_DIR}/config:/etc/gitlab \
    -v ${GITLAB_DIR}/logs:/var/log/gitlab \
    -v ${GITLAB_DIR}/data:/var/opt/gitlab \
    gitlab/gitlab-ce:latest

3:修改gitlab.rb

~/gitlab/data/config/gitlab.rb

01-note笔记

note笔记

1:note 文件

1.1 alias 别名设置

1
2
3
4
alias gs = "git status"
alias ga = "git add -A"
alias gd = "git diff"
alias gc = "git commit -m"

1.2 git 局部代理

1
git clone -c http.proxy="127.0.0.1:7078" xxx

1.3 golang 交叉编译

1
2
3
4
5
6
7
8
CGO_ENABLED=0 GOOS=linux GOARCH=amd64    go build -v -a main.go

CGO_ENABLED=0 GOOS=darwin GOARCH=amd64   go build -v -a main.go

CGO_ENABLED=0 GOOS=windows GOARCH=amd64  go build -v -a main.go

# 编译时添加参数
 -ldflags "-X 'main.Version=v1.0.0' -X 'main.BuildTime=`date +"%Y-%m-%d %H:%M:%S"`' -X 'main.GoVersion=`go version`' "

1.4 date 和时间戳转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# linux  date和时间戳转换
1:查看当前时间
date +"%Y-%m-%d %H:%M:%S"

输出:"2021-03-25 18:12:38"

2:查看指定时间,转为时间戳
date +%s
date -d "2021-03-25 18:12:38" +"%Y-%m-%d %H:%M:%S"
date -d "2021-03-25 18:12:38" +%s

3:时间戳转换为date
date -d @1416387827
date -d @1416387827 +"%Y-%m-%d %H:%M:%S"

1.5 minikube start

1
2
3
4
5
6
minikube start \
    --driver docker \
    --image-mirror-country=cn \
    --registry-mirror https://3laho3y3.mirror.aliyuncs.com \
    --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers \
    --kubernetes-version=v1.20.7

1.6 ip 相关

1
2
3
4
5
6
7
#
curl ipinfo.io
#
curl -vv www.google.com

# ------------------查看ip--------------------
ip addr show | grep "inet"

1.7 查看 Linux 详细 info

1
cat /etc/*elease

1.8 WSL2 压缩磁盘空间

1
2
3
4
# ----------------------WSL2压缩磁盘空间------------------------
PowerShell,直接运行 diskpart 命令就好
select vdisk file=D:\wsl\Ubuntu-20.04\ext4.vhdx
compact vdisk