《Go 简易速速上手小册》第5章:并发编程(2024 最新版)
文章目录
- 5.1 Goroutines 的基础 - Go 语言中的轻盈舞者
- 5.1.1 基础知识讲解
- 5.1.2 重点案例:并发下载器
- 功能描述
- 实现代码
- 扩展功能
- 5.1.3 拓展案例 1:网站健康检查
- 功能描述
- 实现代码
- 扩展功能
- 5.1.4 拓展案例 2:并发日志处理器
- 拓展案例 2:并发日志处理器
- 功能描述
- 实现代码
- 扩展功能
- 5.2 Channels 的使用 - Go 语言中的通信艺术
- 5.2.1 基础知识讲解
- 5.2.2 重点案例:任务分发系统
- 功能描述
- 实现代码
- 5.2.3 拓展案例 1:数据流处理
- 拓展案例 1:数据流处理
- 功能描述
- 实现代码
- 扩展功能
- 5.2.4 拓展案例 2:实时消息系统
- 功能描述
- 实现代码
- 扩展功能
- 5.3 并发模式与同步 - 编织 Go 语言中的并发之网
- 5.3.1 基础知识讲解
- 5.3.2 重点案例:简易聊天服务器
- 功能描述
- 实现代码
- 扩展功能
- 5.3.3 拓展案例 1:实时数据监控
- 功能描述
- 实现代码
- 扩展功能
- 5.3.4 拓展案例 2:并发 Web 爬虫
- 功能描述
- 实现代码
- 扩展功能
5.1 Goroutines 的基础 - Go 语言中的轻盈舞者
Ahoy, 并发编程的舞者们!让我们一起深入探索 Go 语言中的 Goroutines —— 这些轻盈的并发执行单位,它们就像是在 CPU 的舞台上轻盈跳跃的舞者。通过 Goroutines,Go 让并发编程变得异常简单和高效,就像是为我们的应用程序注入了一剂速效的能量药剂。
5.1.1 基础知识讲解
Goroutines 的定义
Goroutines 是 Go 语言中实现并发的核心。你可以把它们想象成轻量级的线程,由 Go 运行时管理。与操作系统的线程相比,Goroutines 的启动和销毁成本更低,内存占用也更小,这使得你可以轻松地创建成千上万的 Goroutines。
go function() {// 这里是你的代码
}
只需在函数调用前加上 go
关键字,这个函数就会在新的 Goroutine 中异步执行。是的,就是这么简单!
Goroutines 的特点
- 轻量级:每个 Goroutine 在堆栈上只占用几 KB 的内存。
- 动态增长的堆栈:Goroutines 的堆栈大小不是固定的,可以根据需要动态增长和缩小。
- 简单的创建和销毁:创建和销毁 Goroutines 的成本远低于重量级线程。
5.1.2 重点案例:并发下载器
在这个快速发展的互联网时代,下载多个文件是一项常见的任务。利用 Go 语言的 Goroutines,我们可以轻松实现一个并发下载器,这样可以大大加快下载速度,提升用户体验。让我们一起来扩展这个并发下载器的案例,使其更加实用和高效。
功能描述
- 并发下载:使用 Goroutines 并发下载多个文件。
- 错误处理:捕获下载过程中的错误,并报告。
- 进度反馈:实时显示每个文件的下载进度和状态。
- 同步等待:使用
sync.WaitGroup
确保所有下载任务完成后程序才退出。
实现代码
首先,我们模拟一个下载函数,它接收文件名和一个用于报告下载进度的通道:
package mainimport ("fmt""math/rand""sync""time"
)// downloadFile 模拟文件下载
func downloadFile(file string, progress chan<- string, wg *sync.WaitGroup) {defer wg.Done()for i := 0; i <= 100; i += rand.Intn(25) {progress <- fmt.Sprintf("%s 下载进度: %d%%", file, i)time.Sleep(time.Duration(rand.Intn(300)) * time.Millisecond)}progress <- fmt.Sprintf("%s 下载完成", file)
}
然后,我们创建一个 Goroutine 来处理每个文件的下载,并使用sync.WaitGroup
来同步等待所有下载任务完成:
func main() {files := []string{"file1.zip", "file2.zip", "file3.zip"}var wg sync.WaitGroup// 创建一个通道来报告下载进度progress := make(chan string)// 计数器设置为需要下载的文件数wg.Add(len(files))for _, file := range files {go downloadFile(file, progress, &wg)}// 启动一个 Goroutine 来打印进度信息go func() {for p := range progress {fmt.Println(p)}}()// 等待所有下载任务完成wg.Wait()close(progress) // 关闭通道,停止打印进度信息
}
扩展功能
- 错误处理:我们可以修改
downloadFile
函数,让它有一定概率模拟下载失败的情况,并通过另一个通道报告错误。 - 限制并发数:为避免同时启动过多的 Goroutines,我们可以使用带缓冲的通道作为并发限制的信号量。
通过这个扩展案例,我们构建了一个更加健壮和实用的并发下载器,它不仅可以并发下载多个文件,还能处理错误、报告下载进度,并且保证所有任务完成后才退出程序。这个案例展示了 Goroutines 和通道在实际应用中的强大能力,为我们解决并发任务提供了简单有效的工具。现在,就让我们利用这些工具,去构建更多令人激动的并发应用吧!
5.1.3 拓展案例 1:网站健康检查
在维护任何在线服务时,定期检查网站的健康状况是至关重要的。通过并发执行网站健康检查,我们可以在最短的时间内获得多个网站的状态,从而迅速响应可能出现的问题。利用 Go 语言的 Goroutines 和 Channels,我们可以构建一个高效的网站健康检查工具。
功能描述
- 并发执行网站健康检查:使用 Goroutines 并发地向多个网站发送请求。
- 收集并报告结果:收集每个网站的健康检查结果,并汇总报告。
实现代码
首先,定义一个简单的函数来检查单个网站的健康状况:
package mainimport ("fmt""net/http""sync""time"
)// checkWebsite 检查网站健康状况
func checkWebsite(url string, wg *sync.WaitGroup, results chan<- string) {defer wg.Done()start := time.Now()resp, err := http.Get(url)duration := time.Since(start)if err != nil || resp.StatusCode != 200 {results <- fmt.Sprintf("[失败] %s 耗时 %s", url, duration)return}results <- fmt.Sprintf("[成功] %s 状态码 %d 耗时 %s", url, resp.StatusCode, duration)
}
然后,使用 Goroutines 并发执行多个网站的健康检查,并使用sync.WaitGroup
同步等待所有检查任务完成:
func main() {websites := []string{"https://www.google.com","https://www.github.com","https://www.stackoverflow.com","https://golang.org","https://www.example.com",}var wg sync.WaitGroupresults := make(chan string, len(websites))wg.Add(len(websites))for _, url := range websites {go checkWebsite(url, &wg, results)}go func() {wg.Wait()close(results)}()// 打印检查结果for result := range results {fmt.Println(result)}
}
扩展功能
- 超时控制:为
http.Get
请求添加超时控制,防止某些网站响应过慢影响整体检查进程。 - 重试机制:对于检查失败的网站,可以实现重试机制,以确保偶发的网络问题不会导致误报。
通过这个扩展案例,我们构建了一个可以并发执行网站健康检查的工具,它能够快速收集和报告多个网站的状态。利用 Go 语言的并发特性,我们的工具不仅执行效率高,而且代码结构清晰简洁。这种并发模式的应用,在开发高效且可靠的网络服务和工具时非常有价值。现在,就让我们继续探索 Go 语言的并发世界,开发更多强大的应用吧!
5.1.4 拓展案例 2:并发日志处理器
拓展案例 2:并发日志处理器
在大型系统中,日志是监控系统健康、诊断问题的重要手段。随着系统规模的扩大,日志量也会急剧增加。使用并发日志处理器,我们可以高效地从多个来源并发地收集、处理日志,提高日志处理的速度和效率。
功能描述
- 并发收集日志:使用 Goroutines 并发地从多个日志来源(如文件、网络等)收集日志。
- 日志处理:对收集到的日志执行一系列处理操作,如过滤、格式化。
- 日志聚合:将处理后的日志聚合到一个中心位置,以便分析和存储。
实现代码
首先,定义一个模拟的日志收集函数,假设日志来自不同的文件:
package mainimport ("fmt""sync""time"
)// collectLogs 从指定的日志来源收集日志
func collectLogs(source string, wg *sync.WaitGroup, logChan chan<- string) {defer wg.Done()// 模拟从不同来源收集日志的时间消耗time.Sleep(time.Duration(1+rand.Intn(5)) * time.Second)logMsg := fmt.Sprintf("日志来自 %s: 日志内容", source)logChan <- logMsg
}
接着,实现并发的日志收集和处理逻辑:
func main() {logSources := []string{"文件1", "文件2", "网络流", "数据库"}var wg sync.WaitGrouplogChan := make(chan string, len(logSources))// 并发从各个日志来源收集日志wg.Add(len(logSources))for _, source := range logSources {go collectLogs(source, &wg, logChan)}// 启动一个 Goroutine 来处理日志go func() {for logMsg := range logChan {fmt.Println("处理日志:", logMsg)// 这里可以添加更复杂的日志处理逻辑}}()// 等待所有日志收集任务完成wg.Wait()close(logChan) // 关闭通道,结束日志处理 Goroutine
}
扩展功能
- 日志过滤:可以在处理日志的 Goroutine 中加入过滤逻辑,只保留符合特定条件的日志。
- 日志格式化:对日志进行格式化处理,例如转换为 JSON 格式,以便于后续处理和存储。
- 错误处理:增加错误处理逻辑,确保日志收集和处理过程中的错误能够被妥善处理。
通过这个扩展案例,我们构建了一个能够高效处理大量日志的并发日志处理器。利用 Go 语言的并发特性,我们的处理器可以轻松应对来自不同来源的日志,提高了日志处理的速度和灵活性。这种并发处理模式对于构建高性能的日志系统来说是非常有价值的。现在,让我们继续探索 Go 语言的并发特性,开发更多强大且高效的系统吧!
5.2 Channels 的使用 - Go 语言中的通信艺术
Ahoy,并发航海者们!进入 Go 的并发世界后,我们已经学会了如何让多个 Goroutines 舞动起来。现在,是时候让这些舞者学会如何交流了。在 Go 语言中,Channels 是 Goroutines 之间沟通的红绸带,让并发的执行流可以优雅地传递消息。
5.2.1 基础知识讲解
Channels 的定义
Channels 是 Go 语言中的一种类型,用于在 Goroutines 之间进行通信和数据的传递。你可以将 Channel 想象为一条河流,数据就像是河流中的水,可以从一个地方流向另一个地方。
ch := make(chan int)
上面的代码创建了一个传递int
类型数据的 Channel。
Channels 的发送和接收
向 Channel 发送数据和从 Channel 接收数据,都使用<-
运算符。
ch <- 42 // 向 Channel 发送数据
v := <-ch // 从 Channel 接收数据并赋值给 v
关闭 Channels
当你完成了 Channel 的使用,可以关闭它来防止发生更多的数据发送。接收操作可以继续进行,直到 Channel 中的现有数据都被接收完毕。
close(ch)
5.2.2 重点案例:任务分发系统
在许多应用场景中,我们需要将大量任务分发给不同的工作单元进行并发处理,然后收集和汇总处理结果。这不仅可以显著提高任务处理的效率,还能优化资源的利用。通过使用 Go 语言的 Goroutines 和 Channels,我们可以构建一个高效的任务分发系统。
功能描述
- 并发任务处理:创建多个工作 Goroutines 并发处理任务。
- 任务队列:使用 Channel 作为任务队列,分发任务给工作 Goroutines。
- 结果收集:工作 Goroutines 处理完成后,通过另一个 Channel 将结果返回。
实现代码
首先,定义Task
和Result
的结构体,以及一个模拟的任务处理函数:
package mainimport ("fmt""sync""time"
)type Task struct {ID intData string
}type Result struct {TaskID intOutput string
}// 模拟任务处理函数
func processTask(data string) string {// 模拟处理时间time.Sleep(time.Second)return data + " processed"
}
实现工作 Goroutines,从任务 Channel 接收任务,处理任务,并将结果发送到结果 Channel:
func worker(taskChan <-chan Task, resultChan chan<- Result, wg *sync.WaitGroup) {defer wg.Done()for task := range taskChan {// 处理任务output := processTask(task.Data)resultChan <- Result{TaskID: task.ID, Output: output}}
}
构建任务分发和结果收集的主逻辑:
func main() {// 创建任务和结果的 ChannelstaskChan := make(chan Task, 10)resultChan := make(chan Result, 10)// 使用 WaitGroup 等待所有工作 Goroutines 完成var wg sync.WaitGroup// 启动工作 Goroutinesfor w := 1; w <= 3; w++ {wg.Add(1)go worker(taskChan, resultChan, &wg)}// 分发任务for i := 1; i <= 5; i++ {taskChan <- Task{ID: i, Data: fmt.Sprintf("Task %d", i)}}close(taskChan)// 启动一个 Goroutine 等待所有工作完成后关闭结果 Channelgo func() {wg.Wait()close(resultChan)}()// 收集并打印处理结果for result := range resultChan {fmt.Printf("Task %d: %s\n", result.TaskID, result.Output)}
}
通过这个扩展案例,我们构建了一个灵活且高效的任务分发系统。它展示了如何利用 Go 语言的并发特性来并行处理任务,并通过 Channels 安全地在 Goroutines 之间传递数据。这种模式非常适合于处理那些可以并行化的独立任务,极大地提高了任务处理的速度和效率。现在,就让我们继续探索 Go 语言的并发世界,发现更多的可能性吧!
5.2.3 拓展案例 1:数据流处理
拓展案例 1:数据流处理
数据流处理是一种常见的编程模式,特别适用于需要对数据进行一系列转换或计算的场景。在 Go 语言中,我们可以利用 Channels 和 Goroutines 构建一个高效的数据流处理管道(pipeline),这样可以并发地对数据进行处理,提高处理效率。
功能描述
- 创建处理管道:使用 Channels 将一系列的数据处理步骤连接起来,形成一个处理管道。
- 并发数据处理:每个处理步骤都运行在独立的 Goroutine 中,以实现并发处理。
- 灵活的数据传递:通过 Channels 在管道的各个阶段之间传递数据。
实现代码
首先,定义几个简单的数据处理函数,每个函数代表管道中的一个处理阶段:
package mainimport ("fmt""strings""time"
)// stage1:将字符串转换为大写
func stage1(input <-chan string) <-chan string {output := make(chan string)go func() {for s := range input {output <- strings.ToUpper(s)}close(output)}()return output
}// stage2:在字符串后添加特定后缀
func stage2(input <-chan string) <-chan string {output := make(chan string)go func() {for s := range input {output <- s + " PROCESSED"}close(output)}()return output
}// stage3:模拟耗时操作,如写入数据库
func stage3(input <-chan string) <-chan string {output := make(chan string)go func() {for s := range input {// 模拟耗时操作time.Sleep(1 * time.Second)output <- s + " -> SAVED"}close(output)}()return output
}
接着,构建并运行数据流处理管道:
func main() {// 初始数据源input := make(chan string)go func() {for _, s := range []string{"data1", "data2", "data3"} {input <- s}close(input)}()// 构建处理管道stage1Output := stage1(input)stage2Output := stage2(stage1Output)stage3Output := stage3(stage2Output)// 收集并打印处理结果for result := range stage3Output {fmt.Println(result)}
}
扩展功能
- 错误处理:可以在管道的每个阶段添加错误处理逻辑,确保处理过程的健壮性。
- 动态管道构建:根据实际需求动态地添加或移除处理阶段,使管道更加灵活。
通过这个扩展案例,我们构建了一个并发的数据流处理管道,它展示了如何使用 Go 语言的 Channels 和 Goroutines 来实现数据的并发处理。这种模式非常适合处理大量数据或进行复杂的数据转换和计算任务,能够显著提高处理效率。利用这种模式,我们可以轻松地构建出灵活、高效的数据处理应用。现在,让我们继续探索 Go 语言并发编程的强大功能,开发更多高效的应用吧!
5.2.4 拓展案例 2:实时消息系统
实时消息系统是现代应用中常见的需求,无论是聊天应用、实时数据处理系统还是监控告警系统,都需要快速有效地处理和分发消息。利用 Go 语言的 Channels 和 Goroutines,我们可以构建一个高效且响应迅速的实时消息系统。
功能描述
- 消息接收:并发接收来自不同来源的消息。
- 消息分发:将接收到的消息分发给多个消费者 Goroutines,以并发方式处理。
- 动态消费者管理:能够动态添加或移除消费者 Goroutines。
实现代码
首先,定义消息结构和消费者处理函数:
package mainimport ("fmt""sync""time"
)// Message 定义消息结构
type Message struct {ID intContent string
}// consumer 消费者处理函数
func consumer(id int, messages <-chan Message) {for msg := range messages {fmt.Printf("消费者 %d 处理消息: %v\n", id, msg)time.Sleep(time.Second) // 模拟消息处理时间}fmt.Printf("消费者 %d 结束\n", id)
}
接着,实现消息接收和分发逻辑:
func main() {messages := make(chan Message, 10)// 启动多个消费者 Goroutinesvar wg sync.WaitGroupfor i := 1; i <= 3; i++ {wg.Add(1)go func(id int) {defer wg.Done()consumer(id, messages)}(i)}// 模拟消息生产go func() {for i := 1; i <= 5; i++ {messages <- Message{ID: i, Content: fmt.Sprintf("消息内容 %d", i)}}close(messages) // 关闭 Channel,通知消费者结束}()wg.Wait() // 等待所有消费者 Goroutines 完成
}
扩展功能
- 消息过滤:在消息分发前添加过滤逻辑,只将符合特定条件的消息分发给消费者。
- 消费者负载均衡:实现更复杂的分发逻辑,根据消费者的处理能力动态调整其接收的消息量,实现负载均衡。
- 消息确认机制:引入消息确认机制,确保每条消息都被正确处理,增强系统的可靠性。
通过这个扩展案例,我们构建了一个基本的实时消息系统,它展示了如何使用 Go 语言的并发特性来实现消息的接收、分发和处理。这种模式在需要快速响应的系统中特别有用,能够保证消息在最短时间内被处理。利用 Go 的 Channels 和 Goroutines,我们可以轻松扩展和维护这个系统,以满足不断增长的需求。现在,让我们继续探索 Go 语言,并发编程的可能性,开发出更多功能丰富、响应迅速的应用吧!
5.3 并发模式与同步 - 编织 Go 语言中的并发之网
Ahoy,并发编程的舵手们!在 Go 语言的海洋中,我们不仅需要让 Goroutines 如舞者般自由舞动,还需要确保他们能够和谐地在同一舞台上表演,不发生踩脚或错位的尴尬情况。这就引出了并发模式与同步的主题,它们像是指挥家的手杖,确保每个动作都准确无误地完成。
5.3.1 基础知识讲解
并发模式
并发模式是一组解决并发问题的模板或策略。在 Go 中,常见的并发模式包括但不限于:
- 管道(Pipeline):通过一系列处理阶段的 Channels 传递数据,每个阶段由 Goroutines 处理。
- 工作池(Worker Pool):创建一组 Goroutines 来处理任务,可以有效控制并发量,避免资源耗尽。
- 发布/订阅(Pub/Sub):允许一个或多个生产者发布消息,一个或多个消费者订阅并处理消息。
同步机制
在并发执行时,同步是确保数据一致性和避免竞态条件的关键。Go 语言提供了多种同步机制:
- WaitGroup:等待一组 Goroutines 完成。
- Mutex(互斥锁):防止多个 Goroutines 同时访问共享资源。
- Channel:用于在 Goroutines 之间安全地传递数据。
5.3.2 重点案例:简易聊天服务器
在这个案例中,我们将构建一个简易的聊天服务器,该服务器能够处理多个客户端的连接请求,并实现消息的实时广播功能。通过使用 Go 语言的并发特性,我们可以让服务器同时接受多个客户端连接,并且当任一客户端发送消息时,服务器能够将该消息广播给所有连接的客户端。
功能描述
- 客户端连接处理:服务器并发接受来自多个客户端的连接请求。
- 实时消息广播:服务器接收到来自任一客户端的消息后,实时将其广播给所有已连接的客户端。
- 并发控制:通过同步机制确保服务器在处理客户端连接和消息广播时的线程安全。
实现代码
首先,我们定义聊天服务器的基本结构和构造函数:
package mainimport ("bufio""fmt""net""sync"
)// ChatServer 定义聊天服务器的结构
type ChatServer struct {clients map[net.Conn]boolbroadcast chan stringlock sync.Mutex
}// NewChatServer 创建新的聊天服务器实例
func NewChatServer() *ChatServer {return &ChatServer{clients: make(map[net.Conn]bool),broadcast: make(chan string),}
}
接下来,实现处理客户端连接的方法:
// handleConnection 处理新的客户端连接
func (cs *ChatServer) handleConnection(conn net.Conn) {defer conn.Close()// 将新客户端添加到 clients 集合中cs.lock.Lock()cs.clients[conn] = truecs.lock.Unlock()// 监听客户端发送的消息scanner := bufio.NewScanner(conn)for scanner.Scan() {msg := scanner.Text()cs.broadcast <- msg}// 客户端断开连接后,从 clients 集合中移除cs.lock.Lock()delete(cs.clients, conn)cs.lock.Unlock()
}
实现消息广播的方法:
// startBroadcasting 监听广播频道并向所有客户端广播消息
func (cs *ChatServer) startBroadcasting() {for msg := range cs.broadcast {cs.lock.Lock()for client := range cs.clients {fmt.Fprintln(client, msg)}cs.lock.Unlock()}
}
最后,启动聊天服务器,监听端口并接受客户端连接:
// Start 启动聊天服务器
func (cs *ChatServer) Start(port string) {listener, err := net.Listen("tcp", "localhost:"+port)if err != nil {fmt.Println("Error starting server:", err)return}defer listener.Close()go cs.startBroadcasting()fmt.Println("Chat server started on port", port)for {conn, err := listener.Accept()if err != nil {fmt.Println("Error accepting connection:", err)continue}go cs.handleConnection(conn)}
}func main() {chatServer := NewChatServer()chatServer.Start("8080")
}
扩展功能
- 昵称支持:允许客户端在连接时设置昵称,将昵称包含在广播的消息中。
- 私聊功能:实现客户端之间的私聊功能,允许消息只发送给特定的客户端。
- 客户端退出通知:当客户端断开连接时,服务器向所有客户端广播一条退出通知消息。
通过这个扩展案例,我们展示了如何使用 Go 语言构建一个简易的聊天服务器,它能够处理多个客户端的并发连接并实现实时消息广播。这个案例体现了 Go 语言在并发编程方面的强大能力,通过 Goroutines 和 Channels 轻松管理并发任务和数据通信。现在,让我们继续探索 Go 并发编程的更多可能性,开发出更多功能丰富、响应迅速的应用吧!
5.3.3 拓展案例 1:实时数据监控
在许多现代应用场景中,实时数据监控对于确保系统的稳定性和性能至关重要。通过构建一个实时数据监控系统,我们可以并发地收集、处理和分析来自不同数据源的监控数据,实时反馈系统的运行状况。
功能描述
- 并发数据收集:从多个数据源并发收集监控数据。
- 数据处理和分析:对收集到的数据进行实时处理和分析,提取有价值的监控指标。
- 实时反馈:将处理和分析结果实时展示给用户或触发告警。
实现代码
首先,定义一个模拟的数据收集函数,表示从一个数据源收集数据:
package mainimport ("fmt""math/rand""sync""time"
)// collectData 模拟从数据源收集数据
func collectData(source string, dataChan chan<- int) {for {data := rand.Intn(100) // 模拟生成监控数据fmt.Printf("数据源 %s 收集到数据: %d\n", source, data)dataChan <- datatime.Sleep(time.Second * time.Duration(rand.Intn(5))) // 模拟数据收集的间隔}
}
接下来,实现数据处理和分析的逻辑,这里简单地模拟数据的处理过程:
// processData 模拟数据处理和分析
func processData(dataChan <-chan int, resultChan chan<- string) {for data := range dataChan {// 模拟数据处理逻辑result := fmt.Sprintf("处理后的数据: %d", data*2)resultChan <- result}
}
构建主程序逻辑,包括并发的数据收集、处理和实时反馈:
func main() {dataSources := []string{"数据源1", "数据源2", "数据源3"}dataChan := make(chan int, 10)resultChan := make(chan string, 10)// 并发收集数据for _, source := range dataSources {go collectData(source, dataChan)}// 启动数据处理 Goroutinego processData(dataChan, resultChan)// 实时展示处理结果go func() {for result := range resultChan {fmt.Println(result)}}()// 模拟主程序运行一段时间后退出time.Sleep(30 * time.Second)fmt.Println("监控程序结束运行")
}
扩展功能
- 数据过滤和聚合:在数据处理阶段,可以引入更复杂的逻辑,如对数据进行过滤、聚合等,以提取更有价值的监控指标。
- 动态数据源管理:实现动态添加或移除数据源的功能,以适应监控需求的变化。
- 告警机制:根据处理和分析的结果,实现实时告警机制,当监控指标超出预设阈值时触发告警。
通过这个扩展案例,我们构建了一个基本的实时数据监控系统,它展示了如何利用 Go 语言的并发特性来实现数据的实时收集、处理和分析。这种模式适用于需要快速响应和处理大量实时数据的场景,能够帮助我们及时了解和优化系统的运行状况。现在,让我们继续利用 Go 的并发编程能力,开发出更多高效、可靠的实时处理系统吧!
5.3.4 拓展案例 2:并发 Web 爬虫
构建一个并发 Web 爬虫可以显著提高数据抓取的效率,特别适合处理大规模的网页数据收集任务。通过使用 Go 语言的并发特性,我们可以同时对多个网页进行爬取和分析,大大缩短整个抓取过程的时间。
功能描述
- 并发爬取网页:使用 Goroutines 并发地对多个网页进行爬取。
- 数据提取:从爬取的网页中提取有价值的信息。
- 结果汇总:将所有爬取的结果汇总并存储或进行进一步的处理。
实现代码
首先,定义一个模拟的网页爬取函数,表示对单个网页的爬取过程:
package mainimport ("fmt""math/rand""sync""time"
)// fetchURL 模拟爬取单个网页,返回模拟的网页内容
func fetchURL(url string) string {// 模拟网络延迟time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))return fmt.Sprintf("网页内容: [%s]", url) // 模拟返回网页内容
}
接下来,实现并发爬取网页的逻辑,并提取数据:
// crawlWebsite 并发爬取多个网页,并提取数据
func crawlWebsite(urls []string) {var wg sync.WaitGroupresultChan := make(chan string, len(urls))for _, url := range urls {wg.Add(1)go func(u string) {defer wg.Done()content := fetchURL(u)resultChan <- content // 将抓取结果发送到结果 Channel}(url)}// 等待所有爬取任务完成go func() {wg.Wait()close(resultChan) // 所有任务完成后关闭 Channel}()// 收集并打印爬取结果for result := range resultChan {fmt.Println(result)}
}
最后,定义主函数,启动并发 Web 爬虫:
func main() {urls := []string{"http://example.com/page1","http://example.com/page2","http://example.com/page3",}fmt.Println("开始并发爬取网页...")crawlWebsite(urls)fmt.Println("所有网页爬取完成。")
}
扩展功能
- 错误处理:在爬取过程中,增加错误处理逻辑,确保单个任务的失败不会影响整体进程。
- 限速控制:实现限速控制,防止因请求过快而被目标网站封禁。
- 动态任务分配:根据任务的完成速度动态调整 Goroutines 的数量,以达到最优的资源利用和爬取效率。
通过这个扩展案例,我们演示了如何构建一个基本的并发 Web 爬虫,它能够有效地提高数据爬取的速度和效率。利用 Go 语言的并发特性,我们可以轻松地扩展爬虫的规模,处理大量的网页爬取任务。这种并发爬虫的模式非常适合进行网页数据的大规模收集和分析。现在,让我们继续探索 Go 并发编程的强大能力,开发出更多高效的应用吧!
相关文章:

《Go 简易速速上手小册》第5章:并发编程(2024 最新版)
文章目录 5.1 Goroutines 的基础 - Go 语言中的轻盈舞者5.1.1 基础知识讲解5.1.2 重点案例:并发下载器功能描述实现代码扩展功能 5.1.3 拓展案例 1:网站健康检查功能描述实现代码扩展功能 5.1.4 拓展案例 2:并发日志处理器拓展案例 2…...
python - 模块
rootlearning ~]# cat gcdfunction.py #写一个模块,并调用此模块 def gcd(n1,n2): #之前用过的求最大公约数的代码gcd 1k 2while k< n1 and k<n2:if n1%k 0 and n2 % k 0:gcd kk k 1return gcd [rootlearning ~]# cat module.py #完整代码 from gc…...

【Web】CTFSHOW java刷题记录(全)
目录 web279 web280 web281 web282 web283 web284 web285 web286 web287 web288 web289 web290 web291 web292 web293 web294 web295 web296 web297 web298 web299 web300 web279 题目提示 url里告诉我们是S2-001 直接进行一个exp的搜 S2-001漏洞分析…...
全球付汇业务的流程
全球付汇业务,主要是针对的进口类业务,并且是一般贸易进口的业务。 主要流程如下: 1.境内客户通过大额系统将人民币转入支付公司的备付金账户(一般此客户为企业客户),转账需要通过大额系统; 2.至…...

ubuntu22.04@laptop OpenCV Get Started: 012_mouse_and_trackbar
ubuntu22.04laptop OpenCV Get Started: 012_mouse_and_trackbar 1. 源由2. mouse/trackbar应用Demo2.1 C应用Demo2.2 Python应用Demo 3. 鼠标位置跟踪注释3.1 注册回调函数3.2 回调操作3.3 效果 4. 使用轨迹栏调整图像大小4.1 初始化轨迹栏&注册回调函数4.2 回调操作4.3 效…...
信息安全性测试
1 信息安全性测试 信息安全性测试是确保产品或系统能够有效地保护信息和数据,使得用户、其他产品或系统的访问权限与其授权类型和级别相一致的一系列检查过程。信息安全性测试也应该是一个持续的过程,确保信息系统能够抵御恶意攻击,并保护数…...

[HTML]Web前端开发技术26(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页
希望你开心,希望你健康,希望你幸福,希望你点赞! 最后的最后,关注喵,关注喵,关注喵,佬佬会看到更多有趣的博客哦!!! 喵喵喵,你对我真的…...

【Java】文件操作与IO
文件操作与IO Java中操作文件针对文件系统的操作File类概述字段构造方法方法及示例 文件内容的读写 —— 数据流Java提供的 “流” API文件流读写文件内容InputStream 示例读文件示例1:将文件完全读完的两种方式示例二:读取汉字 写文件谈谈 OutputStream…...

开关电源电路主要元器件基础知识详解
在学习电子电路过程中,电源我们无法绕开的一个重要部分,很多时候,故障就出现在电源部分,特别是开关电源。开关电源电路主要是由熔断器、热敏电阻器、互感滤波器、桥式整流电路、滤波电容器、开关振荡集成电路、开关变压器、光耦合…...

- 项目落地 - 《选择项目工具的方法论》
本文属于专栏《构建工业级QPS百万级服务》 提纲: 选择大概率能完成业务目标的工具选择最适合的工具制作最适合的工具 本文所说的项目工具,泛指业务软件开发,所依赖的第三方提供的成熟的资源。包括但不限于开发语言、编辑工具、编译工具、三方…...

美国突然致敬中本聪
作者:秦晋 有点看不懂美国的神操作。 2月16日,据《Bitcoin Magazine》报道,比特币的竞争对手、美国参议员伊丽莎白-沃伦对比特币的立场突然180度大转弯。由反对立场转为支持立场。让很多行业媒体出乎意料,甚至惊掉下巴。 报道称&a…...

精品springboot基于大数据的电脑主机硬件选购助手-可视化大屏
《[含文档PPT源码等]精品基于springboot基于大数据的电脑主机硬件选购助手[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功! 软件开发环境及开发工具: Java——涉及技术: 前端使用技术&a…...
全量和已占用字符集 、字符串统计
题目描述: 全量和已占用字符集 、字符串统计(分值100) 给定两个字符集合,一个是全量字符集,一个是已占用字符集,已占用字符集中的字符不能再使用。 要求输出剩余可用字符集。 输入描述 输入一个字符串 一…...

什么是智慧公厕,智慧公厕有哪些功能
1.什么是智慧公厕? 随着智慧城市的快速发展,公共厕所作为城市基础设施的一部分,也在逐步升级转型。那么,什么是智慧公厕?智慧公厕作为智慧城市的重要组成部分,将公共厕所的建设、设计、使用、运营和管理等…...

给定n个结点m条边的简单无向图,判断该图是否存在鱼形状的子图:有一个环,其中有一个结点有另外两条边,连向不在环内的两个结点。若有,输出子图的连边
题目 思路: #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18 * 3, maxm 4e4 …...

深入理解lambda表达式
深入理解ASP.NET Core中的中间件和Lambda表达式 var builder WebApplication.CreateBuilder(args); var app builder.Build(); app.Use(async (context, next) > { // Add code before request. await next(context);// Add code after request.}); 这段C#代码是用于设…...

删除 Windows 设备和驱动器中的 WPS网盘、百度网盘等快捷图标
在安装诸如WPS软件、百度云盘、爱奇艺等客户端后,Windows 的“我的电脑”(或“此电脑”)中的“设备和驱动器”部分会出现对应的软件图标。这种情况被许多技术人员视为不必要的干扰,因此许多用户想要知道如何隐藏或删除这些图标。 …...

【深度学习:DICOM 注释工具】在 DICOM 注释工具中寻找的 7 个功能
【深度学习:DICOM 注释工具】在 DICOM 注释工具中寻找的 7 个功能 原生 DICOM 支持原生 3D 注释易于使用的界面DICOM 图像的自动注释质量控制功能审计跟踪SOC2 和 HIPAA 合规性 如果您尝试为医疗 AI 模型创建训练数据,您可能已经使用了免费的开源工具&am…...
Spring Boot与Kafka集成教程
当然可以,这里为您提供一个简化版的Spring Boot与Kafka集成教程: 新建Spring Boot项目 使用Spring Initializr或您喜欢的IDE(如IntelliJ IDEA, Eclipse等)新建一个Spring Boot项目。 添加依赖 在项目的pom.xml文件中,…...

基于飞腾ARM+FPGA国产化计算模块联合解决方案
联合解决方案概述 随着特殊领域电子信息系统对自主创新需求的日益提升,需不断开展国产抗恶劣环境计算整机及模块产 品的研制和升级。特殊领域电子信息系统的自主创新,是指依靠自身技术手段和安全机制,实现信息系统从硬 件到软件的自主研发…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...