Java 在Netty处理程序中重用HTTP响应
我将Netty用于一个服务器,该服务器需要每秒处理数十万个请求,同时在响应延迟方面保持尽可能小的差异。我正在做一些最后的优化,我目前正在研究通过重用任何我能重用的对象来减少不必要的内存分配。突出显示我的问题的服务器的简化示例如下:Java 在Netty处理程序中重用HTTP响应,java,netty,Java,Netty,我将Netty用于一个服务器,该服务器需要每秒处理数十万个请求,同时在响应延迟方面保持尽可能小的差异。我正在做一些最后的优化,我目前正在研究通过重用任何我能重用的对象来减少不必要的内存分配。突出显示我的问题的服务器的简化示例如下: import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import i
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
public class NettyServer {
public void run() throws Exception {
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 {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(1048576));
p.addLast(new NettyHandler());
}
});
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyServer().run();
}
}
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;
public class NettyHandler extends SimpleChannelInboundHandler<Object> {
private static final FullHttpResponse okResponse = OkResponse();
private static final FullHttpResponse failResponse = FailResponse();
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
FullHttpRequest request = (FullHttpRequest) msg;
QueryStringDecoder query = new QueryStringDecoder(request.getUri());
String path = query.path();
ChannelFuture f;
boolean keepAlive = HttpUtil.isKeepAlive(request);
if ("/ok".equals(path)) {
f = ctx.write(okResponse);
} else {
f = ctx.write(failResponse);
keepAlive = false;
}
if (!keepAlive) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
private static FullHttpResponse OkResponse() {
String data = "{ \"status\": ok }";
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer(data, CharsetUtil.UTF_8)
);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "max-age=0, no-cache, must-revalidate, proxy-revalidate");
return response;
}
private static FullHttpResponse FailResponse() {
String data = "{ \"status\": fail }";
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer(data, CharsetUtil.UTF_8)
);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "max-age=0, no-cache, must-revalidate, proxy-revalidate");
return response;
}
}
导入io.netty.bootstrap.ServerBootstrap;
导入io.netty.channel.ChannelFuture;
导入io.netty.channel.ChannelInitializer;
导入io.netty.channel.ChannelPipeline;
导入io.netty.channel.EventLoopGroup;
导入io.netty.channel.nio.NioEventLoopGroup;
导入io.netty.channel.socket.SocketChannel;
导入io.netty.channel.socket.nio.NioServerSocketChannel;
导入io.netty.handler.codec.http.HttpServerCodec;
导入io.netty.handler.codec.http.HttpObjectAggregator;
公共类NettyServer{
public void run()引发异常{
EventLoopGroup bossGroup=新的NioEventLoopGroup();
EventLoopGroup workerGroup=新的NioEventLoopGroup();
试一试{
ServerBootstrap b=新的ServerBootstrap();
b、 组(bossGroup、workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(新的通道初始值设定项(){
@凌驾
public void initChannel(SocketChannel ch)引发异常{
ChannelPipeline p=通道管道();
p、 addLast(新的HttpServerCodec());
p、 addLast(新的HttpObjectAggregator(1048576));
p、 addLast(新NettyHandler());
}
});
ChannelFuture f=b.bind(8080.sync();
f、 通道().closeFuture().sync();
}最后{
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
公共静态void main(字符串[]args)引发异常{
新建NettyServer().run();
}
}
处理程序代码如下所示:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
public class NettyServer {
public void run() throws Exception {
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 {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(1048576));
p.addLast(new NettyHandler());
}
});
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyServer().run();
}
}
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;
public class NettyHandler extends SimpleChannelInboundHandler<Object> {
private static final FullHttpResponse okResponse = OkResponse();
private static final FullHttpResponse failResponse = FailResponse();
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
FullHttpRequest request = (FullHttpRequest) msg;
QueryStringDecoder query = new QueryStringDecoder(request.getUri());
String path = query.path();
ChannelFuture f;
boolean keepAlive = HttpUtil.isKeepAlive(request);
if ("/ok".equals(path)) {
f = ctx.write(okResponse);
} else {
f = ctx.write(failResponse);
keepAlive = false;
}
if (!keepAlive) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
private static FullHttpResponse OkResponse() {
String data = "{ \"status\": ok }";
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer(data, CharsetUtil.UTF_8)
);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "max-age=0, no-cache, must-revalidate, proxy-revalidate");
return response;
}
private static FullHttpResponse FailResponse() {
String data = "{ \"status\": fail }";
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer(data, CharsetUtil.UTF_8)
);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "max-age=0, no-cache, must-revalidate, proxy-revalidate");
return response;
}
}
import io.netty.buffer.unmooled;
导入io.netty.channel.ChannelFuture;
导入io.netty.channel.ChannelFutureListener;
导入io.netty.channel.ChannelHandlerContext;
导入io.netty.channel.SimpleChannelInboundHandler;
导入io.netty.handler.codec.http.DefaultFullHttpResponse;
导入io.netty.handler.codec.http.FullHttpRequest;
导入io.netty.handler.codec.http.FullHttpResponse;
导入io.netty.handler.codec.http.HttpUtil;
导入io.netty.handler.codec.http.HttpHeaderNames;
导入io.netty.handler.codec.http.HttpHeaderValue;
导入io.netty.handler.codec.http.HttpResponseStatus;
导入io.netty.handler.codec.http.HttpVersion;
导入io.netty.handler.codec.http.QueryStringDecoder;
导入io.netty.util.CharsetUtil;
公共类NettyHandler扩展了SimpleChannelInboundHandler{
私有静态最终FullHttpResponse okResponse=okResponse();
私有静态最终FullHttpResponse failResponse=failResponse();
@凌驾
公共无效channelReadComplete(ChannelHandlerContext ctx){
ctx.flush();
}
@凌驾
受保护的无效channelRead0(ChannelHandlerContext ctx,对象消息){
FullHttpRequest请求=(FullHttpRequest)消息;
QueryStringDecoder query=新的QueryStringDecoder(request.getUri());
字符串路径=query.path();
渠道f;
布尔keepAlive=HttpUtil.isKeepAlive(请求);
如果(“/ok”。等于(路径)){
f=ctx.write(okResponse);
}否则{
f=ctx.write(故障响应);
keepAlive=false;
}
如果(!keepAlive){
f、 addListener(ChannelFutureListener.CLOSE);
}
}
私有静态FullHttpResponse OkResponse(){
字符串数据=“{\”状态\“:ok}”;
FullHttpResponse响应=新的默认FullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
未冷却的.copiedBuffer(数据,CharsetUtil.UTF_8)
);
response.headers().set(HttpHeaderNames.CONTENT\u TYPE,HttpHeaderValues.APPLICATION\u JSON);
response.headers().set(HttpHeaderNames.CACHE_控件,“max age=0,无缓存,必须重新验证,代理重新验证”);
返回响应;
}
专用静态FullHttpResponse FailResponse(){
字符串数据=“{\”状态\“:失败}”;
FullHttpResponse响应=新的默认FullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
未冷却的.copiedBuffer(数据,CharsetUtil.UTF_8)
);
response.headers().set(HttpHeaderNames.CONTENT\u TYPE,HttpHeaderValues.APPLICATION\u JSON);
response.headers().set(HttpHeaderNames.CACHE_控件,“max age=0,无缓存,必须重新验证,代理重新验证”);
返回响应;
}
}
处理程序显示了我试图完成的任务。处理程序包含固定HTTP响应的静态实例。对于服务器,除错误代码外的所有响应都来自一个小的组,可以预先构造。使用上述代码,对处理程序的第二次查询将失败,因为响应的Netty的ref计数已降至零。我原以为只要在对象上调用retain()
就足够了,但事实并非如此
在请求之间重用HTTP响应对象的最有效方法是什么?您应该调用
retainedDuplicate()
,否则readerIndex等可能会变得“无效”您应该调用retainedDuplicate()
,否则readerIndex等可能会变得“无效”听起来这仍然复制了对象,我猜这仍然比重建FullHttpResponse便宜?它至少不会复制内容。听起来这仍然复制了对象,我猜这仍然比重建FullHttpResponse便宜?它至少不会复制内容。