Opening
During this period, I focused all my energy on the K8S and almost forgot Golang. Then this article will share concurrency-related content (Goroutine and channel). This article gives four scenarios, which are relatively common and typical scenarios in operation and maintenance development work. Through these code examples, let you know how Goroutine and channels are used in operation and maintenance development. In short, when it comes to handling concurrent and parallel tasks, Goroutine and channels are very powerful, allowing us to develop efficient and awesome concurrent programs.
Practical scenes
1. Scenarios for concurrent tasks
Scenario: Suppose you need to write a program that will perform an operation in batches (such as deploying applications, updating configurations, etc.) on multiple servers.
Example code for reference:
package main import ( "fmt" "sync" ) // Server structuretype Server struct { Name string // Other server-related fields} // Functions that execute tasksfunc executeTask(server Server, task string) { // The logic of connecting to the server and executing tasks ("Execute task [%s] to server [%s]\n", task, ) // Code to execute the operation} func main() { // Server list servers := []Server{ {Name: "Server1"}, {Name: "Server2"}, {Name: "Server3"}, // Add more servers } // Task List tasks := []string{"Deploy the application", "Update configuration", "Execute command", "Other tasks"} // Create a task channel for sending tasks to the Goroutine pool taskChannel := make(chan string) // Create a waiting group to wait for all Goroutine execution to complete var wg (len(servers)) // Start the Goroutine pool for _, server := range servers { go func(server Server) { // When marking the task is completed, notify the waiting group to reduce the count by one defer () // Receive tasks from the task channel and execute for task := range taskChannel { executeTask(server, task) } }(server) } // Send the task to the task channel for _, task := range tasks { taskChannel <- task } // Close the task channel, which means that all tasks have been sent close(taskChannel) // Wait for all Goroutines to be executed () }
The above code creates a Goroutine pool, each Goroutine represents a server, and the tasks are distributed to Goroutine through the channel for concurrent execution. Each Goroutine is responsible for connecting to the server and performing corresponding operations. This can speed up the execution of tasks and improve resource utilization.
2. Concurrent log processing scenarios
Scenario: Suppose that a large amount of log data needs to be written concurrently into different targets (such as files, databases, message queues, etc.).
Example code for reference:
package main import ( "fmt" "log" "os" "sync" ) // Log structuretype Log struct { Message string // Other log-related fields} func main() { // Create a log channel to send log data logChannel := make(chan Log) // Create a waiting group to wait for all Goroutine execution to complete var wg // Start a Goroutine log write operation (1) go func() { // When the mark log is written, notify the waiting group to reduce the count by one defer () // Open the file for log writing file, err := ("", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { (err) } defer () // Create a log writer logger := (file, "", ) // Receive log data from the log channel and write it to the target for log := range logChannel { () } }() // Concurrently send log data to the log channel for i := 0; i < 10; i++ { (1) go func(index int) { // When the marker sends the log data, notify the waiting group to reduce the count by one defer () // Construct log data log := Log{ Message: ("Log message %d", index), // Set other log fields } // Send log data to log channel logChannel <- log }(i) } // Close the log channel, indicating that all log data has been sent close(logChannel) // Wait for the Goroutine to be executed for the log write operation () }
In the above code, a special Goroutine is used to handle log write operations, which reads log data from a log channel and writes it to the target. Other Goroutines can send log data to the channel concurrently without blocking due to write operations.
3. Scenarios of asynchronous task scheduling
In actual operation and maintenance work, there is a scenario where some tasks need to be executed asynchronously according to certain scheduling strategies. For example, in this scenario: regular backup of databases, regular cleaning of invalid data, etc.
Example code for reference:
package main import ( "fmt" "time" ) // Functions that execute tasksfunc executeTask(task string) { // The logic of executing tasks ("Execute task:", task) // Specific task operation code} func main() { // Create a timer that triggers every once in a while timer := (5 * ) // Start a trigger event of a Goroutine waiting timer go func() { // Wait for the timer to trigger event <- // Execute task after the timer is triggered executeTask("Timed Task 1") // Reset the timer to achieve loop scheduling (10 * ) }() // The main thread continues to execute other tasks // ... // Block the main thread and keep the program running select {} }
4. Concurrent task collaboration scenarios
In some cases, you may need to collaborate and synchronize between multiple Goroutines. For example, one Goroutine is responsible for production tasks, while another Goroutine is responsible for consuming tasks and processing them. You can use channels to implement the producer-consumer model. The producer sends the task to the channel, and the consumer receives the task from the channel and processes it. In this way, effective allocation and coordinated processing of tasks can be achieved.
package main import ( "fmt" "time" ) // Task structuretype Task struct { ID int Data string // Other tasks related fields} // Producer, responsible for production tasks and sending them to the channelfunc producer(ch chan<- Task) { for i := 1; i <= 10; i++ { task := Task{ ID: i, Data: ("Task %d", i), // Set other task fields } ch <- task ("Production Task:", ) (500 * ) // The time-consuming process of simulated production tasks } close(ch) // Close the channel, indicating that all tasks have been produced} // Consumer, responsible for receiving tasks from the channel and processingfunc consumer(ch <-chan Task) { for task := range ch { ("Consumption Task:", ) // The processing logic of the execution task (1 * ) // The time-consuming process of simulation tasks } } func main() { // Create a task channel for communication between producer and consumer taskChannel := make(chan Task) // Start Producer Goroutine go producer(taskChannel) // Start multiple consumer Goroutine for i := 1; i <= 3; i++ { go consumer(taskChannel) } // Block the main thread and keep the program running select {} }
In the above code, timer() is used in combination with Goroutine to implement asynchronous task scheduling. By starting a Goroutine, wait for the timer to trigger events and perform the corresponding tasks. This allows tasks to be automatically executed in the background without blocking the main thread.
This is the article about 4 scenarios teaching you how to use Goroutine and channel in Go. For more information about Go Goroutine channel, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!