16.1 批量任务调度和心跳优化竟然还能这样做?

1 阅读8分钟

16.1 太震撼了!批量任务调度和心跳优化竟然还能这样做?

在分布式任务调度系统中,性能优化是确保系统能够处理大规模任务的关键。今天我们将深入探讨批量任务调度和心跳优化技术,这些技术能够显著提升系统的吞吐量和响应速度。

批量任务调度机制

批量任务调度是提升系统性能的重要手段,通过将多个小任务合并为批量任务,可以显著减少调度开销。

package batch

import (
    "context"
    "fmt"
    "sync"
    "time"
)

// BatchScheduler 批量调度器
type BatchScheduler struct {
    batchSize     int
    batchTimeout  time.Duration
    taskQueue     chan *Task
    batchQueue    chan []*Task
    workerCount   int
    workers       []*BatchWorker
    mu            sync.RWMutex
    stopCh        chan struct{}
}

// Task 任务
type Task struct {
    ID       string
    Payload  interface{}
    Priority int
    Callback func(result interface{}, err error)
}

// BatchWorker 批量工作器
type BatchWorker struct {
    id       int
    batchScheduler *BatchScheduler
    stopCh   chan struct{}
}

// NewBatchScheduler 创建批量调度器
func NewBatchScheduler(batchSize int, batchTimeout time.Duration, workerCount int) *BatchScheduler {
    bs := &BatchScheduler{
        batchSize:    batchSize,
        batchTimeout: batchTimeout,
        taskQueue:    make(chan *Task, 10000),
        batchQueue:   make(chan []*Task, 100),
        workerCount:  workerCount,
        stopCh:       make(chan struct{}),
    }
    
    // 创建工作器
    for i := 0; i < workerCount; i++ {
        worker := &BatchWorker{
            id:             i,
            batchScheduler: bs,
            stopCh:         make(chan struct{}),
        }
        bs.workers = append(bs.workers, worker)
        go worker.run()
    }
    
    // 启动批处理协程
    go bs.batchProcessor()
    
    return bs
}

// SubmitTask 提交任务
func (bs *BatchScheduler) SubmitTask(task *Task) error {
    select {
    case bs.taskQueue <- task:
        return nil
    case <-bs.stopCh:
        return fmt.Errorf("scheduler is stopped")
    }
}

// batchProcessor 批处理处理器
func (bs *BatchScheduler) batchProcessor() {
    ticker := time.NewTicker(bs.batchTimeout)
    defer ticker.Stop()
    
    batch := make([]*Task, 0, bs.batchSize)
    
    for {
        select {
        case task := <-bs.taskQueue:
            batch = append(batch, task)
            
            // 如果批次已满,立即处理
            if len(batch) >= bs.batchSize {
                bs.processBatch(batch)
                batch = make([]*Task, 0, bs.batchSize)
                ticker.Reset(bs.batchTimeout)
            }
            
        case <-ticker.C:
            // 超时处理当前批次
            if len(batch) > 0 {
                bs.processBatch(batch)
                batch = make([]*Task, 0, bs.batchSize)
            }
            ticker.Reset(bs.batchTimeout)
            
        case <-bs.stopCh:
            // 处理剩余任务
            if len(batch) > 0 {
                bs.processBatch(batch)
            }
            return
        }
    }
}

// processBatch 处理批次
func (bs *BatchScheduler) processBatch(tasks []*Task) {
    select {
    case bs.batchQueue <- tasks:
    case <-bs.stopCh:
        // 调度器已停止,直接执行任务
        bs.executeBatch(tasks)
    }
}

// executeBatch 执行批次任务
func (bs *BatchScheduler) executeBatch(tasks []*Task) {
    // 这里是批量执行任务的逻辑
    // 实际应用中可能需要根据任务类型进行分组处理
    
    fmt.Printf("Executing batch of %d tasks\n", len(tasks))
    
    // 模拟批量处理
    results := make([]interface{}, len(tasks))
    errors := make([]error, len(tasks))
    
    var wg sync.WaitGroup
    for i, task := range tasks {
        wg.Add(1)
        go func(index int, t *Task) {
            defer wg.Done()
            // 模拟任务执行
            result, err := bs.executeTask(t)
            results[index] = result
            errors[index] = err
        }(i, task)
    }
    
    wg.Wait()
    
    // 调用回调函数
    for i, task := range tasks {
        if task.Callback != nil {
            task.Callback(results[i], errors[i])
        }
    }
}

// executeTask 执行单个任务
func (bs *BatchScheduler) executeTask(task *Task) (interface{}, error) {
    // 模拟任务执行
    // 实际应用中这里会执行具体的业务逻辑
    fmt.Printf("Executing task %s\n", task.ID)
    return fmt.Sprintf("result_%s", task.ID), nil
}

// Stop 停止调度器
func (bs *BatchScheduler) Stop() {
    close(bs.stopCh)
    
    // 停止所有工作器
    for _, worker := range bs.workers {
        close(worker.stopCh)
    }
}

// run 工作器运行方法
func (bw *BatchWorker) run() {
    for {
        select {
        case batch := <-bw.batchScheduler.batchQueue:
            bw.processBatch(batch)
        case <-bw.stopCh:
            return
        }
    }
}

// processBatch 工作器处理批次
func (bw *BatchWorker) processBatch(batch []*Task) {
    fmt.Printf("Worker %d processing batch of %d tasks\n", bw.id, len(batch))
    bw.batchScheduler.executeBatch(batch)
}

// 使用示例
func ExampleBatchScheduler() {
    // 创建批量调度器
    scheduler := NewBatchScheduler(10, 1*time.Second, 3)
    defer scheduler.Stop()
    
    // 提交任务
    for i := 0; i < 25; i++ {
        task := &Task{
            ID:       fmt.Sprintf("task_%d", i),
            Payload:  fmt.Sprintf("payload_%d", i),
            Priority: i % 5,
            Callback: func(result interface{}, err error) {
                if err != nil {
                    fmt.Printf("Task failed: %v\n", err)
                } else {
                    fmt.Printf("Task completed with result: %v\n", result)
                }
            },
        }
        
        if err := scheduler.SubmitTask(task); err != nil {
            fmt.Printf("Failed to submit task %d: %v\n", i, err)
        }
    }
    
    // 等待任务完成
    time.Sleep(5 * time.Second)
    fmt.Println("Batch scheduling example completed")
}

智能分片调度策略

对于大数据量的任务,我们需要实现智能分片调度策略,将大任务拆分为多个小任务并行处理。

package sharding

import (
    "fmt"
    "math"
    "sort"
    "sync"
    "time"
)

// Shard 分片
type Shard struct {
    ID       string
    Data     interface{}
    Size     int64
    Priority int
}

// ShardScheduler 分片调度器
type ShardScheduler struct {
    shards       []*Shard
    nodes        map[string]*NodeInfo
    mu           sync.RWMutex
    shardQueue   chan *ShardAssignment
    stopCh       chan struct{}
}

// ShardAssignment 分片分配
type ShardAssignment struct {
    ShardID string
    NodeID  string
}

// NodeInfo 节点信息
type NodeInfo struct {
    ID       string
    Capacity int64
    Load     int64
    Status   NodeStatus
}

// NodeStatus 节点状态
type NodeStatus int

const (
    NodeStatusHealthy NodeStatus = iota
    NodeStatusUnhealthy
    NodeStatusOffline
)

// NewShardScheduler 创建分片调度器
func NewShardScheduler() *ShardScheduler {
    ss := &ShardScheduler{
        shards:     make([]*Shard, 0),
        nodes:      make(map[string]*NodeInfo),
        shardQueue: make(chan *ShardAssignment, 1000),
        stopCh:     make(chan struct{}),
    }
    
    // 启动分片分配协程
    go ss.shardAllocator()
    
    return ss
}

// AddShard 添加分片
func (ss *ShardScheduler) AddShard(shard *Shard) error {
    ss.mu.Lock()
    defer ss.mu.Unlock()
    
    ss.shards = append(ss.shards, shard)
    return nil
}

// AddNode 添加节点
func (ss *ShardScheduler) AddNode(node *NodeInfo) error {
    ss.mu.Lock()
    defer ss.mu.Unlock()
    
    ss.nodes[node.ID] = node
    return nil
}

// UpdateNodeLoad 更新节点负载
func (ss *ShardScheduler) UpdateNodeLoad(nodeID string, load int64) error {
    ss.mu.Lock()
    defer ss.mu.Unlock()
    
    node, exists := ss.nodes[nodeID]
    if !exists {
        return fmt.Errorf("node %s not found", nodeID)
    }
    
    node.Load = load
    return nil
}

// shardAllocator 分片分配器
func (ss *ShardScheduler) shardAllocator() {
    ticker := time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()
    
    for {
        select {
        case <-ticker.C:
            ss.allocateShards()
        case <-ss.stopCh:
            return
        }
    }
}

// allocateShards 分配分片
func (ss *ShardScheduler) allocateShards() {
    ss.mu.Lock()
    defer ss.mu.Unlock()
    
    // 按优先级排序分片
    sort.Slice(ss.shards, func(i, j int) bool {
        return ss.shards[i].Priority > ss.shards[j].Priority
    })
    
    // 获取健康节点
    var healthyNodes []*NodeInfo
    for _, node := range ss.nodes {
        if node.Status == NodeStatusHealthy {
            healthyNodes = append(healthyNodes, node)
        }
    }
    
    if len(healthyNodes) == 0 || len(ss.shards) == 0 {
        return
    }
    
    // 按负载排序节点
    sort.Slice(healthyNodes, func(i, j int) bool {
        return healthyNodes[i].Load < healthyNodes[j].Load
    })
    
    // 分配分片到节点
    nodeIndex := 0
    for _, shard := range ss.shards {
        // 选择负载最低的节点
        node := healthyNodes[nodeIndex%len(healthyNodes)]
        
        // 检查节点容量
        if node.Load+shard.Size <= node.Capacity {
            assignment := &ShardAssignment{
                ShardID: shard.ID,
                NodeID:  node.ID,
            }
            
            select {
            case ss.shardQueue <- assignment:
                node.Load += shard.Size
            default:
                // 队列满时跳过
                continue
            }
            
            nodeIndex++
        }
    }
}

// GetShardAssignment 获取分片分配
func (ss *ShardScheduler) GetShardAssignment() (*ShardAssignment, bool) {
    select {
    case assignment := <-ss.shardQueue:
        return assignment, true
    default:
        return nil, false
    }
}

// SmartSharding 智能分片算法
func SmartSharding(dataSize int64, maxShardSize int64, nodeCount int) []*Shard {
    if dataSize <= 0 || maxShardSize <= 0 || nodeCount <= 0 {
        return nil
    }
    
    // 计算分片数量
    shardCount := int(math.Ceil(float64(dataSize) / float64(maxShardSize)))
    if shardCount > nodeCount {
        shardCount = nodeCount
    }
    
    // 计算每个分片的大小
    shardSize := dataSize / int64(shardCount)
    remainder := dataSize % int64(shardCount)
    
    // 创建分片
    shards := make([]*Shard, 0, shardCount)
    for i := 0; i < shardCount; i++ {
        size := shardSize
        if int64(i) < remainder {
            size++ // 分配余数
        }
        
        shard := &Shard{
            ID:       fmt.Sprintf("shard_%d", i),
            Data:     nil, // 实际应用中这里会包含分片数据
            Size:     size,
            Priority: shardCount - i, // 优先级递减
        }
        
        shards = append(shards, shard)
    }
    
    return shards
}

// 使用示例
func ExampleShardScheduler() {
    scheduler := NewShardScheduler()
    defer close(scheduler.stopCh)
    
    // 添加节点
    nodes := []*NodeInfo{
        {ID: "node_1", Capacity: 1000, Load: 200, Status: NodeStatusHealthy},
        {ID: "node_2", Capacity: 1000, Load: 300, Status: NodeStatusHealthy},
        {ID: "node_3", Capacity: 1000, Load: 100, Status: NodeStatusHealthy},
    }
    
    for _, node := range nodes {
        scheduler.AddNode(node)
    }
    
    // 创建智能分片
    shards := SmartSharding(2500, 1000, 3)
    for _, shard := range shards {
        scheduler.AddShard(shard)
    }
    
    // 等待分配
    time.Sleep(1 * time.Second)
    
    // 获取分片分配结果
    for i := 0; i < len(shards); i++ {
        if assignment, ok := scheduler.GetShardAssignment(); ok {
            fmt.Printf("Shard %s assigned to node %s\n", assignment.ShardID, assignment.NodeID)
        }
    }
    
    fmt.Println("Shard scheduling example completed")
}

心跳优化机制

心跳机制是分布式系统中保持节点状态同步的重要手段,但频繁的心跳会消耗大量资源。我们需要实现心跳优化机制。

package heartbeat

import (
    "fmt"
    "sync"
    "time"
)

// HeartbeatManager 心跳管理器
type HeartbeatManager struct {
    nodes        map[string]*NodeHeartbeat
    mu           sync.RWMutex
    baseInterval time.Duration
    maxInterval  time.Duration
    minInterval  time.Duration
    stopCh       chan struct{}
}

// NodeHeartbeat 节点心跳信息
type NodeHeartbeat struct {
    NodeID        string
    LastHeartbeat time.Time
    Interval      time.Duration
    Status        NodeStatus
    FailureCount  int
    Data          map[string]interface{}
}

// NodeStatus 节点状态
type NodeStatus int

const (
    NodeStatusActive NodeStatus = iota
    NodeStatusInactive
    NodeStatusFailed
)

// HeartbeatCallback 心跳回调函数
type HeartbeatCallback func(nodeID string, data map[string]interface{}) error

// NewHeartbeatManager 创建心跳管理器
func NewHeartbeatManager(baseInterval, minInterval, maxInterval time.Duration) *HeartbeatManager {
    hm := &HeartbeatManager{
        nodes:        make(map[string]*NodeHeartbeat),
        baseInterval: baseInterval,
        minInterval:  minInterval,
        maxInterval:  maxInterval,
        stopCh:       make(chan struct{}),
    }
    
    // 启动心跳检查协程
    go hm.heartbeatChecker()
    
    return hm
}

// RegisterNode 注册节点
func (hm *HeartbeatManager) RegisterNode(nodeID string) error {
    hm.mu.Lock()
    defer hm.mu.Unlock()
    
    if _, exists := hm.nodes[nodeID]; exists {
        return fmt.Errorf("node %s already registered", nodeID)
    }
    
    hm.nodes[nodeID] = &NodeHeartbeat{
        NodeID:        nodeID,
        LastHeartbeat: time.Now(),
        Interval:      hm.baseInterval,
        Status:        NodeStatusActive,
        Data:          make(map[string]interface{}),
    }
    
    return nil
}

// UnregisterNode 注销节点
func (hm *HeartbeatManager) UnregisterNode(nodeID string) error {
    hm.mu.Lock()
    defer hm.mu.Unlock()
    
    if _, exists := hm.nodes[nodeID]; !exists {
        return fmt.Errorf("node %s not found", nodeID)
    }
    
    delete(hm.nodes, nodeID)
    return nil
}

// UpdateHeartbeat 更新心跳
func (hm *HeartbeatManager) UpdateHeartbeat(nodeID string, data map[string]interface{}) error {
    hm.mu.Lock()
    defer hm.mu.Unlock()
    
    node, exists := hm.nodes[nodeID]
    if !exists {
        return fmt.Errorf("node %s not found", nodeID)
    }
    
    node.LastHeartbeat = time.Now()
    node.Status = NodeStatusActive
    node.FailureCount = 0
    
    // 根据数据更新动态间隔
    if data != nil {
        node.Data = data
        hm.adjustInterval(node, data)
    }
    
    return nil
}

// adjustInterval 调整心跳间隔
func (hm *HeartbeatManager) adjustInterval(node *NodeHeartbeat, data map[string]interface{}) {
    // 根据节点负载动态调整心跳间隔
    if load, ok := data["load"].(float64); ok {
        if load > 0.8 { // 高负载
            node.Interval = hm.minInterval
        } else if load < 0.2 { // 低负载
            node.Interval = hm.maxInterval
        } else { // 中等负载
            // 线性调整间隔
            loadRatio := (load - 0.2) / 0.6
            node.Interval = time.Duration(
                float64(hm.minInterval) + 
                (1-loadRatio)*float64(hm.maxInterval-hm.minInterval))
        }
    }
    
    // 确保间隔在合理范围内
    if node.Interval < hm.minInterval {
        node.Interval = hm.minInterval
    }
    if node.Interval > hm.maxInterval {
        node.Interval = hm.maxInterval
    }
}

// heartbeatChecker 心跳检查器
func (hm *HeartbeatManager) heartbeatChecker() {
    ticker := time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()
    
    for {
        select {
        case <-ticker.C:
            hm.checkHeartbeats()
        case <-hm.stopCh:
            return
        }
    }
}

// checkHeartbeats 检查心跳
func (hm *HeartbeatManager) checkHeartbeats() {
    hm.mu.Lock()
    defer hm.mu.Unlock()
    
    now := time.Now()
    
    for _, node := range hm.nodes {
        // 检查是否超时
        if now.Sub(node.LastHeartbeat) > node.Interval*3 {
            node.FailureCount++
            if node.FailureCount >= 3 {
                node.Status = NodeStatusFailed
                fmt.Printf("Node %s marked as failed\n", node.NodeID)
            } else {
                node.Status = NodeStatusInactive
                fmt.Printf("Node %s marked as inactive\n", node.NodeID)
            }
        }
    }
}

// GetNodeStatus 获取节点状态
func (hm *HeartbeatManager) GetNodeStatus(nodeID string) (NodeStatus, time.Time, error) {
    hm.mu.RLock()
    defer hm.mu.RUnlock()
    
    node, exists := hm.nodes[nodeID]
    if !exists {
        return 0, time.Time{}, fmt.Errorf("node %s not found", nodeID)
    }
    
    return node.Status, node.LastHeartbeat, nil
}

// GetAllNodes 获取所有节点
func (hm *HeartbeatManager) GetAllNodes() map[string]*NodeHeartbeat {
    hm.mu.RLock()
    defer hm.mu.RUnlock()
    
    // 返回副本以避免并发问题
    result := make(map[string]*NodeHeartbeat)
    for id, node := range hm.nodes {
        result[id] = &NodeHeartbeat{
            NodeID:        node.NodeID,
            LastHeartbeat: node.LastHeartbeat,
            Interval:      node.Interval,
            Status:        node.Status,
            FailureCount:  node.FailureCount,
            Data:          node.Data,
        }
    }
    
    return result
}

// Stop 停止心跳管理器
func (hm *HeartbeatManager) Stop() {
    close(hm.stopCh)
}

// 使用示例
func ExampleHeartbeatManager() {
    // 创建心跳管理器
    hm := NewHeartbeatManager(
        1*time.Second,  // 基础间隔
        100*time.Millisecond, // 最小间隔
        5*time.Second,  // 最大间隔
    )
    defer hm.Stop()
    
    // 注册节点
    hm.RegisterNode("node_1")
    hm.RegisterNode("node_2")
    
    // 模拟发送心跳
    for i := 0; i < 10; i++ {
        // 节点1发送心跳,模拟不同负载
        load := float64(i%5) / 10.0
        hm.UpdateHeartbeat("node_1", map[string]interface{}{
            "load": load,
            "tasks": i,
        })
        
        // 节点2发送心跳
        hm.UpdateHeartbeat("node_2", map[string]interface{}{
            "load": 0.3,
            "tasks": i * 2,
        })
        
        time.Sleep(500 * time.Millisecond)
        
        // 检查节点状态
        if i%3 == 0 {
            nodes := hm.GetAllNodes()
            for id, node := range nodes {
                fmt.Printf("Node %s: status=%d, interval=%v, load=%v\n", 
                    id, node.Status, node.Interval, node.Data["load"])
            }
        }
    }
    
    fmt.Println("Heartbeat optimization example completed")
}

总结

今天我们深入探讨了分布式任务调度系统的高性能设计技术:

  1. 批量任务调度机制

    • 实现了基于队列的批量任务调度器
    • 支持批量大小和超时两种触发机制
    • 通过并行工作器提高处理效率
  2. 智能分片调度策略

    • 实现了基于数据大小的智能分片算法
    • 支持根据节点负载动态分配分片
    • 通过优先级调度确保重要任务优先执行
  3. 心跳优化机制

    • 实现了动态调整心跳间隔的机制
    • 根据节点负载自动调整心跳频率
    • 通过失败计数机制检测节点状态

这些技术的组合应用能够显著提升分布式任务调度系统的性能和可扩展性,使其能够处理更大规模的任务负载。在实际应用中,需要根据具体业务场景调整参数和策略,以达到最佳的性能效果。