模块一:Go语言特性
统一思想-12 factors
-
I. 基准代码
一份基准代码,多份部署 -
II. 依赖
显式声明依赖关系 -
III. 配置
在环境中存储配置 -
IV. 后端服务
把后端服务当作附加资源 -
V. 构建,发布,运行
严格分离构建和运行 -
VI. 进程
以一个或多个无状态进程运行应用 -
VII. 端口绑定
通过端口绑定提供服务 -
VIII. 并发
通过进程模型进行扩展 -
IX. 易处理
快速启动和优雅终止可最大化健壮性 -
X. 开发环境与线上环境等价
尽可能的保持开发,预发布,线上环境相同 -
XI. 日志
把日志当作事件流 -
XII. 管理进程
后台管理任务当作一次性进程运行
一,为什么需要另外一种语言
语言原则
- Less is exponentially more
- Do Less, Enable More
为什么需要Go语言
-
其他编程语言的弊端。
-
Go语言是一个可以编译高效,支持高并发的,面向垃圾回收的全新语言。\
特性:
• 秒级完成大型程序的单节点编译。
• 依赖管理清晰。
• 不支持继承,程序员无需花费精力定义不同类型之间的关系。
• 支持垃圾回收,支持并发执行,支持多线程通讯。
• 对多核计算机支持友好。
Go 语言不支持的特性
- 不支持函数重载和操作符重载。
- 为了避免在 C/C++ 开发中的一些 Bug 和混乱,不支持隐式转换。
- 支持接口抽象,不支持继承。
- 不支持动态加载代码。
- 不支持动态链接库。(所有依赖都可以打包在工程里)
- 通过 recover 和 panic 来替代异常机制。
- 不支持断言。
- 不支持静态变量 。
Go语言特性衍生来源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LJOCrJhJ-1632742431659)(images/image-20210916201905110.png)]
二,GO语言环境搭建
1,下载安装
-
go安装安装文件
https://golang.google.cn/dl/
-
下载安装二级制文件
-
重要环境配置
- GOROOT:Go的安装路径
- GOPATH: 项目路径
- src:存放源代码
- pkg:存放依赖包
- bin:存放可执行文件
- 其他变量
- GOOS,GOARCH,GOPROXY
- 国内建议设置 goproxy:export GOPROXY=https://goproxy.cn
-
查看本机环境变量: go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=E:\f.Coding\Golang\bin
set GOCACHE=C:\Users\Administrator\AppData\Local\go-build
set GOENV=C:\Users\Administrator\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=E:\f.Coding\Golang\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=E:\f.Coding\Golang
set GOPRIVATE=
set GOPROXY=https://goproxy.cn
set GOROOT=D:\Tools\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=D:\Tools\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.17
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=NUL
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\ADMINI~1\AppData\Local\Temp\go-build1871142733=/tmp/go-build -gno-record-gcc-switches
2,IDE设置(VS Code)
- 下载安装vscode
- 安装插件
- 其他可选项
- goland
- vim,sublime
3,一些基本命令
命令 | 含义 |
---|---|
install | compile and install packages and dependencies (编译安装包) |
list | list packages or modules |
mod | module maintenance( 依赖管理) |
run | compile and run Go program |
test | test packages (做测试,单元测试) |
tool | run specified go tool (工具测试) |
version | print Go version |
vet | report likely mistakes in packages( 代码静态,编译器检查不出来的错误(不一定导致程序出错,写法错误)) |
get | add dependencies to current module and install them(下载包到本地) |
Go build
-
go build main.go 编译
go build –o bin/mybinary .
GOOS= linux go build mian.go
-
fmt 给你格式化,空格
go fmt main.go
Go test
(1) 示例
import "testing"
func TestIncrease(t *testing.T) {
t.Log("Start testing")
increase(1, 2)
}
- go test ./… -v 运行测试
- go test命令扫描所有*_test.go为结尾的文件,惯例是将测试代码与正式代码放在同目录,
- 如 foo.go 的测试代码一般写在 foo_test.go .
Go vet
代码静态检查,发现可能的 bug 或者可疑的构造
- Print-format 错误,检查类型不匹配的print
str := “hello world!”
// str类型是字符串,格式化输出的时候应该用s%,这里用的d%
fmt.Printf("%d\n", str)
- Boolean 错误,检查一直为 true、false 或者冗余的表达式
//
fmt.Println(i != 0 || i != 1)
- Range 循环,比如如下代码主协程会先退出,go routine无法被执行
Unreachable的代码,如 return 之后的代码
//
words := []string{"foo", "bar", "baz"}
for _, word := range words {
go func() {
fmt.Println(word).
}()
}
- 其他错误,比如变量自赋值,error 检查滞后等
res, err := http.Get("https://www.spreadsheetdb.io/")
defer res.Body.Close()
if err != nil {
log.Fatal(err)
}
(1) 示例
package main
import (
"fmt"
)
func main() {
name := "testing"
fmt.Printf("%d\n", name)
fmt.Printf("%s\n", name, name)
}
PS E:\f.Coding\Golang\src\github.com\cncamp\golang\examples\module1\govet> go vet main.go
# command-line-arguments
.\main.go:9:2: Printf format %d has arg name of wrong type string
.\main.go:10:2: Printf call needs 1 arg but has 2 args
4,代码管理管理
-
下载并安装 Git Command Line
https://git-scm.com/downloads -
Github
- 本课程示例代码均上传在 https://github.com/cncamp/golang
- 创建代码目录
mkdir –p $GOPATH/src/github.com/cncamp cd $GOPATH/src/github.com/cncamp
-
代码下载
git clone https://github.com/cncamp/golang.git
-
修改代码
-
上传代码
git add filename git commit –m 'change logs' git push
5,Golang playground
-
官方 playground
https://play.golang.org/
可直接编写和运行 Go 语言程序 -
国内可直接访问的 playground
https://goplay.tool -
go指南
https://tour.go-zh.org/
三,控制结构
1,if 表达式
- 基本形式
if condition1 {
// do something
} else if condition2 {
// do something else
} else {
// catch-all or default
}
- 简短形式
- 同 for 一样, if 语句可以在条件表达式前执行一个简单的语句。
if v:=x-100; v<0{
return v
}
实例一:基本形式
package main
import (
"fmt"
"math"
)
func sqrt(x float64) string {
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}
func main() {
fmt.Println(sqrt(2), sqrt(-4))
}
示例二:简短形式
package main
import (
"fmt"
"math"
)
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
2,switch:
分支很多的情况,if的多条件
switch var1 {
case val1: //空分支
case val2:
fallthrough //执行case3中的f()
case val3:
f()
default: //默认分支
...
示例
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("When's Saturday?")
today := time.Now().Weekday()
switch time.Saturday {
case today + 0:
fmt.Println("Today.")
case today + 1:
fmt.Println("Tomorrow.")
case today + 2:
fmt.Println("In two days.")
default:
fmt.Println("Too far away.")
}
}
特别注意
1,golang中switch每个case中默认自带break,执行完后就退出了,不会执行下面条件。
2,fallthrouth意思是不退出继续执行下面的语句。
3,for 循环
go 语言只有一种循环:for循环
- 计入计数器循环
- for 初始化语句; 条件语句; 修饰语句 {}
for i:=0; i<10;i++{
sum+=i
}
- 初始化语句和后置可选,此场景与while等价(go语言不支持while)
for ; sum < 1000; {
sum += sum
}
- 无限循环
for {
if condition1 {
break
}
}
示例
package main
import (
"fmt"
)
func main() {
for i := 0; i < 3; i++ {
fmt.Println(i)
}
fullString := "hello world"
fmt.Println(fullString)
for i, c := range fullString {
fmt.Println(i, string(c))//循环打印下标和字符串
}
}
4,for-range
- 遍历数组,切片,字符串,MAP等
for index, char := range myString {
...
}
for key, value := range MyMap {
...
}
for index, value := range MyArray {
...
}
需要注意:如果 for range 遍历指针数组,则 value 取出的指
针地址为原指针地址的拷贝
示例
package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
四、常用数据结构
1,变量和常量定义
-
变量
var 语句用于声明一个变量列表,跟函数的参数列表一样,类型在最后。var c, python, java bool
-
变量的初始化
• 变量声明可以包含初始值,每个变量对应一个。
• 如果初始化值已存在,则可以省略类型;变量会从初始值中获得类型。var i, j int = 1, 2
-
短变量声明
• 在函数中,简洁赋值语句 := 可在类型明确的地方代替 var 声明。
• 函数外的每个语句都必须以关键字开始(var, func 等等),因此 := 结构不能在函数外使用。c, python, java := true, false, "no!"
2,类型转换和推导
-
类型转换
表达式 T(v) 将值 v 转换为类型 T。- 一些关于数值的转换:
var i int = 42 var f float64 = float64(i) var u uint = uint(f)
- 或者,更加简单的形式:
i := 42 f := float64(i) u := uint(f)
-
类型推导
在声明一个变量而不指定其类型时(即使用不带类型的 := 语法或 var = 表达式语法),变量的类型由右值推导得出。var i int j := i // j 也是一个 int
3,数组
**定义:**相同类型且长度固定连续内存片段
-
以编号访问每个元素
-
定义方法
var identifier [len]type
-
示例
yArray := [3]int{1,2,3}
4,slice切片
**定义:**切片是对数组一个连续片段的引用
-
数组定义中不指定长度即为切片
var identifier []type
-
切片在未初始化之前默认为nil, 长度为0
-
常用方法
func main() {
myArray := [5]int{1, 2, 3, 4, 5}
mySlice := myArray[1:3]
fmt.Printf("mySlice %+v\n", mySlice)
fullSlice := myArray[:]
remove3rdItem := deleteItem(fullSlice, 2)
fmt.Printf("remove3rdItem %+v\n",
remove3rdItem)
}
func deleteItem(slice []int, index int) []int {
return append(slice[:index], slice[index+1:]...)
}
Make 和 New
-
New 返回指针地址
-
Make 返回第一个元素,可预设内存空间,避免未来的内存拷贝
-
示例
package main import ( "fmt" ) func main() { mySlice1 := new([]int) mySlice2 := make([]int, 0) mySlice3 := make([]int, 10) mySlice4 := make([]int, 10, 20) fmt.Printf("mySlice1 %+v\n", mySlice1) fmt.Printf("mySlice2 %+v\n", mySlice2) fmt.Printf("mySlice3 %+v\n", mySlice3) fmt.Printf("mySlice4 %+v\n", mySlice4) }
关于切片的常见问题
-
切片是连续内存并且可以动态扩展,由此引发的问题?
a := []int{} b := []int{1,2,3} c := a a = append(b, 1)
-
修改切片的值?
mySlice := []int{10, 20, 30, 40, 50} for _, value := range mySlice { value *= 2 } fmt.Printf("mySlice %+v\n", mySlice) for index, _ := range mySlice { mySlice[index] *= 2 } fmt.Printf("mySlice %+v\n", mySlice)
5,Map
定义声明
-
声明方法
var map1 map[keytype]valuetype
-
示例
myMap := make(map[string]string, 10) myMap["a"] = "b" myFuncMap := map[string]func() int{ "funcA": func() int { return 1 }, } fmt.Println(myFuncMap) f := myFuncMap["funcA"] fmt.Println(f())
访问 Map 元素
-
按 Key 取值
value, exists := myMap["a"] if exists { println(value) }
-
遍历 Map
for k, v := range myMap { println(k, v) }
6,结构体和指针
定义方式
-
通过 type … struct 关键字自定义结构体
-
Go 语言支持指针,但不支持指针运算
-
指针变量的值为内存地址
-
未赋值的指针为 nil(不存在)
type MyType struct { Name string } func printMyType(t *MyType){ println(t.Name) } func main(){ t := MyType{Name: "test"} printMyType(&t) }
7,结构体标签
- 结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag)
- 使用场景:Kubernetes APIServer 对所有资源的定义都用 Json tag 和 protoBuff tag
- NodeName string
json:"nodeName,omitempty" protobuf:"bytes,10,opt,name=nodeName"
type MyType struct {
Name string `json:"name"`
}
func main() {
mt := MyType{Name: "test"}
myType := reflect.TypeOf(mt)
name := myType.Field(0)
tag := name.Tag.Get("json")
println(tag)
}
8,类型别名
// Service Type string describes ingress methods for a service
type ServiceType string
const (
// ServiceTypeClusterIP means a service will only be accessible inside the
// cluster, via the ClusterIP.
ServiceTypeClusterIP ServiceType = "ClusterIP"
// ServiceTypeNodePort means a service will be exposed on one port of
// every node, in addition to 'ClusterIP' type.
ServiceTypeNodePort ServiceType = "NodePort"
// ServiceTypeLoadBalancer means a service will be exposed via an
// external load balancer (if the cloud provider supports it), in addition
// to 'NodePort' type.
ServiceTypeLoadBalancer ServiceType = "LoadBalancer"
// ServiceTypeExternalName means a service consists of only a reference to
// an external name that kubedns or equivalent will return as a CNAME
// record, with no exposing or proxying of any pods involved.
ServiceTypeExternalName ServiceType = "ExternalName"
)
本节练习
课后练习 1.1
-
安装 Go
-
安装 IDE 并安装 Go 语言插件
-
编写一个小程序
给定一个字符串数组 ["I","am","stupid","and","weak"] 用 for 循环遍历该数组并修改为 ["I","am","smart","and","strong"]
-
解答
package main
import (
"fmt"
)
func main() {
mylist :=[5]string{"I","am","stupid","and","weak"}
fmt.Println(mylist)
for i, v := range mylist{
//fmt.Println(i, v)
switch v {
case "stupid":
mylist[i]="smart"
case "weak":
mylist[i]="strong"
default:
break
}
}
fmt.Println(mylist)
}