基於embedded jetty server的websocket開發

基於embedded jetty server的websocket開發

websocket提供一種在瀏覽器和服務器之間的全雙工通信(full-duplex),替代了傳統的輪詢(polling)做法。目前,在一些web-base的富文本編輯器中有廣泛的應用,例如Etherpad,石墨文檔,有道雲筆記等。具體的關於websocket的定義請參見:http://baike.baidu.com/view/3623887.htmhttps://en.wikipedia.org/wiki/WebSocket。下文是一個基於embedded jetty server的websocket開發的實例,供大家參考。

基本架構:
瀏覽器(javasscript) --(websocket with ws/wss)--> nginx --(websocket with ws/wss)--> embedded Jetty Server

服務端開發:
關於Jetty Server的介紹,請參見http://www.eclipse.org/jetty/。以下是一些實際的使用經驗和技巧,其中包括:
建立服務器端鏈接偵聽(包括ws和wss),
創建消息處理句柄(其中包括WebSocketServlet和WebSocketAdapter)

  1. 構建server端的websocket鏈接
本實例採用ServerConnector來建立server端鏈接偵聽。代碼示例如下:
HttpConfiguration http_config = new HttpConfiguration();
ServerConnector http = new ServerConnector(
websocketServer, new HttpConnectionFactory(http_config));
http.setHost(serverName);
http.setPort(serverPort);
websocketServer.setConnectors(new Connector[]{http});

以上代碼基於ws(相當於http),如果需要建立wss鏈接(相當於https),需要設置KeyStore,代碼示例如下:
HttpConfiguration https_config = new HttpConfiguration();
https_config.setSecureScheme("https");
https_config.setSecurePort(securityPort);
https_config.setSendServerVersion(true);
https_config.setSendDateHeader(false);
https_config.addCustomizer(new SecureRequestCustomizer());

SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath(".\\ssl\\keystore");
sslContextFactory.setTrustStorePath(".\\ssl\\keystore");
// key store password when generate the store
sslContextFactory.setKeyStorePassword("testPassword");
sslContextFactory.setExcludeCipherSuites(
"SSL_RSA_WITH_DES_CBC_SHA",
"SSL_DHE_RSA_WITH_DES_CBC_SHA",
"SSL_DHE_DSS_WITH_DES_CBC_SHA",
"SSL_RSA_EXPORT_WITH_RC4_40_MD5",
"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
ServerConnector sslConnector = new ServerConnector(websocketServer,
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(https_config));
sslConnector.setPort(securityPort);
websocketServer.setConnectors(new Connector[]{sslConnector});

  1. 建立客戶端消息處理句柄
本實例採用ServletContextHandler來處理髮自客戶端(javascript)的消息。注:同時引入ContextHandlerCollection,添加一個healthStatus的句柄,來監控jettyServer的運行狀態。示例代碼如下:
// Add health contextHandler
ContextHandler healthHandler = createHealthStatusHandler();

// Add websocket contextHandler
ServletContextHandler websocketHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
websocketHandler.setContextPath("/");
ServletHolder wsHolder = new ServletHolder(
"collabWebsocket",
new TestCollabSocketServlet());
websocketHandler.addServlet(wsHolder, "/");

// merge two contextHandlers
ContextHandlerCollection handlers = new ContextHandlerCollection();
handlers.setHandlers(new Handler[]{websocketHandler, healthHandler});
websocketServer.setHandler(handlers);

  1. 實現WebSocket Serverlet和Adapter:
public class TestCollabSocketServlet extends WebSocketServlet {

public TestCollabSocketServlet() {
}

@Override
public void configure(WebSocketServletFactory factory) {
factory.register(TestCollabSocketAdapter.class);
}
}
public class TestCollabSocketAdapter extends WebSocketAdapter{
public void onWebSocketClose(int statusCode, String reason){
// 處理關閉事件
}
public void onWebSocketConnect(Session websocketSession) {
// 處理鏈接事件
}
public void onWebSocketText(String msgText) {
// 處理接收到的消息
}
}


客戶端(javascript開發):
  1. 建立和server的鏈接:
// 這個URL類似 ws://richEditor.com/collab 或者wss://richEditor.com/collab
that._websocket = new WebSocket(websocketURL);
  1. 註冊websocket事件,包括open,close,error和message
that._websocket.onopen = that.onOpen.bind(that);
that._websocket.onclose = that.onClose.bind(that);
that._websocket.onmessage = that.onMessage.bind(that);
that._websocket.onerror = that.onError.bind(that);
onOpen: function() {
// 處理鏈接打開事件
},
onClose: function() {
// 處理鏈接關閉事件
},
onMessage: function() {
// 處理接受的消息
},
onError: function() {
// 處理鏈接失敗事件
},
總結:
整體來說,這套代碼實現相對簡單,對於百人級別的併發也可以從容處理。建議:
1, 對於wss的支持,只需要實現從客戶端到nginx這一層即可,nginx和Jetty Sever基本同處於內網,可以使用ws連接。
2,可以對傳送消息進行一定的壓縮,來降低對於帶寬的依賴
3,實現自定義的ping/pong消息,用於保證web socket長連接的持續有效(nginx默認會關閉1分鐘內沒有消息通信的鏈接)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章