不求外物,不惧内心,不避因果
1. 简述
1.1 背景
Go语言自己的早期源码使用C语言和汇编语言写成
Go的三个作者分别是:Rob Pike(罗伯.派克),Ken Thompson(肯.汤普森)和Robert Griesemer(罗伯特.格利茨默)
2009年11月 GO语言第一个版本发布。2012年3月 第一个正式版本Go1.0发布
到go1.8时,相同业务场景下的GC时延已经可以从go1.1的数秒,控制在1ms以内。GC问题的解决,可以说GO语言在服务端开发方面,几乎抹平了所有的弱点
一家叫做 Docker 的公司。就是使用 Go 进行项目开发,并促进了计算机领域的容器行业,进而出现了像 Kubernetes 这样的项目
1.2 定义
将简单、实用体现得淋漓尽致的一种编程语言
1.3 GO项目
Docker 是一种操作系统层面的虚拟化技术,可以在操作系统和应用程序之间进行隔离,也可以称之为容器。Docker 可以在一台物理服务器上快速运行一个或多个实例。例如,启动一个 CentOS 操作系统,并在其内部命令行执行指令后结束,整个过程就像自己在操作系统一样高效。
Go语言自己的早期源码使用C语言和汇编语言写成。从 Go 1.5 版本后,完全使用Go语言自身进行编写。Go语言的源码对了解Go语言的底层调度有极大的参考意义,建议希望对Go语言有深入了解的读者读一读。
Google 公司开发的构建于 Docker 之上的容器调度服务,用户可以通过 Kubernetes 集群进行云端容器集群管理。系统会自动选取合适的工作节点来执行具体的容器集群调度处理工作。其核心概念是 Container Pod(容器仓)
一款分布式、可靠的 KV 存储系统,可以快速进行云配置。由 CoreOS 开发并维护键值存储系统,它使用Go语言编写,并通过 Raft 一致性算法处理日志复制以保证强一致性
beego 是一个类似 Python 的 Tornado 框架,采用了 RESTFul 的设计思路,使用Go语言编写的一个极轻量级、高可伸缩性和高性能的 Web 应用框架
一款快速构建模块化的 Web 应用的Go语言框架
国产的优秀分布式 Redis 解决方案。可以将 codis 理解成为 Web 服务领域的 Nginx,它实现了对 Redis 的反向代理和负载均衡。
Go语言强大的调试器,被很多集成环境和编辑器整合。
1.4 用处
- 区块链
- 云平台
- 嵌入式
- 网络编程
- 服务器编程
1.5 编程库
Go语言标准库包名 | 功 能 |
---|---|
bufio | 带缓冲的 I/O 操作 |
bytes | 实现字节操作 |
container | 封装堆、列表和环形列表等容器 |
crypto | 加密算法 |
database | 数据库驱动和接口 |
debug | 各种调试文件格式访问及调试功能 |
encoding | 常见算法如 JSON、XML、Base64 等 |
flag | 命令行解析 |
fmt | 格式化操作 |
go | Go语言的词法、语法树、类型等。可通过这个包进行代码信息提取和修改 |
html | HTML 转义及模板系统 |
image | 常见图形格式的访问及生成 |
io | 实现 I/O 原始访问接口及访问封装 |
math | 数学库 |
net | 网络库,支持 Socket、HTTP、邮件、RPC、SMTP 等 |
os | 操作系统平台不依赖平台操作封装 |
path | 兼容各操作系统的路径操作实用函数 |
plugin | Go 1.7 加入的插件系统。支持将代码编译为插件,按需加载 |
reflect | 语言反射支持。可以动态获得代码中的类型信息,获取和修改变量的值 |
regexp | 正则表达式封装 |
runtime | 运行时接口 |
sort | 排序接口 |
strings | 字符串转换、解析及实用函数 |
time | 时间接口 |
text | 文本模板及 Token 词法器 |
2. 下载和环境
尽量使用压缩包安装方式,注意GoWorks工作目录下三个包 bin src pkg
2.1 下载go
1 | # 安装目录 |
- 进行检测是否成功
1 | go env |
开发工具:idea、vscode、Go Reviverevive、Goland
vscode需要安装go工具包,在工作/bin目录下,运行F5
程序名 | 程序用途 |
---|---|
dlv.exe | go 语言调试工具 |
gocode.exe | go语言代码检查,自动补全 |
godef.exe | go语言代码定义和引用的跳转 |
golint.exe | go语言代码规范检查 |
go-outline.exe | 用于在Go源文件中提取JSON形式声明的简单工具 |
gopkgs.exe | 快速列出可用包的工具 |
gorename.exe | 在Go源代码中执行标识符的精确类型安全重命名 |
goreturns.exe | 类似fmt和import的工具,使用零值填充Go返回语句以匹配func返回类型 |
go-symbols.exe | 从go源码树中提取JSON形式的包符号的工具 |
gotour.exe | go语言指南网页版 |
guru.exe | go语言源代码有关工具,如代码高亮等 |
3. Go基础
3.1 hello.go
在Go语言里,命名为main的包具有特殊的含义。Go语言的编译程序会试图把这种名字的包编译为二进制可执行文件。所有用Go语言编译的可执行程序都必须有一个名叫main的包,一个可执行程序 有且仅有一个 main包
1 | package main |
1 | go run hello.go |
当编译器发现某个包的名字为main时,它一定也会发现名为main()的函数,否则不会创建可执行文件。main()函数是程序的入口,所以,如果没有这个函数,程序就没有办法开始执行。程序编译时,会使用声明main包的代码所在的目录的目录名作为二进制可执行文件的文件名。
3.2 Go注释
方便其他人理解代码
1 | //我是单行注释 |
3.3 变量
能够指向内存地址的可以变化的值,Go语言是静态类型语言
- 变量的定义(声明)
- 格式
1 | var [name变量名] [type变量类型] |
- eg
1 | var ( |
- 初始化
- 格式
1 | //变量声明 |
- eg
1 | name = "dd" |
- 声明并初始化
由于使用了 := ,而不是赋值的 = ,因此推导声明写法的左值必须是没有被定义过的,若定义过,将会发生编译错误
- 格式
1 | [name未声明变量名] := [value变量值] |
- eg
1 | name := "dd" |
- 匿名变量
匿名变量的特点是一个下划线 “_”,但任何赋给这个标识符的值都将被抛弃,可以极大地增强代码的灵活性,匿名变量不占用内存空间,不会分配内存。
- 格式
1 | _ := [func/value] |
- eg
1 | func test() (int, int) { |
- 变量作用域
一个变量(常量、类型或函数)在程序中都有一定的作用范围,称之为作用域,遵循就近原则
- 局部变量
在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,函数的参数和返回值变量都属于局部变量
- 全局变量
在函数体外声明的变量称之为全局变量,只需要在一个源文件中定义,就可以在所有源文件中使用,当然,不包含这个全局变量的源文件需要使用 import 关键字 引入
全局变量必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写
- 注意
Go语言程序中全局变量与局部变量名称可以相同,但是函数体内的局部变量会被优先考虑!
3.4 常量
是一个简单值得标识符,在程序运行时,不会被修改的量
- 普通常量
- 格式
1 | const [name常量名] [type] = [value] |
- eg
1 | const url string = "ssss.xx" |
- 特殊常量iota
未赋值的常量向前面的常量寻找
- 格式
1 | const [name常量名] [type] = iota |
- eg
1 | const ( |
3.5 数据类型
- 布尔型
1 | var isFlang bool //默认false |
- 数字型
- 整数型
1 | var age int//默认0 |
序号 | 类型和描述 |
---|---|
1 | uint8无符号8位整型(O到255) |
2 | uint16无符号16位整型(O到65535) |
3 | uint32无符号32位整型(O到4294967295) |
4 | uint64无符号64位整型(0到18446744073709551615) |
5 | int8有符号8位整型(-128到127) |
6 | int16有符号16位整型(-32768到32767) |
7 | int32有符号32位整型(-2147483648到2147483647) |
8 | int64有符号64位整型(-9223372036854775808到 9223372036854775807) |
- 浮点型
float32 不要进行运算,会精度损失
1 | var money float64 |
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 lEEE-75464位浮点型数 |
3 | complex64 32位实数和虚数 |
4 | complex128 64位实数和虚数 |
- 其他别名
序号 | 类型和描述 |
---|---|
1 | byte类似于uint8 |
2 | rune类似于int32 |
3 | uInt32或64位 |
4 | int与uint一样大小 |
5 | uintprt无符号整形,用于存放一个指针 |
- 字符
单引号
1 | //英文编码表 ASCII字符表 |
- 字符串
双引号
1 | v2 := "在" |
- 字符串连接和转义
1 | fmt.Printf("%T,%s", v2, v2+"gg\"ff") |
3.6 数据类型转换
数据类型转换都为显示,Go语言不存在隐式类型转换,每个分配的内存不同,bool类型和整数类型不能相互转换
1 | c := float64(a) |
3.7 运算符
- 算术运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 相加 | A+B输出结果30 |
- | 相减 | A-B输出结果-10 |
* | 相乘 | A*B输出结果为200 |
/ | 相除 | B/A输出结果为2 |
% | 求余 | B%A输出结果为0 |
++ | 自增 | A++输出结果11 |
– | 自减 | A–输出结果为9 |
1 | v1 := 10 |
- 关系运算符
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个值是否相等,如果相等返回True否则返回false | A==B 为false |
!= | 检查两个值是否不相等,如果不相等返回True否则返回false | A!=B为true |
> | 检查左边值是否大于右边值,如果是返回True否则返回false | A>B 为false |
< | 检查左边值是否小于右边值,如果是返回True否则返回false | A<B为True |
>= | 检查左边值是否大于等于右边值,如果是返回True否则返回false | A>=B 为false |
<= | 检查左边值是否小于等于右边值,如果是返回True否则返回false | A<=B 为true |
- 逻辑运算符
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑AND运算符,如果两边的操作数都是True,则条件True,否则为False。 | A&&B 为false |
|| | 逻辑OR运算符,如果两边的操作数有一个True,则条件True,否则为False。 | A||B为true |
! | 逻辑NOT运算符,如果条件为True,则逻辑NOT条件False,否则为True。 | !(A&&B )为true |
- 位运算符
加密、解密、底层
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符”&”是双目运算符。都是1结果为1,否则是0 | (A&B)结果为12,二进制为0000 1100 |
| | 按位或运算符”|”是双目运算符。只要有一个1就为1,都是0结果为0,否则是1 | (A |B)结果为61,二进制0011 1101 |
^ | 按位异或运算符”A”是双目运算符。不同则为1,相同为0 | (A^B)结果为49,二进制为0011 0001 |
&^ | 位清空,a &^b,对于b上的每个数值,如果为0,则取a对应位上的数值,如果为1,则取0. | (A&^B)结果为48,二进制为0011 0000 |
<< | 左移运算符”<<”是双目运算符。左移n位就是乘以2的n次方。其功能把”<<”左边的运算数的各二进位全部左移若干位,由”<<”右边的数指定移动的位数,高位丢弃,低位补O。 | A<<2结果为240,二进制为1111 0000 |
>> | 右移运算符”>>”是双目运算符。右移n位就是除以2的n次方。其功能是把”>>”左边的运算数的各二进位全部右移若干位,”>>”右边的数指定移动的位数。 | A>>2结果为15,二进制为0000 1111 |
1 | v1 := 10 |
- 赋值运算符
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 | C =A+B将A+B表达式结果赋值给C |
+= | 相加后再赋值 | C +=A等于C=C+A |
-= | 相减后再赋值 | C -=A等于C=C-A |
*= | 相乘后再赋值 | C *=A等于C=C *A |
/= | 相除后再赋值 | C/=A等于C=C/ A |
%= | 求余后再赋值 | C%=A等于C=C%A |
<<= | 左移后赋值 | C<<=2等于C=C<<2 |
>>= | 右移后赋值 | C>>=2等于C=c>>2 |
&= | 按位与后赋值 | C&=2等于C=C&2 |
^= | 按位异或后赋值 | C=2等于C=C2 |
!= | 按位或后赋值 | C|=2等于C=C|=2 |
- 其他运算符
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量存储地址 | &a;将给出变量的实际地址 |
* | 指针变量 | *a;是一个指针变量 |
3.8 输入输出
1 | var x int |
3.9 编码规范
当命名(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的public) ;
命名如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的private )
保持package的名字和目录名保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库不要冲突。包名应该为小写单词,不要使用下划线或者混合大小写。
尽量采取有意义的文件名,简短,有意义,应该为小写单词,使用下划线分隔各个单词(蛇形命名)。
3.10 流程控制
- 顺序结构
:从上到下,逐行执行。默认的逻辑
- 选择结构
- if
1 | a := 2 |
- switch
1 | a := 2 |
- select
- 循环结构
- for
1 | for i := 1; i <= 9; i++ { |
- break与continue
break 跳出,continue结束当前继续
3.11 string
string字符串里面字符是不能修改的,Go中的字符串是一个字节的切片,可以通过将其内容封装在””中来创建字符串,Go中的字符串Unicode兼容的,并且是UTF-8编码,字符串是一些字节的集合
1 | str := "xx xy" |
3.12 函数
- 定义
函数的名称,返回类型和参数的代码块
- 格式
1 | func [函数名](函数参数...)(函数返回值) { |
- 可变参数函数
如果一个函数的参数时可变参数,同时还有其他的参数,可变参数要放在列表的最后,一个函数的参数列表最多只能有一个可变参数
1 | func myfunc(nums ...int) { |
- 参数传递
- 值传递[深克隆]:数值,布尔,数组,字符串
1 | arr := [4]int{1, 2, 3, 4} |
- 引用传递【不会拷贝,传送是地址】:slice,map,chan
1 | s1 := []int{1, 2, 3, 4} |
- 递归函数
十分耗费内存,不建议使用
1 | sum := digui(5) |
- defer函数
推迟、延迟最后执行,先进后出,类似于栈,参数按照顺序传送进去,延迟执行
1 | func main() { |
- 函数数据类型(引用数据类型)
函数在Go语言中是复合类型,可以看做是一种特殊的变量。函数名()︰调用返回结果, 函数名:指向函数体的内存地址,一种特殊类型的指针变量
1 | /* |
- 匿名函数
Go语言是支持函数式编程:
1、将匿名函数作为另外一个函数的参数,回调函数
2、将匿名函数作为另外一个函数的返回值,可以形成闭包结构
1 | func(a, b int) { |
- 回调函数
将函数作为参数传入另外函数
1 | func main() { |
- 闭包
个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量并且该外层函数的返回值就是这个内层函数。
这个内层函数和外层函数的局部变量,统称为闭包结构。
局部变量的生命周期就会发生改变,正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用。
1 | package main |
3.13 泛型
Go的1.18版本包括了参数类型参数的实现,也就是俗称的泛型,不限制传进去的参数类型Type,减少重复逻辑
- 发现问题
1 | func printIntSlice(arr interface{}) { |
- 改变代码
1 | fanxing1(s1) |
- 泛型类型
1 | type slice[T int|float32|float64][]T |
Map 中的KEY和VALUE是类型形参
int|string 是KEY的类型约束, float32 | float64是VALUE的类型约束
KEY int|string, VALUE float32| float64整个一串文本因为定义了所有形参所以被称为类型形参列表Map[KEY,VALUE]是泛型类型,类型的名字就叫Map[KEY,VALUE]
var a MyMap[string,float64]= xx 中的string和float64是类型实参,用于分别替换KEY和VALUE,实例化出了具体的类型MyMap[string, float64]
- 泛型函数
1 | // 1、定义泛型切片 |
- 自定义泛型类型
1 | //1、定义接口,约束类型为int |
4. GO Web开发
4.1 web开发框架
- Beego
- Gin
- Echo
- Iris
4.2 Gin开发
Gin 是一个用 Go (Golang) 编写的 HTTP Web 框架。 它具有类似 Martini 的 API,但性能比 Martini 快 40 倍。
- 下载gin框架
1 | # 得到go.mod文件 |
- 进行开发
- 前端
1 |
|
- 后端
1 | package main |
4.3 xorm开发
- 简述
xorm是一个Go语言的ORM库
- 开发环境
- 初始化
1 | go mod init go.mod |
- xorm开发
import 下划线(如:_ “github.com/go-sql-driver/mysql”)的作用:当导入一个包时,该包下的文件里所有 init() 函数 都会被执行
import 和引用的包名之间加点(.)操作的含义就是这个包导入之后在调用这个包的函数时,可以省略前缀的包名 import . “fmt”
别名操作顾名思义可以把包命名成另一个用起来容易记忆的名字import f “fmt”
1 | package main |
4.4 gRPC
- 简述
一个高性能、开源的通用RPC框架
- 优点
- 更高效的进程通信:使用基于protocol buffer在Http2 中以二进制协议通信,而不是JSON、XML文本格式,序列化和反序列化
- 简单定义的服务接口、易扩展
- 强类型、跨语言
- 一元RPC、服务端流、客户端流、双工流
- 下载和环境配置
1 | # 配置系统环境Path |
Protocol Buffers一种数据描述语言编译器,不依赖于语言和平台并且可扩展性极强,广泛用于各种结构化信息存储和交换
- 安装grpc核心库
1 | # 初始化 |
- 安装go的protoc代码生成工具库
已经下载的可以使用go install,没有下载用go get
1 | go install google.golang.org/protobuf/cmd/protoc-gen-go@latest |
完成后可以查看自己gowork目录下bin有没有exe文件
- 编写proto文件
1 | //这是说明我们使用的是proto3语法 |
- 在/proto目录下执行,会在/proto生成go文件
1 | # --go_out不是命令,加配置环境path %GOPATH%\bin |
- proto文件介绍
- message
message:protobuf 中定义一个消息类型式是通过关键字message字段指定的。消息就是需要传输数据格式定义。message 关键字类似于C++中的class,JAVA中的class,go中的struct。在消息中承载的数据分别对应于每一个字段,其中每个字段都有一个名字和一种类型,一个proto文件中可以定义多个消息类型
- 字段规则
optional:消息体中的可选字段,protobuf3没有了required,optional等说明关键字,都默认为optional,
repeate:消息体中可重复字段,重复的值的顺序会被保留着go中重复的回被定义为切片
- 消息号
在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号是[1,2^29-1]范围内的一个整数。
- 嵌套信息
1 | message PersonInfo{ |
- 服务定义
如果想要将消息类型用在PRC系统中,可以在==.proto文件中定义一个RPC服务接口==,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根
1 | service SayHello{ |
- 服务端编写
- 创建gRPC Server 对象,你可以理解为它是Server端的抽象对象
- 将server(其包含需要被调用的服务端接口)注册到gRPC Server的内部注册中心,这样可以在接受到请求时,通过内部的服务发现,发现该服务端接口并转接进行逻辑处理
- 创建Listen,监听TCP端口
- gRPC Server 开始 lis.Accept,知道Stop
无法导入
1 | go mod tidy |
1 | package main |
- 客户端编写
- 创建与给定目标(服务端)的连接交互
- 创建server的客户端对象
- 发送RPC请求,等待同步响应,得到回调后返回响应结果
- 输出响应结果
1 | package main |
- 认证-安全
gRPC 通常默认是使用 protobuf 来作为传输协议,grpc将各种认证方式浓缩统一到一个凭证上,可以单独使用一种凭证,也可以多种凭证组合,提供统一的API认证方式
- 方式
- SSL/TLS 认证方式(采用 http2 协议)
- 基于 Token 的认证方式(基于安全连接)
- 不采用任何措施的连接,这是不安全的连接(默认采用 http1)
- 自定义的身份认证
- SSL/TLS认证方式
证书参数
- key:服务器上的私钥文件,用于对发送给客户端数据的加密,以及对从客户端接收到的数据解密
- csr:证书签名请求文件,用于提交给证书颁发机构(CA) 对证书签名
- crt:由证书颁发机构(CA) 签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有人的公钥,以及签名者的签名等信息
- pem: 是基于Base64编码的证书格式,扩展名包括PEM、CRT和CER
生成参数
1 | # 1、生成私钥 |
- 更改openssl.cnf
1 | # 更改openssl.cnf |
- 生成证书
1 | #生成证书私钥test.key |
- Server端
1 | package main |
- Client端
1 | package main |
- Token认证
- Server端
1 | package main |
- client端
1 | package main |
5. 总结
了解最基础的Go语言,语言只是工具,思想永远最重要