文章目录
- 前言
- 需求说明
- 目录结构说明
- 需求拆分
- 步骤一:配置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 mainimport ("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.DBNamedb, 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 taskimport ("fmt""github.com/gin-gonic/gin""sync""time"
)// ProcessTask 模拟一个处理任务的函数
func ProcessTask(taskID int, wg *sync.WaitGroup) {// 在goroutine结束时通知WaitGroupdefer 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来同步goroutinevar wg sync.WaitGroup// 假设我们要启动5个goroutine来并发处理任务taskCount := 5for i := 1; i <= taskCount; i++ {wg.Add(1) // 每启动一个goroutine,WaitGroup计数加1go 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。有任何问题,欢迎在评论区与我互动,感谢各位的观看~