Новые обработчики не создаются для каждого запроса в 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=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);
    }
}
Альтернативные WebSockets для netty/java: удвоение пропускной способности небольших сообщений
Альтернативные WebSockets для netty/java: удвоение пропускной способности небольших сообщений
Этот пост - краткая презентация netty-websocket-http1 - альтернативной netty/java реализации RFC6455 - протокола WebSocket.
1
0
687
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Одна из вещей, которую вы должны помнить при использовании ChannelInitializer, это тот факт, что метод initChannel вызывается для каждого нового открываемого соединения.

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

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

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

Есть 2 других решения, которые мы можем использовать:

Заводская выкройка

Вместо прямого автоматического подключения экземпляра 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);
    }
}

Я удалил пару переменных (buf и foo) из экземпляра в область уровня метода. Но я не мог избежать val. поэтому я использовал поле AttributeKey на основе поля, поскольку в моем случае это кажется более элегантным.

brain storm 09.04.2019 23:18

У меня такая же проблема даже с новой реализацией 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 не уникален для каждого вызова.

Другие вопросы по теме