没有为 netty 中的每个请求创建新的处理程序
New handlers not created for each requests in netty
我注意到没有为每个 http 请求创建新的处理程序实例。我在实例级别定义的变量很少。这些值是根据要求设置的。通过仔细检查,我发现这些值不是新设置的,而是来自第一个请求的值。
这是我的处理程序代码
@Component
@ChannelHandler.Sharable
public class CustomHandler extends ChannelDuplexHandler {
private final StringBuilder buf = new StringBuilder();
private final String foo;
private final String val;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//parse the request and set the variables
if (foo!=null) {
foo = request.getUri()
}
if (val!=null) {
val = getQueryParamsOf("key");
}
buf.append(val);
}
}
缓冲区没有清除。对于每个新请求,我仍然看到旧缓冲区。
即如果我提出请求 /foobar?key=netty
我在第一次调用中看到 buf = netty。
随后的调用,buf = nettynetty 和 buf=nettynettynetty 等等。
此外,foo
和 val
变量在第一次请求后永远不会为 null。
我的理解是因为将为每个请求创建新的处理程序。但是因为我使用了 @ChannelHander.Sharable
可能是相同的处理程序被重用
所以我注释掉了 @ChannelHander.Sharable
,第一个请求顺利通过。从下一个请求中,我收到以下错误。
io.netty.channel.ChannelPipelineException: my.example.handlers.CustomHandler is not a @Sharable handler, so can't be added or removed multiple times.
at io.netty.channel.DefaultChannelPipeline.checkMultiplicity(DefaultChannelPipeline.java:625)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:208)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:409)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:396)
at my.example.CustomInitializer.initChannel(CustomInitializer.java:35)
at my.example.CustomInitializer.initChannel(CustomInitializer.java:16)
at io.netty.channel.ChannelInitializer.initChannel(ChannelInitializer.java:113)
at io.netty.channel.ChannelInitializer.handlerAdded(ChannelInitializer.java:105)
at io.netty.channel.DefaultChannelPipeline.callHandlerAdded0(DefaultChannelPipeline.java:637)
这是我的初始化代码
CustomIniatializer
public class CustomIniatializer extends ChannelInitializer<SocketChannel> {
@Autowired
private ChannelDuplexHandler customHandler;
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(8*1024, true));
p.addLast(customHandler);
}
}
使用 ChannelInitializer
时必须记住的一件事是,每个打开的新连接都会调用方法 initChannel
。
任何需要唯一状态的东西都应该在这个方法中构造。
查看您的代码,我发现您正在正确创建 LoggingHandler
、HttpServerCodec
和 HttpObjectAggregator
的新实例,但您引用的是 "shared" 实例你的 customHandler
class.
虽然您可以通过在 initChannel
方法中使用 new CustomHandler ()
来解决您的问题,但实际上您通过使用 springs 自动装配系统显示出不同的意图。
我们还可以使用 2 个其他解决方案:
工厂模式
不是直接自动装配 ChannelDuplexHandler
的实例,而是需要装配一个生产此 class 实例的工厂:
public interface ChannelDuplexHandlerFactory {
public ChannelDuplexHandler getChannelDuplexHandler();
}
@Component
public class ChannelDuplexHandlerFactoryImplementation implements ChannelDuplexHandlerFactory {
public ChannelDuplexHandler getChannelDuplexHandler() {
return new CustomHandler();
}
}
public class CustomIniatializer extends ChannelInitializer<SocketChannel> {
@Autowired
private ChannelDuplexHandler customHandler;
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(8*1024, true));
p.addLast(customHandler.getChannelDuplexHandler());
}
}
使用基于频道的字段,而不是 class 个字段
您可以使用的另一种解决方案是将变量存储在当前通道内,这是一种更高级的技术,在某些情况下很有用:
@Component
@ChannelHandler.Sharable
public class CustomHandler extends ChannelDuplexHandler {
private static final AttributeKey<StringBuilder> BUF_KEY = AttributeKey.newInstance("BUF_KEY");
private static final AttributeKey<String> FOO_KEY = AttributeKey.newInstance("FOO_KEY");
private static final AttributeKey<String> VAL_KEY = AttributeKey.newInstance("VAL_KEY");
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Channel ch = ctx.channel();
final StringBuilder buf = ch.attr(BUF_KEY).get();
String foo = ch.attr(FOO_KEY).get();
String val = ch.attr(VAL_KEY).get();
// Parse the request and set the variables
if (foo != null) {
foo = request.getUri()
}
if (val != null) {
val = getQueryParamsOf("key");
}
buf.append(val);
ch.attr(FOO_KEY).set(foo);
ch.attr(VAL_KEY).set(val);
}
}
即使使用新的 Handler() 实现,我也遇到同样的问题
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
.....
p.addLast("httprequest", new HttpServerHandler(config));
}
}
HttpServerHander
private String uuid;
public HttpServerHandler(final StaticConfig staticConfig) {
this.uuid = UUID.randomUUID().toString();
}
这个 uuid 不是每次调用都是唯一的。
我注意到没有为每个 http 请求创建新的处理程序实例。我在实例级别定义的变量很少。这些值是根据要求设置的。通过仔细检查,我发现这些值不是新设置的,而是来自第一个请求的值。
这是我的处理程序代码
@Component
@ChannelHandler.Sharable
public class CustomHandler extends ChannelDuplexHandler {
private final StringBuilder buf = new StringBuilder();
private final String foo;
private final String val;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//parse the request and set the variables
if (foo!=null) {
foo = request.getUri()
}
if (val!=null) {
val = getQueryParamsOf("key");
}
buf.append(val);
}
}
缓冲区没有清除。对于每个新请求,我仍然看到旧缓冲区。
即如果我提出请求 /foobar?key=netty
我在第一次调用中看到 buf = netty。
随后的调用,buf = nettynetty 和 buf=nettynettynetty 等等。
此外,foo
和 val
变量在第一次请求后永远不会为 null。
我的理解是因为将为每个请求创建新的处理程序。但是因为我使用了 @ChannelHander.Sharable
可能是相同的处理程序被重用
所以我注释掉了 @ChannelHander.Sharable
,第一个请求顺利通过。从下一个请求中,我收到以下错误。
io.netty.channel.ChannelPipelineException: my.example.handlers.CustomHandler is not a @Sharable handler, so can't be added or removed multiple times.
at io.netty.channel.DefaultChannelPipeline.checkMultiplicity(DefaultChannelPipeline.java:625)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:208)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:409)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:396)
at my.example.CustomInitializer.initChannel(CustomInitializer.java:35)
at my.example.CustomInitializer.initChannel(CustomInitializer.java:16)
at io.netty.channel.ChannelInitializer.initChannel(ChannelInitializer.java:113)
at io.netty.channel.ChannelInitializer.handlerAdded(ChannelInitializer.java:105)
at io.netty.channel.DefaultChannelPipeline.callHandlerAdded0(DefaultChannelPipeline.java:637)
这是我的初始化代码
CustomIniatializer
public class CustomIniatializer extends ChannelInitializer<SocketChannel> {
@Autowired
private ChannelDuplexHandler customHandler;
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(8*1024, true));
p.addLast(customHandler);
}
}
使用 ChannelInitializer
时必须记住的一件事是,每个打开的新连接都会调用方法 initChannel
。
任何需要唯一状态的东西都应该在这个方法中构造。
查看您的代码,我发现您正在正确创建 LoggingHandler
、HttpServerCodec
和 HttpObjectAggregator
的新实例,但您引用的是 "shared" 实例你的 customHandler
class.
虽然您可以通过在 initChannel
方法中使用 new CustomHandler ()
来解决您的问题,但实际上您通过使用 springs 自动装配系统显示出不同的意图。
我们还可以使用 2 个其他解决方案:
工厂模式
不是直接自动装配 ChannelDuplexHandler
的实例,而是需要装配一个生产此 class 实例的工厂:
public interface ChannelDuplexHandlerFactory {
public ChannelDuplexHandler getChannelDuplexHandler();
}
@Component
public class ChannelDuplexHandlerFactoryImplementation implements ChannelDuplexHandlerFactory {
public ChannelDuplexHandler getChannelDuplexHandler() {
return new CustomHandler();
}
}
public class CustomIniatializer extends ChannelInitializer<SocketChannel> {
@Autowired
private ChannelDuplexHandler customHandler;
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(8*1024, true));
p.addLast(customHandler.getChannelDuplexHandler());
}
}
使用基于频道的字段,而不是 class 个字段
您可以使用的另一种解决方案是将变量存储在当前通道内,这是一种更高级的技术,在某些情况下很有用:
@Component
@ChannelHandler.Sharable
public class CustomHandler extends ChannelDuplexHandler {
private static final AttributeKey<StringBuilder> BUF_KEY = AttributeKey.newInstance("BUF_KEY");
private static final AttributeKey<String> FOO_KEY = AttributeKey.newInstance("FOO_KEY");
private static final AttributeKey<String> VAL_KEY = AttributeKey.newInstance("VAL_KEY");
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Channel ch = ctx.channel();
final StringBuilder buf = ch.attr(BUF_KEY).get();
String foo = ch.attr(FOO_KEY).get();
String val = ch.attr(VAL_KEY).get();
// Parse the request and set the variables
if (foo != null) {
foo = request.getUri()
}
if (val != null) {
val = getQueryParamsOf("key");
}
buf.append(val);
ch.attr(FOO_KEY).set(foo);
ch.attr(VAL_KEY).set(val);
}
}
即使使用新的 Handler() 实现,我也遇到同样的问题
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
.....
p.addLast("httprequest", new HttpServerHandler(config));
}
}
HttpServerHander
private String uuid;
public HttpServerHandler(final StaticConfig staticConfig) {
this.uuid = UUID.randomUUID().toString();
}
这个 uuid 不是每次调用都是唯一的。