由于公司的业务需要,我跟着公司的大神学习了Go语言,Go高并发性能使其慢慢变成了服务端开发的主流语言,在做Go开发的过程中我也深深被其强大所吸引。下面主要介绍一下我学习的重点。
配置环境变量
MacOS
-
官网直接下载对应操作系统的安装包, 点击安装即可(也可以下载源解压).
-
安装包安装完成之后配置环境变量. 具体步骤如下
-
打开
.bash_profile
文件(目录路径:~/
) -
复制如下配置到 .bash_profile 中
1
2export GOROOT=/usr/local/go export PATH=$PATH:$GOROOT/bin
-
-
执行
source ~/.bash_profile
让配置生效
查看go的版本
go version
如果输出go version gox.x.x darwin/amd64
, 说明go的完成以上
查看go的环境配置
go env
如果go env
有正确的输出, 那么go的安装和环境配置就完成了.
创建第一个 go
项目
终端
-
选择一个目录, 新建一个
hello.go
文件, 然后在其中写入如下代码:1
2
3
4
5
6
7
8
9
10//包名一定要命名为main, 并且要包含main函数, 否则无法运行 package main import ( "fmt" ) func main() { fmt.Println("hello"); }
注意: ==包名(package)一定要命名为main, 并且要包含main函数, 否则无法运行==
GoLand
-
使用GoLand创建一个新的项目
-
然后作相应的配置, 配置好之后如下图:
-
配置路径,具体配置方式请参考点击查看
配置完之后就可以点击运行了.
包的引用
包引用时的寻找路径为根目录下的 src
文件件, 所以在构建工程的时候最好的办法是构建一个 src 文件夹, 用来存放所有自己编写的代码.
第三方包可以在 gopath中配置多个目录, 然后在该目录新建src文件夹, 在此文件夹中存放第三方的包
细小知识点
- 每一个文件夹代表一个包, 这个文件夹下面的所有报名必须一致
godoc
print, printf, println, sprint, sprintf, sprintln, fprint, fprintln, fprintf
log
设置log文件路径:
logfile, err := os.OpenFile(“test.log”, os.O_CREATE | os.O_WRONLY | os.O_APPEND, os.ModePerm) if err != nil { log.Fatalln(“file open failed”) }
1 |
|
文件读写
读写模式
代码 | 含义 |
---|---|
os.O_RDONLY | 以只读的方式打开 |
os.O_WRONLY | 以只写的方式打开 |
os.O_RDWR | 以读写的方式打开 |
os.O_NONBLOCK | 打开时不阻塞 |
os.O_APPEND | 以追加的方式打开 |
os.O_CREAT | 创建并打开一个新文件 |
os.O_TRUNC | 打开一个文件并截断它的长度为零(必须有写权限) |
os.O_EXCL | 如果指定的文件存在,返回错误 |
os.O_SHLOCK | 自动获取共享锁 |
os.O_EXLOCK | 自动获取独立锁 |
os.O_DIRECT | 消除或减少缓存效果 |
os.O_FSYNC | 同步写入 |
os.O_NOFOLLOW | 不追踪软链接 |
读写权限
ModeDir FileMode = 1 « (32 - 1 - iota) // d: is a directory 文件夹模式 ModeAppend // a: append-only 追加模式 ModeExclusive // l: exclusive use 单独使用 ModeTemporary // T: temporary file (not backed up) 临时文件 ModeSymlink // L: symbolic link 象征性的关联 ModeDevice // D: device file 设备文件 ModeNamedPipe // p: named pipe (FIFO) 命名管道 ModeSocket // S: Unix domain socket Unix 主机 socket ModeSetuid // u: setuid 设置uid ModeSetgid // g: setgid 设置gid ModeCharDevice // c: Unix character device, when ModeDevice is set Unix 字符设备,当设备模式是设置Unix ModeSticky // t: sticky 粘性的 // Mask for the type bits. For regular files, none will be set. bit位遮盖.不变的文件设置为none ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice ModePerm FileMode = 0777 // Unix permission bits 权限位.
文件写入的模式
命令行参数
所属包: os
获取命令行参数(切片) os.Args
. (注意: Args[0]
就是执行的命令本身。)
命令行传递参数: go run xxx.go "参数1" "参数2" ...
go 命令行
描述 | 指令 | 参数 |
---|---|---|
标准库函数列表 | go doc xxx | xxx代表库名 |
当前工程的环境配置 | go env | |
执行go的可执行文件 | go run xxx.go | xxx.go必须包含main函数 |
快捷键
描述 | 快捷键 |
---|---|
查看api提示 | ctrl + J |
跳转api源代码 | cmd + B |
代码格式化 | opt + cmd + L |
代码块向上移动一行 | shift + cmd + ↑ |
代码块向下移动一行 | shift + cmd + ↓ |
go输出占位符
普通占位符
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%v | 相应值的默认格式。 | Printf(“%v”, people) | {zhangsan} |
%+v | 打印结构体时,会添加字段名 | Printf(“%+v”, people) | {Name:zhangsan} |
%#v | 相应值的Go语法表示 | Printf(“#v”, people) | main.Human{Name:”zhangsan”} |
%T | 相应值的类型的Go语法表示 | Printf(“%T”, people) | main.Human |
%% | 字面上的百分号,并非值的占位符 | Printf(“%%”) | % |
布尔占位符
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%t | true 或 false。 | Printf(“%t”, true) | true |
整数占位符
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%b | 二进制表示 | Printf(“%b”, 5) | 101 |
%c | 相应Unicode码点所表示的字符 | Printf(“%c”, 0x4E2D) | 中 |
%d | 十进制表示 | Printf(“%d”, 0x12) | 18 |
%o | 八进制表示 | Printf(“%d”, 10) | 12 |
%q | 单引号围绕的字符字面值,由Go语法安全地转义 | Printf(“%q”, 0x4E2D) | ‘中’ |
%x | 十六进制表示,字母形式为小写 a-f | Printf(“%x”, 13) | d |
%X | 十六进制表示,字母形式为大写 A-F | Printf(“%x”, 13) | D |
%U | Unicode格式:U+1234,等同于 “U+%04X” | Printf(“%U”, 0x4E2D) | U+4E2D |
浮点数和复数的组成部分(实部和虚部)
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%b | 无小数部分的,指数为二的幂的科学计数法,与 strconv.FormatFloat 的 ‘b’ 转换格式一致。例如 -123456p-78 | - | - |
%e | 科学计数法,例如 -1234.456e+78 | Printf(“%e”, 10.2) | 1.020000e+01 |
%E | 科学计数法,例如 -1234.456E+78 | Printf(“%e”, 10.2) | 1.020000E+01 |
%f | 有小数点而无指数,例如 123.456 | Printf(“%f”, 10.2) | 10.200000 |
%g | 根据情况选择 %e 或 %f 以产生更紧凑的(无末尾的0)输出 | Printf(“%g”, 10.20) | 10.2 |
%G | 根据情况选择 %E 或 %f 以产生更紧凑的(无末尾的0)输出 | Printf(“%G”, 10.20+2i) | (10.2+2i) |
字符串与字节切片
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%s | 输出字符串表示(string类型或[]byte) | Printf(“%s”, []byte(“Go语言”)) | Go语言 |
%q | 双引号围绕的字符串,由Go语法安全地转义 | Printf(“%q”, “Go语言”) | “Go语言” |
%x | 十六进制,小写字母,每字节两个字符 | Printf(“%x”, “golang”) | 676f6c616e67 |
%X | 十六进制,大写字母,每字节两个字符 | Printf(“%X”, “golang”) | 676F6C616E67 |
指针
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%p | 十六进制表示,前缀 0x | Printf(“%p”, &people) | 0x4f57f0 |
引用类型和值类型
-
值类型:
int、float、bool,string, array(数组)和(struct)结构体
, 这些类型都属于值类型,使用这些类型的变量直接指向存在内存中的值,值类型的变量的值存储在栈中。当使用等号=将一个变量的值赋给另一个变量时,如j = i
,实际上是在内存中将i
的值进行了拷贝。可以通过&i
获取变量i
的内存地址。 值拷贝 -
引用类型:特指
slice、map、channel
这三种预定义类型。引用类型拥有更复杂的存储结构:- (1)分配内存
- (2)初始化一系列属性等一个引用类型的变量
r1
存储的是r1
的值所在的内存地址(数字),或内存地址中第一个字所在的位置,这个内存地址被称之为指针,这个指针实际上也被存在另外的某一个字中。
go中单引号和双引号的区别
- 单引号是 byte 类型 ‘\n’
- 双引号是字符穿类型 “\n”
goland 文件头注释
打开GoLand的setting选项
依次选择Editor,CodeStyle ,File and Code Templates ,Go File
根据自己需要添加即可
1 |
|
go linux打包命令
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build xxx
go windows打包命令
CGO_ENABLED=1 GOOS=windows GOARCH= go build xxx
go调用JS
goja性能比otto快6-7倍
time包
time包里面主要包含有 timer 和 ticker,
- 计时器(Timer)的原理和倒计时闹钟类似,都是给定多少时间后触发。
- 打点器(Ticker)的原理和钟表类似,钟表每到整点就会触发。这两种方法创建后会返回
time.Ticker 对象和 time.Timer 对象,里面通过一个 C
成员,类型是只能接收的时间通道(<-chan Time),使用这个通道就可以获得时间触发的通知. 案例如下:
1 |
|
mqtt使用方式
多个类型可以实现相同的接口
==一个接口的方法,不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其他类型或者结构体来实现。也就是说,使用者并不关心某个接口的方法是通过一个类型完全实现的,还是通过多个结构嵌入到一个结构体中拼凑起来共同实现的==
Service 接口定义了两个方法:一个是开启服务的方法(Start()),一个是输出日志的方法(Log())。使用 GameService 结构体来实现 Service,GameService 自己的结构只能实现 Start() 方法,而 Service 接口中的 Log() 方法已经被一个能输出日志的日志器(Logger)实现了,无须再进行 GameService 封装,或者重新实现一遍。所以,选择将 Logger 嵌入到 GameService 能最大程度地避免代码冗余,简化代码结构。详细实现过程如下:
1 |
|
代码说明如下:
- 第 2 行,定义服务接口,一个服务需要实现 Start() 方法和日志方法。
- 第 8 行,定义能输出日志的日志器结构。
- 第 12 行,为 Logger 添加 Log() 方法,同时实现 Service 的 Log() 方法。
- 第 17 行,定义 GameService 结构。
- 第 18 行,在 GameService 中嵌入 Logger 日志器,以实现日志功能。
- 第 22 行,GameService 的 Start() 方法实现了 Service 的 Start() 方法。
此时,实例化 GameService,并将实例赋给 Service,代码如下:
1 |
|
s 就可以使用 Start() 方法和 Log() 方法,其中,Start() 由 GameService 实现,Log() 方法由 Logger 实现。
结构体实例化的三种方式
一. 基本的实例化形式
结构体本身是一种类型,可以像整型、字符串等类型一样,以 var 的方式声明结构体即可完成实例化。
基本实例化格式如下:
1 |
|
其中,T 为结构体类型,ins 为结构体的实例。
用结构体表示的点结构(Point)的实例化过程请参见下面的代码:
1 |
|
在例子中,使用.来访问结构体的成员变量,如p.X和p.Y等,结构体成员变量的赋值方法与普通变量一致。
二. 创建指针类型的结构体
Go语言中,还可以使用 new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。
使用 new 的格式如下:
1 |
|
其中: T 为类型,可以是结构体、整型、字符串等。 ins:T 类型被实例化后保存到 ins 变量中,ins 的类型为 *T,属于指针。
Go语言让我们可以像访问普通结构体一样使用.来访问结构体指针的成员。
下面的例子定义了一个玩家(Player)的结构,玩家拥有名字、生命值和魔法值,实例化玩家(Player)结构体后,可对成员进行赋值,代码如下:
1 |
|
经过 new 实例化的结构体实例在成员赋值上与基本实例化的写法一致。 Go语言和 C/C++ 在 C/C++ 语言中,使用 new 实例化类型后,访问其成员变量时必须使用->操作符。
在Go语言中,访问结构体指针的成员变量时可以继续使用.,这是因为Go语言为了方便开发者访问结构体指针的成员变量,使用了语法糖(Syntactic sugar)技术,将 ins.Name 形式转换为 (*ins).Name。
三. 取结构体的地址实例化
在Go语言中,对结构体进行&取地址操作时,视为对该类型进行一次 new 的实例化操作,取地址格式如下:
1 |
|
其中: T 表示结构体类型。 ins 为结构体的实例,类型为 *T,是指针类型。
下面使用结构体定义一个命令行指令(Command),指令中包含名称、变量关联和注释等,对 Command 进行指针地址的实例化,并完成赋值过程,代码如下: 纯文本复制
1 |
|
通道
一.非阻塞接收数据
使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:
1 |
|
非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行
select
Go语言没有提供直接的超时处理机制,所谓超时可以理解为当我们上网浏览一些网站时,如果一段时间之后不作操作,就需要重新登录。
那么我们应该如何实现这一功能呢,这时就可以使用 select 来设置超时。
虽然 select 机制不是专门为超时而设计的,却能很方便的解决超时问题,因为 select 的特点是只要其中有一个 case 已经完成,程序就会继续往下执行,而不会考虑其他 case 的情况。
超时机制本身虽然也会带来一些问题,比如在运行比较快的机器或者高速的网络上运行正常的程序,到了慢速的机器或者网络上运行就会出问题,从而出现结果不一致的现象,但从根本上来说,解决死锁问题的价值要远大于所带来的问题。
select 的用法与 switch 语言非常类似,由 select 开始一个新的选择块,每个选择条件由 case 语句来描述。
与 switch 语句相比***,select 有比较多的限制,其中最大的一条限制就是每个 case 语句里必须是一个 IO 操作***,大致的结构如下:
1 |
|
在一个 select 语句中,Go语言会按顺序从头至尾评估每一个发送和接收的语句。
如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。
如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有如下两种可能的情况: 如果给出了 default 语句,那么就会执行 default 语句,同时程序的执行会从 select 语句后的语句中恢复; 如果没有 default 语句,那么 select 语句将被阻塞,直到至少有一个通信可以进行下去。
附录一
goland中使用 go get 无法下载第三方库
解决办法: 可以使用 git clone xxx
将该库下载到本地, 然后复制到对应的gopath
目录中即可
goland中配置多个gopath
路径一: 第三方库都放在thirdLibs文件夹下的src目录中, 路径二: 项目根目录
如果路径二在路径一的上面配置, 那么使用 go get xxx 下载新包时, 默认会在最上面的路径中去自动创建一路库目录, 所以不会下载到路径一种, 解决办法是将路径一设置在最上面