Save SSE-Emitter object into MongoDB/Redis, Fetch it from Database and send event over it

故事扮演 提交于 2020-08-26 07:27:37

问题


I am trying to make my REST API stateless. For that what I need is, to save client's SSE-Emitter object to mongo or redis so that it will be centrally accessible by other instances.

Current behavior:

I am able to save the SSE-emitter object to mongoDb but I think that object is being modified somehow because of which, after fetching it from mongoDb I am not able to send an event to client. If I save same emitter object in Map/List locally, the events are being sent out successfully.

Expected behaviour:

I should be able to fetch the emitter object from mongoDb and send EventData to client over it.

Source Code:

Controller where client subcribes:

@GetMapping("/memory/{userName}")
public SseEmitter handle(@PathVariable("userName") String userName) {
 SseEmitter emitter = new SseEmitter();
 try{
         MongoSession session = new MongoSession();
         session.setId(userName);
         session.setAttribute("emitter", emitter);
         mongoSessionRepo.save(session);
 }catch(Exception e){
         e.printStackTrace();
 }
 this.emitters.add(emitter);// adding it to list as well just for testing.
 emitter.onCompletion(() -> this.emitters.remove(emitter));
 emitter.onTimeout(() -> this.emitters.remove(emitter));

 return emitter;
}

MongoSession class which represents document in mongoDb:

package ch.rasc.sse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.session.ExpiringSession;

@Document(collection = "springMongoSession")
public class MongoSession implements ExpiringSession{

  public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS = 1800;

  /**
  * MongoDB Object ID
  */
  @Indexed(unique = true)
  @Id
  private String id;

  public void setId(String id) {
    this.id = id;
  }
  /**
  * Session ID
  */
  public static final String KEY_SESSION_ID = "_id";

  /**
   * Serialized session attributes
   */
  private byte[] serializedAttributes;

  /**
  * Session attributes (not saved to MongoDB)
   */
  private Map<String,Object> attributes;

/**
 * Creation time (epoch in ms)
 */
  private long creationTime;

/**
 * Last accessed time (epoch in ms)
 */
  private long lastAccessedTime;

/**
 * Max inactive interval (sec)
 */
  private int maxInactiveIntervalInSeconds;

/**
 * Expire time (epoch in ms)
 */
  @Indexed
  private long expireTime;
  public static final String KEY_EXPIRE_TIME = "expireTime";

/**
 * Constructor
 */
 public MongoSession() {
    attributes = new HashMap<>();
    creationTime = System.currentTimeMillis();
    lastAccessedTime = creationTime;
    maxInactiveIntervalInSeconds = DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
    updateExpireTime();
 }

/**
 * Constructor
 */
 public MongoSession(String sessionId) {
    this.id = sessionId;
    //this.sessionId = sessionId;
    attributes = new HashMap<>();
    creationTime = System.currentTimeMillis();
    lastAccessedTime = creationTime;
    maxInactiveIntervalInSeconds = DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
    updateExpireTime();
 }


public String getId() {
    return id;
}

public void setLastAccessedTime(long lastAccessedTime) {
    this.lastAccessedTime = lastAccessedTime;
    updateExpireTime();
}


public long getCreationTime() {
    return creationTime;
}

public long getLastAccessedTime() {
    return lastAccessedTime;
}

public void setMaxInactiveIntervalInSeconds(int interval) {
    maxInactiveIntervalInSeconds = interval;
    updateExpireTime();
}

public int getMaxInactiveIntervalInSeconds() {
    return maxInactiveIntervalInSeconds;
}

protected long getExpireTime() {
    return expireTime;
}

private void updateExpireTime() {
    expireTime = lastAccessedTime + maxInactiveIntervalInSeconds * 1000;
}

public boolean isExpired() {
    long now = System.currentTimeMillis();
    return expireTime <= now;
}

public <T> T getAttribute(String attributeName) {
    return (T)attributes.get(attributeName);
}

public Set<String> getAttributeNames() {
    return attributes.keySet();
}

public void setAttribute(String attributeName, Object attributeValue) {

   attributes.put(attributeName, attributeValue);
}

public void removeAttribute(String attributeName) {
    attributes.remove(attributeName);
}
/**
 * Serialize session attributes
 */
 public void serializeAttributes() {
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(bos)) {
        oos.writeObject(attributes);
        oos.flush();
        serializedAttributes = bos.toByteArray();
    } catch (IOException e) {
        //e.printStackTrace();
        serializedAttributes = new byte[0];
    }
}
public void serializeAttributesThis(Object attributeValue) {
   try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos)) {
       oos.writeObject(attributeValue);
       oos.flush();
       serializedAttributes = bos.toByteArray();
   } catch (IOException e) {
       //e.printStackTrace();
       serializedAttributes = new byte[0];
   }
 }
   /**
   * Deserialize session attributes
   */
  public void deserializeAttributes() {
    try (ByteArrayInputStream bis = new ByteArrayInputStream(serializedAttributes);
         ObjectInputStream ois = new ObjectInputStream(bis))  {
        attributes = (Map<String,Object>)ois.readObject();
    } catch (IOException | ClassNotFoundException e) {
        //e.printStackTrace();
        attributes = new HashMap<>();
    }
  }
 }

On below request I want to send eventData back to client:

    @RequestMapping("/qmevents/{sessionId}")
    public void readQmEvents(@PathVariable("sessionId") String userName)
    {
       try{
        System.out.println("Emitter Object: 
         "+mongoSessionRepo._getSession(userName));
        System.out.println("Emitter Object: 
  "+mongoSessionRepo._getSession(userName).getAttributeNames());
        System.out.println("Emitter Object: 
  "+mongoSessionRepo._getSession(userName)
    .getAttribute("emitter").toString());
    sessionRepo.getSessionAttributes(userName, "emitter");
    SseEmitter emitter =mongoSessionRepo._getSession(userName).
            getAttribute("emitter");
    MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
    MemoryUsage heap = memBean.getHeapMemoryUsage();
    MemoryUsage nonHeap = memBean.getNonHeapMemoryUsage();
    MemoryInfo mi = new MemoryInfo(heap.getUsed(), nonHeap.getUsed());
        mi.setForUserName("Event raised by QM");
        System.out.println("Emitter from map: 
     "+SSEControllerPerUser.emitters.get(0));
        SSEControllerPerUser.emitters.get(0).send(mi);
        //emitter.send(mi);
    }catch(Exception e){
        e.printStackTrace();
    }

} 

回答1:


Subclass the Spring SseEmitter(refer below) and use that component, I have used this solution for the similar scenario that you have described(server crash).

public class SerializableSSE extends SseEmitter implements Serializable{

    public SerializableSSE() {
    }

    public SerializableSSE(Long timeout) {
        super(timeout);
    }
}

Hope it helps!



来源:https://stackoverflow.com/questions/45036309/save-sse-emitter-object-into-mongodb-redis-fetch-it-from-database-and-send-even

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!