Java后端 服务器单向实时推送消息

前言:

如果有人购买商品,后台实时推送一个消息到用户。如果用websocket来做还需要写websocket服务器和配置。用轮询,会影响服务器性能之外,还达不到实时效果。有没有别的选择呢?当然有!

一、简介:Server-Sent 事件指的是网页自动获取来自服务器的更新。

二、局限性:

1.WebSocket 比 SSE 更强大,Websocket 在客户端和服务器之间建立了双向的实时通信。而 SSE 只支持从服务器到客户端的单向实时通信。

2.WebSocket 在浏览器方面支持更全面,IE / Edge 几乎根本不支持 SSE

如果业务上不考虑浏览器的兼容性仅需要服务器推送消息,实时更新的功能,建议优先选择SSE。

三、优势性:

1.SSE 实现内置断线重连和消息追踪的功能,WebSocket 也能实现,但是不在协议设计范围内,需要手动处理。

2.SSE 实现简单,完全复用现有的 HTTP 协议,而 WebSocket 是相对独立于 HTTP 的一套标准,跨平台实现较为复杂。

四、适合功能:Facebook/Twitter 更新、股价更新、新的博文、赛事结果等

五、服务器后端代码:

服务器端的响应的内容类型是“text/event-stream” 必须写,编码可写可不写,最好是写上。

@RestController

public class MySysMessageEvent {

    private final static Logger logger = Logger.getLogger(MySysMessageEvent.class);

    public static final Map<String, SseEmitter> SSE_HOLDER = new ConcurrentHashMap<>();

    @GetMapping(value = "/messageSSE/{userid}", produces = "text/event-stream;charset=UTF-8")

    public SseEmitter messageSSE(HttpSession session, @PathVariable String userid) {

        long millis = TimeUnit.SECONDS.toMillis(30*60);

        //设置存活时间

        SseEmitter sseEmitter = new SseEmitter(millis);

        String sessionid = session.getId();

        //userid与sessionid进行绑定

        String newId = sessionid + "|" + userid;

        SSE_HOLDER.put(newId, sseEmitter);

        userSsePush(newId, "连接成功!");

        logger.info("SSE连接成功!");

        return sseEmitter;

    }

    /**

    * 通过sessionId获取对应的客户端进行推送消息

    */

    public static void userSsePush(String sessionid, String content) {

        SseEmitter emitter = MySysMessageEvent.SSE_HOLDER.get(sessionid);

        if (Objects.nonNull(emitter)) {

            try {

                emitter.send(content);

            } catch (IOException | IllegalStateException e) {

                e.printStackTrace();

                logger.warn("sse send error", e);

                MySysMessageEvent.SSE_HOLDER.remove(sessionid);

            }

        }

    }

}

注释: SseEmitter是springframework 中 SpringMVC提供的,它是基于Http协议的,相比WebSocket,它更轻量,但是它只能从服务端向客户端单向发送信息。

六、如果需要在业务上直接将此代码复用即可:


Set<String> key = MySysMessageEvent.SSE_HOLDER.keySet();

                    Iterator<String> iterator = key.iterator();

                    while (iterator.hasNext()) {

                        String e = iterator.next();

                        if (Integer.valueOf(e.split("[|]")[1]).equals(approve.getUserid())) {//----此语句根据业务修改

                          MySysMessageEvent.userSsePush(e, "您有新的消息,请尽快查收!");

                      }

                  }


七、注意:有nginx代理的才需要此步骤:

需要查看是否有nginx拦截,如果有拦截,需要在nginx配置上配置


location /messageSSE {

proxy_http_version 1.1;

# proxy_request_buffering off;

# proxy_buffering off;

# proxy_cache off;

# chunked_transfer_encoding off;

proxy_set_header Connection '';

proxy_set_header Host $host;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_pass http://localhost:28080/messageSSE;

}


友情提示:由于SSE的局限性,导致无法广泛使用,相应处理问题的方法会缺乏,在使用该功能时需要去搞清楚,将来业务是否会有所改动,能否及时去处理,如果有困难,请斟酌考量去使用!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容