前端時間在研究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()); } });
