一个基于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
- 状态机管理:
- 严格的状态转换控制
- 事件驱动的状态变更
- 完整的重试机制
- 并发处理:
- 基于事件的异步处理
- 线程池管理
- 事务保证
- 持久化:
- 数据库存储任务状态
- JSON存储任务参数
- 完整的审计信息
- API接口:
- RESTful API设计
- 状态转换接口
- 任务管理接口
使用示例:
- 提交任务:
POST /api/tasks
{
"taskType": "DATA_PROCESSING",
"payload": {
"dataSource": "s3://bucket/file.csv",
"parameters": {
"format": "CSV",
"delimiter": ","
}
},
"priority": "HIGH"
}
- 更新状态:
PUT /api/tasks/{taskId}/state
{
"newState": "RUNNING",
"error": null
}
- 暂停任务:
PUT /api/tasks/{taskId}/pause
优化项:
- 监控指标
- 任务优先级队列
- 任务超时处理
- 分布式锁支持
- 更多的任务类型支持