1

I am trying to build a notification system where-in my back-end, upon receiving a webhook from a third party, will notify the front-end of a particular status, and have decided to go ahead with the Server Sent Events implementation in Spring Boot. Having gone through 10s of implementations of the same, I found that the common way of doing it is:

  1. having your client hit a subscription API on your back-end
  2. Create an object of SseEmitter class and store it in an in-memory HashMap or ArrayList
  3. Fetch the events stored in your memory, to send notification when the desired event occurs

However, my issue is that I can't store these SseEmitter Objects in-memory, in a production grade code. I have tried serializing the object, but upon deserialization a new object of SseEmitter is created and the connection to client is lost. How do I do this gracefully?

@RestController
public class EventController {

  Map<String, SseEmitter> sseEmitters = new ConcurrentHashMap<>();

  @Autowired
  SerializerUtils serializerUtils;

  String sseEmitterSerialized = null;

  @CrossOrigin
  @GetMapping(value="/subscribe", consumes = MediaType.ALL_VALUE)
  public SseEmitter subscribe(@RequestParam(value = "tokenId") String tokenId) throws IOException {

    DefaultSseEmitter sseEmitter = new DefaultSseEmitter(Long.MAX_VALUE);
    sseEmitter.send(SseEmitter.event().name("latestEvent").data("INITIALIZED"));
    sseEmitter.onCompletion(() -> sseEmitters.remove(sseEmitter));

    sseEmitters.put(tokenId, sseEmitter);
    return sseEmitter;
  }

  @CrossOrigin
  @PostMapping(value="/dispatchEvent", produces = MediaType.ALL_VALUE)
  public void dispatchToClients(@RequestParam (value = "freshEvent") String freshEvent, @RequestParam(value = "tokenId") String tokenId)
      throws IOException, ClassNotFoundException {

    sseEmitters.get(tokenId).send(SseEmitter.event().name("latestEvent").data(freshEvent));
  }

I have tried serializing and conversion to JSON. None of that stuff works.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197

1 Answers1

1

However my issue is that I can't be storing these SseEmitter Objects in-memory, in a production grade code

There is nothing inherently bad about storing these SSE sessions in a hashmap mapping id key to the emitter as value. (Make sure to handle disconnection / hashmap clean up).

I don't think it's possible to even persist these sessions for your intended purpose. It's not possible with SSE for the server to initiate the connection with the client.

Darren Cook
  • 27,837
  • 13
  • 117
  • 217
Viktor Baert
  • 686
  • 8
  • 22
  • The challenge is that the service will be running in multiple kubernetes pods and different pods might not be sharing context of the SseEmitter Object. – Akshay Thakur Feb 01 '23 at 09:52
  • Most developers solve that problem by introducing a micro-service that has the single responsibility of emitting events from a queue or topic to the client. This queue or topic consumes events from different contexts. (An example of such a queue/topic is ActiveMQ, Kafka, google pub sub,...) This way you can even scale up multiple pods of the SSE instance if you ever reach the limits of a single instance. – Viktor Baert Feb 01 '23 at 21:54