基於JSR-356實現的Tyrus WebSocket框架的消息傳遞機制初步瞭解

對阻塞、非阻塞,同步、異步初步瞭解了,不是太明白,其中大多數將的是對於CPU的使用率及對一個事件的處理效率。 阻塞與非阻塞各有優缺點,在知乎上也看到了下面的結論:java

在處理 IO 的時候,阻塞和非阻塞都是同步 IO。 只有使用了特殊的 API 纔是異步 IO。web

1、Tyrus發送消息的兩種方式:編程

The interface javax.websocket.RemoteEndpoint, part of Java API for
WebSocket, is designed to represent the other end of the communication
(related to the endpoint), so the developer uses it to send thec#

  1. There are two basic interfaces the user may use -
    javax.websocket.RemoteEndpoint$Basic緩存

andjavax.websocket.RemoteEndpoint$Async.websocket

javax.websocket.RemoteEndpoint接口,是JAVA API中的WebSocket的一部分,它被設計爲與鏈接的另外一端進行通訊,開發人員可使用它發送消息。有兩種基本的接口可使用:接口javax.websocket.RemoteEndpoint$Basic(同步消息)和javax.websocket.RemoteEndpoint$Async(異步消息)session

1:同步消息傳遞(javax.websocket.RemoteEndpoint$Basic)多線程

is used to send synchronous messages The point of completion of the
send is defined when all the supplied data has been written to the
underlying connection. The methods for sending messages on the
javax.websocket.RemoteEndpoint$Basic block until this point of
completion is reached, except
forjavax.websocket.RemoteEndpoint$Basic#getSendStream() and
javax.websocket.RemoteEndpoint$Basic#getSendWriter() which present
traditional blocking I/O streams to write messages. See the example
"Sending message via RemoteEndpoint.Basic instance" to see how the
whole text message is send. The following example demonstrates a
method which sends the partial text method to the peer:併發

javax.websocket.RemoteEndpoint$Basic接口被用來發送同步消息,當全部提供的數據都被寫入到底層鏈接時,完成發送的標識被肯定。經過javax.websocket.RemoteEndpoint$Basic接口發送消息的方法會被阻塞,直到完成發送的標識到達。javax.websocket.RemoteEndpoint$Basic#getSendStream()方法和javax.websocket.RemoteEndpoint$Basic#getSendWriter()方法 經過傳統的傳統的阻塞I / O流寫入消息。app

這裏的完成發送的標識應該是方法中的isLast參數(Boolean),這裏的瞭解的還不深刻,以前一直在找WebSocket客戶端處理消息的方式是什麼,經過本身的幾個小測試應該是多線程的。

(1)在發送同步消息時,可能一條消息被分爲幾回發送,相應的方法爲:

例1:發送一條消息的部分消息

public void sendPartialTextMessage(String message, Boolean isLast, Session session){  
    try {  
        session.getBasicRemote().sendText(message, isLast);  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
}

(2)發送同步消息,發送整條消息

例2:發送整條消息

@OnMessage  
public void echo(String message, Session session) {  
    session.getBasicRemote().sendText(message);  
}

這裏沒有說明一條完整的消息發送完是阻塞的方式仍是非阻塞的方式。 若是是阻塞的方式在server-client這類型的系統中,client對於單個server在client處理相關事務時,server發送的消息就沒有使用消息隊列等相關的將並行消息轉爲串行消息的處理。

在sever-client系統中 假設一條完整的消息發送完是阻塞的,可是對於client接收到了相關消息並做出了處理,處理後又給server發送的結果消息,在消息沒有標識的狀況下,server是怎麼知道client發送的結果消息是對應以前哪條消息的呢?

2.異步消息傳遞(javax.websocket.RemoteEndpoint$Async) This representation of

the peer of a web socket conversation has the ability to send messages

  1. The point of completion of the send is defined when

  2. the supplied data has been written to the underlying connection.

The completion handlers for the asynchronous methods are always called
with a different thread from that which initiated the send.

每個WebSocket的端點都有異步發送消息的能力。當全部提供的數據都被寫入到底層鏈接時,完成發送的標識被肯定。異步消息處理一般是調用不一樣的線程來開始發送消息。

例3:使用Future異步發送消息

public void sendWholeAsyncMessage(String message, Session session){  
    Future<Void> future = session.getAsyncRemote().sendText(message);  
}

這裏講的都是發送消息的方式,在另外一端處理接收的消息並無進行介紹,不過WebSocket是全雙工通訊機制,兩端的接收、發送消息的方式正好相反的方式。

2、Tyrus接收消息的方式

MessagHandler(消息處理器)

Implementing the javax.websocket.MessageHandler interface is one of
the ways how to receive messages on endpoints (both server and
client). It is aimed primarily on programmatic endpoints, as the
annotated ones use the method level annotation
javax.websocket.OnMessage to denote the method which receives
messages.

實現javax.websocket.MessageHandler接口是接收消息的一種方式(服務端和客戶端)。。它主要針對的編程性端點,做爲註解的人使用的方法級別的註釋javax.websocket.OnMessage表示接收消息的方法。

The MessageHandlers get registered on the Session instance

MessageHandlers 經過Session實例註冊

There are two orthogonal criterions which classify MessageHandlers.
According the WebSocket Protocol (RFC 6455) the message may be sent
either complete, or in chunks. In Java API for WebSocket this fact is
reflected by the interface which the handler implements. Whole
messages are processed by handler which
implementsjavax.websocket.MessageHandler.Whole interface. Partial
messages are processed by handlers that implement
javax.websocket.MessageHandler.Partial interface. However, if user
registers just the whole message handler, it doesn't mean that the
handler will process solely whole messages. If partial message is
received, the parts are cached by Tyrus until the final part is

  1. Then the whole message is passed to the handler. Similarly,

  2. the user registers just the partial message handler and whole

message is received, it is passed directly to the handler.

MessageHandlers有兩個規則。根據WebSocket協議(RFC6455)的消息可能會被髮送要麼完成,或發送一部分。在Java API中的WebSocket,這個規則是處理器實現的接口反射實現的。整個消息由它實現javax.websocket.MessageHandler.Whole接口處理程序進行處理。部分消息由實現javax.websocket.MessageHandler.Partial接口處理程序進行處理。可是,若是用戶註冊的只是整個消息的處理程序,但這並不意味着該處理器將只處理整個消息。若是接收部分消息中,部分消息由Tyrus緩存接收直到接收到總體的全部消息。而後整個消息傳遞給處理程序。一樣地,若是用戶註冊的只是部分消息的處理器,這個處理器能夠處理部分消息也能夠處理整個消息,若是接收到的是整個的消息會直接發送給處理器。

The second criterion is the data type of the message. WebSocket
Protocol (RFC 6455) defines four message data type - text message,
According to Java API for

WebSocket the text messages will be processed by MessageHandlers with
the following types

java.lang.String

java.io.Reader

any developer object for which there is a corresponding
javax.websocket.Decoder.Text or javax.websocket.Decoder.TextStream.

The binary messages will be processed by MessageHandlers with the
following types:

java.nio.ByteBuffer

java.io.InputStream

any developer object for which there is a corresponding
javax.websocket.Decoder.Binary or
javax.websocket.Decoder.BinaryStream.

第二個規則是關於消息類型的。WebSocket協議(RFC-6455)規定了四種消息類型-文本消息,根據Java API中的WebSocket定義,文本消息能被消息處理器處理的類型以下:

java.lang.String

java.io.Reader

任意開發者開發的對象類型,能夠經過javax.websocket.Decoder.Text 或 javax.websocket.Decoder.TextStream進行編碼解碼處理後進行傳輸、解析

能被消息處理器處理的二進制類型以下:

java.nio.ByteBuffer

java.io.InputStream

任意開發者開發的對象類型,能夠經過 javax.websocket.Decoder.Binary 或 javax.websocket.Decoder.BinaryStream進行編碼解碼處理後進行傳輸、解析

注意:只能爲每一個類型的消息註冊一個消息處理器消息類型分別爲: text messages, binary messages, pong messages

到這裏根據官方文檔的解釋及我的的理解爲以上內容,介紹了發送消息的兩種方式,一種爲同步方式、另外一種爲異步方式,其中同步方式又分爲將消息分爲幾個部分進行發送,每發送一部分消息就進行阻塞,另外一種同步方式是發送一個完整的消息。

接收消息這裏只介紹了每一個sever端或client端中註冊的消息處理器處理的消息的類型的規則。

尚未找到介紹接收消息時,對接收的消息的處理方式,多線程仍是其餘處理方式是否Tyrus能夠進行相關的設置?

在官方文檔中發現了shared client container 這一小節,這小節介紹了client端的線程池的默認設置,默認設置爲一個選擇線程,兩個工做線程

By default, WebSocket client implementation in Tyrus re-creates client
runtime whenever WebSocketContainer#connectToServer is invoked. This
approach gives us some perks like out-of-the-box isolation and
relatively low thread count (currently we have 1 selector thread and 2
worker threads). Also it gives you the ability to stop the client
runtime – one Session instance is tied to exactly one client runtime,
so we can stop it when Session is closed. This seems as a good
solution for most of WebSocket client use cases – you usually use java
client from application which uses it for communicating with server
side and you typically don’t need more than 10 instances (my personal
estimate is that more than 90% applications won’t use more than 1
connection). There are several reasons for it – of it is just a
client, it needs to preserve server resources – one WebSocket
connection means one TCP connection and we don’t really want clients
to consume more than needed. Previous statement may be invalidated by
WebSocket multiplexing extension, but for now, it is still valid.

一個WebSocket鏈接就是一個TCP鏈接,在沒有特別繁忙的消息接收的場景下不須要設置過大的線程數,只有當你確認你的程序真的須要更多的線程,推薦你設置最大線程數 設置最大線程池數的方法

client.getProperties().put(GrizzlyClientProperties.SELECTOR_THREAD_POOL_CONFIG,
ThreadPoolConfig.defaultConfig().setMaxPoolSize(3));
client.getProperties().put(GrizzlyClientProperties.WORKER_THREAD_POOL_CONFIG,
ThreadPoolConfig.defaultConfig().setMaxPoolSize(10));

(此段有待查證) 經過實驗,server端單線程下按順序發送數條消息後,client端的處理應該是 先將全部消息進行在緩衝區存儲,而後逐條消息處理; server端默認不支持多線程,若是在沒有修改線程池的最大併發數時,默認爲1。

相關文章
相關標籤/搜索