spring-bootspring-webfluxproject-reactorreactivespring-resttemplate

Server Side events consumer from spring boot


Given that i have this service-A with this endpoint which returns contentType(MediaType.TEXT_EVENT_STREAM)

@Bean
    public RouterFunction<ServerResponse> notificationsRoute(CustomHandler someCustomHandler) {
        return route()
                .GET("/orders", someCustomHandler:handle)
                .build();
    }

I need to consume that from another spring boot service-B, which currently uses restTemplate (its not reactive yet).

  1. Question 1-> can this be done using restTemplate, and somehow SseEmitter (to stream to UI) class to stream the events from service-A.
  2. Question 2 -> Given that this service-B is a normal MVC, with spring security integration. can i also bring webflux and have both working ?
  3. Question 3 -> is there any other potential solutions for that scenario.

Even if i bring spring webflux i was gettting some security filter chain duplicate beans.


Solution

  • Question 1-> can this be done using restTemplate, and somehow SseEmitter (to stream to UI) class to stream the events from service-A.

    Question 1: Yes, it's possible to consume an SSE (Server-Sent Events) stream from service-A using RestTemplate, but it's not the most efficient approach. RestTemplate is synchronous and blocking, while SSE is an asynchronous and non-blocking protocol. However, you can still use RestTemplate with a workaround to handle SSE. You would need to create a separate thread to continuously read from the SSE stream and process the events. SseEmitter is not directly compatible with RestTemplate, as it's a part of Spring WebFlux, which is designed for reactive programming.

    Given that this service-B is a normal MVC, with spring security integration. can i also bring webflux and have both working ?

    Question 2: Yes, you can bring in Spring WebFlux alongside Spring MVC with Spring Security integration. Spring WebFlux is designed to work seamlessly with Spring MVC, allowing you to gradually migrate towards a reactive architecture. You can have both working together in the same application.

    is there any other potential solutions for that scenario.

    Question 3: Potential solutions for your scenario include:

    a. Use WebClient (Reactive Approach): Instead of RestTemplate, you can use WebClient from Spring WebFlux, which is non-blocking and reactive. It's better suited for consuming reactive streams like SSE.

    b.Proxy Service: Create a separate Spring WebFlux service that consumes the SSE stream from service-A and exposes a REST API. Then, your Spring MVC service-B can consume this REST API using RestTemplate.

    NON-BLOCKING - Reactive -SSE

    
    
    @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

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    

    SERLVET BLOCKING-MODEL SSE

    
    @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;
        }
    }
    
    

    Besides spring.main.web-application-type: servlet , you can code like following to start up different web service container .

     SpringApplication app = new SpringApplication(Application.class);
            app.setWebApplicationType(WebApplicationType.SERVLET);
            app.run(args);
    
    public enum WebApplicationType {
        NONE,
        SERVLET,
        REACTIVE;
    }