云原生训练营模块一 Go语言特性
Posted 果子哥丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了云原生训练营模块一 Go语言特性相关的知识,希望对你有一定的参考价值。
Go语言特性
如何学习云原生技术?
代码驱动
- 掌握Go语言编程能力
从点到面
- 学习容器技术
- cgroup,namespace
- 网络协议栈
- 文件系统
- 抓住核心掌控全局
- 深入理解Kubernetes
- API定义
- 控制器模式
- 核心组件
- 二次开发(webhook、鉴权、控制器)
- 深入理解Kubernetes
- 大规模生产化
- 多集群
- 服务网格和多网格
构建高可用微服务架构统一思想
1、基准代码
一份基准代码,多份部署
2、依赖
显式声明依赖关系
3、配置
在环境中存储配置
4、后端服务
把后端服务当作附加资源
5、构建、发布、运行
严格分离构建和运行
6、进程
以一个或多个无状态进程运行应用
7、端口绑定
通过端口绑定提供服务
8、并发
通过进程模型进行扩展
9、易处理
快速启动或优雅终止可最大化健壮性
10、开发环境与线上环境等价
尽可能的保持开发,预发布,线上环境相同
11、日志
把日志当作事件流
12、管理进程
后台管理任务当作一次性进程运行
Go语言
Go语言特点
Go支持的特性:
Go语言是一个可以编译高效,支持高并发的,面向垃圾回收的全新语言。
- 秒级完成大型程序的单节点编译
- 依赖管理清晰
- 不支持继承,程序员无需花费精力定义不同类型之间的关系
- 支持垃圾回收,支持并发执行,支持多线程通讯
- 对多核计算机支持友好
Go不支持的特性:
- 不支持函数重载和操作符重载
- 为了避免在C/C++开发中的一些Bug和混乱,不支持隐式转换
- 支持接口抽象,不支持继承
- 不支持动态加载代码
- 不支持动态链接库
- 通过recover和panic来替代异常机制
- 不支持断言
- 不支持静态变量
Go语言编译环境
- Go安装文件以及源代码
https://golang.google.cn/dl - 下载对应平台的二进程文件并安装
- 环境变量
- GOROOT
- go的安装目录
- GOPATH
- src:存放源代码
- pkg:存放依赖包
- bin:存放可执行文件
- 其他常用变量
- GOOS、GOARCH、GOPROXY
- 国内用户建议设置goproxy:export GOPROXY=https://goproxy.cn
- GOROOT
IDE设置(VS Code)
- 下载并安装Visual Studio Code
https://code.visualstudio.com/download - 安装Go语言插件
https://marketplace.visualstudio.com/items?itemName=golang.go
基本命令
- bug
- build 编译可执行文件
- clean
- doc
- env
- fix
- fmt
go fmt test.go
- generate
- get
- install
- list
- mode
- run 可以编译并运行命令源码文件。
- test
- tool
- version
- vet
go env用于打印Go语言的环境信息。
go run命令可以编译并运行命令源码文件。
go get可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。
go build命令用于编译我们指定的源码文件或代码包以及它们的依赖包。
go install用于编译并安装指定的代码包及它们的依赖包。
go clean命令会删除掉执行其它命令时产生的一些文件和目录。
go doc命令可以打印附于Go语言程序实体上的文档。我们可以通过把程序实体的标识符作为该命令的参数来达到查看其文档的目的。
go test命令用于对Go语言编写的程序进行测试。
go list命令的作用是列出指定的代码包的信息。
go fix会把指定代码包的所有Go语言源码文件中的旧版本代码修正为新版本的代码。
go vet是一个用于检查Go语言源码中静态错误的简单工具。
go tool pprof命令来交互式的访问概要文件的内容。
go build
- Go语言不支持动态链接,因此编译时会把所有依赖编译进同一个二进制文件。
- 指定输出目录。
- go build -o bin/mybinary
- 常用环境设置编译操作系统和CPU架构。
- GOOS=linux GOARCH=amd64 go build
- 全支持列表。
$GOROOT/src/go/build/syslist.go
go test
Go语言原生自带测试
import "testing"
func TestIncrease(t *testting.T)
t.Log("Start testing")
increase(1,2)
go test ./… -v 运行测试
go test命令扫描所有*_test.go
为结尾的文件,惯例是将测试代码与正式代码放在同目录,如foo.go的测试代码一般写在foo_test.go
go vet
检查编译器可能发现不出来的错误
代码版本控制
- 下载并安装 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
在线环境
国内可直接访问的playgroup:https://goplay.tools
控制结构
If条件语句
基本形式:
if condition1
// do something
else if condition2
// do something else
else
// catch-all or default
if的简短语句
同for一样,if语句可以在条件表达式前执行一个简单的语句
if v := x - 100; v < 0
return v
intVal := 1 相等于:
var intVal int
intVal =1
switch分支语句
switch var1
case val1: // 空分支
case val2:
fallthrough // 执行case3中的f()
case val3:
f()
default: //默认分支
...
如果case带有fallthrough,程序会继续执行下一条case,不会再判断下一条case的expr
for循环语句
Go只有一种循环结构:for循环
- 计入计数器的循环
for 初始化语句;条件语句;修饰语句
for i := 0;i < 10; i++
sum += i
- 初始化语句和后置语句酸可选的,此场景与while等价
for ; sum < 1000;
sum += sum
- 无限循环
for
if condition1
break
for-range遍历数组
遍历数组,切片,字符串,Map等
for index,char := range myString
...
for key,value := range MyMap
...
for index,value := range MyArray
...
需要注意:如果for range遍历指针数组,则value取出的指针地址为原指针地址拷贝。
package main
import
"flag"
"fmt"
"os"
func main()
name := flag.String("name", "world", "specify the name you want to say hi")
flag.Parse()
fmt.Println("os args is:", os.Args). // Args是go运行时传入的参数
fmt.Println("input parameter is", *name)
fullString := fmt.Sprintf("Hello %s from Go\\n", *name)
fmt.Println(fullString)
flag的话是 运行时,带上--参数。 --name jesse ,就会把jesse传进去,参数名与参数值, Hello jesses from Go。 如果不带上参数,按照上面逻辑是 Hello World from Go
go run test.go --name jesse
数据结构
变量与常量
- 常量:const
- 变量:var
var a,b,c bool
var i,j int = 1,2
函数内,简洁赋值语句 := 可在类型明确的地方代替var声明
函数外,每个语句都必须以关键字(var, func等等),因此 := 结构不能再函数外使用
类型转换:
var i int = 42
var f float64 = float64(i)
数组
- 相同类型且长度固定连续内存片段
- 以编号访问每个元素
- 定义方法
- var identifier [len] type
- myArray := [3]int[1,2,3]
切片
- 切片是对数据一个连续片段的引用
- 数组定义中不指定长度的即为切片
var identifier [] type
- 切片在未初始化之前默认为nil,长度为0
- 常用方法
package main
import (
"fmt"
)
func main()
myArray := [5]int1, 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)
// 没有原生的删除,通过append的前后组合到一起
func deleteItem(slice []int, index int) []int
return append(slice[:index], slice[index+1:]...)
Make和New
- New返回指针地址
- Make返回第一个元素,可预设内存空间,避免未来的内存拷贝
Map字典
package main
import (
"fmt"
)
func main()
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())
value, exists := myMap["a"]
if exists
println(value)
for k, v := range myMap
println(k, v)
关于切片的常见问题
(1)b假设达到空间上限,再新增元素,会导致开辟新的空间,b的地址和新的地址是不一样。 —— 这里需要保证前后的地址一致,即a地址赋值给a,而不是b赋值到a。
(2)假设有个切片是int型,当value*2的时候,这个变动会回去吗? 不会的,是因为把遍历的值临时赋给了value。
(3)通过取下标访问切片,访问的就是指针地址,这时会生效。
结构体和指针
- 通过type-struct关键字自定义结构体
- Go语言支持指针,但不支持指针运算
- 指针变量的值为内存地址
- 未赋值的指针为nil
- 结构体里只能包含属性,接口只能定义行为
- New返回指针地址
结构体标签
- 结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag)
- 使用场景:Kubernetes APIServer
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type MyType struct
Name string `json:"name"`
Address
type Address struct
City string `json:"city"`
func main()
mt := MyTypeName: "test",Address: AddressCity: "shanghai"
b, _ := json.Marshal(&mt)
fmt.Println(string(b))
myType := reflect.TypeOf(mt)
name := myType.Field(0)
tag := name.Tag.Get("json")
println(tag)
tb := TypeBP2: "p2", TypeA: TypeAP1: "p1"
//可以直接访问 TypeA.P1
println(tb.P1)
type TypeA struct
P1 string
type TypeB struct
P2 string
TypeA
练习:
给定一个字符串数组:[“I”,“am”,“stupid”,“and”,“weak”],用for循环遍历该数组并修改为[“I”,“am”,“smart”,“and”,“strong”]
package main
import (
"fmt"
)
func main()
arr := [5]string"I","am","stupid","and","weak"
for i,v := range arr
switch v
case "stupid":
arr[i] = "smart"
case "weak":
arr[i] = "strong"
default:
break
fmt.Println(arr)
接下来计划:
- 函数调用
- 常用语法
- 多线程
- 深入理解channel
- 基于channel编写一个生产者消费者程序
生产者与消费者代码
基于同步锁
import (
"fmt"
"sync"
)
var (
productCount = 5
mutex sync.Mutex
cond = sync.NewCond(&mutex)
)
type producer struct
func (r *producer) produce()
for
cond.L.Lock()
if productCount < 10
productCount++
fmt.Printf("生产者生产了一个产品,当前存量%d\\n", productCount)
else
fmt.Printf("仓库满了,当前容量%d\\n", productCount)
cond.Wait()
cond.L.Unlock()
cond.Broadcast()
func (r *consumer) consumer()
for
cond.L.Lock()
if productCount > 0
productCount--
fmt.Printf("消费者消费了一个产品,当前存量%d\\n", productCount)
else
fmt.Printf("仓库空了,当前存量%d\\n", productCount)
cond.Wait()
cond.L.Unlock()
cond.Broadcast()
type consumer struct
func main()
var p = producer
var c = consumer
go p.produce()
go c.consumer()
time.Sleep(time.Microsecond * 10) //防止因为生产者消费者执行方法时主线程已经结束了,给主线程加个时延
基于通道实现
基于通道实现的这种方式需要注意避免从空通道中取数,否则就会出现死锁
var countChan chan int
type producer struct
func (r *producer) produce()
for i := 0; i < 10; i++
count := <-countChan
if count < 10
count++
fmt.Printf("生产者生产了一个产品,当前存量%d\\n", count)
else
fmt.Printf("仓库满了,当前容量%d\\n", count)
countChan <- count
var t = rand.Intn(100)
time.Sleep(time.Duration(t))
func (r *consumer) consumer()
for i := 0; i < 10; i++
count := <-countChan
if count > 0
count--
fmt.Printf("消费者消费了一个产品,当前存量%d\\n", count)
else
fmt.Printf("仓库空了,当前存量%d\\n", count)
countChan <- count
var t = rand.Intn(100)
time.Sleep(time.Duration(t))
type consumer struct
func main()
countChan = make(chan int, 1)
countChan <- 0
var p = producer
var c = consumer
go p.produce()
go c.consumer()
time.Sleep(1000)
基于select解决通道阻塞问题
import (
"fmt"
"math/rand"
"time"
)
var countChan chan int
type producer struct
func (r *producer) produce()
for i := 0; i < 10; i++
select
case count := <-countChan:
if count < 10
count++
fmt.Printf("生产者生产了一个产品,当前存量%d\\n", count)
else
fmt.Printf("仓库满了,当前容量%d\\n", count)
countChan <- count
var t = rand.Intn(100)
time.Sleep(time.Duration(t))
default:
fmt.Println("初始化产品池")
countChan <- 0
func (r *consumer) consumer()
for i := 0; i < 10; i++
select
case count := <-countChan:
if count > 0
count--
fmt.Printf("消费者消费了一个产品,当前存量%d\\n", count)
else
fmt.Printf("仓库空了,当前存量%d\\n", count)
countChan <- count
var t = rand.Intn(100)
time.Sleep(time.Duration(t))
default:
fmt.Println("初始化产品池")
countChan <- 0
type consumer struct
func main()
countChan = make(chan int, 1)
var p = producer
var c = consumer
go p.produce()
go c.consumer()
time.Sleep(10000)
以上是关于云原生训练营模块一 Go语言特性的主要内容,如果未能解决你的问题,请参考以下文章