項目需求:需要實時的讀取日志文件里的數據,並且使用Echart實時更新折線圖。
使用ajax實現客戶端與服務器端的數據傳輸。
目的:我想通過ajax與服務器建立一個長連接,服務器會不斷的傳輸數據給前台,由於日志不斷的更新,我想把新的數據不斷的傳給前台。
設計:本來想着使用服務器使用一個死循環去讀取日志信息,一個線程去提交數據。
參考:https://www.cnblogs.com/hoojo/p/longPolling_comet_jquery_iframe_ajax.html
發現與想象的不同,這個長連接並不是不間斷的連接,而是連接成功一次,傳輸完數據斷開,再重新建立新連接,會重新去調用函數。
由於一開始的理解錯誤,因為用死循環去讀取日志,這樣會導致每次都會調用一次函數,這樣會開啟多個死循環,打開任務管理器,發現CPU和內存的不斷飆升,所以每次連接都要跳出死循環。
這樣會產生新問題,每次都要重新讀日志。
設計,保存上一次讀取的位置,避免重頭開始。
前台代碼:
//異步請求獲取數據,遞歸調用自己,實現長連接 function longPolling() { $.ajax({ type:'POST', url:'get', dataType:'json', data:postData, timeout: 20000, success:function(result){ if(result){ //保存文件指針,再傳給后台 postData.lastTimeFileSize = result.pointer //添加數據 for(var i=0;i<result.axis.length;i++){ datax.push(result.axis[i]); datay.push(result.series[0].data[i]); } //畫圖 myChart.hideLoading(); myChart.setOption({ xAxis: { data: datax }, series: [{ // 根據名字對應到相應的系列 name: 'alpha.water', data: datay }] }); longPolling(); } }, error:function(XMLHttpRequest, textStatus, errorThrown){ console.error("加載數據失敗"); longPolling(); }, }); };
后台代碼:
發送數據
public class getData extends HttpServlet {
File logFile = new File("D:\\workspace\\drawChart\\src\\read_log\\log.txt");
private long lastTimeFileSize = 0; // 上次文件大小
//打包數據為Json格式傳給前台 public void pickData(HttpServletRequest request,HttpServletResponse response,Float message[][],long pointer) throws ServletException, IOException { List<String> legend = new ArrayList<String>(Arrays.asList(new String[]{"alpha.water"})); //x軸時間 List<Float> axis = new ArrayList<Float>(Arrays.asList(message[0])); List<Series> series=new ArrayList<Series>(); //series series.add(new Series("alpha.water","line",new ArrayList<Float>(Arrays.asList(message[1])))); //更改下一行代碼,添加多個series Echarts echarts = new Echarts(legend,axis,series,pointer); ObjectMapper objectMapper = new ObjectMapper(); String str = objectMapper.writeValueAsString(echarts); System.out.println(objectMapper.writeValueAsString(echarts)); response.setContentType("text/html;charset=utf-8"); //發送數據 PrintWriter out = response.getWriter(); out.write(str); System.out.println("發送post方法。。。。。。。。。。"); out.flush(); out.close(); }
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{ String regEx = "(-?\\d+)(\\.\\d+)?"; String regEx0 = "^Time = [0-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$"; Pattern pattern = Pattern.compile(regEx); Pattern pattern0 = Pattern.compile(regEx0); float alpha = 0; float p_rgh = 0; float omega = 0; float k = 0; float DTime = 0; //用來保存所有數據的二維數組 Float message[][] = new Float[14][5]; //計數器,個數據傳一次 int flag = -1; //獲取保存的上一次讀取的位置 String str = request.getParameter("lastTimeFileSize"); lastTimeFileSize = Long.parseLong(str); System.out.println(lastTimeFileSize); try { long len = logFile.length(); System.out.println(len); if(lastTimeFileSize >= len){ lastTimeFileSize = len; try { //如果讀取的速度超過寫的速度,等待5秒 Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } }else{ RandomAccessFile randomFile = new RandomAccessFile(logFile, "r"); randomFile.seek(lastTimeFileSize); String tmp = null; while ((tmp = randomFile.readLine()) != null) { //正則匹配時間 Matcher matcher0 = pattern0.matcher(tmp); if(matcher0.lookingAt()){ if(flag==4){ //記錄讀取文件的位置 lastTimeFileSize = randomFile.getFilePointer(); System.out.println(lastTimeFileSize); //傳數據 try { Thread.sleep(3000); //由於是根據time決定是否提交,所以提交5次需要讀6個time,文件指針會只在time后面,因此要倒回 lastTimeFileSize -= 50; pickData(request,response,message,lastTimeFileSize); } catch (InterruptedException e1) { e1.printStackTrace(); } randomFile.close(); flag = -1; break; } DTime = Float.parseFloat(tmp.split("=")[1]); flag++; message[0][flag] = DTime; } //yangmo //找到 alpha p_rgh Omega k; else if((tmp.startsWith("smoothSolver: Solving for alpha.water"))) { String[] splitAddress = tmp.split(",")[1].split("="); alpha = Float.parseFloat(splitAddress[1]); System.out.println(flag); message[1][flag] = alpha; //System.out.println(alpha); } else if((tmp.startsWith("GAMG: Solving for p_rgh"))) { String[] splitAddress = tmp.split(",")[1].split("="); p_rgh = Float.parseFloat(splitAddress[1]); message[2][flag] = p_rgh; //System.out.println(p_rgh); } else if((tmp.startsWith("smoothSolver: Solving for omega"))) { String[] splitAddress = tmp.split(",")[1].split("="); omega = Float.parseFloat(splitAddress[1]); message[3][flag] = omega; //System.out.println(omega); } else if((tmp.startsWith("smoothSolver: Solving for k"))) { String[] splitAddress = tmp.split(",")[1].split("="); k = Float.parseFloat(splitAddress[1]); message[4][flag] = k; //System.out.println(k); } //找到 x,y,z else if(tmp.equals("Sum of forces")) { int j = 5; for(int i=1;i<=3;i++){ String line = randomFile.readLine(); Matcher matcher = pattern.matcher(line); //正則匹配數值 while(matcher.find()){ message[j][flag] = Float.parseFloat(matcher.group()); j++; } } } } //讀到文件的末尾需要把剩余數據的提交 if(flag>-1){ //提交最后的數據 lastTimeFileSize = randomFile.getFilePointer(); lastTimeFileSize -= 50; pickData(request,response,message,lastTimeFileSize); }else { //等待,可以有效的避免前台無間歇的詢問導致CPU占用增大 try { Thread.sleep(10000); } catch (InterruptedException e1) { e1.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); }finally{ //其它操作 } }
}
紅色標記內容是相關內容
注意:類中如果使用構造器去傳遞文件的路徑名,則ajax請求會失敗
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<servlet>
<description></description>
<display-name>getData</display-name>
<servlet-name>getData</servlet-name>
<servlet-class>read_log.getData</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getData</servlet-name>
<url-pattern>/get</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>chart.jsp</welcome-file>
</welcome-file-list>
</web-app>
