Spring Integration - 可靠的TCP,适用于大批量应用

时间:2014-05-17 22:48:41

标签: java sockets tcp nio spring-integration

我使用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条消息,然后客户端获得&#34;连接被拒绝:连接&#34;例外。之后,即使在连接超时之后,服务器也不能再接受任何新连接。增加池大小只会有助于获得更多消息。

修改

我正在使用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));
    }
}

Here is the log with trace level logging.

Sources

2 个答案:

答案 0 :(得分:2)

我知道问题所在,请打开JIRA issue

问题是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选择器和读取器线程使用专用的任务执行程序之外,我还不确定如何解决这个问题,因此它们永远不会用作汇编程序。

答案 1 :(得分:0)

昨天我写了一个示例,只是为了使用spring集成创建tcp高性能服务器代码。我使用JMeter TCP采样器成功测试了1000个并发客户端请求。

以下是代码 - https://github.com/rajeshgheware/spring-integration-samples,包括JMeter测试配置文件。

我成功测试了具有Intel核心i5 M520 2.4GHz的64位笔记本电脑上的1000个并发tcp客户端请求(服务器代码和在此机器上运行的JMeter测试)

我还尝试了1500个并发客户端请求,但发现服务器无法承认许多请求。我将继续尝试增强此代码以服务10000个并发客户端请求(我知道我可能需要从亚马逊获得这个测试的好EC2机器:))