开篇
这段时间把主要精力都放在了K8S上,差点把Golang给忘了。那本篇就分享一下并发相关的内容(Goroutine和通道)。 本篇给出4个场景,这4个场景是在运维开发工作中较为常见的且也是比较典型的场景。通过这些代码示例,让你知道Goroutine和通道在运维开发中是怎么应用的。总而言之,言而总之,当涉及到处理并发和并行任务时,Goroutine和通道是非常强悍的,可以让我们开发出高效的、牛逼的并发程序。
实战场景
-
并发执行任务的场景
场景:假设需要编写一个程序,用于批量执行某个操作(例如部署应用程序、更新配置等)到多台服务器上。
供参考的代码示例:
package main
import (
"fmt"
"sync"
)
// 服务器结构体
type Server struct {
Name string
// 其他服务器相关的字段
}
// 执行任务的函数
func executeTask(server Server, task string) {
// 连接服务器并执行任务的逻辑
fmt.Printf("执行任务 [%s] 到服务器 [%s]n", task, server.Name)
// 执行操作的代码
}
func main() {
// 服务器列表
servers := []Server{
{Name: "Server1"},
{Name: "Server2"},
{Name: "Server3"},
// 添加更多的服务器
}
// 任务列表
tasks := []string{"部署应用程序", "更新配置", "执行命令", "其他任务"}
// 创建一个任务通道,用于发送任务到Goroutine池
taskChannel := make(chan string)
// 创建一个等待组,用于等待所有Goroutine执行完毕
var wg sync.WaitGroup
wg.Add(len(servers))
// 启动Goroutine池
for _, server := range servers {
go func(server Server) {
// 标记任务完成时,通知等待组减少一个计数
defer wg.Done()
// 从任务通道中接收任务,并执行
for task := range taskChannel {
executeTask(server, task)
}
}(server)
}
// 将任务发送到任务通道
for _, task := range tasks {
taskChannel <- task
}
// 关闭任务通道,表示所有任务都已发送
close(taskChannel)
// 等待所有Goroutine执行完毕
wg.Wait()
}
上面的代码,创建了一个Goroutine池,每个Goroutine代表一台服务器,通过通道将任务分发给Goroutine进行并发执行。每个Goroutine负责连接到服务器,并执行相应的操作。这样可以加速任务的执行,同时提高资源利用率。
-
并发日志处理的场景
场景:假设需要将大量的日志数据并发地写入到不同的目标中(例如文件、数据库、消息队列等)。
供参考的代码示例:
package main
import (
"fmt"
"log"
"os"
"sync"
)
// 日志结构体
type Log struct {
Message string
// 其他日志相关的字段
}
func main() {
// 创建一个日志通道,用于发送日志数据
logChannel := make(chan Log)
// 创建一个等待组,用于等待所有Goroutine执行完毕
var wg sync.WaitGroup
// 启动一个Goroutine处理日志写入操作
wg.Add(1)
go func() {
// 标记日志写入完毕时,通知等待组减少一个计数
defer wg.Done()
// 打开文件进行日志写入
file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 创建一个日志写入器
logger := log.New(file, "", log.LstdFlags)
// 从日志通道中接收日志数据,并写入到目标中
for log := range logChannel {
logger.Println(log.Message)
}
}()
// 并发地向日志通道发送日志数据
for i := 0; i < 10; i++ {
wg.Add(1)
go func(index int) {
// 标记发送完日志数据时,通知等待组减少一个计数
defer wg.Done()
// 构造日志数据
log := Log{
Message: fmt.Sprintf("日志消息 %d", index),
// 设置其他日志字段
}
// 发送日志数据到日志通道
logChannel <- log
}(i)
}
// 关闭日志通道,表示所有日志数据都已发送
close(logChannel)
// 等待日志写入操作的Goroutine执行完毕
wg.Wait()
}
在上面的代码中,使用了一个专门的Goroutine来处理日志写入操作,该Goroutine从一个日志通道中读取日志数据,并将其写入到目标中。其他的Goroutine可以并发地向该通道发送日志数据,而不会因为写入操作而阻塞。
-
异步任务调度的场景
在实际的运维工作中,有种场景是需要按照一定的调度策略异步执行一些任务。比如这样的场景:定期备份数据库、定时清理无效数据等。
供参考的代码示例:
package main
import (
"fmt"
"time"
)
// 执行任务的函数
func executeTask(task string) {
// 执行任务的逻辑
fmt.Println("执行任务:", task)
// 具体的任务操作代码
}
func main() {
// 创建一个定时器,每隔一段时间触发一次
timer := time.NewTimer(5 * time.Second)
// 启动一个Goroutine等待定时器的触发事件
go func() {
// 等待定时器的触发事件
<-timer.C
// 定时器触发后执行任务
executeTask("定时任务1")
// 重新设置定时器,以实现循环调度
timer.Reset(10 * time.Second)
}()
// 主线程继续执行其他任务
// ...
// 阻塞主线程,保持程序运行
select {}
}
-
并发任务协作的场景:
在某些情况下,你可能需要多个Goroutine之间进行协作和同步。例如,一个Goroutine负责生产任务,而另一个Goroutine负责消费任务并进行处理。你可以使用通道来实现生产者-消费者模型。生产者将任务发送到通道中,而消费者从通道中接收任务并进行处理。通过这种方式,可以实现任务的有效分配和协同处理。
package main
import (
"fmt"
"time"
)
// 任务结构体
type Task struct {
ID int
Data string
// 其他任务相关的字段
}
// 生产者,负责生产任务并发送到通道
func producer(ch chan<- Task) {
for i := 1; i <= 10; i++ {
task := Task{
ID: i,
Data: fmt.Sprintf("任务 %d", i),
// 设置其他任务字段
}
ch <- task
fmt.Println("生产任务:", task.Data)
time.Sleep(500 * time.Millisecond) // 模拟生产任务的耗时
}
close(ch) // 关闭通道,表示所有任务都已生产完毕
}
// 消费者,负责从通道接收任务并进行处理
func consumer(ch <-chan Task) {
for task := range ch {
fmt.Println("消费任务:", task.Data)
// 执行任务的处理逻辑
time.Sleep(1 * time.Second) // 模拟处理任务的耗时
}
}
func main() {
// 创建一个任务通道,用于生产者和消费者之间的通信
taskChannel := make(chan Task)
// 启动生产者Goroutine
go producer(taskChannel)
// 启动多个消费者Goroutine
for i := 1; i <= 3; i++ {
go consumer(taskChannel)
}
// 阻塞主线程,保持程序运行
select {}
}
在上面的代码种,使用了定时器(time.Timer)结合Goroutine来实现异步任务调度。通过启动一个Goroutine来等待定时器的触发事件,并执行相应的任务。这样可以在后台自动执行任务,而不需要阻塞主线程。
原文始发于微信公众号(不背锅运维):Go:4个场景就可以让你知道Goroutine和通道是怎么用的
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/149460.html