• cloudenative1-1


    模块一:Go 语言特性
    ----------------------------
    云原生很火,有台湾、海外的同学。
    模块一:Go 语言特性
    ----------------------------
    Martin Flower 企业应用架构模式,总结了12要素
    I. 基准代码-一份基准代码,多份部署
    II. 依赖-显式声明依赖关系--JAVA使用POM声明,GO用什么机制?
    III. 配置-在环境中存储配置
    IV. 后端服务-把后端服务当作附加资源
    V. 构建,发布,运行-严格分离构建和运行
    VI. 进程-以一个或多个无状态进程运行应用
    VII. 端口绑定-通过端口绑定提供服务
    VIII. 并发-通过进程模型进行扩展
    IX. 易处理-快速启动和优雅终止可最大化健壮性
    X. 开发环境与线上环境等价-尽可能的保持开发,预发布,线上环境相同
    XI. 日志-把日志当作事件流
    XII. 管理进程-后台管理任务当作一次性进程运行
    ----------------------------
    目录:
    1. 为什么需要另外一种语言?
    2. Go 语言编译环境设置
    3. 控制结构
    4. Go 语言常用数据结构
    5. Go 语言函数调用
    6. 常用语法
    7. 多线程
    •深入理解 channel
    •基于 channel 编写一个生产者消费者程序
    ----------------------------
    1. 为什么需要 Go 语言
    ----------------------------
    Go 语言的原则
    Less is exponentially more
    – Rob Pike, Go Designer
    Do Less, Enable More
    – Russ Cox, Go Tech Lead
    ----------------------------
    为什么需要 Go 语言
    其他编程语言的弊端。
    •硬件发展速度远远超过软件。
    •C 语言等原生语言缺乏好的依赖管理 (依赖头文件)。
    •Java 和 C++ 等语言过于笨重。
    •系统语言对垃圾回收和并行计算等基础功能缺乏支持。
    •对多核计算机缺乏支持。--linux里面线程和进程差不多,开销比较大
    Go 语言是一个可以编译高效,支持高并发的,面向垃圾回收的全新语言。
    •秒级完成大型程序的单节点编译。
    •依赖管理清晰。
    •不支持继承,程序员无需花费精力定义不同类型之间的关系。
    •支持垃圾回收,支持并发执行,支持多线程通讯。
    •对多核计算机支持友好。
    ----------------------------
    Go 语言不支持的特性
    • 不支持函数重载和操作符重载
    • 为了避免在 C/C++ 开发中的一些 Bug 和混乱,不支持隐式转换
    • 支持接口抽象,不支持继承
    • 不支持动态加载代码
    • 不支持动态链接库
    • 通过 recover 和 panic 来替代异常机制
    • 不支持断言
    • 不支持静态变量
    如果你没做过其他语言的开发,
    那么恭喜,以上大部分复杂的问题,在 Go 语言里不存在,你也无需关心。
    ----------------------------
    Go语言特性衍生来源
    C:基本语法和数据结构
    JAVA:接口抽象
    JAVA,C#:包定义
    linbo:CSP模型
    JAVAScript/ruby:多态支持

    应用:Docker,kubernetes,istio

    用Go语言改写以前的软件,CSP模型强大
    阿里新的反向代理,也是使用GO语言改写的。
    ----------------------------
    2.Go语言环境搭建
    ----------------------------
    下载Go
    -Go安装文件以及源代码
    https://golang.google.cn/dl
    -下载对应平台的二进制
    dowload from golang.google.cn
    sudo rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.1.linux-amd64.tar.gz
    上次是1.18.1,现在已经是1.19版本了
    $HOME/.bashrc
    export PATH=$PATH:/usr/local/go/bin
    $ go version
    go version go1.18.1 linux/amd64
    $ sudo rm -rf /usr/local/go
    $ sudo tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz
    $ go version
    go version go1.19 linux/amd64
    $ which go
    /usr/local/go/bin/go
    -环境变量
     -GOROOT
      - go的安装目录
      $HOME/.bashrc
      export GOROOT=/usr/local/go/
      $ echo $GOROOT
      /usr/local/go/
      用处:设置好这个变量,在IDE环境中查看go的源代码,可以跳转过来
      代码在/usr/local/go/src目录下
     -GOPATH
      -src:存放源代码
      -pkg:存放依赖包
      -bin:存放可执行文件
      export GOPATH=$HOME/go
      echo $GOPATH
      /home/???/go

     -其他常用变量
      -GOOS,GOARCH,GOPROXY
      -国内用户建议设置goproxy: export GOPROXY=https://goproxy.cn

    几个相关目录说明:
    /usr/local/go
    /usr/local/go/bin/go
    echo $GOPATH
    users/fmeng/go/src/github.com/cncamp/golang --放置go语言相关案例的地方
    有依赖的包,按src下的路径写,如import github.com/cncamp
    http://github.com/cncamp
    ----------------------------
    IDE设置(VS Code)
    -下载并安装 Visual Studio Code
    https://code.visualstudio.com/download
    -安装 Go 语言插件
    https://marketplace.visualstudio.com/items?itemName=golang.go
    其他可选项
    •Intellj goland,收费软件
    •vim,sublime等
    ---------------------------
    一些基本命令
    bug-start a bug report
    build-compile packages and dependencies
    clean-remove object files and cached files
    doc-show documentation for package or symbol
    env-print Go environment information
    fix-update packages to use new APIs
    fmt-gofmt (reformat) package sources
    generate-generate Go files by processing source
    get-add dependencies to current module and install them

    build的例子:
    https://github.com/cncamp/golang/blob/master/examples/module1/helloworld/main.go
    cd helloworld
    cat main.go
    go build main.go
    交叉编译
    GOOS=linux go build main.go,就可以在mac下编译linux的目标文件
    -fmt
    go fmt main.go
    main.go:10:1: unexpected semicolon or newline before {
    exit status 2
    不定自动修正?
    gofmt -w -l main.go
    main.go:10:1: unexpected semicolon or newline before {
    还是不行,只能找到不符合格式要求的地方,不能修复,没有啥用处。
    ----------------------------
    一些基本命令
    install-compile and install packages and dependencies
    --可以在制作docker文件中,直接提供源码go install
    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
    ----------------------------
    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 *testing.T) {
    t.Log("Start testing")
    increase(1, 2)
    }
    go test ./… -v 运行测试
    go test 命令扫描所有*_test.go为结尾的文件,惯例是将测试代码与正式代码放在同目录,
    如 foo.go 的测试代码一般写在 foo_test.go
    例子:
    go test
    # github.com/cncamp/golang/examples/module1/callbacks
    main_test.go:5:8: no required module provides package github.com/stretchr/testify/assert; to add it:
        go get github.com/stretchr/testify/assert
    FAIL    github.com/cncamp/golang/examples/module1/callbacks [setup failed]
    $ go get github.com/stretchr/testify/assert
    go: downloading github.com/stretchr/testify v1.8.0
    go: downloading github.com/davecgh/go-spew v1.1.1
    go: downloading github.com/pmezard/go-difflib v1.0.0
    go: downloading gopkg.in/yaml.v3 v3.0.1
    go: added github.com/davecgh/go-spew v1.1.1
    go: added github.com/pmezard/go-difflib v1.0.0
    go: added github.com/stretchr/testify v1.8.0
    go: added gopkg.in/yaml.v3 v3.0.1
    zxl@qwq:~/go/src/github.com/cncamp/golang/examples/module1/callbacks$ go test
    PASS
    ok      github.com/cncamp/golang/examples/module1/callbacks    0.009s
    如何在vscode里面直接运行?
    安装了一个go Nightly以后可以在vscode中直接运行go test了

    ----------------------------
    Go vet
    代码静态检查,发现可能的 bug 或者可疑的构造
    -Print-format 错误,检查类型不匹配的print
    str := “hello world!”
    fmt.Printf("%d\n", str)
    -Boolean 错误,检查一直为 true、false 或者冗余的表达式
    fmt.Println(i != 0 || i != 1)
    -Range 循环,比如如下代码主协程会先退出,go routine无法被执行
    words := []string{"foo", "bar", "baz"}
    for _, word := range words {
        go func() {
            fmt.Println(word).
        }()
    }
    -Unreachable的代码,如 return 之后的代码
    -其他错误,比如变量自赋值,error 检查滞后等
    res, err := http.Get("https://www.spreadsheetdb.io/")
    defer res.Body.Close()
    if err != nil {
        Log.Fatal(err)
    }
    ----------------------------
    代码版本控制
    -下载安装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
    ----------------------------
    Golang playground
    官方 playground
    https://play.golang.org/
    可直接编写和运行 Go 语言程序
    国内可直接访问的 playground
    https://goplay.tools/
    ----------------------------
    3控制结构
    ----------------------------
    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
     }
    ----------------------------
    switch
    switch var1 {
      case val1: //空分支
      case val2:
        fallthrough //执行case3中的f()
      case val3:
        f()
      default://默认分支
        ...
    }
    不用写break
    ----------------------------
    For
    Go只有一种循环结构:for循环
    -计入计数器的循环
    for 初始化语句; 条件语句; 修饰语句 {}
    for i:=0;i<10;i++{
        sum+=i
    }
    -初始化语句和后置语句是可选的,此场景与 while 等价(Go 语言不支持 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 取出的指
    针地址为原指针地址的拷贝。
    ----------------------------
    初始化环境:这些都放到$HOME/.bashrc文件中
    export PATH=$PATH:/usr/local/go/bin
    export GOPATH=/home/zxl/go:/home/zxl/mygo/go_learning
    /home/zxl/go/src/github.com/cncamp/golang
    export GOPROXY=https://goproxy.cn
    go mod init github.com/cncamp/golang

    第一个例子:入参
    module1/helloworld/helloworld.go
    package main
    func main(){} -- 入口函数
    没有入参,直接使用os.Args
     go build main.go
    zxl@qwq:~/go/src/github.com/cncamp/golang/examples/module1/helloworld$ ./main
    os args is: [./main]
    input parameter is: world
    Hello world from Go

    zxl@qwq:~/go/src/github.com/cncamp/golang/examples/module1/helloworld$ ./main zzz
    os args is: [./main zzz]
    input parameter is: world
    Hello world from Go

    ./main --name zzz
    os args is: [./main --name zzz]
    input parameter is: zzz
    Hello zzz from Go
    加上--name 就认为是一个参数名,后面是参数值
    name := flag.String("name", "world", "specify the name you want to say hi")
    --第三个参数是备注信息
    kubernetes程序中参数初始化时常用

    第二个例子:go vet
    module1/govet/main.go
        name := "testing"
        fmt.Printf("%s\n", name)
        fmt.Printf("%d\n", name)  -- 提示错误
        fmt.Printf("%s\n", name, name) -- 提示错误
    运行结果:    
    testing
    %!d(string=testing)
    testing
    %!(EXTRA string=testing)
    可以用命令行检查:
    $ go vet
    main.# github.com/cncamp/golang/examples/module1/govet
    ./main.go:8:2: fmt.Printf format %d has arg name of wrong type string
    ./main.go:9:2: fmt.Printf call needs 1 arg but has 2 args
    使用IDE直接给出提示错误

    第三个例子:for loop
        for i, c := range fullString {
            fmt.Println(i, string(c))
        }
    比较有特色的是函数或者range这种可以返回多个值,赋给多个变量。
    ----------------------------
    4常用数据结构
    ----------------------------
    变量与常量
    -常量
    const identifier type
    -变量
    var identifier type
    ----------------------------
    变量定义
    -变量
    var 语句用于声明一个变量列表,跟函数的参数列表一样,类型在最后。
    var c, python, java bool
    -变量初始化
    变量声明可以包含初始值,每个变量对应一个。
    如果初始化值已存在,则可以省略类型;变量会从初始值中获得类型。
    var i, j int = 1, 2
    -短变量声明
     在函数中,简洁赋值语句 := 可在类型明确的地方代替 var 声明。
     函数外的每个语句都必须以关键字开始(var, func 等等),因此 := 结构不能在函数外使用。
     c, python, java := true, false, "no!"
    ----------------------------
    类型转换与推导
    -类型转换
    表达式 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
    ----------------------------
    数组
    -相同类型且长度固定连续内存片段
    -以编号访问每个元素
    -定义方法
     - var identifier [len]type
    -示例
     - myArray := [3]int{1,2,3} 
    ----------------------------
    切片(slice)
    -切片是对数组一个连续片段的引用
    -数组定义中不指定长度即为切片
     - var identifier []type
    -切片在未初始化之前默认为nil, 长度为0
    -常用方法
    例子:
    module1/slice/main.go
    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:]...)
    }

    myArray := [5]int{1,2,3,4,5}
    mySlice := myArray[1:3]
    mySlice1 := []int{}
    mySlice1 = append(mySlice1,1)
    mySlice1 = append(mySlice1,2)
    mySlice1 = append(mySlice1,3)
    删除很麻烦
    append(slice[:index], slice{index+1:}...)
    注意切片的范围,前面的index是包括的,:后面的index不包括
    ----------------------------
    Make和New
    -New返回指针地址
    -Make返回第一个元素,可以预设内存空间,避免未来的内存拷贝
    -示例
    mySlice1 := new([]int)
    mySlice2 := make([]int,0)
    mySlice2 := make([]int,10) -- len 10 cap 10
    mySlice2 := make([]int,10,20) --len 10 cap 20
    ----------------------------
    关于切片的常见问题
    -切片是连续内存并且可以动态扩展
    a :=[]int{}
    b:=[]int{1,2,3}
    c:=a
    a:=append(b,1) --c和a不相等了,所以不要b赋值给a. b append后有可能扩展,开辟一个新的内存空间
    最佳实践:append中的切片一定是同一个: a:=append(a,1)
    -修改切片值
    mySlice:=[]int{10,20,30,40,50}
    for _,value:=range mySlice{
        value*=2
    }
    临时变量编号,切片没有变
    go语言都是值传递
    for index,_=range mySlice {
        mySlice[index]*=2
    }
    这样是改的切片值
    例子:
    module1/slice/forrange/main.go
    for index, _ := range mySlice {
    vet起作用了,改一下就不提示了
    for index := range mySlice {    

    fmt打印
    fmt.Printlin("aa","bb","cc")  -- interface{}
    fmt.Printf("%s%d%v") -- 格式化打印
    %s 字符串
    %d 值
    %v struct结构
    %+v struct结构和其中的值
    fmt.Sprintf(...) -- 拼字符串
    fmt.Errorf(...)

    package组织
    类似JAVA,但不用写全路径
    ----------------------------
    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)
    }
    例子:module1/map/main.go
    ----------------------------
    结构体和指针
    -通过 type … struct 关键字自定义结构体
    -Go 语言支持指针,但不支持指针运算
     指针变量的值为内存地址
     未赋值的指针为 nil
    type MyType struct {
        Name string
    }
    func printMyType(t *MyType){
        println(t.Name)
    }
    func main(){
        t := MyType{Name: "test"}
        printMyType(&t)

    ----------------------------
    结构体标签
    -结构体中的字段除了有名字和类型外,还可以有一个可选的标签(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)
    }
    ----------------------------
    类型重命名
    // 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.
    )
    ----------------------------
    接口
    -接口定义一组方法集合
    type IF interface {
        Method1(param_list)return_type
    }


    ----------------------------
    ----------------------------
    反射机制
    -reflect.TypeOf()返回被检查对象的类型
    -reflect.ValueOf()返回被检查对象的值
    -示例
    myMap := make(map[string]string,10)
    myMap["a"]=“b”
    ----------------------------

    ----------------------------
    defer
    -函数返回之前执行某个语句或函数
     -等同与JAVA和C#的finally
    ----------------------------
    Panic和recover

    defer func() {
        fmt.Println("defer fnc is called)
        if err := recover(); err != nil {
            fmt..Println(err)
        }
    }()
    panic("a panic is triggered")
    ----------------------------
    7.多线程
    ----------------------------
    并发和并行
    -并发concurrency
     两个或者多个事件在同一时间间隔发生
    -并行parallellism
     两个或者多个事件在同一时刻发生
    ----------------------------
    协程
    -进程
    -线程
    -协程
     -Go语言中的轻量级线程实现
     -Golang在runtime、系统调用
    ----------------------------
    Communicating Sequential Process
    -CSP
    -Go 协程 goroutine

    ----------------------------
    线程和协程的差异
    -每个goroutine(协程)默认占用内存远比JAVA,C的线程少
     -goroutine:2KB
     -线程:8MB
    -线程/goroutine切换 
    ----------------------------
    协程示例
    -启动新协程: go functionName()
    ----------------------------

    ----------------------------
    ----------------------------
     

  • 相关阅读:
    安装VMware
    1312. 让字符串成为回文串的最少插入次数
    goquery库编写程序
    2022辽宁省赛(A,B,D,E,F,G,I,M)
    GLTF在线编辑器
    C++ 记录问答 (程序设计)链表插入与删除
    初识C语言——详细入门(系统性学习day4)
    Centos7处理openssh漏洞后造成GNOME桌面启动异常
    JavaScript游戏恢复
    pythonGUI(二)基本元素之二
  • 原文地址:https://blog.csdn.net/hb_zxl/article/details/126457485