1.前言
采用长轮询的处理可以减少不必要的通信请求次数。
2.基本流程
3.实现demo
工具类
public class LongPollUtil {
private static Logger logger = LoggerFactory.getLogger(LongPollUtil.class);
// 配置变量
public static final Map<String, String> propMap = new ConcurrentHashMap<String, String>();
//异步链接
public static final Multimap<String, LongPollUtil> longPollMap = Multimaps
.synchronizedSetMultimap(HashMultimap.create());
// 超时延时处理
private static final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("longPol-timeout-%d").setDaemon(true).build();
private ScheduledExecutorService timeoutChecker = new ScheduledThreadPoolExecutor(1, threadFactory);
private AsyncContext asyncContext;
private String key;
private String value;
private long timeOut;// 超时时间
private boolean isOver;// 已经处理了
// 初始测试k-v
static {
propMap.put("test1", "1");
propMap.put("test2", "2");
}
/**
* 初始化参数
*
* @param asyncContext
* @param timeOut
* @throws IOException
*/
public LongPollUtil(AsyncContext asyncContext, long timeOut) throws IOException {
this.asyncContext = asyncContext;
this.key = asyncContext.getRequest().getParameter("key");
this.value = asyncContext.getRequest().getParameter("value");
this.timeOut = timeOut;
this.isOver=false;
if (isWait()) {
longPollMap.put(key, this);
timeOutBack();
} else {
LongPollUtil.print(asyncContext,HttpServletResponse.SC_OK,"[新数据]key:"+key+",value:"+propMap.get(key));
}
}
/**
* 初始判断是否变更
* @return
* @throws IOException
*/
public boolean isWait() {
if (propMap.containsKey(key) && value.equals(propMap.get(key))) {
return true;
} else {
return false;
}
}
/**
* 超时等待
*/
public void timeOutBack() {
logger.info("超时启动...");
// 启动定时器
timeoutChecker.schedule(() -> {
// 触发定时后,判断任务是否被执行
if (!this.isOver) {
// 清除缓存中的任务
logger.info("超时时间到了...");
longPollMap.remove(key, this);
//HttpServletResponse.SC_NOT_MODIFIED;//304 没变更 200为了显示数据
LongPollUtil.print(asyncContext,HttpServletResponse.SC_OK,"[数据没改变]key:"+key+",value:"+propMap.get(key));
}
}, Integer.parseInt(String.valueOf(timeOut)), TimeUnit.SECONDS);
}
/**
* 打印数据方法
* @param asyncContext
* @param status
* @param str
*/
public static void print(AsyncContext asyncContext,int status,String str) {
try {
HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();
response.setStatus(status);
response.setContentType("text/html;charset=UTF-8");
response.getWriter().print(str);
response.flushBuffer();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}finally {
asyncContext.complete();
}
}
/**
* 更新配置
* @param key
* @param value
*/
public static void updateProperties(String key, String value) {
if (StringUtils.hasLength(key) && StringUtils.hasLength(value)) {
String valueOld = propMap.get(key);
if (!value.equals(valueOld)) {
propMap.put(key, value);
changBack(key);
}
}
}
/**
* 触发返回更新数据
* @param key
*/
public static void changBack(String key) {
logger.info("变更触发...");
if (StringUtils.hasLength(key) && null != longPollMap && longPollMap.size() > 0) {
Collection<LongPollUtil> longPollTask = longPollMap.removeAll(key);
if (null != longPollTask) {
for (LongPollUtil lpu : longPollTask) {
lpu.isOver = true;
lpu.timeoutChecker.shutdown();
LongPollUtil.print(lpu.asyncContext,HttpServletResponse.SC_OK,"key:"+key+",value:"+propMap.get(key));
}
}
}
}
}
http测试demo
/**
* @author 肥皂
* @date 2022年12月15日
*/
@Controller
@RequestMapping("/longPoll")
public class TestLongPoll {
private Logger logger=LoggerFactory.getLogger(TestLongPoll.class);
@RequestMapping("/check")
public void longPoll(HttpServletRequest request,HttpServletResponse response) throws IOException {
String key=request.getParameter("key");
String value=request.getParameter("value");
if(!StringUtils.hasLength(key) && !StringUtils.hasLength(value)) {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().print("[存在数据为空]");
response.flushBuffer();
return;
}
AsyncContext asyncContext=request.startAsync(request, response);
try {
//时间不准自己控制
asyncContext.setTimeout(0L);
//超时测试时间为10S
new LongPollUtil(asyncContext, 10);
}catch (Exception e) {
logger.error(e.getMessage(), e);
asyncContext.complete();
}
}
@RequestMapping("/update")
@ResponseBody
public String update(String key,String value) {
LongPollUtil.updateProperties(key, value);
return "[修改成功]key:"+key+",value:"+value;
}
}