netty 是否长连接
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了netty 是否长连接相关的知识,希望对你有一定的参考价值。
netty是一个网络通信框架,它即可以做为长连接,也可以是短连接。但是长连接和短连接的区别不是根据netty区分的,而是根据你的服务区分的。比如以tcp通信为例,不管是长连接还是短连接,都是socket通信,区另在于,长连接在建立tcp握手成功之后,就不会主动断开连接,而是保持连接,而短连接是一次请求结束之后,服务器就会主动断开连接,以释放资源。 参考技术A netty本身实现的长连接,就是一个连接一个worker。worker的数量是有限的(通常是cpu
cores+1),所以你的服务器要是连接数多的话,得考虑使用“异步”request(netty的http没实现这么个功能),或者说“continuation”,当连接“无事可做”的时候,放弃线程的使用权,当要处理事务的时候,才重新拿到一个线程。
当然,如果你只想实现长连接而不在意request
一直占有worker,那么你只要不放弃连接就可以了(websocket本身也是一种长连接,netty里面有websocket的例子)。
Netty 长连接 Server 主动推送功能实现及问题
背景
使用 Netty 实现长连接,Client 端主动发起请求到 Server 端后,Server 缓存 Channel
,后续业务需要主动向 Client 推送数据时,直接遍历 Channel
写入数据。
用 Netty 实现 Http 服务器还是挺方便的,对 Netty 不太熟悉的时候,看着各种类似的代码,都能用,没有细究添加到 Pipeline 的处理器及区别。用了一个 HttpContentCompressor
,导致主动推送 HttpResponse 数据时写入失败。
本文将记录该问题的始末。
常规 Netty HttpServer 的代码
Netty 作为服务端,构建代码很常见:
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) {
channel.pipeline().addLast(new HttpServerCodec());
// 提供在握手时聚合 HttpRequest,设置最大长度为 100Mb:超长则会报 413Request Entity Too Large
channel.pipeline().addLast(new HttpObjectAggregator(104857600));
// 压缩返回数据 HttpResponse
channel.pipeline().addLast(new HttpContentCompressor());
channel.pipeline().addLast(new ChunkedWriteHandler());
channel.pipeline().addLast(new MyServerHandler());
}
}
服务端与客户端保持长连接,必要的时候通过 Channel 向 Client 主动推送数据。上面的代码,在主动推送数据时,会报异常。
Netty Server 端主动向通道写入异常
Server 端的业务需要通过 Channel 向 Client 推送数据,推送代码为:
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(result, CharsetUtil.UTF_8)); // Unpooled.wrappedBuffer(responseJson)
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8"); // HttpHeaderValues.TEXT_PLAIN.toString()
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
if (channel.isWritable()) {
channel.writeAndFlush(httpResponse).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
logger.info("回写成功");
} else {
logger.error("回写失败",future.cause());
}
});
}
在使用了 HttpContentCompressor
后,writeAndFlush
回调总是进入异常分支:
完整的异常信息:
io.netty.handler.codec.EncoderException: java.lang.IllegalStateException:
cannot send more responses than requests
at io.netty.handler.codec.
MessageToMessageEncoder.write(MessageToMessageEncoder.java:107)
去掉 HttpContentCompressor
去掉 HttpContentCompressor
后Server 主动推送的数据能够正确发送,为什么这个压缩 HttpResponse
的处理器会对数据写入有这么大的影响呢?
启示录
最初以为 Netty 实现的是 http 服务,Client 发送请求通过 Http 协议完成的,Server 主动推送数据失败是因为 Http 协议的局限性导致的。
于是尝试用 WebSocket 协议,这样反向推送是 OK 的。但是 Client 端全程只用一个 Socket 连接,导致其他正常的 Http 请求都升级为 WebSocket 请求了,其他 Http 协议处理流程无法正常运行。
结论:
- 如果要使用
HttpContentCompressor
,那么反向数据推送只能通过 WebSocket 协议进行。Client 端就需要区分 Http 请求和 Websocket 请求,用不同的 Socket 进行数据发送。 - 去掉
HttpContentCompressor
类,Client 采用 Http 请求缓存的 Channel 能正常推送数据,但是响应 Body 数据不会有压缩功能。应用中没有大数据量的传输业务,所以去掉也没啥影响。
以上是关于netty 是否长连接的主要内容,如果未能解决你的问题,请参考以下文章