玩转goroutine:Golang中对goroutine的应用
文章目录
- 前言
- 需求说明
- 目录结构说明
- 需求拆分
- 步骤一:配置HTTP服务
- 步骤二:创建并发任务的实现
- 代码解释
- 步骤三:启动服务并测试
- 总结以及进阶优化
前言
上一篇文章讲到了goroutine
的基本原理和概念的理解,本次将基于 玩转goroutine:Golang中对goroutine的理解述 这篇文章的内容来写一个API接口,实现goroutine
并发处理任务。
需求说明
本次将创建一个简单的示例,展示如处理并发请求。我们将从具体的目录结构出发,实现一个/process
的API接口,触发的时候会启动多个goroutine
来并发处理任务,并且使用sync.WaitGroup
来等待所有并发任务完成。
目录结构说明
cmd/web-app/main.go
:程序主入口,启动HTTP服务internal/home/model.go
:存放需求相关的业务逻辑internal/user/handle.go
:处理用户请求相关的代码pkg/auth/jwt.go
:用于认证的JWT代码(本次暂时用不到,保留作为示例)pkg/config/config.go
:全局配置文件
需求拆分
- 在
/process
接口中,接收到请求后,启动多个goroutine
来并发处理任务。 - 每个
goroutine
模拟一个简单的任务(比如打印任务ID,模拟耗时操作等等) - 在所有任务完成后,返回一个成功响应到接口。
步骤一:配置HTTP服务
在cmd/web-app/main.go
中,设置HTTP服务,注册/process
接口,并启动服务
// @title My E-Commerce API
// @version 1.0
// @description This is the API documentation for My E-Commerce platform.
// @termsOfService http://example.com/terms/
// @contact.name API Support
// @contact.url http://example.com/support
// @contact.email support@example.com
// @license.name MIT
// @license.url https://opensource.org/licenses/MIT
// @host localhost:8088
// @BasePath
package main
import (
"database/sql"
"log"
"my-ecommerce-app/internal/task" // 引入任务处理模块
"my-ecommerce-app/pkg/config"
_ "my-ecommerce-app/docs"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql" // 导入 MySQL 驱动
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
func main() {
config.LoadConfig() // 加载配置
// 使用配置连接数据库
dsn := config.Appconfig.DBUser + ":" + config.Appconfig.DBPassword + "@tcp(" + config.Appconfig.DBHost + ")/" + config.Appconfig.DBName
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatalf("无法连接到数据库:%v", err)
}
defer db.Close()
// 数据库连接
if err = db.Ping(); err != nil {
log.Fatalf("数据库连接失败:%v", err)
}
// 初始化 Gin 引擎
r := gin.New()
// 注册路由
r.GET("/api/process", task.ProcessHandler)
// 添加swagger 路由
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// 启动服务
if err := r.Run(":8088"); err != nil {
log.Fatalf("无法启动服务器:%v", err)
}
}
步骤二:创建并发任务的实现
在internal/user/handle.go
中,实现并发处理任务的逻辑
package task
import (
"fmt"
"github.com/gin-gonic/gin"
"sync"
"time"
)
// ProcessTask 模拟一个处理任务的函数
func ProcessTask(taskID int, wg *sync.WaitGroup) {
// 在goroutine结束时通知WaitGroup
defer wg.Done()
// 模拟任务耗时
fmt.Printf("Task %d started\n", taskID)
time.Sleep(2 * time.Second) // 模拟耗时操作
fmt.Printf("Task %d completed\n", taskID)
}
// ProcessHandler 处理并发任务请求的HTTP处理器
func ProcessHandler(c *gin.Context) {
// 创建WaitGroup来同步goroutine
var wg sync.WaitGroup
// 假设我们要启动5个goroutine来并发处理任务
taskCount := 5
for i := 1; i <= taskCount; i++ {
wg.Add(1) // 每启动一个goroutine,WaitGroup计数加1
go ProcessTask(i, &wg) // 启动一个新的goroutine来处理任务
}
// 等待所有goroutine完成
wg.Wait()
// 返回响应
c.JSON(200, gin.H{
"message": "All tasks completed successfully!",
})
}
代码解释
ProcessTask
函数:- 创建的一个模拟任务处理函数。它会接收一个
taskID
和sync.WaitGroup
作为参数 - 在函数结束时,我们调用
wg.Done()
来通知WaitGroup
当前的goroutine
已经完成。
- 创建的一个模拟任务处理函数。它会接收一个
ProcessHandler
函数:- 这是处理
/process
接口的HTTP处理器 - 定义来一个
sync.WaitGroup
来确保所有的goroutine
都完成后才返回响应到接口 - 通过
wg.Add(1)
来告知WaitGroup有一个新的goroutine
启动,每当一个任务完成时,调用wg.Done()
- 最后,调用
wg.Wait()
来阻塞主线程,直到所有的goroutine
都执行完毕。
- 这是处理
步骤三:启动服务并测试
- 启动Go服务:
go run cmd/web-app/main.go
- 访问
http://127.0.0.1:8088/api/process
来测试接口。在控制台可以看到并发任务的处理过程,任务完成后接口将返回All tasks completed successfully!
总结以及进阶优化
- 任务处理过程中,我们模拟了
time.Sleep
来表示任务的耗时。在实际项目中,可以替换成实际的业务逻辑,比如数据库操作
、调用微服务
等。 - 如果任务数量较多,或者每个任务执行时间较长,考虑引入任务池(Worker Pool)来限制并发数,避免过多的
goroutine
造成系统资源的浪费
对于错误处理和超时处理,可以在goroutine
中添加适当的error
返回和context
控制
以上就是一个基于goroutine
的并发处理API。有任何问题,欢迎在评论区与我互动,感谢各位的观看~