WebSocket实现数据库更新前台实时显示

经过一个小实例来实现数据库更新后,推送消息给前台,让前台进行相应操做。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.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和线程相关的实例,若是有问题但愿你们能够指出!

相关文章
相关标签/搜索