Новые обработчики не создаются для каждого запроса в netty - PullRequest
1 голос
/ 09 апреля 2019

Я заметил, что новые экземпляры обработчиков создаются не для каждого 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 никогда не равны нулю после первого запроса.

Насколько я понимаю, новый обработчик будет создаваться для каждого запроса. Но так как я использовал @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);
    }
}

1 Ответ

2 голосов
/ 09 апреля 2019

При использовании ChannelInitializer следует помнить, что метод initChannel вызывается для каждого нового открываемого соединения.

Все, что требует уникального состояния, должнобыть построенным внутри этого метода.

Глядя на ваш код, я вижу, что вы правильно создаете новые экземпляры LoggingHandler, HttpServerCodec и HttpObjectAggregator, но вы ссылаетесь на "общий" экземплярваш customHandler класс.

Хотя вы можете решить свою проблему, просто используя new CustomHandler () в методе initChannel, вы на самом деле демонстрируете различные намерения, используя систему автоматического подключения пружин.

ТамЕсть еще два решения, которые мы можем использовать:

Шаблон фабрики

Вместо прямого автоматического подключения экземпляра ChannelDuplexHandler, вам необходимо подключить фабрику, которая производит экземпляры этого класса:

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());
    }
}

Использование полей на основе канала вместо полей класса

Еще одно решение, которое вы можете использовать, - это переменные, хранящиеся внутри текущего канала, это болееЭлектронная продвинутая техника, которая может быть полезна в определенных ситуациях:

@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);
    }
}
...