未为netty中的每个请求创建新处理程序

未为netty中的每个请求创建新处理程序,netty,Netty,我注意到没有为每个http请求创建新的处理程序实例。我有几个在实例级别定义的变量。这些值是根据请求设置的。通过仔细检查,我发现这些值不是新设置的,而是来自第一个请求的值 这是我的处理程序代码 @Component @ChannelHandler.Sharable public class CustomHandler extends ChannelDuplexHandler { private final StringBuilder buf = new StringBuilder(); priv

我注意到没有为每个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);
        }
}
缓冲区没有被清除。对于每个新请求,我仍然看到旧的缓冲区。 i、 e如果我提出请求
/foobar?key=netty

我在第一次通话中看到buf=netty。 后续调用,buf=nettynetty和buf=nettynettynetty等等。 此外,
foo
val
变量在第一次请求后从不为空

我的理解是,因为将为每个请求创建新的处理程序。但由于我使用了
@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)
这是我的初始化代码

定制者

    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);
    }
}
public类custominizatizer扩展了ChannelInitializer{
@自动连线
专用通道DuplexHandler customHandler;
@凌驾
公共频道(SocketChannel ch){
ChannelPipeline p=通道管道();
p、 addLast(新的LoggingHandler(LogLevel.INFO));
p、 addLast(新的HttpServerCodec());
p、 addLast(新的HttpObjectAggregator(8*1024,true));
p、 addLast(customHandler);
}
}

使用
通道初始值设定项时,必须记住的一点是,对于每个打开的新连接,都会调用方法
initChannel

任何需要唯一状态的东西都应该在这个方法中构造

查看您的代码,我发现您正在正确地创建
LoggingHandler
HttpServerCodec
HttpObjectAggregator
的新实例,但您引用的是
customHandler
类的“共享”实例

虽然只需在
initChannel
方法中使用
new CustomHandler()
就可以解决问题,但使用springs autowire系统实际上显示了不同的意图

我们还可以使用另外两种解决方案:

工厂模式 与直接自动关联
ChannelDuplexHandler的实例不同,您需要关联一个生成此类实例的工厂:

公共接口通道DuplexHandlerFactory{
公共ChannelDuplexHandler getChannelDuplexHandler();
}
@组成部分
公共类ChannelDuplexHandlerFactory实现实现ChannelDuplexHandlerFactory{
公共ChannelDuplexHandler getChannelDuplexHandler(){
返回新的CustomHandler();
}
}
公共类custominizatizer扩展了通道初始值设定项{
@自动连线
专用通道DuplexHandler customHandler;
@凌驾
公共频道(SocketChannel ch){
ChannelPipeline p=通道管道();
p、 addLast(新的LoggingHandler(LogLevel.INFO));
p、 addLast(新的HttpServerCodec());
p、 addLast(新的HttpObjectAggregator(8*1024,true));
p、 addLast(customHandler.getChannelDuplexHandler());
}
}
使用基于通道的字段,而不是类字段 您可以使用的另一种解决方案是存储在当前通道内的变量,这是一种更先进的技术,可用于某些情况:

@组件
@ChannelHandler.可共享
公共类CustomHandler扩展了ChannelDuplexHandler{
私有静态final AttributeKey BUF_KEY=AttributeKey.newInstance(“BUF_KEY”);
私有静态final AttributeKey FOO_KEY=AttributeKey.newInstance(“FOO_KEY”);
私有静态最终AttributeKey VAL_KEY=AttributeKey.newInstance(“VAL_KEY”);
@凌驾
public void channelRead(ChannelHandlerContext ctx,Object msg){
通道ch=ctx.Channel();
最终StringBuilder buf=ch.attr(buf_键).get();
字符串foo=ch.attr(foo_KEY).get();
字符串val=ch.attr(val_KEY).get();
//解析请求并设置变量
如果(foo!=null){
foo=request.getUri()
}
如果(val!=null){
val=getQueryParamsOf(“键”);
}
buf.追加(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));
   }
}

这个uuid对于每个调用都不是唯一的。

我从实例到方法级范围删除了两个变量(buf和foo)。但我无法避免val,所以我使用了基于AttributeKey字段的方法,因为在我的用例中这看起来更优雅
private String uuid;

public HttpServerHandler(final StaticConfig staticConfig) {

    this.uuid = UUID.randomUUID().toString();

}