没有为 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 = nettynettybuf=nettynettynetty 等等。 此外,fooval 变量在第一次请求后永远不会为 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

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

查看您的代码,我发现您正在正确创建 LoggingHandlerHttpServerCodecHttpObjectAggregator 的新实例,但您引用的是 "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 不是每次调用都是唯一的。