[英]Spring Integration - Reliable TCP for high volume application
我正在使用Spring Integration for TCP服務器,該服務器保持與數千個客戶端的連接。 我需要服務器在過多負載的情況下限制客戶端,而不丟失消息。
我的服務器配置:
<task:executor id="myTaskExecutor"
pool-size="4-8"
queue-capacity="0"
rejection-policy="CALLER_RUNS" />
<int-ip:tcp-connection-factory id="serverTcpConFact"
type="server"
port="60000"
using-nio="true"
single-use="false"
so-timeout="300000"
task-executor="myTaskExecutor" />
<int-ip:tcp-inbound-channel-adapter id="tcpInboundAdapter"
channel="tcpInbound"
connection-factory="serverTcpConFact" />
<channel id="tcpInbound" />
<service-activator input-channel="tcpInbound"
ref="myService"
method="test" />
<beans:bean id="myService" class="org.test.tcpserver.MyService" />
由於連接工廠的默認任務執行程序是無界的,因此我使用池化任務執行程序來防止內存不足錯誤。
一個用於負載測試的簡單客戶端:
public class TCPClientTest {
static Socket socket;
static List<Socket> sl = new ArrayList<>();
static DataOutputStream out;
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10000; i++) {
socket = new Socket("localhost", 60000);
sl.add(socket);
out = new DataOutputStream(socket.getOutputStream());
out.writeBytes("connection " + i + "\r\n");
System.out.println("Using connection #" + i);
}
System.in.read();
}
}
當我運行它時,服務器僅收到大約10-20條消息,然后客戶端會收到“拒絕連接:連接”異常。 之后,即使連接超時后,服務器也無法再接受任何新連接。 增加池的大小只會幫助獲得更多消息。
編輯
我正在使用Spring Integration 3.0.2.RELEASE。 對於生產環境,我使用的是8-40個線程,但只有經過數百次連接后,它才會使此測試稍后失敗。
MyService.test()做不了什么...
public class MyService {
public void test(byte[] input) {
System.out.println("Received: " + new String(input));
}
}
我知道問題出在哪里,請打開JIRA問題 。
問題是執行程序中的CALLER_RUNS
拒絕策略的隊列長度為0
。
有一個線程可以處理所有IO事件(通常是myTaskExecutor-1
); 當讀取事件觸發時,他將執行排隊以讀取數據; 閱讀器線程將執行的執行排隊以組裝數據(該數據將阻塞,直到收到完整的消息(在您的情況下為CRLF終止)為止)。
在這種情況下,如果沒有可用的線程,則CALLER_RUNS
策略意味着IO選擇器線程將進行讀取,並成為匯編器線程,該匯編器線程將阻止等待不會到達的數據,因為他被阻止了,以后會讀取該數據安排另一個線程進行阻塞之后。 因為他被阻止,所以他無法處理新的接受事件。
這是我的測試記錄,顯示了該問題...
TRACE: [May-18 10:43:38,923][myTaskExecutor-1] tcp.connection.TcpNioServerConnectionFactory - Port 60000 SelectionCount: 2
DEBUG: [May-18 10:43:38,923][myTaskExecutor-1] tcp.connection.TcpNioConnection - localhost:58509:60000:bdc36c59-c31b-470e-96c3-6270e7c46a2f Reading...
DEBUG: [May-18 10:43:38,924][myTaskExecutor-1] tcp.connection.TcpNioConnection - localhost:58509:60000:bdc36c59-c31b-470e-96c3-6270e7c46a2f Running an assembler
TRACE: [May-18 10:43:38,924][myTaskExecutor-1] tcp.connection.TcpNioConnection - localhost:58509:60000:bdc36c59-c31b-470e-96c3-6270e7c46a2f Nio message assembler running...
DEBUG: [May-18 10:43:38,926][myTaskExecutor-1] tcp.serializer.ByteArrayCrLfSerializer - Available to read:0
第二行顯示了用於執行讀取操作的選擇器線程。 他檢測到此套接字需要一個匯編程序,並成為匯編程序,阻塞並等待數據。
您是否真的相信使用無限制的任務執行器會出現問題? 這些事件通常壽命很短,因此線程將很快被回收。
將執行程序的隊列容量增加到0以上也應該有所幫助,但不能完全確保不會發生問題(盡管不太可能遇到較大的隊列大小)。
除了為IO選擇器和讀取器線程使用專用的任務執行器之外,我還不確定如何解決此問題,因此它們永遠不會用作匯編程序。
昨天,我確實編寫了一個示例,只是使用spring集成來創建tcp高性能服務器代碼。 我使用JMeter TCP采樣器成功地對1000個並發客戶端請求進行了測試。
這是代碼-https: //github.com/rajeshgheware/spring-integration-samples,其中包括JMeter測試配置文件。
我在具有Intel Core i5 M520 2.4GHz的64位筆記本電腦上成功地測試了1000個並發tcp客戶端請求(服務器代碼和JMeter測試均在此計算機上運行)
我還嘗試了1500個並發客戶端請求,但是發現服務器不能滿足許多請求。 我將繼續嘗試增強此代碼以服務10000個並發客戶端請求(我知道我可能需要從亞馬遜那里獲得良好的EC2計算機以進行此測試:))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.