因为业务需求,需要用到服务器反向推送,采用SEE推送

因为websoket协议问题,采用了see推送,架构使用是SSM+jdk7,spingboot已经集成了,在百度上有,不做代码解释,受限于jdk,spring最少要4.2以上才支持。

前端封装一个SseEmitterServer

package com.work.controller.app;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class SseEmitterServer {
    private static final Logger logger = LoggerFactory.getLogger(SseEmitterServer.class);
    /**
     * 当前连接数
     */
    private static AtomicInteger count=new AtomicInteger(0);

    /**
     * 使用map对象,便于根据userId来获取对应的SseEmitter,或者放redis里面
     */
    private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
    /**
     * 创建用户连接并返回 SseEmitter
     *
     * @param userId 用户ID
     * @return SseEmitter
     */
    public static SseEmitter connect(final String userId) {
        //在连接的时候,如果存在就移除原来连接,生产新的连接
        if (sseEmitterMap.containsKey(userId)){
            removeUser(userId);
        }
        // 设置超时时间,0表示不过期。默认30秒,超过时间未完成会抛出异常:AsyncRequestTimeoutException
        SseEmitter sseEmitter = new SseEmitter(0L);
        //超时回调触发
        sseEmitter.onTimeout(new Runnable() {
            @Override
            public void run() {
                sseEmitterMap.remove(userId);
            }
        });
        // 注册回调
        sseEmitter.onCompletion(new Runnable() {
            @Override
            public void run() {

            }
        });
        sseEmitterMap.put(userId, sseEmitter);
        // 数量+1
        count.getAndIncrement();
        return sseEmitter;
    }

    /**
     * 给指定用户发送信息
     */
    public static void sendMessage(String userId,String message) {
        if (sseEmitterMap.containsKey(userId)) {
            try {
                sseEmitterMap.get(userId).send(message);
            } catch (IOException e) {
                logger.error("用户[{}]推送异常:{}", userId, e.getMessage());
                removeUser(userId);
            }
        }
    }
    /**
     * 群发所有人
     */
   /* public static void batchSendMessage(String message){
        for (String key : sseEmitterMap.keySet()) {
            String value = sseEmitterMap.get(key).toString();
            try {
                sseEmitterMap.get(key).send(message);
            } catch (IOException e) {
                logger.error("用户[{}]推送异常:{}", key, e.getMessage());
                removeUser(key);
            }
        }
    }​*/

    /**
     * 移除用户连接
     * @param userId
     */
    public static void removeUser(String userId) {
        sseEmitterMap.remove(userId);
        // 数量-1
        count.getAndDecrement();
    }

    /**
     * 获取当前连接信息
     */
    public static List<String> getIds(){
        return new ArrayList<>(sseEmitterMap.keySet());
    }


    /**
     * 获取当前连接数量
     */
    public static int getUserCount() {
        return count.intValue();
    }

}

在controller层创建长链接

@RequestMapping(value = "/events/{id}", produces = "text/event-stream;charset=UTF-8")
@ResponseBody
public SseEmitter testSseEmitter(@PathVariable final String id) throws Exception{
    // 默认30秒超时,设置为0L则永不超时
    SseEmitter sseEmitter = SseEmitterServer.connect(id);
    return sseEmitter;
}

js

<script>
    if (!!window.EventSource) {
        var source = new EventSource("http://" + window.location.host + "${zspath}/exterInt/appr/events/123456"); //为http://localhost:8080/testSpringMVC/push
        s = '';
        source.addEventListener('message', function (e) {
            console.log("see连接打开."+e.data);
        });

        source.addEventListener('open', function (e) {
            console.log("see连接打开.");
        }, false);

        source.addEventListener('error', function (e) {
            if (e.readyState == EventSource.CLOSED) {
                console.log("see连接关闭");
            } else {
                console.log(e.readyState);
            }
        }, false);
    } else {
        console.log("没有sse");
    }
</script>

需要特别注意的是,部分架构,搭建的时候,并没有设置异构请求返回,需要在web.xml设置<filter>和<servlet>都加上这两个

dispatcher>ASYNC</dispatcher>和async-supported>true</async-supported>配置,才能返回正确的对象

<filter>
  <filter-name>encodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <async-supported>true</async-supported>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>encodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>ASYNC</dispatcher>
</filter-mapping>


<filter>
  <filter-name>DruidWebStatFilter</filter-name>
  <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
  <async-supported>true</async-supported>
  <init-param>
    <param-name>exclusions</param-name>
    <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>DruidWebStatFilter</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>ASYNC</dispatcher>
</filter-mapping>
<servlet>
  <servlet-name>DruidStatView</servlet-name>
  <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
  <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
  <servlet-name>DruidStatView</servlet-name>
  <url-pattern>/druid/*</url-pattern>
</servlet-mapping>
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值