01 高并发
并发vs并行
并行可以理解为并发的一种手段
go可以充分发挥多核优势,高效运行
函数前+go来调用协程,快速运行,区别于一般调用
time.Sleep(time.Second)使调用子协程时,主协程不退出
协程间通信,数据交换的不同方式
src无缓冲;dest有缓冲
a协程遍历0-9并发送到channel src
b协程遍历src的数据,实现a协程和b协程的通信
通过src和dest的传递能够保证顺序性,并发安全
为什么选择有缓冲:eg消费速度慢于生产,使用缓冲让消费不会影响生产效率
通过共享内存实现通信
undefer问题
对临界区进行控制
避免多协程抢夺数据
02 依赖管理
Go 依赖管理演进 各种开发包
单体函数,原生sdk
原生SDK(Software Development Kit,软件开发工具包)指的是为特定操作系统或平台量身定制的开发工具包。它通常由操作系统的官方机构或平台提供,以便开发者能够利用系统的底层功能和资源,从而开发出性能更高、兼容性更强的应用程序。使用原生SDK的应用程序通常称为“原生应用(Native App)”。
复杂项目,跨平台sdk
commit 时间戳+哈希码
Go 依赖管理演进
Go Module 依赖管理方案
Go Module实践
Go 语言的依赖管理经历了几次重要的演进,从最初的
GOPATH
到 Go Modules
,这个变化极大地简化了 Go 项目的依赖管理。下面是 Go 依赖管理的演进过程,重点介绍 Go Modules
方案。
1. GOPATH 模式(早期)
最初,Go 使用 GOPATH
来管理依赖。在这种模式下,所有的 Go 代码都必须放置在 GOPATH
目录下。依赖库也必须存放在 GOPATH
内的 src
子目录下,go get
命令会自动将第三方库下载到 GOPATH
中。然而,这种方式有许多问题:
- 路径固定:项目必须放在
GOPATH
目录中,无法自由选择项目存放位置。 - 版本管理困难:不同项目可能需要不同版本的相同依赖,
GOPATH
模式不能很好地处理依赖的版本冲突。
2. Dep(早期的依赖管理工具)
为了弥补 GOPATH
模式的不足,社区引入了 dep
工具。dep
提供了对依赖版本的管理,它使用 Gopkg.toml
和 Gopkg.lock
文件来指定和锁定项目的依赖版本。虽然 dep
改善了依赖版本控制的问题,但仍然存在如下问题:
- 没有与 Go 官方集成,且需要额外安装和配置。
- 依赖的版本控制和管理方式比较复杂,需要手动维护锁文件。
3. Go Modules(正式解决方案)
随着 Go 1.11 的发布,Go 官方引入了 Go Modules
(以下简称 Go Mod),从而彻底解决了 GOPATH
和 dep
的问题。Go Mod 是 Go 语言的官方依赖管理工具,支持项目和依赖版本的管理,并且有以下特点:
- 无需 GOPATH:Go Mod 让项目不再依赖
GOPATH
,可以放在任何地方,简化了项目结构。 - 版本控制:通过
go.mod
和go.sum
文件来管理依赖版本。go.mod
文件用于定义模块名称、依赖项和 Go 版本,go.sum
文件记录每个依赖的校验和,保证了依赖的一致性。 - 自动化依赖管理:
go mod tidy
命令可以自动清理不再使用的依赖,保持go.mod
和go.sum
文件的整洁。 - 支持代理:Go Mod 支持通过 Go Proxy 来获取依赖,避免了直接从 GitHub 等源获取的可能不稳定问题。
Go Mod 的关键文件
-
go.mod:定义模块名、Go 版本、依赖的版本等信息。
module example.com/myproject go 1.16 require ( github.com/gorilla/mux v1.8.0 github.com/sirupsen/logrus v1.7.0 )
-
go.sum:记录每个依赖的校验和,确保依赖的完整性。
github.com/gorilla/mux v1.8.0 h1:45D1C56E3F0C... github.com/sirupsen/logrus v1.7.0 h1:kO5U6FgC5Pz...
-
go.mod 和 go.sum 的操作命令:
go mod init
:初始化模块,生成go.mod
文件。go mod tidy
:自动整理go.mod
和go.sum
,删除不再使用的依赖。go mod vendor
:将依赖复制到本地的vendor
目录中(在一些特定情况下使用)。go get
:添加或升级依赖版本。go mod verify
:验证依赖的完整性和一致性。
4. Go 1.16 及以后:Go Modules 默认开启
自 Go 1.16 版本起,Go Modules
成为 Go 的默认依赖管理方式。GOPATH
模式被正式淘汰,Go 项目管理的推荐方式是使用 Go Mod
。
总结
Go 依赖管理的演进从最初的 GOPATH
到社区工具 dep
,最终到官方的 Go Modules
,实现了更为灵活、强大和简便的依赖管理方式。Go Modules 的引入极大地简化了 Go 项目的依赖管理,并解决了多个版本冲突和管理不便的问题,成为现代 Go 项目不可或缺的一部分。
03.测试
测试是事故的最后一道屏障
回归测试:手动通过终端回归主流场景
集成测试:对系统功能维度,对系统接口测试
单元测试:开发过程中对函数测试,一定程度上决定代码质量
单元测试
Mock测试
基准测试
Go 语言的单元测试、Mock 测试和基准测试是开发过程中非常重要的工具,它们帮助开发者确保代码质量、验证功能以及优化性能。
单元测试(Unit Test) 单元测试是测试代码的最小单元——通常是函数或方法,确保它们按照预期的方式工作。
Go 的单元测试框架
Go 语言内置了一个非常简单的测试框架,使用 testing
包。基本的单元测试包括编写一个以 Test
开头的函数,该函数接受一个指向 testing.T
类型的参数。
示例:
假设你有一个函数 Add
,用来返回两个整数的和:
package main
import "fmt"
// Add 返回两个整数的和
func Add(a, b int) int {
return a + b
}
你可以为这个函数编写单元测试:
package main
import "testing"
// TestAdd 是 Add 函数的单元测试
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Expected 5, but got %d", result)
}
}
运行测试
可以使用 go test
命令来运行单元测试。默认情况下,go test
会查找所有以 Test
开头的函数并执行它们:
go test
测试输出
- 如果测试通过,不会显示任何输出。
- 如果测试失败,会显示类似
FAIL
的信息,并且打印错误的详细信息。
Mock 测试(Mock Testing) Mock 测试用于模拟外部依赖,特别是当你的代码依赖于外部服务、数据库、API 或其他模块时,直接进行单元测试会很困难。通过创建 Mock 对象,你可以避免对外部资源的实际调用,而是模拟它们的行为。
Go 中有多个工具可以用来实现 Mock 测试,最常见的库是 github.com/stretchr/testify/mock
和 github.com/golang/mock
。
示例: 假设你有一个依赖于外部数据库的函数,你希望对其进行 Mock 测试。首先定义一个接口:
package main
import "fmt"
// Database 接口模拟数据库操作
type Database interface {
GetUserByID(id int) (string, error)
}
// 获取用户名称
func GetUserName(db Database, id int) (string, error) {
name, err := db.GetUserByID(id)
if err != nil {
return "", err
}
return name, nil
}
然后你可以使用 Mock 来模拟 Database
接口:
package main
import (
"testing"
"github.com/stretchr/testify/mock"
)
// MockDatabase 是 Database 接口的 Mock 实现
type MockDatabase struct {
mock.Mock
}
func (m *MockDatabase) GetUserByID(id int) (string, error) {
args := m.Called(id)
return args.String(0), args.Error(1)
}
func TestGetUserName(t *testing.T) {
// 创建 Mock 对象
mockDB := new(MockDatabase)
// 设置期望
mockDB.On("GetUserByID", 1).Return("John", nil)
// 调用被测试的函数
name, err := GetUserName(mockDB, 1)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
if name != "John" {
t.Errorf("Expected 'John', got %s", name)
}
// 验证期望
mockDB.AssertExpectations(t)
}
运行 Mock 测试
和普通单元测试一样,Mock 测试也可以通过 go test
来运行。
testify/mock
主要功能:
- 期望设置:通过
On
方法指定某个方法的调用以及期望的返回值。 - 断言:使用
AssertExpectations
来确保 Mock 对象的所有预期行为都发生。 - 模拟行为:通过
Return
方法返回模拟值,模拟方法的行为。
基准测试(Benchmark Test)
基准测试用于衡量代码的性能,帮助你理解某段代码的执行效率。Go 提供了 testing
包中的 Benchmark
函数来进行基准测试。
示例: 假设你想要基准测试一个字符串拼接的性能:
package main
import "testing"
// 拼接字符串
func Concatenate(str1, str2 string) string {
return str1 + str2
}
// 基准测试
func BenchmarkConcatenate(b *testing.B) {
for i := 0; i < b.N; i++ {
Concatenate("Hello", "World")
}
}
在基准测试中,b.N
代表运行基准测试的次数。testing.B
提供了一个基准测试的计时器,Go 会根据性能分析自动决定执行次数。
运行基准测试
通过 go test
和 -bench
标志来运行基准测试:
go test -bench .
输出结果: 基准测试结果通常会显示每个操作的平均时间以及每秒的操作次数:
BenchmarkConcatenate-8 2000000000 0.35 ns/op
BenchmarkConcatenate-8
:表示这是BenchmarkConcatenate
测试,-8
是 CPU 核心数。2000000000
:表示测试运行了多少次。0.35 ns/op
:表示每次操作的平均时间。
总结
- 单元测试:通过
testing
包对函数进行基本的功能测试,确保代码的正确性。 - Mock 测试:通过 Mock 对象模拟外部依赖,测试与外部资源交互的代码逻辑。
- 基准测试:通过
testing.B
来测试代码性能,优化代码的执行效率。
这些测试方法帮助开发者在确保代码质量的同时,也能保证系统在不同情况下的正确性与高效性。
04项目实践
需求设计 代码开发 测试运行
4.2需求用例
浏览消费用户
topicpage:topic、postlist
单例减少内存浪费
项目拆解-代码设计-测试运行
没跑通😔