go相关

Posted by Qz on September 24, 2023

“Yeah It’s on. ”

基础

Printf 和 Sprintf

Printf : 只可以打印出格式化的字符串。只可以直接输出字符串类型的变量

  fmt.Printf("%d",a)

Sprintf:格式化并返回一个字符串而不带任何输出。

   s := fmt.Sprintf("a %s", "string") fmt.Printf(s)

GO111MODULE

GO111MODULE 及 Go 模块

GOPATH 和 GOROOT

GOROOT:GOROOT就是Go的安装目录,(类似于java的JDK) GOPATH:GOPATH是我们的工作空间,保存go项目代码和第三方依赖包

环境变量

https://www.uedbox.com/post/67653/

在~/.profile 或者 ~/.zshrc 中设置

export GOPATH=/Users/qinzhen/go
export PATH=$GOPATH/bin:/usr/local/go/bin

注意:go要多设置两个PATH

最终的PATH通过: 一起串联起来

export PATH=PNPMHOME:PNPM_HOME:PATH:/Users/qinzhen/go/bin:/usr/local/go/bin
// 设置代理
export GOPROXY=https://goproxy.io,direct

查看环境变量: echo $PATH

下载包

go install这个命令建议安装那些有主文件(main.go),下载下来是作为一个xxx.exe存在于bin目录下的 go get一般用来下载我们项目中调用某个库时使用的命令。

go get: 下载包 & 更新go.mod,不安装二进制 go install :已下载的包,安装二进制

go get gopm来获取无法下载的包

单元测试

go test

覆盖率测试: go test -coverprofile=c.out

以html形式查看覆盖率报告: go tool cover -html=c.out

性能测试: go test -bench .

cpu耗时报告: go test -bench . -cpuprofile cpu.out 查看: go tool pprof cpu.out 让后: web (以svg形式查看)

api文档

查看文档: go doc

将本地所有项目包括系统api生成文档: (在项目根路径执行) godoc -http :6060

get

got get -u 依赖包

升级依赖包

run

go run命令会编译源码,并且直接执行源码的 main() 函数,不会在当前目录留下可执行文件。

go run xxx.go go run -race xxx.go

build

变成一个可执行文件,将始终打印出Hello World。如果我们想让程序再次运行,我们不需要再次编译程序,我们只需要运行可执行文件。

tool1

pprof 分析性能 go tool pprof http://localhost:8888/debug/profile

doc

查看文档 godoc -http :8888

env

查看环境变量

go env

mod

// 清除mod中无用的依赖
go mod tidy
// 初始化模块 初始化项目mod形式管理
go mod init 名称

log

Fatal

https://stackoverflow.com/questions/33885235/should-a-go-package-ever-use-log-fatal-and-when

formatting

https://gobyexample.com/string-formatting

%v

%v	the value in a default format
	when printing structs, the plus flag (%+v) adds field names
	
// 	%+v  打印结构体的时候,会把里面字段名也打印出来

%s

For basic string printing use %s.

可以用来打印 byte[]

error

https://www.cnblogs.com/qcrao-2018/p/11538387.html

string

string函数与strconv.Itoa 区别

  • strconv.Itoa函数的参数是一个整型数字,它可以将数字转换成对应的字符串类型的数字。

  • string函数的参数若是一个整型数字,它将该整型数字转换成ASCII码值等于该整形数字的字符

例子:

func main() {
	string_number := 97
	result := string(string_number)
	fmt.Println(result)  // a 
}

func main() {
	string_number := 97
	result := strconv.Itoa(string_number)
 
	fmt.Println(result)  // "97"
	fmt.Printf("%T\n", result) // string
}

go example

https://gobyexample.com/

值传递

一定要记住,在 Go 语言中,函数的参数传递只有值传递,而且传递的实参都是原始数据的一份拷贝。如果拷贝的内容是值类型的,那么在函数中就无法修改原始数据;如果拷贝的内容是指针(或者可以理解为引用类型 mapchan 等),那么就可以在函数中修改原始数据。

bufio

Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O.

os

在Go语言中,os包是用于提供与操作系统交互的函数和变量的标准库。

os.Signal

在 Go 语言中,os.Signal 是一个表示来自操作系统的信号的类型。它用于处理操作系统级别的事件,比如收到一个中断信号(SIGINT)或终止信号(SIGTERM)等。

使用场景:

优雅退出:通过监听 SIGINT 和 SIGTERM 信号,程序可以在接收到中断信号时,先进行一些清理工作,如关闭数据库连接、释放资源等,再优雅地退出。

goroutine

go中的goroutins并不是同时在运行。事实上,如果没有在代码中通过runtime.GOMAXPROCS(n) 其中n是整数,指定使用多核的话,goroutins都是在一个线程里的,它们之间通过不停的让出时间片轮流运行,达到类似同时运行的效果。

当一个goroutine发生阻塞,Go会自动地把与该goroutine处于同一系统线程的其他goroutines转移到另一个系统线程上去,以使这些goroutines不阻塞。

Go语言中的并发模型是基于goroutine的,goroutine是一种轻量级的线程,可以在Go语言中轻松创建和销毁,也可以高效地在多个核心上调度。当一个Go程序被执行时,Go运行时系统会为该程序创建一个主goroutine,从主goroutine开始运行。当主goroutine创建其他goroutine时,这些goroutine会被放入一个可运行的Goroutine队列中。Go运行时系统会根据一些调度策略来选择一个goroutine执行。当一个goroutine发生了阻塞(比如等待I/O操作完成)时,Go运行时系统会将该goroutine从运行状态切换到阻塞状态,并将其放入一个等待队列中。当阻塞条件满足时,Go运行时系统会将该goroutine重新放入可运行队列中,等待被调度执行。

Go运行时系统的调度策略采用了M:N线程模型,即将M个goroutine调度到N个线程上执行。每个线程(M)都持有一个或多个goroutine(G),并在它们之间进行切换。线程的数量(N)由Go运行时系统动态决定,通常与当前计算机的CPU核心数量相当。

Go语言的线程调度采用的是抢占式调度,即Go运行时系统会控制线程的调度,并根据一些条件和算法来决定是否切换执行的goroutine。在Go语言中,一段代码的执行时间是不确定的,因此,Go运行时系统会在某个goroutine执行时间过长时,抢占该goroutine,并重新调度其他更为需要执行的goroutine。

总的来说,Go语言的线程调度是由Go运行时系统负责的,通过M:N线程模型来实现goroutine的调度,采用抢占式调度策略来提高并发性能。


Go 语言基于 GMP 模型实现用户态线程

  • G:表示 goroutine,每个 goroutine 都有自己的栈空间,定时器,初始化的栈空间在 2k 左右,空间会随着需求增长。
  • M:抽象化代表内核线程,记录内核线程栈信息,当 goroutine 调度到线程时,使用该 goroutine 自己的栈信息。
  • P:代表调度器,负责调度 goroutine,维护一个本地 goroutine 队列,M 从 P 上获得 goroutine 并执行,同时还负责部分内存的管理。

runtime.Gosched

用于让出CPU时间片。这就像跑接力赛,A跑了一会碰到代码runtime.Gosched()就把接力棒交给B了,A歇着了,B继续跑。

channels

https://www.sohamkamani.com/golang/channels/

sync

sync.WaitGroup

https://gobyexample.com/waitgroups

https://blog.kennycoder.io/2020/12/18/Golang%E6%95%99%E5%AD%B8%E7%B3%BB%E5%88%97-%E4%BD%95%E8%AC%82WaitGroup-%E7%AD%89%E5%BE%85Goroutine%E7%9A%84%E5%A5%BD%E5%B9%AB%E6%89%8B/

syscall.SIGINT和syscall.SIGTERM

在Go程序中,syscall.SIGINT和syscall.SIGTERM是两个特定的操作系统信号常量。

  • syscall.SIGINT 代表 “终止信号”,通常由终端用户按下组合键(Ctrl+C)发送给正在运行的程序。它用于请求进程终止运行,通常用于优雅地停止程序。
  • syscall.SIGTERM 代表 “终止信号”,通常由操作系统发送给正在运行的进程,用于通知进程终止运行。与SIGINT不同的是,SIGTERM信号不一定是有用户产生的,可以由操作系统或其他程序发送。

在Go程序中,可以通过导入osos/signal包来处理这些信号,例如使用signal.Notify函数来捕获并响应信号的发送。

sql.NullString and *string

https://stackoverflow.com/questions/40092155/difference-between-string-and-sql-nullstring

type NullString struct {
    String string
    Valid  bool // Valid is true if String is not NULL
}

As you can see, sql.NullString is a way to represent null string coming from SQL (which correspond to “NULL”). On the other hand, a nil *string is a pointer to a string which is nil, so the two are different.

There’s no effective difference. We thought people might want to use NullString because it is so common and perhaps expresses the intent more clearly than *string. But either will work

使用 sql.NullString 更清晰

io.Reader,io.ReadCloser和io.ReadSeeker

https://github.com/wuyongxiu/wuyongxiu.github.io/issues/15

Struct 和 Interface

  1. interface(接口):接口是一种抽象的数据类型,定义了一组方法的集合。接口定义了对象的行为,而不关心对象的具体类型。任何类型只要实现了接口中定义的方法,就被认为是该接口的实现类型。在Go中,接口是隐式实现的,即不需要显式声明实现接口,只要一个类型的方法集能够匹配接口的方法集,那么该类型就实现了该接口。
  2. struct(结构体):结构体是一种复合类型,用于封装一组不同类型的数据。结构体是由一系列字段组成的,每个字段都有自己的类型和名称。可以在结构体中定义方法,结构体的方法可以访问结构体的字段值。
//declare a rectangle struct
type rectangle struct {
    length int
    width  int
}

//declare an interface with area() as a member
type shape interface {
    area() int
}

//declare a method area()
//the rectangle struct implements area() method in shape interface
func (r rectangle) area() int {
    return r.length * r.width
}

//declare a method with type shape as a parameter
func info(s shape) {
    fmt.Println("the area: ", s.area())
}

所以,interface和struct的区别主要在于:

  • 接口是一种抽象类型,用于定义行为,不关心具体类型的实现。结构体是一种具体类型,用于封装数据。
  • 接口定义了一组方法的集合,结构体定义了一组字段的集合。
  • 一个类型可以实现多个接口,但一个类型只能有一个结构体。

总的来说,interface用于定义行为,让不同的类型实现接口来实现多态性;而struct则用于封装数据,让不同字段类型的数据组合在一起。

new

new 是一个内置函数,用于创建某个类型的零值,并返回该类型的指针。它的语法如下:

new(Type)

其中,Type 是所需创建的类型。

new 函数与使用 & 运算符创建指针的方式相似,但更为简洁。使用 new 函数可以直接创建某个类型的指针,而无需手动指定&Type{}。返回的指针指向该类型的零值。

以下是使用 new 函数创建一个指向整型的指针的示例:

num := new(int)

这将创建一个指向整型的指针 num,并将其初始化为整型的零值 0

& 和 *

&是取地址符号,用于获取变量的内存地址。例如:

x := 10
p := &x  // 取出变量x的内存地址,赋值给指针p

*是指针取值符号,用于获取指针指向的变量的值。例如:

x := 10
p := &x  // 取出变量x的内存地址,赋值给指针p
fmt.Println(*p)  // 输出10,*p表示取出指针p指向的变量的值

int32和int

在Go语言中,int32和int是两种不同的数字类型。

int32是int32位整数类型,表示一个带符号的32位整数,范围是-2147483648到2147483647。

int是int的默认类型,它根据操作系统的架构决定位数,可以是32位或64位整数类型。在大多数情况下,int是int32的别名。在32位操作系统上,int是32位整数类型,在64位操作系统上,int是64位整数类型。

因此,在大多数情况下,int32和int可以视为相同的东西,但在某些特定的环境下,它们可能会有不同的大小。为了保持代码的可移植性,建议明确指定所需的整数类型。

grpc

https://grpc.io/docs/what-is-grpc/

In gRPC, a client application can directly call a method on a server application on a different machine as if it were a local object, making it easier for you to create distributed applications and services

gRPC-Go

https://pkg.go.dev/google.golang.org/grpc#section-readme

	conn, err := grpc.Dial(dialAddr,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robbin"}`),
	)

grpc.WithTransportCredentials是gRPC客户端的选项之一,用于指定要使用的传输层安全凭据(Transport Credentials)。传输层安全是一种通过加密和身份验证来保护通信的协议,用于在客户端和服务器之间建立安全连接。

使用grpc.WithTransportCredentials选项,可以在创建gRPC客户端时指定要使用的安全凭据,以确保通信的机密性和完整性。可以将其与TLS(传输层安全)或其他身份验证机制一起使用,以根据需求配置gRPC连接的安全性。

grpc.WithDefaultServiceConfig函数的作用是设置客户端连接的默认服务配置(service config)。

gRPC的服务配置是一组JSON格式的字符串,用于指定一些特定的行为,例如:最大消息大小、流控、超时等。

jwt-go

zap

gin

Gorm

foreignKey

例子:

	SubCategory      []*CateGory `gorm:"foreignKey:ParentCategoryID;references:ID"`

通过 foreignKey 方法来定义外键的引用关系

Gorm Associations

Gorm Associations 是 Gorm 框架中用来定义和管理数据库表之间关系的一种功能。

使用 Gorm Associations,可以简化数据库表之间的关联操作,比如定义一对一关系、一对多关系、多对多关系等。通过定义这些关系,可以方便地进行数据库查询和操作。

应用 Gorm Associations 的主要优点包括:

  1. 简化代码:使用 Gorm Associations 可以简化关联关系的定义和查询操作,减少冗余代码。
  2. 方便的查询:通过定义关联关系,可以方便地进行复杂的查询操作,包括跨表查询、嵌套查询等。
  3. 数据库一致性:使用 Gorm Associations 可以确保数据库中的关联关系保持一致性,避免了手动管理关联关系带来的问题。
  4. 代码可读性:通过使用 Gorm Associations,可以更清晰地表达实体之间的关系,提高代码可读性和可维护性。

总之,Gorm Associations 提供了一种方便、简化和规范的方式来管理数据库表之间的关系,提高了开发效率和代码质量。

protoc

protoc 使用

protoc go-grpc_out 是一个 protoc 的插件,用于生成 Go 的 gRPC 代码。以下是它的使用方法:

  1. 确保已经安装了 protoc 编译器和 Go 的 protoc-gen-go 插件。
  2. 定义一个 .proto 文件,其中包含了 gRPC 服务的接口定义和消息定义。
  3. 使用以下命令生成 Go 的 gRPC 代码:
protoc book.proto --go_out=. --go-grpc_out=.

--go_out=. 表示生成的 Go 代码将保存在当前目录下,--go-grpc_out=. 表示生成的 gRPC 代码同样保存在当前目录下

viper

https://pkg.go.dev/github.com/spf13/viper@v1.10.1#section-readme

Viper is a complete configuration solution for Go applications

Viper是一个完整的Go应用程序配置解决方案

补充

https://www.cnblogs.com/liyutian/p/10050320.html 表达式 new(Type) 和 &Type{} 是等价的。

new 关键字时 t := new(T),变量 t 则是一个指向 T 的指针

don’t close response.Body?

https://stackoverflow.com/questions/33238518/what-could-happen-if-i-dont-close-response-body

string和[]byte

https://zboya.github.io/post/golang_byte_slice_and_string/

string:

type stringStruct struct {
    str unsafe.Pointer
    len int
}

简单的来说字符串是一系列8位字节的集合,通常但不一定代表UTF-8编码的文本。字符串可以为空,但不能为nil。而且字符串的值是不能改变的。

[]byte:

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

array是数组的指针,len表示长度,cap表示容量。除了cap,其他看起来和string的结构很像。

标准转换

// string to []byte
s1 := "hello"
b := []byte(s1)

// []byte to string
s2 := string(b)

[]byte转int

var b = []byte{49,49} 
strconv.Atoi(string(b))

dereference the pointer

对指针解引用

Mysql 无法启动

Mac 下 Mysql 无法启动

sudo /usr/local/mysql/support-files/mysql.server start

vscode 打造舒适go体验

使用 VScode 打造舒适的 Go 开发环境