语言技巧
gofmt 格式化代码
gofmt -s -w xxx.go
嵌套结构体屏蔽指定字段 json 输出
type User struct {
Pwd string `json:"pwd"`
Age int `json:"age"`
}
type UserOut struct {
User
Pwd *struct{} `json:"pwd,omitempty"`// 这里的字段json名需要和嵌套的字段json名一致,否则无效
}
func TestJson(t *testing.T) {
u := User{Pwd: "123", Age: 1}
bb := UserOut{User: u}
b, _ := json.MarshalIndent(bb, "", " ")
t.Log(string(b))
}
github action 使用 ubuntu-latest 执行 make xxx 操作时,需要在 Makefile 指定 shell
在首行添加
SHELL := /bin/bash
go get 指定版本
从 Go 1.11 开始,使用 Go modules 可以做到这一点。在为 Go 模块安装依赖项时,你可以指定一个模块查询,其中可能包含分支或标记名称:
拉取最新的版本(优先择取 tag)
go get golang.org/x/text@latest
拉取 master 分支的最新 commit
go get golang.org/x/text@master
# 如果分支带斜杠需要设置直连
GOPROXY=direct GOSUMDB=off go install github.com/gogf/gf/cmd/gf/v2@feat/docrun
拉取 tag 为 v0.3.2 的 commit
go get golang.org/x/text@v0.3.2
拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2:
go get golang.org/x/text@342b2e
指定版本拉取,拉取 v3 版本
go get github.com/smartwalle/alipay/v3
清理 go mod 缓存
go clean -modcache
查看模块有哪些版本
go list -m -versions github.com/gogf/gf/v2
go list -m -versions github.com/gogf/gf/cmd/gf/v2
go mod 查看间接依赖来源
go mod why github.com/gogf/gf/v2
# 再详细的依赖树
go mod graph | grep github.com/gogf/gf/v2
构建
windows
在 Windows 下如果开启了 cgo,那么极大概率是无法交叉编译。参考
set GOOS=linux
set GOARCH=amd64
GCC
https://jmeubank.github.io/tdm-gcc/download/
目标平台
https://golang.google.cn/doc/install/source
darwin amd64
darwin arm64
ios amd64
ios arm64
freebsd 386
freebsd amd64
freebsd arm
linux 386
linux amd64
linux arm
linux arm64
linux ppc64
linux ppc64le
linux mips
linux mipsle
linux mips64
linux mips64le
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
windows 386
windows amd64
android arm
dragonfly amd64
plan9 386
plan9 amd64
solaris amd64
pprof 使用
gf 的配置方式,https://goframe.org/pages/viewpage.action?pageId=1114350
开启后使用视图形式查看
需要下载安装graphviz
go tool pprof -http="127.0.0.1:8080" http://127.0.0.1:8000/debug/pprof/heap
# 实时内存
# http://127.0.0.1:8080/ui/peek?si=inuse_space
# http://127.0.0.1:8080/ui/top?si=inuse_space
显示 init 顺序
GODEBUG=inittrace=1 go run main.go
$env:GODEBUG="inittrace=1"
go run main.go
set GODEBUG=inittrace=1
go run main.go
判断是否 Windows 双击启动
// +build windows
//go:generate go build -ldflags "-s -w -extldflags '-static'" $GOFILE
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
clickRun := isDoubleClickRun()
fmt.Println("Double click run:", clickRun)
if clickRun {
fmt.Print("press Enter to exit")
var b byte
_, _ = fmt.Scanf("%v", &b)
}
}
// Detect if windows golang executable file is running via double click or from cmd/shell terminator
// https://stackoverflow.com/questions/8610489/distinguish-if-program-runs-by-clicking-on-the-icon-typing-its-name-in-the-cons?rq=1
// https://github.com/shirou/w32/blob/master/kernel32.go
// https://github.com/kbinani/win/blob/master/kernel32.go#L3268
// win.GetConsoleProcessList(new(uint32), win.DWORD(2))
func isDoubleClickRun() bool {
kernel32 := syscall.NewLazyDLL("kernel32.dll")
lp := kernel32.NewProc("GetConsoleProcessList")
if lp != nil {
var pids [2]uint32
var maxCount uint32 = 2
ret, _, _ := lp.Call(uintptr(unsafe.Pointer(&pids)), uintptr(maxCount))
if ret > 1 {
return false
}
}
return true
}
protobuf
下载对应的 protoc,https://github.com/protocolbuffers/protobuf/releases
配置到环境变量 PATH 中
protoc --version
安装 go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
生成 go 代码
protoc --go_out=. *.proto
带执行权限
非 root,执行二进制带 root,再运行对应的 shell
// chmod 4755 restart
func main() {
syscall.Setuid(0)
syscall.Setgid(0)
RunShell("restart.sh")
}
func RunShell(name string, arg ...string) (string, error) {
cmd := exec.Command(name, arg...)
fmt.Println(name, arg)
out, err := cmd.Output()
if err != nil {
fmt.Printf("out: %s\n", string(out))
fmt.Printf("err: %s\n", err)
if exitErr, ok := err.(*exec.ExitError); ok {
// 命令执行出错,获取错误输出
fmt.Println("命令执行出错:\n", string(exitErr.Stderr))
} else {
// 其他错误
fmt.Printf("执行命令时发生错误: %v", err)
}
return "", err
} else {
fmt.Printf("out: %s\n", string(out))
return string(out), nil
}
}
win10 修改时区报错
可以通过引入 import \_ "time/tzdata"
或者构建时加入 -tags timetzdata
go build -tags timetzdata
https://github.com/gogf/gf/issues/3676
触发context canceled
package main
import (
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.WriteTplContent(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="connect-src 'self'">
<title>页面标题</title>
</head>
<body>
<button onclick="req(500)">正常请求</button>
<button onclick="req(50)">快速取消</button>
</body>
<script>
function req(delay) {
const controller = new AbortController();
const signal = controller.signal;
fetch('/test', { signal })
.then(response => {
// 处理响应数据
console.log('请求成功');
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('请求被取消');
} else {
console.error('请求失败', error);
}
});
setTimeout(() => {
// 取消请求
controller.abort();
}, delay);
};
</script>
</html>
`)
})
s.BindHandler("/test", func(r *ghttp.Request) {
ctx := r.GetCtx()
g.Log().Info(ctx, "请求开始")
select {
case <-ctx.Done():
g.Log().Info(ctx, "请求取消")
return
case <-time.After(300 * time.Millisecond):
g.Log().Info(ctx, "请求结束")
return
}
})
s.SetPort(80)
s.Run()
}
// http://127.0.0.1