这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
语言进阶
并发与并行的区别
- 并行是指两个或者多个事件在同一时刻发生,而并发是指两个或多个事件在同一时间间隔发生。
- 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
- 个人理解:并发是对于单个cpu核而言的,在计算机运行的时候,有许多指令会使cpu在空闲状态,等待指令完成,这时候cpu可以先处理下一个任务,并发在同一时刻只有一个任务在执行。而并行是对于多个cpu核的,指的是多个任务同时在不同的cpu核上运行。
线程和协程的区别
- 协程是一种轻量级的线程,因为线程的创建必须进行操作系统的调用,而协程的创建则是由编程语言完成的,消耗的资源小得多
- 两者要解决的问题不同,线程可以并发也可以并行,而协程只能并发,是用于解决并发问题的。
并发安全问题
- 加上Lock锁
WaitGroup
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
通过计时器来知晓所有并发任务实现完成,Add表示有几个协程,Done表示一个协程完成后-1,wait用于阻塞知道计数器为0
依赖管理
go依赖管理演进
GOPATH->Go Vender->Go Module
Go Module
- 通过go.mod文件管理依赖包版本
- 通过go get/go mod 指令工具管理依赖包
依赖管理三要素
1.配置文件,描述依赖 go.mod
- 主版本2+模块会在模块路径增加/vN后缀
- 对于没有go.mod文件并且主版本2+的依赖,会有+incompatible的后缀
2. 中心仓库管理依赖库 Proxy
由于直接使用第三方代码托管平台会无法保证构建稳定性,无法保证依赖可用性,并且会增加第三方压力,所以出现了Proxy
3. 本地工具 go get/mod
测试
测试包括:回归测试,集成测试,单元测试,从左至右覆盖率逐渐变大,成本缺逐渐降低。
单元测试
单元测试的规则
单元测试的例子
package main
func HelloTom() string {
return "Jerry"
} //源程序
package main
import "testing"
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
if output != expectOutput {
t.Errorf("Expected %s do not match actual %s", expectOutput, output)
}
} //测试程序
然后只需要运行测试程序,或者在终端在本文件目录下执行go test命令即可
开源assert包的使用
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
assert.Equal(t,expectOutput,output)
} //用开源的assert包来进行equal和not equal的判断
这里使用了github上的开源包,对于包的获取只需要在终端执行
go get github.com/stretchr/testify/assert即可
单元测试的评估-覆盖率
package main
func JudgePassLine(score int64) bool {
if score >= 60 {
return true
}
return false
} //源程序
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestJudgePassLineTrue(t *testing.T) {
isPass := JudgePassLine(70)
assert.Equal(t, true, isPass)
} //测试程序
在终端执行go test -cover即可获取覆盖率
单元测试稳定性-mock包
项目通常会有许多依赖,如数据库和本地文件,但是单元测试需要每次测试的结果一样,并且在任何时间任何函数都能独立运行,即幂等和稳定。为了达到稳定性这个目标,我们引入了mock机制
package test
import (
"bufio"
"os"
"strings"
)
func ReadFirstLine() string {
open, err := os.Open("log")
defer open.Close()
if err != nil {
return ""
}
scanner := bufio.NewScanner(open)
for scanner.Scan() {
return scanner.Text()
}
return ""
}
func ProcessFirstLine() string {
line := ReadFirstLine()
destLine := strings.ReplaceAll(line, "11", "00")
return destLine
} //源程序
这里的源程序就需要依赖本地文件log,当log文件发生变动,就会影响测试的结果,所以我们引入了monkey这个包来进行mock操作,为函数打桩,使测试结果不再依赖本地文件
package test
import (
"bou.ke/monkey"
"github.com/stretchr/testify/assert"
"testing"
)
func TestProcessFirstLine(t *testing.T) {
firstLine := ProcessFirstLine()
assert.Equal(t, "line00", firstLine)
}
func TestProcessFirstLineWithMock(t *testing.T) {
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
defer monkey.Unpatch(ReadFirstLine)
line := ProcessFirstLine()
assert.Equal(t, "line000", line)
} //测试程序
基准测试
在实际项目过程中,我们有时候会遇到性能瓶颈的问题,这时候我们就需要改进项目的性能,为了定位问题,我们就需要对代码进行性能分析,这就是基准测试的意义。
基准测试的使用和单元测试非常相似,只需要将函数命名前缀从单元测试的前缀Test改为Bench,传入的参数从testing.A改为testing.B,然后在执行的命令加上后缀-bench即可,这里放上一个基准测试的函数和运行结果方便比较。
func BenchmarkSelect(b *testing.B) {
InitServerIndex()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Select()
}
}
tips:使用vscode在编写好测试函数后,函数上方会有运行的按钮,比较方便,即下图左上角
项目实践-社区话题页面
需求描述
- 展示话题(标题,文字描述)和回帖列表
- 暂不考虑前端页面实现,仅仅实现一个本地web服务
- 话题和回帖数据用文件存储
模型ER图
项目通用分层结构
结构需要根据项目的不同进行调整,不必要硬套
组件工具
项目需要用到gin框架
go mod init {项目名} //初始化mod文件
go get gopkg.in/gin-gonic/gin.v1@v1.3.0 //下载gin框架
在gin框架的下载过程中遇到了很多问题,这篇文章成功的解决了这些问题(blog.csdn.net/qq_34284638…)
运行结果
具体函数的编写和逻辑较为复杂,这里不赘述了(根本不会),这里只讲一下怎么测试服务。
curl 127.0.0.1:8080/community/page/get/2 //终端输入curl命令
也可以在浏览器直接访问localhost:8080/community/page/get/1 运行结果如图