tomcat和netty下对长连接的测试

本文探讨了Tomcat在默认配置下处理长连接的问题,指出其可能无法满足高QPS需求。通过对比实验,展示了Netty如何通过多线程优化长连接性能,实验证明多线程处理在处理速度上有显著优势。同时,提供了Netty服务端和客户端的代码示例,以及两者在处理效率上的实际测量数据。
摘要由CSDN通过智能技术生成

1、前言

​ 本文用于探讨默认配置下tomcat处理长连接的问题。

​ 何为长连接:即以此tcp连接中发生多次http交互。

2、环境准备

​ 使用到的框架为spring-boot(内嵌tomcat),netty(用于比较)

2.1、服务端代码

​ 每次接受数据时打印出当前处理线程的id。

@RestController
public class HelloController {
    @PostMapping("/hello")
    public String hello(@RequestBody JSONObject jsonObject) throws InterruptedException {
        System.out.println("接收到客户端的hello消息:" + jsonObject.getString("hello"));
        System.out.println("线程id"+Thread.currentThread().getId());
        return "hello world";
    }
}

2.2、客户端代码

​ 模拟长连接中连续发送100次http报文

	@Test
    void contextLoads() throws IOException, InterruptedException {
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 8080));
        ByteBuffer write = ByteBuffer.wrap(("POST /hello HTTP/1.1\r\n" +
                "Content-Type: application/json\r\n" +
                "User-Agent: PostmanRuntime/7.29.0\r\n" +
                "Accept: */*\r\n" +
                "Postman-Token: fc10ee5d-a7b7-441f-a7e8-156cadd7778b\r\n" +
                "Host: localhost:6666\r\n" +
                "Accept-Encoding: gzip, deflate, br\r\n" +
                "Connection: keep-alive\r\n" +
                "Content-Length: 17\r\n" +
                "\r\n" +
                "{\"hello\":\"world\"}").getBytes(StandardCharsets.UTF_8));
        for (int i = 0; i < 100; i++) {
            socketChannel.write(write);
            write.position(0);
        }
    }

2.3、结果

在这里插入图片描述

可以看到tomcat在长连接中数据处理共用一个线程,此时就会存在一个问题,如果生产环境下有qps需求,那么使用默认tomcat配置下进行编程很有可能会达不到压测的理想效果。

​ 而我们通常面临的qps需求往往是短连接需求,即对不同用户的访问在预期时间内回包。比如,我们使用tomcat对服务发起两次访问,tomcat会使用两个不同的线程对数据进行处理,截图如下:
在这里插入图片描述

在短连接的情况下,我们可以很方便的对其进行性能的提升,如调大tomcat的并发请求数、使用nginx对服务进行负载均衡等。

3、环境改善

​ 让我们使用netty来对长连接进行性能优化,预期效果是netty能够针对长连接中不同的数据包进行分线程处理。

3.1、服务端handler代码

public class NettyHttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    private static final ExecutorService executorService = Executors.newFixedThreadPool(10);

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) throws Exception {
        executorService.execute(() -> {
            dataHandle(fullHttpRequest);
        });

        // 构造返回数据
        ByteBuf byteBuf = Unpooled.copiedBuffer("hello world", StandardCharsets.UTF_8);
        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, byteBuf.readableBytes());
        channelHandlerContext.writeAndFlush(response);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    private void dataHandle(FullHttpRequest fullHttpRequest) {
        System.out.println(String.format("接收到客户端的消息:%s,使用的线程id为:%s", fullHttpRequest.content().toString(StandardCharsets.UTF_8), Thread.currentThread().getId()));
    }
}

3.2、客户端代码

​ 客户端代码还是沿用tomcat测试中的测试代码。

3.3、结果

在这里插入图片描述

因为我这里线程池数只存放10个线程,因此总共能并发处理的报文只能为10。但可以看出与tomcat测试中明显不同的是长连接的数据报文处理由单线程变为了多线程,这意味着在压测上肯定能得到更好的效果。

4、进一步验证

​ 为了进一步证实我们的想法——多线程处理长连接报文状态下处理时间要优于单线程,我们来做抓包验证。

4.1、测试环境准备

  • 我们将在服务端分别部署一套上文编写的tomcat服务端代码和netty服务端代码,端口分别为8080和6666。
  • 测试仍使用我们的客户端测试代码。

4.2结果

4.2.1、netty

在这里插入图片描述
在这里插入图片描述

可以看到回送100个报文大概花的时间为:0.034250 - 0.005034 = 0.029216

4.2.2、tomcat

在这里插入图片描述

在这里插入图片描述

可以看到回送100个报文大概花的时间为:0.069127 - 0.002375 = 0.066752

​ 综上可以得出,多线程下处理长连接速度确实要比单线程下快,并且速度差距将随着线程数、报文数增大而差距愈发明显。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值