鉴于我有这个 service-A ,此端点返回 contentType(MediaType.TEXT_EVENT_STREAM)
@Bean
public RouterFunction<ServerResponse> notificationsRoute(CustomHandler someCustomHandler) {
return route()
.GET("/orders", someCustomHandler:handle)
.build();
}
我需要从另一个 Spring Boot service-B 中使用它,它当前使用 RestTemplate (它还没有响应)。
即使我带了 spring webflux,我也会得到一些安全过滤器链重复的 beans。
问题 1-> 可以使用restTemplate 来完成此操作,并以某种方式使用 SseEmitter(流式传输到 UI)类来流式传输来自服务 A 的事件。
问题 1:是的,可以使用 RestTemplate 使用来自服务 A 的 SSE(服务器发送事件)流,但这不是最有效的方法。 RestTemplate是同步、阻塞的,而SSE是异步、非阻塞的协议。但是,您仍然可以使用 RestTemplate 和解决方法来处理 SSE。您需要创建一个单独的线程来连续读取 SSE 流并处理事件。 SseEmitter 不直接与 RestTemplate 兼容,因为它是 Spring WebFlux 的一部分,而 Spring WebFlux 是为响应式编程而设计的。
鉴于此service-B是一个普通的MVC,具有Spring Security集成。我也可以带上 webflux 并且两者都可以工作吗?
问题 2:是的,您可以将 Spring WebFlux 与 Spring MVC 一起引入 Spring Security 集成。 Spring WebFlux 旨在与 Spring MVC 无缝协作,允许您逐步迁移到响应式架构。您可以在同一个应用程序中同时使用两者。
对于这种情况还有其他潜在的解决方案吗?
问题 3:您的场景的潜在解决方案包括:
a.使用 WebClient(响应式方法):您可以使用 Spring WebFlux 中的 WebClient,而不是 RestTemplate,它是非阻塞且响应式的。它更适合使用 SSE 等反应式流。
b.代理服务:创建一个单独的 Spring WebFlux 服务,该服务使用来自服务 A 的 SSE 流并公开 REST API。然后,您的 Spring MVC 服务 B 可以使用 RestTemplate 使用此 REST API。
@RestController
public class SSEController {
@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String>builder()
.id(String.valueOf(sequence))
.event("event")
.data("SSE Event #" + sequence + " at " + LocalTime.now())
.build());
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@RestController
public class SSEController {
@GetMapping("/events")
public SseEmitter streamEvents() {
SseEmitter emitter = new SseEmitter();
// Using a scheduled executor service to simulate periodic events
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(() -> {
try {
// Send event data
emitter.send(SseEmitter.event().data("SSE Event at " + LocalTime.now()));
} catch (IOException e) {
// Log or handle the exception
}
}, 0, 1, TimeUnit.SECONDS);
// Complete the emitter after a fixed delay
executorService.schedule(() -> {
emitter.complete();
executorService.shutdown();
}, 10, TimeUnit.SECONDS);
return emitter;
}
}
除了 spring.main.web-application-type: servlet 之外,你还可以像下面这样编写代码来启动不同的 Web 服务容器。
SpringApplication app = new SpringApplication(Application.class);
app.setWebApplicationType(WebApplicationType.SERVLET);
app.run(args);
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
}