Go安装包下载:https://go.dev/dl/
Go资源包搜索:https://pkg.go.dev/
在Windows环境下编译golang程序的时候提示:
"gcc"不是内部或外部命令,也不是可运行的程序或批处理文件”
这就很烦了,但是可以安装MinGW-w64
编译器解决这个问题。
下载连接:https://sourceforge.net/projects/mingw-w64/files/
下载seh
很不错的go语言学习网站:
https://haicoder.net/golang/golang-complex.html
golang保留字
一. 关键字(25个)
1. 程序声明
import 导入 package 包
2. 程序实体声明和定义
chan 通道 var 变量声明 const 常量声明 func 用于定义函数和方法 interface 定义接口 map 字典/map struct 定义数据类型 type 类型声明
3. 程序流程控制
for break continue select 选择流程 switch case default defer 标识在函数退出之前执行 if else go 用于并行 goto fallthrough 强制执行后面的case, 不能用在switch的最后一个分支。 range 遍历读取slice,map, channel数据 return 用于从函数返回
二. 内建常量(4个)
true false iota nil //iota 是常量计数器, 使go的常量自增
三. 内建类型(20个)
int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr float32 float64 complex128 complex64 bool byte rune string error
go语言
类型分类
:
值类型
:数值类型,bool,string,数组,struct结构体
变量直接存储值,内存通常在栈中分配, 修改值,不会对源对象
产生影响引用类型
:指针,slice切片,管道chan,map,interface
变量存储的是一个地址,这个地址对应的空间才真正存储数据值,内存通常在堆上分配,当没有任何变量引用这个地址时,该地>址对应的数据空间就成为一个垃圾,由GC来回收,同时,当地址指向的数据值改变时,会对源对象产生影响。
四. 内建函数(13个)
make len cap new append copy close delete complex real imag panic recover
Golang语言中的单引号和双引号的区别:
''
用于表示字符类型
""
用于表示字符串类型
函数
定义的基本语法
func 函数名(形参 类型,形参 类型)(返回值 类型){ 执行语句 }
1. func可以当参数传递
func mytype1(num1 int, num2 int)int{
}
func mytype2(n1 func(int,int)int, num1 int, num2 int){
}
fun main(){
num1 := 1
num2 := 2
mytype2(mytype1,num1,num2)
}
2. 可变参数
func args1(args... int){
fmt.Println(args)
}
func main(){
args1(1,2,3,4)
}
args是一个切片类型的数据
当你的包里面定义了一个init函数时,init会在main函数之前被调用
3. 匿名函数细节
func (n1 int, n2 int)int{
return n1 + n2
}(n1,n2)
或
a := func (n1 int, n2 int)int{}(n1,n2)
4. 闭包
func Addupper()func(int,int)int{
return func(n int,n2 int)int{
return n+n2
}
}
func main(){
add := Addupper()
num3 := add(2,3)
fmt.Println(num3)
}
配置环境变量
vim /etc/profile
(在文件最后追加)export GO111MODULE=on export GOROOT=/usr/local/go export GOPATH=/opt/GoProject export GOPROXY=https://goproxy.cn export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
更新环境变量
source /etc/profile
验证环境
go version
go env
go env -w GO111MODULE=on go env -w GOROOT=C:/Program Files/Go #这里是go安装包根目录 go env -w GOPATH=D:/www/svn/ #这里是项目主目录 go env -w GOPROXY=https://goproxy.cn,direct //PowerShell 并执行 $env:GO111MODULE = "on" $env:GOPROXY = "https://goproxy.cn" //go env -w GOSUMDB=off #关闭包的有效性验证 //go env -w GOSUMDB="sum.golang.google.cn" go mod tidy
Golang 的远程 Debug
- go环境安装(略)
- dvl 工具安装
cd $GOPATH
go install github.com/go-delve/delve/cmd/dlv@latest --安装dlv
ln -s $GOPATH/bin/dlv /usr/local/bin/dlv --配置软连接
dlv version
查一下进程号:ps aux | grep 进程号
ps -aux | grep go
启动dlv
centos 服务器上启动测试程序后,需要通过如下命令启用dlv工具,可以看到启动后 dlv在等待客户端连接
dlv attach 335 --headless --listen=:2345 --api-version=2 --accept-multiclient
#335 是 上述启动 go进程的id 2345 是服务监听的端口 这里写哪个端口 goland中就要配置哪个端口,其他参数可以通过 执行 dlv 查看帮助信息
API server listening at: [::]:2345
2021-12-26T13:06:55+08:00 warning layer=rpc Listening for remote connections >(connections are not authenticated nor encrypted)测试程序请自己写个可以打断点的代码
bin
通过go install编译时候,生成的可执行文件。
pkg
通过go install编译时候,生成的包文件。
src
go源码和依赖库。
每个 Go 工作区都包含三个基本文件夹,
创建工作区
cd %GOPATH% mkdir bin\ mkdir src\ mkdir pkg\
bin:包含应用程序中的可执行文件。
src:包括位于工作站中的所有应用程序源代码。
pkg:包含可用库的已编译版本。 编译器可以链接这些库,而无需重新编译它们。
go mod init //生成 go.mod 文件
go mod download //下载 go.mod 文件中指明的所有依赖
go mod tidy //整理现有的依赖
go mod graph //查看现有的依赖结构
go mod edit //编辑 go.mod 文件
go mod vendor //导出项目所有的依赖到vendor目录
go mod verify //校验一个模块是否被篡改过
go mod why //查看为什么需要依赖某模块
对字符进行转义
\n
:新行
\r
:回车符
\t
:制表符
\'
:单引号
\"
:双引号
\\
:反斜杠
变量的默认值:
- 指针类型的,零值都是nil
- 值类型的变量,零值是其所在类型的零值
- map(
引用类型
)的零值是nil,- int32类型的零值是0
- string类型的零值是""
- bool类型的零值是false
- 符合结构struct类型的零值是其每个成员的零值的组合
struct的零值是Student1{0, “”}
struct类型和nil是两种不同的类型,不能比较。
golang数据类型
基本类型
:数字、字符串和布尔值
聚合类型
:数组和结构
引用类型
:指针、切片、映射、函数和通道
接口类型
:接口
1:基本类型
负整型:包括int,int8,int16,int32,int64
正整型:包括uint,uint8,uint32,uint64,
byte 存放字符
rune 代表一个 utf-8 字符
uintptr 存放指针
浮点型:包括float32,float64
复数类型:包括complex64,complex128
字符串类型:string
布尔型:bool
int类型的大小为 8 字节
int8类型大小为 1 字节
int16类型大小为 2 字节
int32类型大小为 4 字节
int64类型大小为 8 字节
2:聚合类型(复合类型)
数组
struct结构体
3:引用类型
slice
map
channel
pointer or 引用类型
4:接口类型
io.Reader, io.Writer,error等
类型比较的总结:
- 复合类型,只有每个元素(成员)可比较,而且类型和值都相等时,两个复合元素才相等
- slice,map不可比较,但是可以用reflect或者cmp包来比较
- func作为golnag的一等公民,也是一个类型,也不能比较。
- 引用类型的比较是看指向的是不是同一个变量
- 类型再定义(type A string)不可比较,是两种不同的类型
- 类型别名(type A = string)可比较,是同一种类型。
if块中的变量在外部无效
if num := somenumber(); num < 0 { fmt.Println(num, "is negative") } else if num < 10 { fmt.Println(num, "has 1 digit") } else { fmt.Println(num, "has multiple digits") }
只在此 if 块内有效,
超出
则显示undefined: num
switch 包含强数据类型验证
//demo1
switch time.Now() {
case 0, 1, 2, 3:
fmt.Print("case可含多个表达式,用逗号分隔")
fallthrough //使逻辑进入到下一个紧邻的 case
case 9
default:
fmt.Print(" 注意看:switch 可调用函数")
}
//demo2
switch {
case "a":
fmt.Println("可省略switch条件, 强制 switch 语句一直运行")
//强制 switch 语句一直运行
数组和切片:
数组
必备三元素
:var 变量名 [长度] 类型
切片不加长度 就是切片
var a []int //nil切片,和nil相等,一般用来表示一个不存在的切片
var b []int{} //空切片,和nil不相等,一般用来表示一个空的集合
var c []int{1, 2, 3} //有3个元素的切片,len和cap都为3
var d = c[:2] //有2个元素的切片,len为2,cap为3
var e = c[:2:cap(c)] //有2个元素的切片,len为2,cap为3
var f = c[:0] //有0个元素的切片,len为0,cap为3
var g = make([]int, 3) //创建一个切片,len和cap均为3
var h = make([]int, 3, 6) //创建一个切片,len为3,cap为5
var i = make([]int, 0, 3) //创建一个切片,len为0,cap为3
函数的参数一
----因为它们属于引用类型在golang语言中,哪些
类型
的变量作为参数
传递给函数
会导致变量的原始储值
发生改变
?
Slice 切片
:当将一个切片作为参数传递给函数,并在函数内部修改切片元素时,原始切片的值会发生改变。Map 映射
:当将一个映射作为参数传递给函数,并在函数内部修改映射的键值对时,原始映射的值会发生改变。Channel 通道
:当将一个通道作为参数传递给函数,并在函数内部向通道发送或接收数据时,原始通道的值会发生改变。这些类型被称为
引用类型
,它们存储的是指向底层数据结构
的指针
。因此,当将它们作为参数传递给函数时,函数接收到的是指向原始变量
的指针
,可以直接修改原始变量的值
。其他类型,如整数、浮点数、字符串等基本类型,以及结构体和数组等复合类型,在函数内部对参数进行修改时,不会影响原始变量的储值。它们被称为
值类型
,函数接收到的是原始变量的拷贝副本
。
map
和slice
作为函数的参数
– 成为值拷贝
— 无法通过函数改变它本身的值再看使用
slice类型
的变量
作为函数的参数
差别:
函数的参数二
----因为它们属于值类型
数组
和结构体
作为函数的参数
– 成为值拷贝
— 无法通过函数改变它本身的值
函数
函数使用
指针
和引用
内存地址, 改变外部
变量
二维数组的定义
:先定义一个双胞胎, 每个双胞胎里面有3个元素 = 定义一个二行3列的数组
实例为一个 3 行 4 列的二维数组
访问 map 中不存在的 key
// 正确示例
func main() {
x := map[string]string{"one": "2", "two": "", "three": "3"}
if _, ok := x["two"]; !ok {
fmt.Println("key two is no entry")
}
}
下面错误的 key 检测方式
func main() {
x := map[string]string{"one": "2", "two": "", "three": "3"}
//Go 则会返回元素对应数据类型的零值,比如 nil、'' 、false 和 0 , 不存在它也会有返回值, 坑
if v := x["two"]; v == "" {
fmt.Println("key two is no entry") // 键 two 存不存在都会返回的空字符串
}
}
在多行 array、slice、map 语句中不能缺少 , 号
func main() {
x := []int {
1,
2 // syntax error: unexpected newline, expecting comma or }
}
y := []int{1,2,}
z := []int{1,2}
// ...
}
不导出的 struct 字段无法被 encode
//以小写字母开头的字段成员是无法被外部直接访问的,所以 struct 在进行 json、xml、gob 等格式的 encode 操作时,这些私有字段会被忽略,导出时得到零值:
func main() {
in := MyData{1, "two"}
fmt.Printf("%#v\n", in) // main.MyData{One:1, two:"two"}
encoded, _ := json.Marshal(in)
fmt.Println(string(encoded)) // {"One":1} // 私有字段 two 被忽略了
var out MyData
json.Unmarshal(encoded, &out)
fmt.Printf("%#v\n", out) // main.MyData{One:1, two:""}
}
Golang语言的接口
定义接口:
type MyInterface interface {
Method1() int
Method2(string) bool
}
该接口包含两个方法Method1和Method2,分别返回int和bool类型。
实现接口:指编写一个类型,实现接口中定义的所有方法。
type MyStruct struct {}
func (m *MyStruct) Method1() int {
// 实现Method1方法
return 1
}
func (m *MyStruct) Method2(str string) bool {
// 实现Method2方法
return len(str) > 0
}
使用接口:
使用接口就是将接口类型的变量指向实现该接口的类型的实例。
var obj MyInterface
obj = &MyStruct{}
然后调用接口
x := obj.Method1()
y := obj.Method2("hello")
注意,调用接口的方法时,实际上是调用实现该接口的类型的方法,这些方法可能会以不同的方式实现,但它们都必须满足接口定义的方法签名
Golang如何将http请求修改为https请求
-
首先必须下载openssl用于生成ssl证书下载地址: http://slproweb.com/products/Win32OpenSSL.html
-
配置环境变量
D:\Openssl\OpenSSL-Win64\bin
-
进入D盘根目录cmd执行命令:
openssl genrsa -out ./server.key 2048
openssl req -new -x509 -key ./server.key -out ./server.pem -days 365
注意:第一个输入框填cn后面的回车就可以了。(国家名称) -
将生成的两个文件拖入main.go文件同级目录
-
这里我们使用的是gin框架,所以只需要修改一行代码即可
r.RunTLS(":8000","./server.pem","./server.key") #将r.run(":8000")修改即可
Golang语言中 var 和 type 一个结构的区别
这种叫
匿名结构体(Anonymous Structure)
, 一般叫临时变量
, 以用于临时使用
, 通过 new() 创建后返回的是它的地址,new它返回的永远是类型的指针,指针指向分配类型的内存地址
,若是字串会是"",number 会是 0,channel, func, map, slice 等等则会是 nil
下面是go项目的虚拟机挂载情况
mkdir /opt/GoProject/bin
mkdir /opt/GoProject/pkg
mkdir /opt/GoProject/src
mkdir /opt/GoProject/Setup
vmhgfs-fuse .host:/bin/ /opt/GoProject/bin -o subtype=vmhgfs-fuse,allow_other,nonempty
vmhgfs-fuse .host:/pkg/ /opt/GoProject/pkg -o subtype=vmhgfs-fuse,allow_other,nonempty
vmhgfs-fuse .host:/src/ /opt/GoProject/src -o subtype=vmhgfs-fuse,allow_other,nonempty
vmhgfs-fuse .host:/Setup/ /opt/GoProject/Setup -o subtype=vmhgfs-fuse,allow_other,nonempty
#下面是go的环境变量
export GO111MODULE=on
export GOROOT=/opt/go
export GOPATH=/opt/GoProject
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
编译到Windows端执行程序 ---- 交叉编译
goland 未解析的依赖项
这个错误的处理办法是需要勾选go模块集成。
关于协程
至少20个最常用的golang的协程使用场景
以下是20个最常见的Golang协程使用场景:
- 并发处理多个HTTP请求
- 通过并发处理I/O操作来提高程序的吞吐量
- 并发处理大型数据集,例如在MapReduce算法中
- 在高并发环境下,使用协程避免阻塞并提高程序的响应速度
- 在后台进行任务,例如日志记录或数据备份
- 实时处理消息队列中的消息
- 响应事件驱动的编程模型,例如在GUI编程中
- 并发处理多个数据库连接
- 执行非阻塞的系统调用,例如读取文件或者网络连接
- 非阻塞等待多个通道事件的发生
- 定时执行任务,例如执行定时备份
- 对多个数据源进行并发读取,例如读取多个文件
- 在高负载的情况下使用协程来避免服务器过载
- 并发处理多个任务,例如并行化测试或压力测试
- 在分布式系统中,通过协程来管理不同节点之间的通信
- 在运行时动态创建协程来处理任务
- 使用协程处理不确定的任务,例如在自动化测试中
- 在分时系统中并行执行多个任务
- 并发处理多个任务,以减少响应时间
- 使用协程处理多个输入源,例如处理多个传感器数据流。
请注意,这只是一些常见的场景,实际上Golang协程的使用场景非常广泛,可以根据应用程序的需要进行任何类型的并发处理。
golang并发处理多个HTTP请求, 哪种情况下才会出现呢? 请举例并说明
在处理多个HTTP请求时,Golang中的协程可以并行处理这些请求,从而提高程序的吞吐量和性能。以下是一些可能需要并发处理多个HTTP请求的情况:
- 在Web应用程序中,处理多个客户端请求时,可以使用协程来并发处理这些请求,以提高并发性能。例如,在一个电子商务网站中,当多个客户同时访问该网站时,可以使用协程来并行处理每个请求,以提高响应速度和吞吐量。
- 在网络爬虫中,当需要爬取多个网页时,可以使用协程来并发地爬取这些网页,从而提高爬取速度。例如,当爬取一个包含数百个页面的网站时,可以使用协程来并行处理这些页面的请求,以提高爬取速度。
- 在API请求代理中,当需要转发多个API请求时,可以使用协程来并行处理这些请求。例如,当需要将多个API请求发送到不同的后端服务中时,可以使用协程来并行处理这些请求,从而提高代理服务的性能。
- 在处理大量用户上传文件时,可以使用协程并发地处理这些文件,以提高处理速度。例如,当一个网站允许用户上传大量文件时,可以使用协程来并行处理这些上传请求,以提高网站的性能。
- 需要注意的是,在并发处理多个HTTP请求时,需要小心处理并发的资源竞争问题,例如共享的数据结构或数据库连接。需要使用合适的同步机制来保证并发访问的正确性。
以下是另外10个最常见的Golang协程使用场景:
- 在实时通信应用程序中,使用协程来处理多个用户的聊天请求。例如,在一个在线游戏中,可以使用协程来并发地处理每个玩家的聊天请求,以提高游戏的响应速度。
- 在数据处理应用程序中,使用协程来并发地处理多个数据文件的读取和处理。例如,在一个数据分析应用程序中,可以使用协程来并发地读取和处理多个数据文件,以提高数据处理速度。
- 在多任务并行编程中,使用协程来并发地执行多个任务。例如,在一个计算密集型应用程序中,可以使用协程来并行处理多个任务,以提高应用程序的性能。
- 在流媒体应用程序中,使用协程来并发地处理多个音视频流。例如,在一个视频会议应用程序中,可以使用协程来并行处理多个参与者的音视频流,以提高会议质量。
- 在并发访问数据库时,使用协程来并发地执行多个数据库查询。例如,在一个电子商务网站中,可以使用协程来并发地执行多个数据库查询,以提高网站的响应速度。
- 在网络服务器应用程序中,使用协程来并发地处理多个TCP连接。例如,在一个聊天服务器应用程序中,可以使用协程来并行处理多个用户的TCP连接,以提高服务器的性能。
- 在处理大量请求时,使用协程来并发地执行任务队列。例如,在一个任务调度应用程序中,可以使用协程来并发地执行任务队列中的任务,以提高调度器的性能。
- 在并发处理多个文件时,使用协程来并发地处理多个文件读写操作。例如,在一个文件同步应用程序中,可以使用协程来并行处理多个文件的读写操作,以提高同步速度。
- 在处理多个用户请求时,使用协程来并发地执行多个后台任务。例如,在一个电子邮件服务应用程序中,可以使用协程来并行处理多个用户的邮件发送请求,以提高邮件发送速度。
- 在多个服务之间进行通信时,使用协程来并发地执行通信操作。例如,在一个微服务架构的应用程序中,可以使用协程来并发地执行多个服务之间的通信,以提高整个应用程序的性能。
需要注意的是,Golang协程非常适合处理高并发的场景,可以应用于各种类型的应用程序和场景中。
首先定义一个数据库查询方法; 然后当一个http请求进来, 我们先获取一个空闲的协程, 然后调用数据库查询方法; 如果当前没有空闲协程并且协程池中最多不能超过10个协程的情况下创建新的协程后调用数据库查询方法, 否则等待新的空闲协程后调用数据库查询方法. 每一个协程使用完成后都应该释放.
// 定义数据库查询方法
func queryFromDB() {
// TODO: 实现具体的数据库查询逻辑
}
func main() {
// 定义协程池
pool := make(chan struct{}, 10)
// 处理HTTP请求
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 尝试获取一个空闲协程
select {
case pool <- struct{}{}:
// 获取到空闲协程,使用该协程执行查询
go func() {
queryFromDB()
<-pool // 查询完成后释放协程
}()
default:
// 没有空闲协程,检查当前协程池中的协程数是否超过了10个
if len(pool) < cap(pool) {
// 创建新的协程执行查询
go func() {
pool <- struct{}{}
queryFromDB()
<-pool // 查询完成后释放协程
}()
} else {
// 协程池已满,等待空闲协程
<-pool
go func() {
queryFromDB()
<-pool // 查询完成后释放协程
}()
}
}
})
// 启动HTTP服务
http.ListenAndServe(":8080", nil)
}
在这个实现中,我们首先定义了一个协程池 pool,它的容量是 10,代表了最多可以同时运行的协程数。每次处理一个 HTTP 请求时,我们先尝试从协程池中获取一个空闲的协程。如果能获取到,就使用该协程执行数据库查询。查询完成后,我们将该协程释放,以便其它请求可以使用它。
如果协程池中没有空闲的协程,我们检查当前协程池中的协程数是否超过了 10 个。如果没有超过 10 个,我们就创建新的协程执行查询。如果已经有 10 个协程在运行,我们就等待其中任意一个协程结束,然后将其释放,以便新的请求可以使用它。无论是新创建的协程还是空闲的协程,都要在查询完成后将自己释放回协程池中。