241117学习日志——[CSDIY] [ByteDance] 后端训练营 [05]
CSDIY:这是一个非科班学生的努力之路,从今天开始这个系列会长期更新,(最好做到日更),我会慢慢把自己目前对CS的努力逐一上传,帮助那些和我一样有着梦想的玩家取得胜利!!!
第一弹:Cpp零基础学习【30 DAYS 从0到1】
第二弹:Cpp刷题文档【LeetCode】
第三弹:Go开发入门【字节后端青训营】
第四弹:Cpp简单项目开发【黑马Rookie】
第五弹:数据结构绪论【数据结构与算法】
第六弹:Go工程实践【字节后端青训营】
1. Go 语言进阶
并发 VS 并行
并发:多线程程序在一个核的CPU上运行
并行:多线程程序在多个核的CPU上运行(可以理解为实现并发的一个手段)
Go 可以充分发挥多核优势,高效运行
1.1 Goroutine
**协程:**用户态,轻量级线程 >栈、KB 级别
**线程:**内核态,线程跑多个协程 >栈、MB 级别
package concurrence
// 并发危险:乱序输出
import (
"fmt"
"sync"
)
func hello(i int) {
println("hello world : " + fmt.Sprint(i))
}
func ManyGo() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
1.2 CSP(Communicating Sequential Processes)
提倡通过通信共享内存而不是共享内存来实现通信
1.3 Channel
make(chan 元素类型,[缓冲大小])
-
无缓冲通道:make(chan int)
-
有缓冲通道:make(chan int,2)
package concurrence
// 并发安全的例子,按序输出
func CalSquare() {
// 定义无缓冲
src := make(chan int)
// 定义缓冲
dest := make(chan int, 3)
go func() {
// A协程的功能:发送0~9的数字
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
go func() {
// B协程的功能计算输入的数字的平方
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
// 主协程输出最后的平方数
println(i)
}
}
1.4 并发安全 Lock
package main
import (
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
// 通过锁机制保证并发安全
// 获取临界区资源
lock.Lock()
x += 1
// 释放临界区资源
lock.Unlock()
}
}
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func main() {
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
// 有可能不会输出期望值
println("Without Lock:", x)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
// 输出期望值
println("With Lock:", x)
}
实际开发中,避免对共享内存进行非并发安全的读写操作
1.5 WaitGroup
计数器
开启协程+1;执行结束-1;主协程阻塞直到计数器为0.
package main
import (
"sync"
)
func main() {
var wg sync.WaitGroup
// 开辟五个协程
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
// 通过 Done 方法进行计数器 -1
defer wg.Done()
println("goroutine", j, "start")
}(i)
}
wg.Wait()
}
1.6 Go 并发编程小结
- Goroutine
- Channel
- Sync:实现并发安全操作和协程间操作
2. Go 依赖管理
学会站在巨人的肩膀上
- 工程项目不可能基于标准库 0~1 编码搭建(0基础开始到1)
- 管理依赖库(框架、日志、driver等依赖,通过sdk方式引入)
2.1 Go 依赖管理演进
控制依赖库的版本
- GOPATH
- Go Vender
- Go Module
不同环境(项目)依赖的版本不同
2.1.1 GOPATH
是Go语言支持的一个环境变量
- src:存放 Go 项目源码
- pkg:存放编译的中间产物,加快编译速度
- bin:存放 Go 项目编译生成的二进制产物
弊端:
- 如果项目A和项目B同时依赖某一package的不同版本
- 那么GOPATH无法实现package的多版本控制
2.1.2 Go Vender
- 项目目录下增加vender文件,所有依赖包副本形式放在vender
- 依赖寻址方式:vender=>GOPATH
通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题
弊端:
- 如果项目A依赖pkg B和C,而B和C依赖了D的不同版本
- 通过vender的管理模式不能很好控制对于D的依赖版本
- 更新项目又可能出现依赖冲突,导致编译出错
2.1.3 Go Module
- 通过 go.mod 文件管理依赖包版本
- 通过 go get/go mod 指令工具管理依赖包
终极目标:定义版本规则和管理项目依赖关系
2.2 依赖管理三要素
- 配置文件,描述依赖:go.mod
- 中心仓库管理依赖库:proxy
- 本地工具:go get/mod
2.3
2.3.1 依赖管理 - go.mod
module example/project/app // 依赖管理基本单元
go 1.16 // 原生库
require (
example/lib1 v1.0.2 // 单元依赖
// 依赖标识:[Module Path][Version/Pseudo-version]
)
2.3.2 依赖配置 - version
两种版本规则:语义化版本、基于 commit 伪版本
语义化版本:
v{MAJOR:不同模块}.{MINOR:新增函数功能}.{PATCH:修复bug}
v1.3.0
基于 commit 伪版本:
vX.0.0{和语义化版本一样}-yyyymmddhhmmss{时间戳}-abcdefgh1234{校验码 哈希前缀}
2.3.3 依赖配置 - indirect
用来标识间接依赖
2.3.4 依赖配置 - incompatible
主版本在 2+ 的依赖,会 +incompatible
2.3.5 依赖分发 - 回源 - Proxy
代码托管系统:Github、SVN、…
-
无法保证构建稳定性
-
无法保证依赖可用性
-
增加第三方压力
Proxy
直接从Proxy拉取依赖:稳定可靠。
2.3.6 依赖分发 - 变量 - GOPROXY
2.3.7 工具 - go get
go get example.org
2.3.8 工具 - go mod
go mod
- init:初始化,创建go.mod文件
- download:下载模块到本地缓存
- tidy:增加需要的依赖,删除不需要的依赖
3. Go 工程测试
测试就是保证质量
质量就是生命
-
回归测试:回归用户体验
-
集成测试:集成的接口测试
-
单元测试:模块单元测试
从上到下,覆盖率逐层增大,测试成本逐层降低
3.1 单元测试
3.1.1 单元测试 - 规则
- 所有测试文件以 _test.go 结尾(方便分清源代码和测试代码)
- func TestXxx(*testing.T)
- 初始化逻辑放到 TestMain中
3.1.3 单元测试 - 运行
3.1.5 单元测试 - 覆盖率
已测试代码量 / 总代码量
- 一般覆盖率:50%~60%,较高覆盖率80%
- 测试分支相互独立、全面覆盖
- 测试单元粒度足够小,函数单一职责
3.2 单元测试 - 依赖
外部依赖 => 稳定&幂等
3.3 单元测试 - 文件处理
对文件:
- 打开关闭操作
- 文件内容替换操作
3.4 单元测试 - Mock
快速 Mock 函数
- 为一个函数打桩
- 为一个方法打桩
3.5 基准测试
3.5.1 基准测试 - 例子
随机选择执行服务器
3.5.2 基准测试 - 运行
3.5.3 基准测试 - 优化
4. Go 项目实践
4.1 需求描述
- 展示话题和回帖列表
- 暂不考虑前端页面实现,仅仅实现一个本地web服务
- 话题和回帖数据用文件存储
需求用例
- 用户消费浏览:话题和回帖列表
4.3 ER 图 - Entity Relationship Diagram
- 话题
- 帖子
4.4 分层结构
- Repository 数据层:数据 Module,外部数据的增删查改
- Service 逻辑层:业务 Entity,处理核心业务逻辑输出
- Controller 视图层:视图 view,处理和外部的交互逻辑
4.5 组件工具
- Gin 高性能 go web 框架
- Go Mod
4.6 Repository
4.7 Service
4.8 Controller
4.9 Router
4.10 运行
碎碎念:作业好多啊!!!屁事也好多,根本没有成片的时间来好好磨技术…真的老实了,再也不选那么多课了,课也不想好好听了…我指的是水课。搞得后面的项目都想摆烂了…这样应付大学老师的日子什么时候是个头啊!感觉时间都不是自己的…但还是要坚持啊…为了我的BAT梦…