LEC2-《Go 语言工程实践》
并发编程
协程(goroutine
轻量级的用户级的线程
Go语言中的协程(Goroutine)是一种轻量级的线程,用于在程序中并发执行任务。协程是Go语言中并发编程的基本单位,与传统的操作系统线程不同,协程由Go运行时(runtime)管理,可以在单个线程上同时运行多个协程。这使得Go能够高效地处理大量的并发任务,而不会消耗过多的系统资源。
协程的主要特点包括:
- 轻量级: 创建一个协程的开销很小,因此可以创建数千甚至数百万个协程,而不会造成显著的性能损失。
- 并发性: 多个协程可以在同一时间执行,它们之间通过通信来共享数据和协调任务。这种并发性使得在Go程序中可以更自然地编写高效的并发代码。
- 通信: 协程之间的通信是通过通道(Channel)来完成的。通道是一种特殊的数据结构,允许协程之间传递数据,实现同步和协作。
- 调度: Go运行时具有自己的调度器,可以在多个协程之间进行切换,以实现并发执行。这个调度器基于一种称为"GOMAXPROCS"的设置来控制同时执行的协程数量。
创建一个协程非常简单,只需在函数调用前加上关键字"go"即可
很好理解,字面意思
通信共享一块内存 vs 共享一块内存来互相通信
channel
example
使用通道实现两个子协程之间的通信
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3) // 使用带缓冲的channel,是为了协调生产者和消费者的速度可能存在的差异
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}() // 这个协程是为了向src中写入数据
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}() // 这个协程是为了从src中读取数据并写入到dest中
for i := range dest {
//复杂操作
println(i)
}
}
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 20000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func addWithoutLock() {
for i := 0; i < 20000; i++ {
x += 1
}
}
func Add() {
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
println("WithoutLock:", x)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
println("WithLock:", x)
}
依赖管理
SDK
SDK是软件开发工具包(Software Development Kit)的缩写。它是一组软件开发工具和资源的集合,旨在帮助开发人员在特定平台、操作系统或框架上创建应用程序、插件或其他软件。SDK通常包含一系列API(应用程序编程接口)、示例代码、文档和工具,以便开发者能够更轻松地构建应用程序并与特定平台进行交互。
gopath -> vendor -> module
Go Module
incompatible
后来才引入规则需要在主版本2+模块路径增加/vN的后缀
所以一开始没有按照要求的依赖就加上incompatible的后缀进行标识
GOPROXY变量
其实在LEC1中有设置过
GOPROXY 是一个环境变量,用于设置 Go 语言模块(module)下载的代理服务器。在 Go 语言中,模块是用于组织、版本控制和共享代码的一种机制。GOPROXY 的作用是指定一个代理服务器,用于下载和缓存模块,从而加速模块的获取过程。
通过设置 GOPROXY,可以控制从哪个代理服务器获取模块。常见的代理服务器包括 Go 官方的代理、私有代理服务器以及国内的镜像站点。
测试
单元测试
testing.M的用法(简单了解,非必需
覆盖率 加上--cover参数
这个cover只是简单显示下行数覆盖而已
比如
2/3 = 66.7%
Mock
稳定 是指相互隔离,能在任何时间,任何环境,运行测试。
幂等 是指每一 次测试运行都应该产生与之 前一样的
例如只测试processFiestLine()
readFirstLine()则使用一个固定值返回
这样就能消除对本地文件的依赖
本例使用的是monkey的包
基准测试(Benchmark)
ns/op 应该就是ns/每个操作
实践
实践过程
首先获取gin包
go get gopkg.in/gin-gonic/gin.v1@v1.3.0
Topic和Post对象
parent_id就是帖子所在话题的id
索引
var (
topicIndexMap map[int64]*Topic
postIndexMap map[int64][]*Post
)
map: int64 -> *Topic
初始化
func initTopicIndexMap(filePath string) error {
open, err := os.Open(filePath + "topic")
if err != nil {
return err
}
scanner := bufio.NewScanner(open)
topicTmpMap := make(map[int64]*Topic)
for scanner.Scan() {
text := scanner.Text()
var topic Topic
if err := json.Unmarshal([]byte(text), &topic); err != nil {
return err
}
topicTmpMap[topic.Id] = &topic
}
topicIndexMap = topicTmpMap
return nil
}
关键步骤如下: 打开文件 将json字符串转换为Topic对象,使用Unmarshal()函数 make()函数创建一个临时map,key为int64类型,value为*Topic类型 将临时map赋值给全局变量topicIndexMap
成功运行
开着代理的情况下
使用http://127.0.0.1:8080/community/page/get/2能get到
但是用http://0.0.0.0:8080/community/page/get/2就会502Bad Gateway
127.0.0.1是本地回环地址(localhost),它指向本机的网络接口。当您访问http://127.0.0.1:8080/community/page/get/2时,请求是直接发往本机的 Web 服务器,而不需要经过代理。0.0.0.0是一个特殊的 IP 地址,表示所有可用的 IP 地址或网络接口。在代理服务器上,当您访问http://0.0.0.0:8080/community/page/get/2时,代理服务器会尝试将请求转发到其他网络接口或上游服务器。但由于代理配置或其他原因,该请求无法成功转发到上游服务器,导致出现 502 Bad Gateway 错误。当您关闭代理并使用
0.0.0.0地址时能够成功,很可能是因为关闭代理后,0.0.0.0地址被解释为本地主机地址,而不是在代理服务器中作为特殊的监听地址。