A:数据库初始化,并增加User表示例

This commit is contained in:
jimmy committed 2025-06-15 17:37:31 +08:00
1 parent da2222f2d9
commit b8dc199cff
15 files changed
+1459 -108

No files matched your search

@@ -1,4 +1,4 @@
package model
package config
import (
"rear/internal/utils"
+32
View File
@@ -0,0 +1,32 @@
package config
import (
"time"
)
// DatabaseConfig 数据库配置
type DatabaseConfig struct {
Type DatabaseType
// Mysql 属性
Host string
// Mysql 属性
Port string
Database string
// Mysql 属性
Username string
// Mysql 属性
Password string
// SQLite specific
DBPath string
MaxIdleConns int
MaxOpenConns int
MaxLifetime time.Duration
}
// DatabaseType 数据库类型
type DatabaseType string
const (
SQLite DatabaseType = "sqlite"
MySQL DatabaseType = "mysql"
)
+290
View File
@@ -0,0 +1,290 @@
package db
import (
"context"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm/logger"
"rear/internal/config"
"sync"
"time"
"gorm.io/gorm"
)
// WriteTask 写任务接口
type WriteTask interface {
Execute(db *gorm.DB) error
GetPriority() int // 优先级,用于任务排序
GetID() string // 任务ID,用于去重或追踪
}
// WriteTaskResult 写任务结果
type WriteTaskResult struct {
TaskID string
Error error
}
// TaskCallback 任务回调
type TaskCallback struct {
OnSuccess func(taskID string)
OnError func(taskID string, err error)
}
// DatabaseManager 数据库管理器
type DatabaseManager struct {
db *gorm.DB
dbType config.DatabaseType
// 写任务队列
writeQueue chan WriteTaskWithCallback
resultQueue chan WriteTaskResult
stopCh chan struct{}
wg sync.WaitGroup
// 读写分离配置
maxWriteWorkers int
maxRetries int
}
// WriteTaskWithCallback 带回调的写任务
type WriteTaskWithCallback struct {
Task WriteTask
Callback TaskCallback
}
// NewDatabaseManager 创建数据库管理器
func NewDatabaseManager(db *gorm.DB, dbType config.DatabaseType) *DatabaseManager {
maxWorkers := 1 // SQLite默认单线程
if dbType == config.MySQL {
maxWorkers = 10 // MySQL可以多线程
}
dm := &DatabaseManager{
db: db,
dbType: dbType,
writeQueue: make(chan WriteTaskWithCallback, 1000), // 缓冲队列
resultQueue: make(chan WriteTaskResult, 1000),
stopCh: make(chan struct{}),
maxWriteWorkers: maxWorkers,
maxRetries: 3,
}
dm.start()
return dm
}
// start 启动数据库管理器
func (dm *DatabaseManager) start() {
// 启动写任务处理器
for i := 0; i < dm.maxWriteWorkers; i++ {
dm.wg.Add(1)
go dm.writeWorker(i)
}
// 启动结果处理器
dm.wg.Add(1)
go dm.resultProcessor()
}
// writeWorker 写任务工作协程
func (dm *DatabaseManager) writeWorker(workerID int) {
defer dm.wg.Done()
for {
select {
case taskWithCallback := <-dm.writeQueue:
dm.executeWriteTask(taskWithCallback)
case <-dm.stopCh:
return
}
}
}
// executeWriteTask 执行写任务
func (dm *DatabaseManager) executeWriteTask(taskWithCallback WriteTaskWithCallback) {
task := taskWithCallback.Task
callback := taskWithCallback.Callback
var err error
for retry := 0; retry < dm.maxRetries; retry++ {
// 对于SQLite,可以使用事务来优化性能
if dm.dbType == config.SQLite {
err = dm.db.Transaction(func(tx *gorm.DB) error {
return task.Execute(tx)
})
} else {
err = task.Execute(dm.db)
}
if err == nil {
break
}
// 重试延迟
if retry < dm.maxRetries-1 {
time.Sleep(time.Millisecond * time.Duration(100*(retry+1)))
}
}
// 发送结果
result := WriteTaskResult{
TaskID: task.GetID(),
Error: err,
}
select {
case dm.resultQueue <- result:
default:
// 结果队列满了,直接执行回调
if err != nil && callback.OnError != nil {
callback.OnError(task.GetID(), err)
} else if err == nil && callback.OnSuccess != nil {
callback.OnSuccess(task.GetID())
}
}
}
// resultProcessor 结果处理器
func (dm *DatabaseManager) resultProcessor() {
defer dm.wg.Done()
for {
select {
case result := <-dm.resultQueue:
// 这里可以进行结果处理,比如记录日志、监控等
fmt.Printf("Task %s completed with error: %v\n", result.TaskID, result.Error)
case <-dm.stopCh:
return
}
}
}
// SubmitWriteTask 提交写任务
func (dm *DatabaseManager) SubmitWriteTask(task WriteTask, callback TaskCallback) error {
taskWithCallback := WriteTaskWithCallback{
Task: task,
Callback: callback,
}
select {
case dm.writeQueue <- taskWithCallback:
return nil
default:
return fmt.Errorf("write queue is full")
}
}
// SubmitWriteTaskSync 同步提交写任务
func (dm *DatabaseManager) SubmitWriteTaskSync(ctx context.Context, task WriteTask) error {
done := make(chan error, 1)
callback := TaskCallback{
OnSuccess: func(taskID string) {
done <- nil
},
OnError: func(taskID string, err error) {
done <- err
},
}
if err := dm.SubmitWriteTask(task, callback); err != nil {
return err
}
select {
case err := <-done:
return err
case <-ctx.Done():
return ctx.Err()
}
}
// ReadDirect 直接读取(不经过队列)
func (dm *DatabaseManager) ReadDirect() *gorm.DB {
return dm.db
}
// GetDB 获取数据库连接(仅用于读操作)
func (dm *DatabaseManager) GetDB() *gorm.DB {
return dm.db
}
// Stop 停止数据库管理器
func (dm *DatabaseManager) Stop() {
close(dm.stopCh)
dm.wg.Wait()
}
func InitDatabase() error {
databaseConfig := getDatabaseConfig()
var db *gorm.DB
var err error
switch databaseConfig.Type {
case config.MySQL:
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
databaseConfig.Username, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Database)
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
case config.SQLite:
// SQLite特殊配置
db, err = gorm.Open(sqlite.Open(databaseConfig.Database), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
return err
}
// SQLite优化设置
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(1) // SQLite单连接
sqlDB.SetMaxOpenConns(1) // SQLite单连接
sqlDB.SetConnMaxLifetime(0) // 永不关闭
// SQLite WAL模式优化
db.Exec("PRAGMA journal_mode=WAL;")
db.Exec("PRAGMA synchronous=NORMAL;")
db.Exec("PRAGMA cache_size=1000000;")
db.Exec("PRAGMA foreign_keys=true;")
db.Exec("PRAGMA temp_store=memory;")
default:
return fmt.Errorf("unsupported database type: %s", databaseConfig.Type)
}
if err != nil {
return fmt.Errorf("failed to connect database: %w", err)
}
DB = db
Manger = NewDatabaseManager(db, databaseConfig.Type)
return nil
}
func getDatabaseConfig() config.DatabaseConfig {
// 初始化数据库
return config.DatabaseConfig{
Type: config.SQLite,
Database: "test.db",
MaxIdleConns: 1,
MaxOpenConns: 1,
MaxLifetime: 0,
}
}
var DB *gorm.DB
var Manger *DatabaseManager
func GetDB() *gorm.DB {
return DB
}
func GetManger() *DatabaseManager {
return Manger
}
func IsSQLite() bool {
cfg := getDatabaseConfig()
return cfg.Type == config.SQLite
}
+9
View File
@@ -0,0 +1,9 @@
package db
import "rear/internal/config"
// OptimizedDatabaseManager 优化版数据库管理器
type OptimizedDatabaseManager struct {
*DatabaseManager
config config.DatabaseConfig
}
+12
View File
@@ -0,0 +1,12 @@
package db
import (
"rear/internal/model"
)
func AutoMigrate() error {
return DB.AutoMigrate(
&model.User{},
// 在这里添加其他模型
)
}
+25 -9
View File
@@ -3,8 +3,10 @@ package handler
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
"rear/internal/model"
"rear/internal/repositories"
)
// 全局变量
@@ -58,8 +60,8 @@ func GetUserByID(c *gin.Context) {
// InitSampleData 初始化示例数据
func InitSampleData() {
Users = []model.User{
{ID: 1, Name: "John Doe", Email: "john@example.com"},
{ID: 2, Name: "Jane Smith", Email: "jane@example.com"},
{Username: "John Doe", Email: "john@example.com"},
{Username: "Jane Smith", Email: "jane@example.com"},
}
userNextID = 3
}
@@ -99,11 +101,26 @@ func UpdateUser(c *gin.Context) {
// GetUsers 获取所有用户
func GetUsers(c *gin.Context) {
c.JSON(http.StatusOK, model.Response{
Code: http.StatusOK,
Message: "Success",
Data: Users,
})
// 使用示例
userService := repositories.NewUserService()
if users, err := userService.GetAllUsers(); err != nil {
log.Printf("Failed to get all users: %v", err)
} else {
log.Printf("Total users: %d", len(users))
c.JSON(http.StatusOK, model.Response{
Code: http.StatusOK,
Message: "Success",
Data: users,
})
}
//c.JSON(http.StatusOK, model.Response{
// Code: http.StatusOK,
// Message: "Success",
// Data: Users,
//})
}
// CreateUser 创建用户
@@ -118,7 +135,7 @@ func CreateUser(c *gin.Context) {
}
// 简单验证
if user.Name == "" || user.Email == "" {
if user.Username == "" || user.Email == "" {
c.JSON(http.StatusBadRequest, model.Response{
Code: http.StatusBadRequest,
Message: "Name and email are required",
@@ -126,7 +143,6 @@ func CreateUser(c *gin.Context) {
return
}
user.ID = userNextID
userNextID++
Users = append(Users, user)
+15
View File
@@ -0,0 +1,15 @@
package model
import (
"gorm.io/gorm"
"time"
)
// BaseModel 基础模型,包含通用字段
type BaseModel struct {
ID uint `gorm:"primaryKey" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
IsDeleted bool `json:"is_deleted"`
}
+25 -4
View File
@@ -1,8 +1,29 @@
package model
// User 示例用户结构
import "gorm.io/gorm"
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
BaseModel
Username string `gorm:"uniqueIndex;not null;size:50" json:"username"`
Email string `gorm:"uniqueIndex;not null;size:100" json:"email"`
Password string `gorm:"not null;size:255" json:"-"`
Age int `gorm:"default:0" json:"age"`
Status int `gorm:"default:1;comment:1-active,0-inactive" json:"status"`
}
// TableName 指定表名
func (User) GetTableName() string {
return "users"
}
// BeforeCreate GORM钩子,创建前执行
func (u *User) BeforeCreate(tx *gorm.DB) error {
// 这里可以添加创建前的逻辑,比如密码加密
return nil
}
// AfterCreate GORM钩子,创建后执行
func (u *User) AfterCreate(tx *gorm.DB) error {
// 这里可以添加创建后的逻辑
return nil
}
@@ -0,0 +1,60 @@
package repositories
import (
"rear/internal/db"
"sync"
)
// 写操作类型
type WriteOperation struct {
Execute func() error
Response chan error
}
// BaseService 处理数据库写操作的单线程执行
type BaseService struct {
writeQueue chan WriteOperation
once sync.Once
}
var baseService = &BaseService{
writeQueue: make(chan WriteOperation, 1000),
}
// 初始化并启动写操作处理协程
func InitBaseService() {
baseService.once.Do(func() {
go baseService.processWriteOperations()
})
}
// 处理写操作的协程
func (s *BaseService) processWriteOperations() {
for op := range s.writeQueue {
err := op.Execute()
op.Response <- err
close(op.Response)
}
}
// ExecuteWrite 执行写操作(增删改)
func ExecuteWrite(fn func() error) error {
// 如果不是 SQLite,直接执行
if !db.IsSQLite() {
return fn()
}
// SQLite 走单线程队列
op := WriteOperation{
Execute: fn,
Response: make(chan error, 1),
}
baseService.writeQueue <- op
return <-op.Response
}
// ExecuteRead 执行读操作(可以并发)
func ExecuteRead(fn func() error) error {
return fn()
}
@@ -0,0 +1,158 @@
package repositories
import (
"errors"
"rear/internal/db"
"rear/internal/model"
"gorm.io/gorm"
)
type UserService struct{}
func NewUserService() *UserService {
return &UserService{}
}
// 创建用户(写操作)
func (s *UserService) CreateUser(user *model.User) error {
return ExecuteWrite(func() error {
return db.GetDB().Create(user).Error
})
}
// 更新用户(写操作)
func (s *UserService) UpdateUser(id uint, updates map[string]interface{}) error {
return ExecuteWrite(func() error {
return db.GetDB().Model(&model.User{}).
Where("id = ?", id).
Updates(updates).Error
})
}
// 更新用户名称(写操作)
func (s *UserService) UpdateUserName(id uint, name string) error {
return ExecuteWrite(func() error {
return db.GetDB().Model(&model.User{}).
Where("id = ?", id).
Update("name", name).Error
})
}
// 删除用户(写操作)
func (s *UserService) DeleteUser(id uint) error {
return ExecuteWrite(func() error {
return db.GetDB().Delete(&model.User{}, id).Error
})
}
// 软删除用户(写操作)
func (s *UserService) SoftDeleteUser(id uint) error {
return ExecuteWrite(func() error {
return db.GetDB().Delete(&model.User{}, id).Error
})
}
// 批量创建用户(写操作)
func (s *UserService) CreateUsers(users []model.User) error {
return ExecuteWrite(func() error {
return db.GetDB().Create(&users).Error
})
}
// ============ 读操作(可以并发)============
// 根据 ID 获取用户
func (s *UserService) GetUserByID(id uint) (*model.User, error) {
var user model.User
err := ExecuteRead(func() error {
return db.GetDB().First(&user, id).Error
})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &user, nil
}
// 根据用户名获取用户
func (s *UserService) GetUserByUsername(username string) (*model.User, error) {
var user model.User
err := ExecuteRead(func() error {
return db.GetDB().Where("username = ?", username).First(&user).Error
})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &user, nil
}
// 获取所有用户
func (s *UserService) GetAllUsers() ([]model.User, error) {
var users []model.User
err := ExecuteRead(func() error {
return db.GetDB().Find(&users).Error
})
return users, err
}
// 分页获取用户
func (s *UserService) GetUsersPaginated(offset, limit int) ([]model.User, int64, error) {
var users []model.User
var total int64
err := ExecuteRead(func() error {
// 获取总数
if err := db.GetDB().Model(&model.User{}).Count(&total).Error; err != nil {
return err
}
// 获取分页数据
return db.GetDB().Offset(offset).Limit(limit).Find(&users).Error
})
return users, total, err
}
// 根据条件查询用户
func (s *UserService) GetUsersByCondition(condition map[string]interface{}) ([]model.User, error) {
var users []model.User
err := ExecuteRead(func() error {
query := db.GetDB()
for key, value := range condition {
query = query.Where(key+" = ?", value)
}
return query.Find(&users).Error
})
return users, err
}
// 事务示例(写操作)
func (s *UserService) CreateUserWithTransaction(user *model.User, callback func(tx *gorm.DB) error) error {
return ExecuteWrite(func() error {
return db.GetDB().Transaction(func(tx *gorm.DB) error {
// 创建用户
if err := tx.Create(user).Error; err != nil {
return err
}
// 执行其他操作
if callback != nil {
return callback(tx)
}
return nil
})
})
}
+400
View File
@@ -0,0 +1,400 @@
package utils
import (
"context"
"fmt"
"log"
"os/exec"
"sync"
"sync/atomic"
"time"
)
// Task 任务接口
type Task interface {
// Execute 执行任务
Execute(ctx context.Context) error
// ID 获取任务ID
ID() string
// Type 获取任务类型
Type() string
/*
紧急任务:优先级 8-10
正常任务:优先级 4-7
后台任务:优先级 1-3
*/
// Priority 获取任务优先级 (数字越大优先级越高)
Priority() int
}
// TaskResult 任务执行结果
type TaskResult struct {
TaskID string
TaskType string
Success bool
Error error
Duration time.Duration
}
// TaskScheduler 任务调度器
type TaskScheduler struct {
workers int
taskQueue chan Task
resultQueue chan TaskResult
workerPool chan struct{}
wg sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
// 统计信息
totalTasks int64
completedTasks int64
failedTasks int64
// 任务类型统计
taskStats map[string]*TaskTypeStats
statsMutex sync.RWMutex
}
// TaskTypeStats 任务类型统计
type TaskTypeStats struct {
Total int64
Completed int64
Failed int64
AvgTime time.Duration
}
// NewTaskScheduler 创建任务调度器
func NewTaskScheduler(workers int, queueSize int) *TaskScheduler {
ctx, cancel := context.WithCancel(context.Background())
ts := &TaskScheduler{
workers: workers,
taskQueue: make(chan Task, queueSize),
resultQueue: make(chan TaskResult, workers),
workerPool: make(chan struct{}, workers),
ctx: ctx,
cancel: cancel,
taskStats: make(map[string]*TaskTypeStats),
}
// 初始化worker池
for i := 0; i < workers; i++ {
ts.workerPool <- struct{}{}
}
// 启动worker
ts.startWorkers()
// 启动结果处理器
go ts.resultProcessor()
return ts
}
// startWorkers 启动worker
func (ts *TaskScheduler) startWorkers() {
for i := 0; i < ts.workers; i++ {
ts.wg.Add(1)
go ts.worker(i)
}
}
// worker 工作协程
func (ts *TaskScheduler) worker(id int) {
defer ts.wg.Done()
for {
select {
case <-ts.ctx.Done():
return
case task, ok := <-ts.taskQueue:
if !ok {
return
}
// 获取worker令牌
<-ts.workerPool
// 执行任务
start := time.Now()
err := task.Execute(ts.ctx)
duration := time.Since(start)
// 发送结果
ts.resultQueue <- TaskResult{
TaskID: task.ID(),
TaskType: task.Type(),
Success: err == nil,
Error: err,
Duration: duration,
}
// 归还worker令牌
ts.workerPool <- struct{}{}
}
}
}
// resultProcessor 结果处理器
func (ts *TaskScheduler) resultProcessor() {
for result := range ts.resultQueue {
atomic.AddInt64(&ts.completedTasks, 1)
if !result.Success {
atomic.AddInt64(&ts.failedTasks, 1)
log.Printf("Task %s failed: %v", result.TaskID, result.Error)
}
// 更新统计信息
ts.updateStats(result)
}
}
// updateStats 更新统计信息
func (ts *TaskScheduler) updateStats(result TaskResult) {
ts.statsMutex.Lock()
defer ts.statsMutex.Unlock()
stats, exists := ts.taskStats[result.TaskType]
if !exists {
stats = &TaskTypeStats{}
ts.taskStats[result.TaskType] = stats
}
stats.Total++
if result.Success {
stats.Completed++
} else {
stats.Failed++
}
// 更新平均执行时间
if stats.Completed > 0 {
stats.AvgTime = time.Duration(
(int64(stats.AvgTime)*(stats.Completed-1) + int64(result.Duration)) / stats.Completed,
)
}
}
// Submit 提交任务
func (ts *TaskScheduler) Submit(task Task) error {
select {
case <-ts.ctx.Done():
return fmt.Errorf("scheduler is shutting down")
case ts.taskQueue <- task:
atomic.AddInt64(&ts.totalTasks, 1)
return nil
default:
return fmt.Errorf("task queue is full")
}
}
// SubmitBatch 批量提交任务
func (ts *TaskScheduler) SubmitBatch(tasks []Task) error {
for _, task := range tasks {
if err := ts.Submit(task); err != nil {
return fmt.Errorf("failed to submit task %s: %w", task.ID(), err)
}
}
return nil
}
// GetStats 获取统计信息
func (ts *TaskScheduler) GetStats() map[string]interface{} {
ts.statsMutex.RLock()
defer ts.statsMutex.RUnlock()
stats := make(map[string]interface{})
stats["total_tasks"] = atomic.LoadInt64(&ts.totalTasks)
stats["completed_tasks"] = atomic.LoadInt64(&ts.completedTasks)
stats["failed_tasks"] = atomic.LoadInt64(&ts.failedTasks)
stats["pending_tasks"] = len(ts.taskQueue)
stats["active_workers"] = ts.workers - len(ts.workerPool)
// 复制任务类型统计
typeStats := make(map[string]*TaskTypeStats)
for k, v := range ts.taskStats {
typeStats[k] = &TaskTypeStats{
Total: v.Total,
Completed: v.Completed,
Failed: v.Failed,
AvgTime: v.AvgTime,
}
}
stats["type_stats"] = typeStats
return stats
}
// Shutdown 关闭调度器
func (ts *TaskScheduler) Shutdown() {
ts.cancel()
close(ts.taskQueue)
ts.wg.Wait()
close(ts.resultQueue)
}
// --- 具体任务实现示例 ---
// ImageCompressTask 图片压缩任务
type ImageCompressTask struct {
id string
filePath string
quality int
}
func NewImageCompressTask(id, filePath string, quality int) *ImageCompressTask {
return &ImageCompressTask{
id: id,
filePath: filePath,
quality: quality,
}
}
func (t *ImageCompressTask) Execute(ctx context.Context) error {
// 这里是实际的图片压缩逻辑
// 可以使用 imaging 库或调用外部工具
select {
case <-ctx.Done():
return ctx.Err()
default:
// 模拟压缩操作
// 实际实现中,这里会调用图片处理库
log.Printf("Compressing image: %s with quality %d", t.filePath, t.quality)
time.Sleep(100 * time.Millisecond) // 模拟耗时操作
return nil
}
}
func (t *ImageCompressTask) ID() string { return t.id }
func (t *ImageCompressTask) Type() string { return "image_compress" }
func (t *ImageCompressTask) Priority() int { return 5 }
// ExifReadTask EXIF读取任务
type ExifReadTask struct {
id string
filePath string
exifData map[string]interface{}
}
func NewExifReadTask(id, filePath string) *ExifReadTask {
return &ExifReadTask{
id: id,
filePath: filePath,
exifData: make(map[string]interface{}),
}
}
func (t *ExifReadTask) Execute(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// 使用 exiftool 命令行工具
cmd := exec.CommandContext(ctx, "exiftool", "-j", t.filePath)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("exiftool failed: %w", err)
}
// 这里应该解析 JSON 输出到 t.exifData
log.Printf("Read EXIF from: %s, data size: %d", t.filePath, len(output))
return nil
}
}
func (t *ExifReadTask) ID() string { return t.id }
func (t *ExifReadTask) Type() string { return "exif_read" }
func (t *ExifReadTask) Priority() int { return 3 }
// PriorityTaskScheduler 支持优先级的任务调度器
type PriorityTaskScheduler struct {
*TaskScheduler
priorityQueue *PriorityQueue
queueMutex sync.Mutex
}
// PriorityQueue 优先级队列实现
type PriorityQueue struct {
tasks []Task
}
func (pq *PriorityQueue) Push(task Task) {
pq.tasks = append(pq.tasks, task)
// 简单的插入排序,实际可以使用堆
for i := len(pq.tasks) - 1; i > 0; i-- {
if pq.tasks[i].Priority() > pq.tasks[i-1].Priority() {
pq.tasks[i], pq.tasks[i-1] = pq.tasks[i-1], pq.tasks[i]
} else {
break
}
}
}
func (pq *PriorityQueue) Pop() Task {
if len(pq.tasks) == 0 {
return nil
}
task := pq.tasks[0]
pq.tasks = pq.tasks[1:]
return task
}
func (pq *PriorityQueue) Len() int {
return len(pq.tasks)
}
// NewPriorityTaskScheduler 创建支持优先级的调度器
func NewPriorityTaskScheduler(workers int) *PriorityTaskScheduler {
pts := &PriorityTaskScheduler{
TaskScheduler: NewTaskScheduler(workers, 0), // 不使用内置队列
priorityQueue: &PriorityQueue{tasks: make([]Task, 0)},
}
// 启动优先级调度协程
go pts.priorityDispatcher()
return pts
}
// priorityDispatcher 优先级调度器
func (pts *PriorityTaskScheduler) priorityDispatcher() {
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-pts.ctx.Done():
return
case <-ticker.C:
pts.queueMutex.Lock()
if pts.priorityQueue.Len() > 0 && len(pts.taskQueue) < cap(pts.taskQueue) {
task := pts.priorityQueue.Pop()
select {
case pts.taskQueue <- task:
// 成功发送
default:
// 队列满了,放回优先级队列
pts.priorityQueue.Push(task)
}
}
pts.queueMutex.Unlock()
}
}
}
// SubmitWithPriority 提交带优先级的任务
func (pts *PriorityTaskScheduler) SubmitWithPriority(task Task) error {
select {
case <-pts.ctx.Done():
return fmt.Errorf("scheduler is shutting down")
default:
pts.queueMutex.Lock()
pts.priorityQueue.Push(task)
atomic.AddInt64(&pts.totalTasks, 1)
pts.queueMutex.Unlock()
return nil
}
}
+348
View File
@@ -0,0 +1,348 @@
package utils
import (
"context"
"fmt"
"log"
"time"
)
// 示例:批量处理图片任务
func processImages(scheduler *TaskScheduler) {
// 假设有10000张图片需要处理
imagePaths := make([]string, 10000)
for i := 0; i < 10000; i++ {
imagePaths[i] = fmt.Sprintf("/path/to/image_%d.jpg", i)
}
// 批量提交压缩任务
log.Println("提交图片压缩任务...")
for i, path := range imagePaths {
task := NewImageCompressTask(
fmt.Sprintf("compress_%d", i),
path,
85, // 压缩质量
)
if err := scheduler.Submit(task); err != nil {
log.Printf("Failed to submit task: %v", err)
// 可以实现重试逻辑
time.Sleep(100 * time.Millisecond)
scheduler.Submit(task) // 重试一次
}
}
// 同时提交EXIF读取任务
log.Println("提交EXIF读取任务...")
for i, path := range imagePaths[:1000] { // 只处理前1000张
task := NewExifReadTask(
fmt.Sprintf("exif_%d", i),
path,
)
scheduler.Submit(task)
}
}
// 高级任务管理器 - 支持任务组和依赖
type TaskManager struct {
scheduler *PriorityTaskScheduler
taskGroups map[string]*TaskGroup
}
type TaskGroup struct {
ID string
Tasks []Task
Status string
Progress float64
StartTime time.Time
EndTime time.Time
}
func NewTaskManager(workers int) *TaskManager {
return &TaskManager{
scheduler: NewPriorityTaskScheduler(workers),
taskGroups: make(map[string]*TaskGroup),
}
}
// CreateImageProcessingGroup 创建图片处理任务组
func (tm *TaskManager) CreateImageProcessingGroup(groupID string, imagePaths []string) error {
group := &TaskGroup{
ID: groupID,
Tasks: make([]Task, 0),
Status: "pending",
StartTime: time.Now(),
}
// 为每张图片创建压缩和EXIF读取任务
for i, path := range imagePaths {
// 压缩任务
compressTask := NewImageCompressTask(
fmt.Sprintf("%s_compress_%d", groupID, i),
path,
85,
)
group.Tasks = append(group.Tasks, compressTask)
// EXIF任务
exifTask := NewExifReadTask(
fmt.Sprintf("%s_exif_%d", groupID, i),
path,
)
group.Tasks = append(group.Tasks, exifTask)
}
tm.taskGroups[groupID] = group
// 提交所有任务
for _, task := range group.Tasks {
if err := tm.scheduler.SubmitWithPriority(task); err != nil {
return err
}
}
group.Status = "running"
return nil
}
// 动态Worker调整器
type DynamicWorkerManager struct {
scheduler *TaskScheduler
minWorkers int
maxWorkers int
checkInterval time.Duration
scaleThreshold float64 // 队列使用率阈值
}
func NewDynamicWorkerManager(scheduler *TaskScheduler, min, max int) *DynamicWorkerManager {
return &DynamicWorkerManager{
scheduler: scheduler,
minWorkers: min,
maxWorkers: max,
checkInterval: 5 * time.Second,
scaleThreshold: 0.8,
}
}
func (dwm *DynamicWorkerManager) Start(ctx context.Context) {
ticker := time.NewTicker(dwm.checkInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
dwm.adjustWorkers()
}
}
}
func (dwm *DynamicWorkerManager) adjustWorkers() {
stats := dwm.scheduler.GetStats()
pending := stats["pending_tasks"].(int)
//active := stats["active_workers"].(int)
// 计算队列使用率
queueUsage := float64(pending) / float64(cap(dwm.scheduler.taskQueue))
if queueUsage > dwm.scaleThreshold && dwm.scheduler.workers < dwm.maxWorkers {
// 增加worker
log.Printf("Scaling up workers: queue usage %.2f%%", queueUsage*100)
// 这里需要实现动态增加worker的逻辑
} else if queueUsage < 0.2 && dwm.scheduler.workers > dwm.minWorkers {
// 减少worker
log.Printf("Scaling down workers: queue usage %.2f%%", queueUsage*100)
// 这里需要实现动态减少worker的逻辑
}
}
// 任务监控器
type TaskMonitor struct {
scheduler *TaskScheduler
}
func NewTaskMonitor(scheduler *TaskScheduler) *TaskMonitor {
return &TaskMonitor{scheduler: scheduler}
}
func (tm *TaskMonitor) Start(ctx context.Context) {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
tm.printStats()
}
}
}
func (tm *TaskMonitor) printStats() {
stats := tm.scheduler.GetStats()
fmt.Println("\n========== Task Scheduler Stats ==========")
fmt.Printf("Total Tasks: %d\n", stats["total_tasks"])
fmt.Printf("Completed: %d\n", stats["completed_tasks"])
fmt.Printf("Failed: %d\n", stats["failed_tasks"])
fmt.Printf("Pending: %d\n", stats["pending_tasks"])
fmt.Printf("Active Workers: %d\n", stats["active_workers"])
if typeStats, ok := stats["type_stats"].(map[string]*TaskTypeStats); ok {
fmt.Println("\nTask Type Statistics:")
for taskType, stat := range typeStats {
fmt.Printf(" %s: Total=%d, Completed=%d, Failed=%d, AvgTime=%v\n",
taskType, stat.Total, stat.Completed, stat.Failed, stat.AvgTime)
}
}
fmt.Println("==========================================\n")
}
// 主函数示例
func main() {
// 创建调度器 - 50个worker,队列容量10000
scheduler := NewTaskScheduler(50, 10000)
defer scheduler.Shutdown()
// 创建任务管理器
taskManager := NewTaskManager(50)
// 创建监控器
monitor := NewTaskMonitor(scheduler)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go monitor.Start(ctx)
// 场景1:处理第一批10000张图片
log.Println("开始处理第一批图片...")
imagePaths1 := make([]string, 10000)
for i := 0; i < 10000; i++ {
imagePaths1[i] = fmt.Sprintf("/batch1/image_%d.jpg", i)
}
taskManager.CreateImageProcessingGroup("batch1", imagePaths1)
// 模拟过一段时间后又来了新任务
time.Sleep(5 * time.Second)
// 场景2:又来了10000张图片
log.Println("开始处理第二批图片...")
imagePaths2 := make([]string, 10000)
for i := 0; i < 10000; i++ {
imagePaths2[i] = fmt.Sprintf("/batch2/image_%d.jpg", i)
}
taskManager.CreateImageProcessingGroup("batch2", imagePaths2)
// 场景3:批量解析EXIF(优先级更高)
log.Println("开始批量解析EXIF...")
exifPaths := make([]string, 1000)
for i := 0; i < 1000; i++ {
exifPaths[i] = fmt.Sprintf("/priority/image_%d.jpg", i)
}
// 这些任务会有更高的优先级
for i, path := range exifPaths {
task := &HighPriorityExifTask{
ExifReadTask: *NewExifReadTask(fmt.Sprintf("priority_exif_%d", i), path),
}
taskManager.scheduler.SubmitWithPriority(task)
}
// 等待一段时间查看执行情况
time.Sleep(30 * time.Second)
// 打印最终统计
monitor.printStats()
}
// HighPriorityExifTask 高优先级EXIF任务
type HighPriorityExifTask struct {
ExifReadTask
}
func (t *HighPriorityExifTask) Priority() int {
return 10 // 更高的优先级
}
// 任务重试装饰器
type RetryableTask struct {
task Task
maxRetries int
retries int
}
func NewRetryableTask(task Task, maxRetries int) *RetryableTask {
return &RetryableTask{
task: task,
maxRetries: maxRetries,
retries: 0,
}
}
func (rt *RetryableTask) Execute(ctx context.Context) error {
var lastErr error
for rt.retries <= rt.maxRetries {
err := rt.task.Execute(ctx)
if err == nil {
return nil
}
lastErr = err
rt.retries++
if rt.retries <= rt.maxRetries {
// 指数退避
backoff := time.Duration(rt.retries) * time.Second
log.Printf("Task %s failed, retrying in %v (attempt %d/%d)",
rt.task.ID(), backoff, rt.retries, rt.maxRetries)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(backoff):
// 继续重试
}
}
}
return fmt.Errorf("task failed after %d retries: %w", rt.maxRetries, lastErr)
}
func (rt *RetryableTask) ID() string { return rt.task.ID() }
func (rt *RetryableTask) Type() string { return rt.task.Type() }
func (rt *RetryableTask) Priority() int { return rt.task.Priority() }
// 批处理优化 - 将多个小任务合并为一个大任务
type BatchImageCompressTask struct {
id string
filePaths []string
quality int
}
func NewBatchImageCompressTask(id string, filePaths []string, quality int) *BatchImageCompressTask {
return &BatchImageCompressTask{
id: id,
filePaths: filePaths,
quality: quality,
}
}
func (t *BatchImageCompressTask) Execute(ctx context.Context) error {
// 批量处理多张图片,减少任务切换开销
for _, path := range t.filePaths {
select {
case <-ctx.Done():
return ctx.Err()
default:
// 处理单张图片
log.Printf("Batch compressing: %s", path)
// 实际的压缩逻辑
}
}
return nil
}
func (t *BatchImageCompressTask) ID() string { return t.id }
func (t *BatchImageCompressTask) Type() string { return "batch_image_compress" }
func (t *BatchImageCompressTask) Priority() int { return 5 }