Go 语言进阶 - 工程进阶 | 青训营笔记
这是我参与「第五届青训营 」笔记创作活动的第2天,今天主要对Go语言的进阶内容进行学习。
一、本堂课重点内容:
本次课程讲了以下几个方面:
-
并发编程
-
依赖管理
-
单元测试
-
项目实战
二、详细知识点介绍:
1.并发编程
并发编程在当前软件领域是一个非常重要的概念,随着CPU等硬件的发展,我们无一例外的想让我们的程序运行的快一点、再快一点。
(1)并发vs并行
并发:同一时间段内执行多个任务(可以理解成你在用微信和两个女朋友聊天)。
并行:同一时刻执行多个任务(可以理解成你和你朋友都在用微信和女朋友聊天)。
(2)Goroutine
Goroutine 是 Go 语言支持并发的核心,Go语言中使用 goroutine 非常简单,只需要在函数或方法调用前加上go关键字就可以创建一个 goroutine ,从而让该函数或方法在新创建的 goroutine 中执行。
go f1() // 创建一个新的 goroutine 运行函数f1
(3)Channel
Go语言提倡通过通信共享内存而不是通过共享内存而实现通信。Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,保证收发数据的顺序。
channel是 Go 语言中一种特有的类型。声明通道类型变量的格式如下:
```go
var 变量名称 chan 元素类型
```
(4)并发安全和锁
有时候我们的代码中可能会存在多个 goroutine 同时操作一个资源(临界区)的情况,这种情况下就会发生竞态问题(数据竞态)。在Go语言中我们可以使用sync包下的WaitGroup和Mutex 来应对并发安全和锁问题
2.依赖管理
Go语言的依赖管理经历了 GOPATH -> Go Vendor -> Go Module,由于GOPATH存在无法实现package的多版本控制的问题,引入了Go Vendor,随之而来就是Go Vendor存在无法控制依赖的版本和更新项目又可能出现依赖冲突,导致编译出错等问题,最终Go Module出现了,Go Module通过go.mod文件管理依赖包版本,通过go get/go mod指令工具管理依赖包,终极目标是定义版本规则和管理项目依赖关系。
常用的go get和go mod命令:
go get example.org/pkg +
@update 默认
@none 删除依赖
@v1.1.2 tag版本,语义版本
@23dfdd5 特定的commit
@master 分支的最新commit
go mod +
init 初始化,创建go.mod文件
download 下载模块到本地缓存
tidy 增加需要的依赖,删除不需要的依赖
3.单元测试
测试是避免事故的最后一道屏障,测试可以分为回归测试、集成测试、单元测试,从上到下,覆盖率逐层变大,成本却逐层降低。Go语言中的测试依赖go test命令,go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。
4.项目实战
(1)需求描述:
实现青训营话题页
1.实现一个展示话题(标题,文字描述)和回帖列表的后端http接口;
2.本地文件存储数据
三、实践练习例子:
在这个部分主要记录一下青训营话题页案例的实现思路和关键代码。
使用到的组件及技术点有:
(1)web框架:Gin 是一个用 Go 编写的 HTTP Web 框架,具有快速、支持中间件、路由组等特性。
(2)分层结构设计
(3)文件操作
(4)数据查询
入口文件server.go代码如下:
package main
//引用第三方模块
import (
"github.com/Moonlight-Zhao/go-project-example/handler"
"github.com/Moonlight-Zhao/go-project-example/repository"
"github.com/Moonlight-Zhao/go-project-example/util"
"gopkg.in/gin-gonic/gin.v1"
"os"
)
//主函数
func main() {
//调用Init函数进行初始化
if err := Init(); err != nil {
os.Exit(-1)
}
//调用gin包的Default函数,生成r引擎
r := gin.Default()
//使用Logger中间键
r.Use(gin.Logger())
//GET请求的测试接口,路径为/ping,响应状态码为200,内容为{"message":"pong"}
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
//GET请求的获取文章内容接口,路径为/community/page/get/:id,id为文章id
r.GET("/community/page/get/:id", func(c *gin.Context) {
topicId := c.Param("id")
data := handler.QueryPageInfo(topicId)
c.JSON(200, data)
})
//POST请求的提交文章内容接口,路径为/community/post/do
r.POST("/community/post/do", func(c *gin.Context) {
uid, _ := c.GetPostForm("uid")
topicId, _ := c.GetPostForm("topic_id")
content, _ := c.GetPostForm("content")
data := handler.PublishPost(uid, topicId, content)
c.JSON(200, data)
})
//使用Run方法启动后台服务器,默认端口为8080
err := r.Run()
if err != nil {
return
}
}
//Init函数
func Init() error {
//初始化存储仓库
if err := repository.Init(); err != nil {
return err
}
//初始化日志
if err := util.InitLogger(); err != nil {
return err
}
return nil
}
四、课后个人总结:
通过学习Go 语言进阶 - 工程进阶,学习到了Go语言的并发编程,还学到了Go语言的Go Module模块化管理、测试,最后通过一个青训营话题页案例,了解到Gin框架,以及使用Gin框架进行服务端开发。