SseEmitter后端主动推送消息给前端

公司有个需求,前端界面需要展示实时展示下单消息。

解决方案:

1.前端间隔一定时常,轮询向后端发送请求,查询下单数据

2.利用长链接,后端向前端主动推送下单消息

果断选择第二种。从以往的经验,首先想到websocket,但是websocket属于双向通道,且服务端比较琐碎,就在网上找了下其他类似技术,看到了SseMmitter,查看了向网上相关文章,及用例。正好符合我们的需求,话不多说,上代码

一、新增see对象

public class SeeResult {
    /**
     * 消息推送根据clientId。
     */
    public String clientId;
    public long timestamp;
    public SseEmitter sseEmitter;

    public SeeResult(String clientId, long timestamp, SseEmitter sseEmitter) {
        this.clientId = clientId;
        this.timestamp = timestamp;
        this.sseEmitter = sseEmitter;
    }

}

二、新增连接存储map

public class SseMap {
    public static final Map sseEmitterMap = new ConcurrentHashMap<>();
}

三、新增controller

前端连接我采取的方案时 业务标识+时间戳;例如 1-1652685165648

@Slf4j
@CrossOrigin
@RestController
@RequestMapping("/api/message/see")
public class SseEmitterController {

    /**
     * 返回SseEmitter对象
     *
     * @param clientId
     * @return
     */
    @GetMapping("/start")
    public SseEmitter testSseEmitter(String clientId) throws IOException{
        if (StringUtils.isBlank(clientId)){
            throw new DeliveryException("参数不能为空");
        }
        // 默认30秒超时,设置为0L则永不超时
        SseEmitter sseEmitter = new SseEmitter(0L);
        SseMap.sseEmitterMap.put(clientId, new SeeResult(clientId, System.currentTimeMillis(), sseEmitter));
        return sseEmitter;
    }

    /**
     * 向SseEmitter对象发送数据
     *
     * @param clientId
     * @return
     */
    @GetMapping("/send")
    public String setSseEmitter(String clientId) {
        try {
            SeeResult result =  SseMap.sseEmitterMap.get(clientId);
            if (result != null && result.sseEmitter != null) {
                long timestamp = System.currentTimeMillis();
                String re = "{\"success\":true,\"message\":\"操作成功!\",\"code\":200,\"result\":{\"id\":1018},\"timestamp\":1629254216358}";
                result.sseEmitter.send(SseEmitter.event().name("msg").data(re));
            }
        } catch (IOException e) {
            log.error("IOException!", e);
            return "error";
        }

        return "Succeed!";
    }

    /**
     * 将SseEmitter对象设置成完成
     *
     * @param clientId
     * @return
     */
    @GetMapping("/end")
    public String completeSseEmitter(String clientId) {
        SeeResult result =  SseMap.sseEmitterMap.get(clientId);
        if (result != null) {
            SseMap.sseEmitterMap.remove(clientId);
            result.sseEmitter.complete();
        }

        return "Succeed!";
    }

四、前端(Vue)

因连接需要经过网关,token验证。
EventSource EventSource - Web API 接口参考 | MDN相关文档没找到有设置请求头参数的方式
github 上有组件event-source-polyfill,附上地址

GitHub - Yaffle/EventSource: a polyfill for http://www.w3.org/TR/eventsource/

 五、部署至生产环境

nginx配置ssl 443反向代理,前端一直连接不上,但是http协议是正常的。

经过测试,需要nginx 中增加 proxy_buffering off参数

网上查看proxy_buffering 参数详解以及介绍,

proxy_buffering
语法: proxy_buffering on|off
默认值: proxy_buffering on
上下文: http, server, location

这个参数用来控制是否打开后端响应内容的缓冲区,如果这个设置为off,那么proxy_buffers和proxy_busy_buffers_size这两个指令将会失效。 但是无论proxy_buffering是否开启,对proxy_buffer_size都是生效的。

proxy_buffering开启的情况下,nignx会把后端返回的内容先放到缓冲区当中,然后再返回给客户端(边收边传,不是全部接收完再传给客户端)。 临时文件由proxy_max_temp_file_size和proxy_temp_file_write_size这两个指令决定的。如果响应内容无法放在内存里边,那么部分内容会被写到磁盘上。

如果proxy_buffering关闭,那么nginx会立即把从后端收到的响应内容传送给客户端,每次取的大小为proxy_buffer_size的大小,这样效率肯定会比较低。
nginx不尝试计算被代理服务器整个响应内容的大小,nginx能从服务器接受的最大数据,是由指令proxy_buffer_size指定的.

注:1、 proxy_buffering启用时,要提防使用的代理缓冲区太大。这可能会吃掉你的内存,限制代理能够支持的最大并发连接数。

2、对于基于长轮询(long-polling)的Comet 应用来说,关闭 proxy_buffering 是重要的,不然异步响应将被缓存导致Comet无法工作

但是不知道和https和http有什么关系?
 

你可能感兴趣的:(工作那些坑,前端,vue.js,javascript,spring,java)