最近编写一个调用后台接口,使用sse不断发送消息到浏览器页面的业务,发现总是会出现response对象被回收的异常,上各个搜搜索引擎和社区找解决办法都搞不定,发现github上面有人已经实现了,直接把代码扒下来,ok,解决~~~
下面是链接
https://github.com/xubaodian/JAVA-SSE
下面是对我有用的代码
controll层
//基于sse实现 订阅主题,接收订阅内容
@RequestMapping("/subscribe")
public void subscribe(HttpServletRequest req, HttpServletResponse res, @RequestParam("topic") String topic) {
ReqContextUtils.addSubscrib(topic, req, res);
}
//发布订阅内容
@RequestMapping("/publish")
public void publish(@RequestParam("topic") String topic, @RequestParam("content") String content) {
ReqContextUtils.publishMessage(topic, content);
}
工具类
package com.test.util;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
public class ReqContextUtils {
//超时时间
private static int DEFAULT_TIME_OUT = 60*60*1000;
//订阅列表,存储所有主题的订阅请求,每个topic对应一个ArrayList,ArrayList里该topic的所有订阅请求
private static HashMap<String, ArrayList<AsyncContext>> subscribeArray = new LinkedHashMap<>();
//添加订阅消息
public static void addSubscrib(String topic, HttpServletRequest request, HttpServletResponse response) {
if (null == topic || "".equals(topic)) {
return;
}
//设置响应头ContentType
response.setContentType("text/event-stream");
//设置响应编码类型
response.setCharacterEncoding("UTF-8");
//request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
//支持异步响应,异步这个概念很多地方都有,就像处理文件时,不是一直等待文件读完,而是让它去读,cpu做其它事情,读完通知cpu来处理即可。
AsyncContext actx = request.startAsync(request, response);
actx.setTimeout(DEFAULT_TIME_OUT);
actx.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("推送结束");
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println("推送超时");
}
@Override
public void onError(AsyncEvent event) throws IOException {
System.out.println("推送错误");
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
System.out.println("推送开始");
}
});
ArrayList<AsyncContext> actxList = subscribeArray.get(topic);
if (null == actxList) {
actxList = new ArrayList<AsyncContext>();
subscribeArray.put(topic, actxList);
}
actxList.add(actx);
}
//获取订阅列表
public static ArrayList<AsyncContext> getSubscribList(String topic) {
return subscribeArray.get(topic);
}
//推送消息
public static void publishMessage(String topic, String content) {
ArrayList<AsyncContext> actxList = subscribeArray.get(topic);
if (null != actxList) {
for(AsyncContext actx :actxList) {
try {
PrintWriter out = actx.getResponse().getWriter();
out.print(content);
actx.getResponse().flushBuffer();
//out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
应用启动后浏览器地址栏访问下面地址一直接收另一个接口(如http://localhost:${端口}/publish?topic=${下面接口的主题}&content=${自定义内容})发布过来的内容
http://localhost:${端口}/subscribe?topic=${自定义主题}
工具类中可根据自己的需求修改订阅接口超时时间
//超时时间
private static int DEFAULT_TIME_OUT = 60*60*1000;