// 自定义状态策略
package com.example.streamingorchestration.strategy;
import com.alibaba.graphscope.groot.runtime.KeyStrategy;
import java.util.ArrayList;
import java.util.List;
/**
* 追加策略 - 用于列表类型数据的累积
*/
public class AppendStrategy implements KeyStrategy {
@Override
public Object apply(Object currentValue, Object newValue) {
if (currentValue == null) {
if (newValue instanceof List) {
return new ArrayList<>((List<?>) newValue);
} else {
List<Object> list = new ArrayList<>();
list.add(newValue);
return list;
}
}
if (currentValue instanceof List && newValue instanceof List) {
List<Object> currentList = (List<Object>) currentValue;
List<Object> newList = (List<Object>) newValue;
currentList.addAll(newList);
return currentList;
} else if (currentValue instanceof List) {
List<Object> currentList = (List<Object>) currentValue;
currentList.add(newValue);
return currentList;
} else {
List<Object> newList = new ArrayList<>();
newList.add(currentValue);
newList.add(newValue);
return newList;
}
}
}
/**
* 递增策略 - 用于计数器
*/
public class IncrementStrategy implements KeyStrategy {
@Override
public Object apply(Object currentValue, Object newValue) {
Integer current = currentValue != null ? (Integer) currentValue : 0;
Integer increment = newValue != null ? (Integer) newValue : 1;
return current + increment;
}
}
/**
* 最大值策略 - 保留最大值
*/
public class MaxValueStrategy implements KeyStrategy {
@Override
public Object apply(Object currentValue, Object newValue) {
if (currentValue == null) return newValue;
if (newValue == null) return currentValue;
if (currentValue instanceof Comparable && newValue instanceof Comparable) {
return ((Comparable) currentValue).compareTo(newValue) >= 0 ? currentValue : newValue;
}
return newValue; // 无法比较时使用新值
}
}
// StreamWeatherService.java
@Service
public class StreamWeatherService {
private final Random random = new Random();
// 服务A - 快速但精度一般
public Flux<String> getWeatherStreamA(String city) {
return Flux.range(0, 5)
.delayElements(Duration.ofMillis(100))
.map(i -> {
double temp = 20.0 + (city.hashCode() % 15) + random.nextDouble() * 4 - 2;
String condition = getRandomConditionA();
return String.format("ServiceA|%s|%.1f|%s|%d", city, temp, condition, i);
});
}
// 服务B - 中等速度,较好精度
public Flux<String> getWeatherStreamB(String city) {
return Flux.range(0, 8)
.delayElements(Duration.ofMillis(150))
.map(i -> {
double temp = 20.5 + (city.hashCode() % 14) + random.nextDouble() * 3 - 1.5;
String condition = getRandomConditionB();
return String.format("ServiceB|%s|%.1f|%s|%d", city, temp, condition, i);
});
}
// 服务C - 较慢但高精度
public Flux<String> getWeatherStreamC(String city) {
return Flux.range(0, 10)
.delayElements(Duration.ofMillis(200))
.map(i -> {
double temp = 19.8 + (city.hashCode() % 16) + random.nextDouble() * 2 - 1;
String condition = getRandomConditionC();
return String.format("ServiceC|%s|%.1f|%s|%d", city, temp, condition, i);
});
}
private String getRandomConditionA() {
String[] conditions = {"Sunny", "Partly Cloudy", "Cloudy", "Light Rain"};
return conditions[random.nextInt(conditions.length)];
}
private String getRandomConditionB() {
String[] conditions = {"Clear", "Mostly Sunny", "Overcast", "Drizzle"};
return conditions[random.nextInt(conditions.length)];
}
private String getRandomConditionC() {
String[] conditions = {"Fair", "Scattered Clouds", "Broken Clouds", "Light Showers"};
return conditions[random.nextInt(conditions.length)];
}
// 解析流数据并计算评分
public Double calculateScoreFromStream(List<String> streamData) {
if (streamData == null || streamData.isEmpty()) {
return 0.0;
}
// 基于数据完整性、响应速度和数据一致性评分
double completenessScore = Math.min(1.0, streamData.size() / 8.0);
double consistencyScore = calculateConsistency(streamData);
return (completenessScore * 0.6 + consistencyScore * 0.4) * 10;
}
private double calculateConsistency(List<String> streamData) {
// 简化的数据一致性检查
try {
Set<String> uniqueTemps = streamData.stream()
.map(data -> data.split("\\|")[2]) // 温度字段
.collect(Collectors.toSet());
return Math.min(1.0, 1.0 / (uniqueTemps.size() * 0.5));
} catch (Exception e) {
return 0.5;
}
}
}
// WeatherSelectorEdgeAction.java
@Component
public class WeatherSelectorEdgeAction implements EdgeAction {
@Override
public String apply(OverAllState state) throws Exception {
String currentService = (String) state.value("current_service");
Double currentScore = (Double) state.value("current_score");
System.out.println("🔍 选择器检查: 服务 " + currentService + " 评分=" + currentScore);
// 策略1: 如果评分>=8.0,提前返回最佳结果
if (currentScore != null && currentScore >= 8.0) {
System.out.println("✅ 服务 " + currentService + " 评分达标,提前返回最佳结果");
return "best_result_selector";
}
// 策略2: 根据当前服务决定下一步
switch (currentService) {
case "A":
return "service_b"; // 继续服务B
case "B":
Double scoreA = (Double) state.value("service_a_score");
Double scoreB = (Double) state.value("service_b_score");
// 如果服务B明显优于服务A,可以提前选择
if (scoreB != null && scoreA != null && scoreB - scoreA > 2.0) {
return "best_result_selector";
}
return "service_c"; // 继续服务C
case "C":
return "best_result_selector"; // 所有服务完成,选择最佳结果
default:
return "best_result_selector";
}
}
}
// ParallelWeatherSelector.java
@Component
public class ParallelWeatherSelector implements NodeAction {
@Override
public OverAllState execute(OverAllState state) throws Exception {
System.out.println("🔄 并行选择器开始选择最佳结果...");
// 收集所有服务的评分
Map<String, Double> serviceScores = new HashMap<>();
String[] services = {"A", "B", "C"};
for (String service : services) {
Double score = (Double) state.value("service_" + service + "_score");
if (score != null) {
serviceScores.put(service, score);
System.out.println("📊 服务 " + service + " 最终评分: " + score);
}
}
// 选择评分最高的服务
Map.Entry<String, Double> bestEntry = serviceScores.entrySet().stream()
.max(Map.Entry.comparingByValue())
.orElse(null);
if (bestEntry != null) {
String bestService = bestEntry.getKey();
Double bestScore = bestEntry.getValue();
state.put("best_service", bestService);
state.put("best_score", bestScore);
// 获取最佳服务的流数据
Flux<String> bestStream = (Flux<String>) state.value("service_" + bestService + "_stream");
state.put("best_stream", bestStream);
System.out.println("🏆 选择最佳服务: " + bestService + " (评分: " + bestScore + ")");
}
return state;
}
}
// StreamWeatherNode.java
@Component
public class StreamWeatherNode implements NodeAction {
@Autowired
private StreamWeatherService weatherService;
@Override
public OverAllState execute(OverAllState state) throws Exception {
String city = (String) state.value("city");
String serviceType = (String) state.value("service_type");
System.out.println("🌤️ 开始调用天气服务 " + serviceType + " 查询城市: " + city);
// 调用对应的流式天气服务
Flux<String> weatherStream;
switch (serviceType) {
case "A":
weatherStream = weatherService.getWeatherStreamA(city);
break;
case "B":
weatherStream = weatherService.getWeatherStreamB(city);
break;
case "C":
weatherStream = weatherService.getWeatherStreamC(city);
break;
default:
throw new IllegalArgumentException("未知的服务类型: " + serviceType);
}
// 缓存流数据用于评分
Mono<List<String>> collectedStream = weatherStream.collectList();
List<String> streamData = collectedStream.block(); // 在实际生产环境中应使用异步方式
// 计算评分
Double score = weatherService.calculateScoreFromStream(streamData);
// 重新创建流(因为原来的流已经被消费)
Flux<String> newStream = Flux.fromIterable(streamData);
// 保存结果到状态
state.put("service_" + serviceType + "_stream", newStream);
state.put("service_" + serviceType + "_score", score);
state.put("service_" + serviceType + "_data", streamData);
state.put("current_service", serviceType);
state.put("current_score", score);
System.out.println("✅ 服务 " + serviceType + " 完成,评分: " + score);
return state;
}
}
// StartNode.java
@Component
public class StartNode implements NodeAction {
@Override
public OverAllState execute(OverAllState state) throws Exception {
System.out.println("🚀 开始天气查询工作流");
String city = (String) state.value("city");
System.out.println("📍 查询城市: " + city);
state.put("start_time", System.currentTimeMillis());
return state;
}
}
// FinalResultNode.java
@Component
public class FinalResultNode implements NodeAction {
@Override
public OverAllState execute(OverAllState state) throws Exception {
Long startTime = (Long) state.value("start_time");
Long endTime = System.currentTimeMillis();
state.put("total_execution_time", endTime - startTime);
String bestService = (String) state.value("best_service");
Double bestScore = (Double) state.value("best_score");
System.out.println("🎉 工作流执行完成!");
System.out.println("🏆 最佳服务: " + bestService + ", 评分: " + bestScore);
System.out.println("⏱️ 总执行时间: " + (endTime - startTime) + "ms");
return state;
}
}
// WeatherGraphConfig.java
@Configuration
public class WeatherGraphConfig {
@Bean
public StateGraph sequentialWeatherGraph() throws GraphStateException {
OverAllStateFactory factory = () -> {
OverAllState state = new OverAllState();
// 注册状态键和更新策略
state.registerKeyAndStrategy("city", new ReplaceStrategy());
state.registerKeyAndStrategy("service_type", new ReplaceStrategy());
state.registerKeyAndStrategy("current_service", new ReplaceStrategy());
state.registerKeyAndStrategy("current_score", new ReplaceStrategy());
state.registerKeyAndStrategy("start_time", new ReplaceStrategy());
state.registerKeyAndStrategy("service_A_stream", new ReplaceStrategy());
state.registerKeyAndStrategy("service_B_stream", new ReplaceStrategy());
state.registerKeyAndStrategy("service_C_stream", new ReplaceStrategy());
state.registerKeyAndStrategy("service_A_score", new ReplaceStrategy());
state.registerKeyAndStrategy("service_B_score", new ReplaceStrategy());
state.registerKeyAndStrategy("service_C_score", new ReplaceStrategy());
state.registerKeyAndStrategy("best_service", new ReplaceStrategy());
state.registerKeyAndStrategy("best_score", new ReplaceStrategy());
state.registerKeyAndStrategy("best_stream", new ReplaceStrategy());
state.registerKeyAndStrategy("total_execution_time", new ReplaceStrategy());
return state;
};
StateGraph stateGraph = new StateGraph("串行天气查询工作流", factory)
.addNode("start", AsyncNodeAction.node_async(new StartNode()))
.addNode("service_a", AsyncNodeAction.node_async(new StreamWeatherNode()))
.addNode("service_b", AsyncNodeAction.node_async(new StreamWeatherNode()))
.addNode("service_c", AsyncNodeAction.node_async(new StreamWeatherNode()))
.addNode("best_result_selector", AsyncNodeAction.node_async(new ParallelWeatherSelector()))
.addNode("final_result", AsyncNodeAction.node_async(new FinalResultNode()))
// 串行执行流程
.addEdge(StateGraph.START, "start")
.addEdge("start", "service_a")
// 条件边:根据选择器决定下一步
.addConditionalEdges("service_a",
AsyncEdgeAction.edge_async(new WeatherSelectorEdgeAction()),
Map.of("service_b", "service_b", "best_result_selector", "best_result_selector"))
.addConditionalEdges("service_b",
AsyncEdgeAction.edge_async(new WeatherSelectorEdgeAction()),
Map.of("service_c", "service_c", "best_result_selector", "best_result_selector"))
.addEdge("service_c", "best_result_selector")
.addEdge("best_result_selector", "final_result")
.addEdge("final_result", StateGraph.END);
return stateGraph;
}
@Bean
public StateGraph parallelWeatherGraph() throws GraphStateException {
OverAllStateFactory factory = () -> {
OverAllState state = new OverAllState();
// 注册状态键和更新策略(同串行配置)
state.registerKeyAndStrategy("city", new ReplaceStrategy());
state.registerKeyAndStrategy("service_type", new ReplaceStrategy());
state.registerKeyAndStrategy("start_time", new ReplaceStrategy());
state.registerKeyAndStrategy("service_A_stream", new ReplaceStrategy());
state.registerKeyAndStrategy("service_B_stream", new ReplaceStrategy());
state.registerKeyAndStrategy("service_C_stream", new ReplaceStrategy());
state.registerKeyAndStrategy("service_A_score", new ReplaceStrategy());
state.registerKeyAndStrategy("service_B_score", new ReplaceStrategy());
state.registerKeyAndStrategy("service_C_score", new ReplaceStrategy());
state.registerKeyAndStrategy("best_service", new ReplaceStrategy());
state.registerKeyAndStrategy("best_score", new ReplaceStrategy());
state.registerKeyAndStrategy("best_stream", new ReplaceStrategy());
state.registerKeyAndStrategy("total_execution_time", new ReplaceStrategy());
return state;
};
StateGraph stateGraph = new StateGraph("并行天气查询工作流", factory)
.addNode("start", AsyncNodeAction.node_async(new StartNode()))
.addNode("service_a_parallel", AsyncNodeAction.node_async(new StreamWeatherNode()))
.addNode("service_b_parallel", AsyncNodeAction.node_async(new StreamWeatherNode()))
.addNode("service_c_parallel", AsyncNodeAction.node_async(new StreamWeatherNode()))
.addNode("best_result_selector_parallel", AsyncNodeAction.node_async(new ParallelWeatherSelector()))
.addNode("final_result_parallel", AsyncNodeAction.node_async(new FinalResultNode()))
// 并行执行流程
.addEdge(StateGraph.START, "start")
.addEdge("start", "service_a_parallel")
.addEdge("start", "service_b_parallel")
.addEdge("start", "service_c_parallel")
// 所有服务并行执行后汇聚到选择器
.addEdge("service_a_parallel", "best_result_selector_parallel")
.addEdge("service_b_parallel", "best_result_selector_parallel")
.addEdge("service_c_parallel", "best_result_selector_parallel")
.addEdge("best_result_selector_parallel", "final_result_parallel")
.addEdge("final_result_parallel", StateGraph.END);
return stateGraph;
}
}
// WeatherController.java
@RestController
@RequestMapping("/api/weather")
public class WeatherController {
private final CompiledGraph sequentialGraph;
private final CompiledGraph parallelGraph;
@Autowired
public WeatherController(
@Qualifier("sequentialWeatherGraph") StateGraph sequentialGraph,
@Qualifier("parallelWeatherGraph") StateGraph parallelGraph) throws Exception {
this.sequentialGraph = sequentialGraph.compile();
this.parallelGraph = parallelGraph.compile();
}
@PostMapping(value = "/sequential/{city}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getWeatherSequential(@PathVariable String city) throws Exception {
System.out.println("\n=== 开始串行执行模式 ===");
OverAllState initialState = new OverAllState();
initialState.put("city", city);
initialState.put("service_type", "A");
CompiledGraphResponse response = sequentialGraph.call(initialState);
Flux<String> bestStream = (Flux<String>) response.state().value("best_stream");
if (bestStream != null) {
return bestStream.map(data -> "DATA: " + data)
.concatWith(Flux.just("=== 串行执行完成 ==="));
}
return Flux.just("错误: 未能获取天气数据");
}
@PostMapping(value = "/parallel/{city}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getWeatherParallel(@PathVariable String city) throws Exception {
System.out.println("\n=== 开始并行执行模式 ===");
OverAllState initialState = new OverAllState();
initialState.put("city", city);
// 为并行执行准备多个状态
initialState.put("service_type", "A");
CompiledGraphResponse response = parallelGraph.call(initialState);
Flux<String> bestStream = (Flux<String>) response.state().value("best_stream");
if (bestStream != null) {
return bestStream.map(data -> "DATA: " + data)
.concatWith(Flux.just("=== 并行执行完成 ==="));
}
return Flux.just("错误: 未能获取天气数据");
}
}
// MainApplication.java
@SpringBootApplication
public class WeatherOrchestrationApplication {
public static void main(String[] args) {
SpringApplication.run(WeatherOrchestrationApplication.class, args);
}
}