前端时间在研究Storm,并基于Storm实现了一地区热力图统计项目,如下图所示;基于高德地图的热力图实时统计某地区的人口密集程度并及时推送给前台用户展示;(注:此处使用场景并不合适,只是用于个人技术预研)
项目地址:https://github.com/zlAdmin/didactic-enigma.git,需要执行resource目录下的sql文件;Storm相关文件不在此目录下;
这里是通过Ajax的长连接和Servlet3特性实现,实现思路是:前台请求后台,后台通过子线程方法取出数据,在返回前台,DeferredResult实现的是在未拿到结果之前阻塞子线程,拿到结果后返回前台,前台在立马
向后台发送新的请求,当后台数据有更新后又会推送到前台;
后台代码实现:
一:通过callable实现:
@RequestMapping(value="/mapstatAsync",method = RequestMethod.POST) public Callable<List<BasePosition>> mapstatAsync() { log.info("主线程开始..."); Callable<List<BasePosition>> callable = new Callable<List<BasePosition>>() { @Override public List<BasePosition> call() throws Exception { log.info("从线程开始..."); Thread.sleep(5000); val list = resultService.query(); log.info("从线程结束..."); return list; } }; log.info("主线程结束..."); return callable; }
二:通过
DeferredResult实现
/**
* @Description 基于Ajax长连接,实现主动通知web应用
* @return
* @throws
* @Author zhanglei
* @Date 13:48 2018/12/28
* @Param
**/
@RequestMapping(value="/getPosition",method = RequestMethod.GET)
public DeferredResult<JSONArray> getPosition() throws ExecutionException, InterruptedException {
DeferredResult<JSONArray> deferredResult = new DeferredResult<>();
log.info("主线程执行...");
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("从线程开始执行...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<BasePosition> positionList = resultService.query();
String json= JSONObject.toJSONString(positionList);
JSONArray subMsgs = JSONObject.parseArray(json);
deferredResult.setResult(subMsgs);
log.info("从线程执行完毕...");
}
});
log.info("主线程执行完毕....");
return deferredResult;
}
后台结果日志输出:
DeferredResult处理流程
DeferredResult的处理过程与Callback类似,不一样的地方在于它的结果不是DeferredResult直接返回的,而是由其它线程通过同步的方式设置到该对象中。它的执行过程如下所示:
1.客户端请求服务
2.SpringMVC调用Controller,Controller返回一个DeferredResult对象
3.SpringMVC调用ruquest.startAsync
4.DispatcherServlet以及Filters等从应用服务器线程中结束,但Response仍旧是打开状态,也就是说暂时还不返回给客户端
5.某些其它线程将结果设置到DeferredResult中,SpringMVC将请求发送给应用服务器继续处理
6.DispatcherServlet再次被调用并且继续处理DeferredResult中的结果,最终将其返回给客户端
前台代码实现:
$(function(){ // do something var points; var map = new AMap.Map("container", { resizeEnable: true, center: [116.418261, 39.921984], zoom: 11 }); // map.setFeatures('road','building'); // 热力图 var heatmap; var loopData = function() { $.ajax({ url:'/zl/show/getPosition', type:'get', dataType:'json', success:function(data){ console.log(data); points = data; heatmap.setDataSet({data:points,max:100}); loopData(); } }); }; loopData(); map.plugin(["AMap.Heatmap"],function() { //加载热力图插件 heatmap = new AMap.Heatmap(map,{ radius:50, apacity:[0,0.8] }); //在地图对象叠加热力图 heatmap.setDataSet({data:points,max:100}); //设置热力图数据集 //具体参数见接口文档 }); //测量工具 map.plugin(["AMap.MouseTool"],function(){ var mousetool = new AMap.MouseTool(map); mousetool.marker(); //使用鼠标工具,在地图上画标记点 }); //工具条 AMap.plugin(['AMap.ToolBar']),function(){ map.addControl(new AMap.ToolBar()); } });