經過一個小實例來實現數據庫更新後,推送消息給前臺,讓前臺進行相應操做。html
需求java
數據庫更新以後服務器推送消息給前臺,讓前臺作操做。(數據庫的數據不是由服務器寫入的)web
實現的話說到底都是用輪詢,由於數據庫的數據不是經過後臺插入更新的,因此不管用什麼辦法,都須要循環地去讀取數據庫中的信息或者數據庫的日誌文件。區別就是,究竟是前臺輪詢,仍是後臺輪詢了。數據庫
若是使用前臺輪詢,就是前臺按期給後臺發送請求,來對數據進行更新,用setInterval()就能實現。你F12看Network就能看到一會就有幾十甚至幾百個請求。。由於我也是第一次實現這樣的功能,雖然對性能這方面沒有什麼研究,可是看到短期內這麼多請求仍是以爲心慌慌。api
因此想到了使用後臺輪詢,後臺輪詢的好處就是,前臺不用一直髮送請求給後臺,而是等到後臺發現數據更新了再提醒前臺從新請求數據。這就須要用到WebSocket。瀏覽器
咱們日常使用的http鏈接,都是隻能客戶端向服務器發送請求。tomcat
而WebSocket的最大特色就是,服務器能夠主動向客戶端推送信息,客戶端也能夠主動向服務器發送信息,是真正的雙向平等對話,屬於服務器推送技術的一種。服務器
在查詢資料的時候也查到能夠用數據庫的存儲過程來實現,在存儲數據的時候,調用Java的程序來進行通知。(由於還有一些處理方面的問題沒有去實現)websocket
環境session
Server version : Apache Tomcat/7.0.69
Java version: 1.7.0_80
須要引入的jar包:tomcat自帶的tomcat7-websocket.jar和websocket-api.jar,這兩個jar包都在tomcat安裝目錄的lib文件夾下。
須要注意的是:tomcat須要7.0.47版本以上才支持JSR-356,具體文檔能夠查看
思路
在創建鏈接的時候開啓一個線程對數據庫中的數據進行輪詢,若是查詢到數據變化了,就發消息給WebSocket實現類,實現類接收到消息後,推送消息給鏈接着的用戶。
(若是數據是經過後臺添加的,就不用這麼麻煩了,直接在添加數據的操做類中發送消息給WebSocket實現類就行了)
客戶端代碼
這部分比較簡單,就是經過url來創建WebSocket鏈接,協議名稱ws也就是WebSocket。在websocket.onmessage()方法中對接收到的消息進行處理,你能夠作輸出也能夠更新頁面等等。
var websocket = null; //判斷當前瀏覽器是否支持WebSocket if ('WebSocket' in window) { //創建鏈接,這裏的/websocket ,是Servlet中註解中的那個值 websocket = new WebSocket("ws://localhost:8080/項目名/websocket"); } else { alert('當前瀏覽器 Not support websocket'); } //鏈接發生錯誤的回調方法 websocket.onerror = function () { console.log("WebSocket鏈接發生錯誤"); }; //鏈接成功創建的回調方法 websocket.onopen = function () { console.log("WebSocket鏈接成功"); } //接收到消息的回調方法 websocket.onmessage = function (event) { console.log(event.data); if(event.data=="1"){ console.log("數據更新啦"); } } //鏈接關閉的回調方法 websocket.onclose = function () { console.log("WebSocket鏈接關閉"); } //監聽窗口關閉事件,當窗口關閉時,主動去關閉WebSocket鏈接,防止鏈接還沒斷開就關閉窗口,server端會拋異常。 window.onbeforeunload = function () { closeWebSocket(); } //關閉WebSocket鏈接 function closeWebSocket() { websocket.close(); }
服務器端代碼
在開啓鏈接的時候啓動了一個線程,關閉鏈接的時候調用線程的stopMe()方法,終止線程。當接收到消息的時候,調用sendMessage()方法給全部鏈接着的用戶發送消息。
須要注意的是,一旦創建了鏈接,就會建立一個session,這個session和request中的session不同,可是能夠用相似的想法來理解。因此在發送消息的時候也須要調用session的方法來給鏈接着的用戶發送消息。
WebSocket session發送文本消息有兩個方法:getAsyncRemote()和getBasicRemote(),這兩個方法我只是簡單瞭解了一下,前者是異步發送消息,後者是同步發送消息。也就是說getBasicRemote()要等上一條消息發送完才能發送下一條消息。若是有錯誤的話但願你們指出!
在文檔中咱們看到也能夠在服務器端接收消息的時候也能夠直接在onMessage()方法中return txt.toUpperCase()來發送消息給消息發送方,可是在這個例子中,咱們的消息是線程發送給WebSocket實現類的,因此不用這個方法。
//在相對路徑中發佈端點websocket @ServerEndpoint("/websocket") public class WebSocketServlet { MyThread thread1=new MyThread(); Thread thread=new Thread(thread1); //用來存放每一個客戶端對應的MyWebSocket對象。 private static CopyOnWriteArraySet<WebSocketServlet> webSocketSet = new CopyOnWriteArraySet<WebSocketServlet>(); private javax.websocket.Session session=null; /** * @ClassName: onOpen * @Description: 開啓鏈接的操做 */ @OnOpen public void onOpen(Session session) throws IOException{ this.session=session; webSocketSet.add(this); System.out.println(webSocketSet); //開啓一個線程對數據庫中的數據進行輪詢 thread.start(); } /** * @ClassName: onClose * @Description: 鏈接關閉的操做 */ @OnClose public void onClose(){ thread1.stopMe(); webSocketSet.remove(this); } /** * @ClassName: onMessage * @Description: 給服務器發送消息告知數據庫發生變化 */ @OnMessage public void onMessage(int count) { System.out.println("發生變化"+count); try { sendMessage(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @ClassName: OnError * @Description: 出錯的操做 */ @OnError public void onError(Throwable error){ System.out.println(error); error.printStackTrace(); } /** * 這個方法與上面幾個方法不同。沒有用註解,是根據本身須要添加的方法。 * @throws IOException * 發送自定義信號,「1」表示告訴前臺,數據庫發生改變了,須要刷新 */ public void sendMessage() throws IOException{ //羣發消息 for(WebSocketServlet item: webSocketSet){ item.session.getBasicRemote().sendText("1"); } } }
線程的定義
線程先對數據庫中的數據查詢一次,存在sum變量中,而後再一直對數據庫中的數據進行輪詢,new_sum與sum不一樣的話就發送消息給WebSocket實現類。
public class MyThread implements Runnable{ private int sum; private int new_sum; private boolean stopMe = true; public void stopMe() { stopMe = false; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { UrlDao urlDao=new UrlDao(); sum=urlDao.selectCount(); WebSocketServlet wbs=new WebSocketServlet(); while(stopMe){ new_sum=urlDao.selectCount(); if(sum!=new_sum){ System.out.println("change"); sum=new_sum; wbs.onMessage(sum); } try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
測試
至此個人需求是大概完成了,由於是第一次寫WebSocket和線程相關的實例,若是有問題但願你們能夠指出!