小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
深入/listener接口分析实现长轮询请求
@PostMapping("/listener")
public void listener(HttpSerlvetRequest request,HttpSerlvetRespnse response) throw SerlvetException,IOException{
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED",true);
String probeModify=request.getParameter("Listening-Configs");
if(StringUtils.isBlank(probeModify)){
throw new IllegalArgmentException("invalid probeModify");
}
probeModify=URLDecoder.decode(probeModify,Constants.ENCODE);
Map<String,String> clientMd5Map;
try{
}catch(Throwable e){
throw new IllegalArgumentException("invalid probeModify");
}
inner.doPollingConfig(request,response,clientMd5Map,probeModify.length());
}
doPollingConfig方法是一个长轮询的处理接口,部分核心代码如下:
public String doPollingConfig(HttpServletRequest request,HttpServletResponse response,Map<String,String> clientMd5Map,int probeRequestSize) throws IOException{
//长轮询开始
if(LongPollingService.isSupportLongPolling(request)){
longPollingService.addPollingClient(request,response,clientMd5Map,probeRequestSize);
return HttpServletResponse.SC_OK+“”;
}
//短轮询逻辑
List<String> changedGroups=Md5Util.compareMd5(request,response,clientMd5Map);
//短轮询结果
String oldResult = Md5Util.compareMd5OldResult(changedGroups);
String newResult=Md5Util.compareMd5ResultString(changedGroups);
}
先判断请求是否长轮询,如果是,则调用addLongPollingClient。
- 获取客户端请求的超时时间,减去500ms后赋值给timeout
- 判断isFixedPolling,如果为true,定时任务将会在30s后开始执行,否则,在29.5s后开始执行
- 和服务端的数据进行Md5对比,如果发生修改则直接返回。
- scheduler.execute执行ClientLongPolling线程
public void addLongPollingClient(HttpServletRequest request,HttpServletResponse response,Map<String,String> clientMd5Map,int probeRequestSize){
//获取客户端设置的请求超时时间
String str=request.getHeader(LongPollingService.LONG_POLLING_HEADER);
String noHangUpFlag=request.getHeader(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER);
String appName = request.getHeader(RequestUtil.CLIENT_APPNAME_HEADER);
String tag=request.getHeader("Vipserver-Tag");
int delayTime=SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME,500);
//提前500ms返回响应,避免客户端超时
long timeout = Math.max(10000,Long.parseLong(str)-delayTime);
if(isFixedPolling()){
timeout = Math.max(10000,getFixedPollingInterval());
}else{
long start=Sysetm.currentTimeMills();
List<String> changeGroups=MD5Util.compareMd5(request,response,clientMd5Map);
if(changeGroups.size()>0){
generateResponse(request,response,changedGroups);
return;
}else if(noHangUpFlag != null && noHangUpFlag.equalsIgnore(TRUE_STR)){
return;
}
}
String IP=RequestUtil.getRemoteIp(request);
//由HTTP线程调用,否则离开后容器会马上发送响应
final AsyncContext asyncContext=request.startAsync();
//AsyncContext.setTimeOut()超时时间不准,由自己控制
asyncContext.setTimeOut(0L);
scheduler.excute(
new ClientLongPolling(asyncContext,clientMd5Map,ip,probeRequestSize,timeout,appName,tag));
}
从addLongPollingClient方法得出,它的作用是把客户端的长轮询请求封装成ClientPolling交给scheduler执行。