Go语言进阶 | 青训营笔记

169 阅读5分钟

这是我参与「第五届青训营 」笔记创作活动的第2天。

引言

今天的课程涉及了并发编程(利用Go协程,信道和锁)、Go项目依赖管理、测试和一个服务端项目实战(使用Gin框架),这篇文章主要是针对今天课程的记录和总结。


一、本堂课重点内容

本堂课的知识点

  • 协程和信道
  • 项目依赖 Go Module
  • 测试:单元测试,Mock测试和基准测试
  • 项目实践:利用分层架构,文件读取,Gin(高性能web框架)

二、详细知识点介绍

1. 并发编程

协程

Go 协程是与其他函数或方法一起并发运行的函数或方法。Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。因此在 Go 应用中,常常会看到有数以千计的 Go 协程并发地运行。 协程位于用户态,线程位于核心态(需要内核直接进行操作,用户无权限)。 举例:

package main

import (
	"fmt"
	"time"
)

func hello() {
	fmt.Println("Hello world goroutine")
}
func main() {
	go hello()
	time.Sleep(1 * time.Second) //防止主协程直接结束,导致上面开启的协程还未执行就已经退出程序了
	fmt.Println("main function")
}

信道

利用信道来实现协程之间的信息交换,互相配合的并发执行

信道可以想象成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收。 举例:

package main

import "fmt"

func main() {  
    var a chan int   //声明
    if a == nil {
        fmt.Println("channel a is nil, going to define it")
        a = make(chan int) 
        fmt.Printf("Type of a is %T", a)
    }
    //简短定义
    b := make(chan int)
    data:="dddd"
    data := <- b // 读取信道 b,  将信道中的数据存入data变量中
    b <- data // 写入信道 b,  将数据data 写入信道b中
}

2. 依赖管理

1️⃣在以前版本,Go所依赖的所有的第三方库都放在GOPATH这个目录下面,而这会导致了同一个库只能保存一个版本的代码。当不同的项目依赖同一个第三方的库的不同版本时,就会有问题

2️⃣Go语言从v1.5开始开始引入vendor模式,如果项目目录下有vendor目录,那么go工具链会优先使用vendor内的包进行编译、测试等。

vender文件夹下是项目依赖的包的源代码文件。

vender机制

Go1.5版本之后开始支持能够控制Go语言程序编译时依赖包搜索路径的优先级。

当查找项目的某个依赖包,首先会在项目根目录下的vender文件夹中查找,如果没有找到就会去$GOAPTH/src目录下查找。

3️⃣go module是Go1.11版本之后官方推出的版本管理工具,并且从Go1.13版本开始,go module将是Go语言默认的依赖管理工具。 使用 go module 管理依赖后会在项目根目录下生成两个文件go.modgo.sumgo.mod文件记录了项目的依赖信息,go.sum文件记录每个依赖库的版本和哈希值。 当启用go module管理项目,就会忽略go path和vender文件夹,会根据go.mod文件去下载项目所需依赖

3. 实战-利用Gin

分层架构

整体架构分为三层,repository数据层(如从文件中获取数据,从持久化数据库中获取数据),service逻辑层(处理业务功能的核心逻辑),controller视图层(封装数据(如JSON格式),传输给客户端)

Gin

Gin是由Golang编写的高性能Web框架

基本使用:

package main

import (
   "github.com/gin-gonic/gin"
   "net/http"
)

func main() {
   r := gin.Default()
   //定义路由的GET方法及响应处理函数
   r.GET("/hello", func(c *gin.Context) {
      //将发送的信息封装成JSON发送给浏览器
      c.JSON(http.StatusOK, gin.H{
         //这是我们定义的数据
         "message": "Gin 测试使用",
      })
   })
   r.Run() //默认在本地8080端口启动服务
}

路由

路由方法有 GET, POST, PUT, PATCH, DELETEOPTIONS,还有Any,可匹配以上任意类型的请求。

//URL相同,但路由方式不同
r.GET("/login", func(c *gin.Context) {...})
r.POST("/login", func(c *gin.Context) {...})

//Any 可以匹配任意类型
r.Any("/test", func(c *gin.Context) {...})

路由组

func main() {
	r := gin.Default()
	userGroup := r.Group("/user")
	{
		userGroup.GET("/index", func(c *gin.Context) {...})
		userGroup.GET("/login", func(c *gin.Context) {...})
		userGroup.POST("/login", func(c *gin.Context) {...})

	}
	shopGroup := r.Group("/shop")
	{
		shopGroup.GET("/index", func(c *gin.Context) {...})
		shopGroup.GET("/cart", func(c *gin.Context) {...})
		shopGroup.POST("/checkout", func(c *gin.Context) {...})
	}
	r.Run()
}

三、实践练习例子

协程结合信道

package main

import (
	"fmt"
)

func hello(done chan bool) {
	fmt.Println("Hello world goroutine")
	done <- true
}
func main() {
	done := make(chan bool)
	go hello(done)
	re := <-done //通过信道 done 接收数据。这一行代码发生了阻塞,除非有协程向 done 写入数据,否则程序不会跳到下一行代码
	fmt.Println(re)  //输出true
	fmt.Println("main function")
}

从上面这个例子可知,利用信道,可做到并发的协程之间的合作,如:若一个协程A到达某一个步骤需要另外一个协程B的数据时,可利用信道来达到阻塞A协程,直到B协程利用信道将数据发送给A(且A收到) 反过来,当我们一个协程利用信道传输数据,而没有其他的Go协程去接收数据,那么其自己也会被阻塞,乃至死锁

当然除了上面的双向信道,还有单向信道

package main

import "fmt"

func sendData(sendch chan<- int) {  
    sendch <- 10
}

func main() {  
    sendch := make(chan<- int)  //唯送信道,不能接收数据
    go sendData(sendch)
    fmt.Println(<-sendch)  //报错,因为不能接收
}

四、课后个人总结

今天这堂课程懂得了如何利用Go协程和信道进行并发编程,Go不愧是为并发而生的。然后懂得了配置Go项目依赖等步骤,之后学习了一个很重要的过程就是测试,在项目上线之前,测试十分的重要,然后学习了web开发的一些流程和开发模式,最后学习使用了Gin框架。

五、引用参考

主要参考了青训营课程资料和Go语言中文网中的相关资料