写点什么

netty 系列之: 让 TCP 连接快一点, 再快一点

作者:程序那些事
  • 2022 年 1 月 12 日
  • 本文字数:2123 字

    阅读完需:约 7 分钟

netty系列之:让TCP连接快一点,再快一点

简介

经典的 TCP 三次握手大家应该很熟悉了,三次握手按道理说应该是最优的方案了,当然这是对于通用的情况来说的。那么在某些特殊的情况下是不是可以提升 TCP 建立连接的速度呢?


答案是肯定的,这就是今天我们要讲的 TCP fast open 和 netty。

TCP fast open

什么是 TCP fast open 呢?


TCP fast open 也可以简写为 TFO,它是 TCP 协议的一种扩展。为什么是 fast open 呢?这是因为 TFO 可以在初始化建立连接的时候就带上部分数据,这样在 TCP 连接建立之后,可以减少和服务器交互的次数,从而在特定的情况下减少响应的时间。


既然 TFO 这么好,为什么我们很少见到使用 TFO 协议的呢?


这是因为 TFO 是有缺陷的,因为 TFO 会在 sync 包中带上一些数据信息,那么当 sync 包重发的时候,就会造成接收方接受到重复的数据。


所以,如果是用 TFO,那么接收方则需要具有能够处理重复数据的能力。


在程序界,防止数据重复提交有一个好听的名字叫做幂等性,只有具有幂等性的服务器才能够使用 TFO。

开启 TFO

既然 TFO 这么优秀,怎么才能开启 TFO 呢?


TFO 的开启首先需要操作系统的支持,如果你是 mac 系统,恭喜你,mac 默认情况下已经支持 TFO 了,你不需要进行任何操作。


如果你是 Linux 系统,那么需要查看/proc/sys/net/ipv4/tcp_fastopen 这个文件。


tcp_fastopen 可以有四种值,如下所示:


0 -- 表示 TFO 未开启 1 -- 表示 TFO 开启了,但是只对客户端有效 2 -- 表示 TFO 开启了,但是只对服务器端有效 3 -- 表示 TFO 开启了,同时对客户端和服务器端有效


通过上面的设置,我们就在操作系统层开启了 TFO 的支持。


接下来,我们看一下如何在 netty 中使用 TFO。

netty 对 TFO 的支持

首先我们看下如何在 netty 的服务器端开启 TFO 支持。


在这之前,我们先回顾一下如何建议一个普通的 netty 服务器:


EventLoopGroup bossGroup = new NioEventLoopGroup();        EventLoopGroup workerGroup = new NioEventLoopGroup();        try {            ServerBootstrap b = new ServerBootstrap();            b.group(bossGroup, workerGroup)                    .channel(NioServerSocketChannel.class)                    .childHandler(new ChannelInitializer<SocketChannel>() {                        @Override                        public void initChannel(SocketChannel ch) throws Exception {                            ch.pipeline().addLast(new TFOServerHandler());                        }                    })                    .option(ChannelOption.SO_BACKLOG, 128)                    .childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口并开始接收连接 ChannelFuture f = b.bind(port).sync();
复制代码


上面的代码中,我们看到 ServerBootstrap 可以设置 option 参数,ChannelOption 中包含了所有可以设置的 channel 的参数,对应的 TFO 的参数是 ChannelOption.TCP_FASTOPEN, 所以我们只需要添加到 ServerBootstrap 中即可:


sb.option(ChannelOption.TCP_FASTOPEN, 50)
复制代码


ChannelOption.TCP_FASTOPEN 的值表示的是 socket 连接中可以处于等待状态的 fast-open 请求的个数。


对于客户端来说,同样需要进行一些改动,先来看看传统的 client 端是怎么工作的:


 EventLoopGroup group = new NioEventLoopGroup();        try {            Bootstrap b = new Bootstrap();            b.group(group)             .channel(NioSocketChannel.class)             .handler(new ChannelInitializer<SocketChannel>() {                 @Override                 protected void initChannel(SocketChannel ch) throws Exception {                     ChannelPipeline p = ch.pipeline();                     p.addLast(new TFOClientHandler());                 }             });
// 连接服务器 ChannelFuture f = b.connect(HOST, PORT).sync();
复制代码


client 要支持 TFO,需要添加这样的操作:


b.option(ChannelOption.TCP_FASTOPEN_CONNECT, true)
复制代码


还记得 TFO 是做什么的吗?TFO 就是在 sync 包中发送了一些数据。所以我们需要在 client 端对发送的数据进行处理,也就是说在 client 和 server 端建立连接之前就需要向 channel 中发送消息。


要获得非建立连接的 channel,则可以调用 Bootstrap 的 register 方法来获取 channel:


Channel channel = b.register().sync().channel();
复制代码


然后向该 channel 中写入 byteBuf:


ByteBuf fastOpenData = directBuffer();            fastOpenData.writeBytes("TFO message".getBytes(StandardCharsets.UTF_8));            channel.write(fastOpenData);
复制代码


最后再和服务器建立连接:


// 连接服务器            SocketAddress serverAddress =  SocketUtils.socketAddress("127.0.0.1", 8000);            ChannelFuture f = channel.connect(serverAddress).sync();
复制代码

总结

这样一个一个支持 TFO 的客户端和服务器就完成了。尽情使用吧。


本文的例子可以参考:learn-netty4


本文已收录于 http://www.flydean.com/44-netty-tcp-fast-open/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 刚刚阅读数: 2
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:让TCP连接快一点,再快一点