基于Spring Boot的有限状态机调度系统

一个基于Spring Boot的有限状态机调度系统。

// TaskEntity.java
@Entity
@Table(name = "tasks")
@Data
@Builder
public class TaskEntity {
    @Id
    private String taskId;
    
    @Enumerated(EnumType.STRING)
    private TaskState state;
    
    @Column(columnDefinition = "jsonb")
    private String payload;
    
    private int retryCount;
    private String lastError;
    private Instant createdAt;
    private Instant updatedAt;
    private String taskType;
    private String priority;
}

// TaskState.java
public enum TaskState {
    PENDING,
    SCHEDULED,
    RUNNING,
    PAUSED,
    COMPLETED,
    FAILED
}

// TaskEvent.java
@Data
@Builder
public class TaskEvent {
    private String eventType;
    private String taskId;
    private Map<String, Object> data;
    private Instant timestamp;
}

// TaskRepository.java
@Repository
public interface TaskRepository extends JpaRepository<TaskEntity, String> {
    List<TaskEntity> findByState(TaskState state);
    List<TaskEntity> findByStateAndUpdatedAtBefore(TaskState state, Instant timestamp);
}

// FSMService.java
@Service
@Slf4j
public class FSMService {
    private final TaskRepository taskRepository;
    private final ApplicationEventPublisher eventPublisher;
    private final ObjectMapper objectMapper;
    
    private final Map<TaskState, Set<TaskState>> allowedTransitions;
    
    @Autowired
    public FSMService(TaskRepository taskRepository, 
                     ApplicationEventPublisher eventPublisher,
                     ObjectMapper objectMapper) {
        this.taskRepository = taskRepository;
        this.eventPublisher = eventPublisher;
        this.objectMapper = objectMapper;
        this.allowedTransitions = initializeTransitions();
    }
    
    private Map<TaskState, Set<TaskState>> initializeTransitions() {
        Map<TaskState, Set<TaskState>> transitions = new EnumMap<>(TaskState.class);
        transitions.put(TaskState.PENDING, Set.of(TaskState.SCHEDULED, TaskState.FAILED));
        transitions.put(TaskState.SCHEDULED, Set.of(TaskState.RUNNING, TaskState.FAILED));
        transitions.put(TaskState.RUNNING, Set.of(TaskState.COMPLETED, TaskState.FAILED, TaskState.PAUSED));
        transitions.put(TaskState.PAUSED, Set.of(TaskState.RUNNING, TaskState.FAILED));
        transitions.put(TaskState.FAILED, Set.of(TaskState.PENDING));
        transitions.put(TaskState.COMPLETED, Collections.emptySet());
        return transitions;
    }
    
    @Transactional
    public TaskEntity submitTask(String taskType, Map<String, Object> payload, String priority) {
        TaskEntity task = TaskEntity.builder()
                .taskId(UUID.randomUUID().toString())
                .state(TaskState.PENDING)
                .payload(toJson(payload))
                .retryCount(0)
                .createdAt(Instant.now())
                .updatedAt(Instant.now())
                .taskType(taskType)
                .priority(priority)
                .build();
        
        task = taskRepository.save(task);
        publishEvent("SUBMIT", task);
        return task;
    }
    
    @Transactional
    public void transitionState(String taskId, TaskState newState, String error) {
        TaskEntity task = taskRepository.findById(taskId)
                .orElseThrow(() -> new IllegalArgumentException("Task not found: " + taskId));
                
        if (!isValidTransition(task.getState(), newState)) {
            throw new IllegalStateException("Invalid state transition from " + 
                task.getState() + " to " + newState);
        }
        
        task.setState(newState);
        task.setLastError(error);
        task.setUpdatedAt(Instant.now());
        
        if (newState == TaskState.FAILED) {
            handleFailure(task);
        }
        
        taskRepository.save(task);
        publishEvent("STATE_CHANGE", task);
    }
    
    private void handleFailure(TaskEntity task) {
        if (task.getRetryCount() < 3) { // Max retries
            task.setRetryCount(task.getRetryCount() + 1);
            scheduleRetry(task);
        }
    }
    
    private void scheduleRetry(TaskEntity task) {
        long delay = (long) Math.pow(2, task.getRetryCount()); // Exponential backoff
        publishEvent("RETRY", task, Map.of("delay", delay));
    }
    
    private boolean isValidTransition(TaskState from, TaskState to) {
        return allowedTransitions.get(from).contains(to);
    }
    
    private void publishEvent(String eventType, TaskEntity task) {
        publishEvent(eventType, task, Collections.emptyMap());
    }
    
    private void publishEvent(String eventType, TaskEntity task, Map<String, Object> data) {
        TaskEvent event = TaskEvent.builder()
                .eventType(eventType)
                .taskId(task.getTaskId())
                .data(data)
                .timestamp(Instant.now())
                .build();
        eventPublisher.publishEvent(event);
    }
    
    private String toJson(Map<String, Object> payload) {
        try {
            return objectMapper.writeValueAsString(payload);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Failed to serialize payload", e);
        }
    }
}

// TaskController.java
@RestController
@RequestMapping("/api/tasks")
@Slf4j
public class TaskController {
    private final FSMService fsmService;
    
    @PostMapping
    public ResponseEntity<TaskEntity> submitTask(@RequestBody TaskRequest request) {
        TaskEntity task = fsmService.submitTask(
            request.getTaskType(),
            request.getPayload(),
            request.getPriority()
        );
        return ResponseEntity.ok(task);
    }
    
    @PutMapping("/{taskId}/state")
    public ResponseEntity<Void> updateState(
            @PathVariable String taskId,
            @RequestBody StateUpdateRequest request) {
        fsmService.transitionState(taskId, request.getNewState(), request.getError());
        return ResponseEntity.ok().build();
    }
    
    @PutMapping("/{taskId}/pause")
    public ResponseEntity<Void> pauseTask(@PathVariable String taskId) {
        fsmService.transitionState(taskId, TaskState.PAUSED, null);
        return ResponseEntity.ok().build();
    }
    
    @PutMapping("/{taskId}/resume")
    public ResponseEntity<Void> resumeTask(@PathVariable String taskId) {
        fsmService.transitionState(taskId, TaskState.RUNNING, null);
        return ResponseEntity.ok().build();
    }
}

// TaskEventListener.java
@Component
@Slf4j
public class TaskEventListener {
    private final TaskExecutor taskExecutor;
    
    @EventListener
    public void handleTaskEvent(TaskEvent event) {
        log.info("Received task event: {}", event);
        switch (event.getEventType()) {
            case "SUBMIT":
                handleSubmit(event);
                break;
            case "RETRY":
                handleRetry(event);
                break;
            case "STATE_CHANGE":
                handleStateChange(event);
                break;
        }
    }
    
    private void handleSubmit(TaskEvent event) {
        taskExecutor.executeTask(event.getTaskId());
    }
    
    private void handleRetry(TaskEvent event) {
        long delay = (long) event.getData().get("delay");
        taskExecutor.scheduleRetry(event.getTaskId(), delay);
    }
    
    private void handleStateChange(TaskEvent event) {
        // Handle state change notifications, metrics, etc.
    }
}

还需要添加配置文件:

application.yml

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/scheduler
    username: scheduler
    password: scheduler
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
        
scheduler:
  thread-pool:
    core-size: 10
    max-size: 20
    queue-capacity: 100
  retry:
    max-attempts: 3
    initial-interval: 1000
    multiplier: 2
  1. 状态机管理:
  • 严格的状态转换控制
  • 事件驱动的状态变更
  • 完整的重试机制
  1. 并发处理:
  • 基于事件的异步处理
  • 线程池管理
  • 事务保证
  1. 持久化:
  • 数据库存储任务状态
  • JSON存储任务参数
  • 完整的审计信息
  1. API接口:
  • RESTful API设计
  • 状态转换接口
  • 任务管理接口

使用示例:

  1. 提交任务:
POST /api/tasks
{
    "taskType": "DATA_PROCESSING",
    "payload": {
        "dataSource": "s3://bucket/file.csv",
        "parameters": {
            "format": "CSV",
            "delimiter": ","
        }
    },
    "priority": "HIGH"
}
  1. 更新状态:
PUT /api/tasks/{taskId}/state
{
    "newState": "RUNNING",
    "error": null
}
  1. 暂停任务:
PUT /api/tasks/{taskId}/pause

优化项:

  1. 监控指标
  2. 任务优先级队列
  3. 任务超时处理
  4. 分布式锁支持
  5. 更多的任务类型支持
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值