Spring配置SseEmitter(后端向前端推送消息)

Controller

@Api(tags = "前后端长连接")
@RestController
@RequestMapping("/business/sseEmitter")
public class SseEmitterController extends BaseController {

    /**
     * 用于创建连接(将用户注册到server中)
     */
    @ApiOperation(value = "创建连接")
    @GetMapping("/connect/{userId}")
    public SseEmitter connect(@PathVariable String userId) {
        return SseEmitterServer.connect(userId);
    }

    @ApiOperation(value = "给所有的用户发送信息")
    @GetMapping("/push/{message}")
    public WebResponse push(@PathVariable(name = "message") String message) {
        SseEmitterServer.batchSendMessage(message);
        return WebResponse.ok();
    }
}

Util

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 sseEmitterMap = new ConcurrentHashMap<>();

    /**
     * 创建用户连接并返回 SseEmitter
     * @param employeeCode 用户ID
     * @return SseEmitter
     */
    public static SseEmitter connect(String employeeCode) {
        // 设置超时时间,0表示不过期。默认30秒,超过时间未完成会抛出异常:AsyncRequestTimeoutException
        SseEmitter sseEmitter = new SseEmitter(0L);
        // 注册回调
        sseEmitter.onCompletion(completionCallBack(employeeCode));
//        sseEmitter.completeWithError(errorCallBack(employeeCode));
        sseEmitter.onTimeout(timeoutCallBack(employeeCode));
        sseEmitterMap.put(employeeCode, sseEmitter);
        // 数量+1
        count.getAndIncrement();
        logger.info("创建新的sse连接,当前用户:{}", employeeCode);
        return sseEmitter;
    }

    /**
     * 给指定用户发送信息
     * @param employeeCode
     * @param jsonMsg
     */
    public static void sendMessage(String employeeCode, String jsonMsg) {
        try {
            SseEmitter emitter = sseEmitterMap.get(employeeCode);
            if (emitter == null) {
                logger.warn("sse用户[{}]不在注册表,消息推送失败", employeeCode);
                return;
            }
            emitter.send(jsonMsg, MediaType.APPLICATION_JSON);
        } catch (IOException e) {
            logger.error("sse用户[{}]推送异常:{}", employeeCode, e.getMessage());
            removeUser(employeeCode);
        }
    }

    /**
     * 群发消息
     * @param jsonMsg
     * @param employeeCodes
     */
    public static void batchSendMessage(String jsonMsg, List employeeCodes) {
        employeeCodes.forEach(employeeCode -> sendMessage(employeeCode, jsonMsg));
    }

    /**
     * 群发所有人
     * @param jsonMsg
     */
    public static void batchSendMessage(String jsonMsg) {
        sseEmitterMap.forEach((k, v) -> {
            try {
                v.send(jsonMsg, MediaType.APPLICATION_JSON);
            } catch (IOException e) {
                logger.error("用户[{}]推送异常:{}", k, e.getMessage());
                removeUser(k);
            }
        });
    }

    /**
     * 移除用户连接
     */
    public static void removeUser(String employeeCode) {
        SseEmitter emitter = sseEmitterMap.get(employeeCode);
        if(emitter != null){
            emitter.complete();
        }
        sseEmitterMap.remove(employeeCode);
        // 数量-1
        count.getAndDecrement();
        logger.info("移除sse用户:{}", employeeCode);
    }

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

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

    private static Runnable completionCallBack(String employeeCode) {
        return () -> {
            logger.info("结束sse用户连接:{}", employeeCode);
            removeUser(employeeCode);
        };
    }

    private static Runnable timeoutCallBack(String employeeCode) {
        return () -> {
            logger.info("连接sse用户超时:{}", employeeCode);
            removeUser(employeeCode);
        };
    }

    private static Throwable errorCallBack(String employeeCode) {
        logger.info("sse用户连接异常:{}", employeeCode);
        removeUser(employeeCode);
        return new Throwable();
    }


}

前端




    
    SseEmitter



上文引用:Java后端主动向前端推送消息_牧子与羊的博客-CSDN博客_java后端主动发送数据给前端

前端引入依赖

1,安装依赖 :npm install event-source-polyfill
2,引入: import { EventSourcePolyfill } from "event-source-polyfill";

完成上面之后在web.XML配置,如下:

在web.XML中servlet配置 true

注:只需要在springmvc 控制器的中配置


	
		springMvc
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			classpath*:spring-mvc.xml
		
		1
		true
	

在servlet配置完之后继续在XML里面的加上true

注:在XML里所有的的标签中加上true


		knife4jProductionFilter
		ProductionSecurityFilter
		true
		
			production
			false
		

若出现部署在服务器上之后出现链接不上的问题则在nginx中配置

proxy_buffering off;
location /apis/ {
            proxy_pass     http://127.0.0.1:28080/huaian_warehouse_war/;
            proxy_buffering off;//关闭缓冲
}    

你可能感兴趣的:(java)