1. 程式人生 > >WebSocket實現數據庫更新前臺實時顯示

WebSocket實現數據庫更新前臺實時顯示

throwable implement 技術 exception endpoint CA 建立 round 新頁面

通過一個小實例來實現數據庫更新後,推送消息給前臺,讓前臺進行相應操作。

需求

數據庫更新之後服務器推送消息給前臺,讓前臺做操作。(數據庫的數據不是由服務器寫入的)

實現的話說到底都是用輪詢,因為數據庫的數據不是通過後臺插入更新的,所以無論用什麽辦法,都需要循環地去讀取數據庫中的信息或者數據庫的日誌文件。區別就是,到底是前臺輪詢,還是後臺輪詢了。

如果使用前臺輪詢,就是前臺定期給後臺發送請求,來對數據進行更新,用setInterval()就能實現。你F12看Network就能看到一會就有幾十甚至幾百個請求。。因為我也是第一次實現這樣的功能,雖然對性能這方面沒有什麽研究,但是看到短時間內這麽多請求還是覺得心慌慌。

所以想到了使用後臺輪詢,後臺輪詢的好處就是,前臺不用一直發送請求給後臺,而是等到後臺發現數據更新了再提醒前臺重新請求數據。這就需要用到WebSocket。

我們平常使用的http連接,都是只能客戶端向服務器發送請求。

而WebSocket的最大特點就是,服務器可以主動向客戶端推送信息,客戶端也可以主動向服務器發送信息,是真正的雙向平等對話,屬於服務器推送技術的一種。

在查詢資料的時候也查到可以用數據庫的存儲過程來實現,在存儲數據的時候,調用Java的程序來進行通知。(因為還有一些處理方面的問題沒有去實現)

環境

Server version : Apache Tomcat/7.0.69
Java version: 1.7.0_80

需要引入的jar包:tomcat自帶的tomcat7-websocket.jarwebsocket-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和線程相關的實例,如果有問題希望大家可以指出!

WebSocket實現數據庫更新前臺實時顯示