反向Ajax之WebSocket

壹頁書發表於2013-12-27
WebSocket是HTML5開始提供的一種瀏覽器與伺服器間進行全雙工通訊的網路技術。

反向Ajax在觀察者模型、聊天系統中應用的越來越多。如股票,監控等系統。

以聊天系統為例,如果沒有伺服器推的技術,Web聊天系統只能以固定的時間間隔輪詢伺服器。
這會產生大量的無用的連線,對伺服器造成巨大壓力。
在單位寫過一個IM聊天系統,在配置了執行緒池,記憶體快取等優化手段之後,通過loadrunner壓力測試,併發訪問量卻始終超不過200/s。在這種系統中,輪詢是一種噩夢。

WebSocket作為伺服器推(反向Ajax)的手段之一,可以解決這種需求。
下面的例子,簡單介紹了WebSocket的使用。
伺服器每隔3秒,將自身的記憶體資訊推送到所有連線的客戶端瀏覽器。如果客戶端瀏覽器點選按鈕,則立即對該客戶端返回最新的資訊。
頁面:
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. <title>WebSocket</title>
  6. <script type="text/javascript"
  7.     src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
  8. <script type="text/javascript">
  9.     var ws = null;
  10.     function startWebSocket() {
  11.         if ('WebSocket' in window) {
  12.             ws = new WebSocket('ws://127.0.0.1:8080/Web/ws.do');
  13.         } else if ('MozWebSocket' in window) {
  14.             ws = new MozWebSocket('ws://127.0.0.1:8080/Web/ws.do');
  15.         } else {
  16.             alert('瀏覽器不支援WebSocket');
  17.         }

  18.         ws.onmessage = function(evt) {
  19.             $('#content').html(evt.data);
  20.         }

  21.         ws.onclose = function(evt) {
  22.             $('#content').html('WebSocket關閉');
  23.         }

  24.         ws.onopen = function(evt) {
  25.             $('#content').html('WebSocket已經開啟');
  26.         }
  27.     }

  28.     function getJVMInfo() {
  29.         //隨便發點什麼
  30.         ws.send(new Date());
  31.     }

  32.     $(document).ready(function() {
  33.         startWebSocket();
  34.     });
  35. </script>
  36. </head>
  37. <body>
  38.     <input type="button" value="立即獲取" onclick="getJVMInfo();" />
  39.     <div id="content"></div>
  40. </body>
  41. </html>
程式碼:

  1. import java.io.IOException;
  2. import java.net.URLDecoder;
  3. import java.net.URLEncoder;
  4. import java.nio.ByteBuffer;
  5. import java.nio.CharBuffer;
  6. import java.text.SimpleDateFormat;
  7. import java.util.Date;
  8. import java.util.List;
  9. import java.util.concurrent.CopyOnWriteArrayList;
  10. import java.util.concurrent.Executors;
  11. import java.util.concurrent.ScheduledExecutorService;
  12. import java.util.concurrent.TimeUnit;

  13. import javax.servlet.ServletException;
  14. import javax.servlet.annotation.WebServlet;
  15. import javax.servlet.http.HttpServlet;
  16. import javax.servlet.http.HttpServletRequest;
  17. import javax.servlet.http.HttpServletResponse;

  18. import org.apache.catalina.websocket.MessageInbound;
  19. import org.apache.catalina.websocket.StreamInbound;
  20. import org.apache.catalina.websocket.WebSocketServlet;
  21. import org.apache.catalina.websocket.WsOutbound;

  22. /**
  23.  * Servlet implementation class InitServlet
  24.  */
  25. @WebServlet("/ws.do")
  26. public class InitServlet extends WebSocketServlet implements Runnable {
  27.     List<JVMMessageInbound> list = new CopyOnWriteArrayList<JVMMessageInbound>();
  28.     ScheduledExecutorService singleThread = Executors.newSingleThreadScheduledExecutor();
  29.     {
  30.         singleThread.scheduleWithFixedDelay(this, 3, 3, TimeUnit.SECONDS);
  31.     }

  32.     @Override
  33.     protected StreamInbound createWebSocketInbound(String arg0, HttpServletRequest arg1) {
  34.         return new JVMMessageInbound();
  35.     }

  36.     private class JVMMessageInbound extends MessageInbound {

  37.         @Override
  38.         protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
  39.         }

  40.         @Override
  41.         protected void onTextMessage(CharBuffer arg0) throws IOException {
  42.             CharBuffer cb = CharBuffer.wrap(arg0);
  43.             String data = cb.toString();
  44.             System.out.println(data);
  45.             this.getWsOutbound().writeTextMessage(CharBuffer.wrap(getJVMInfo()));
  46.             this.getWsOutbound().flush();
  47.         }

  48.         @Override
  49.         protected void onClose(int status) {
  50.             System.out.println("Close WebSocket");
  51.             super.onClose(status);
  52.             list.remove(this);
  53.         }

  54.         @Override
  55.         protected void onOpen(WsOutbound outbound) {
  56.             System.out.println("Open new WebSocket");
  57.             super.onOpen(outbound);
  58.             list.add(this);
  59.         }

  60.     }

  61.     private String getJVMInfo() {
  62.         long useMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  63.         useMemory = useMemory / 1024;
  64.         return "已用記憶體:" + String.valueOf(useMemory) + "K";
  65.     }

  66.     @Override
  67.     public void run() {
  68.         System.out.println("傳送廣播通知");
  69.         String data = "廣播通知\n" + new Date() + "\n" + getJVMInfo();
  70.         for (JVMMessageInbound m : list) {
  71.             try {
  72.                 m.getWsOutbound().writeTextMessage(CharBuffer.wrap(data));
  73.                 m.getWsOutbound().flush();
  74.             } catch (IOException e) {
  75.                 e.printStackTrace();
  76.             }
  77.         }
  78.     }

  79. }
效果截圖:

但是在Tomcat 7.0.47版本中,WebSocketServlet已經標識為deprecated,並可能在Tomcat 8中remove。
是不是設計了更好的API



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-1064838/,如需轉載,請註明出處,否則將追究法律責任。

相關文章