У меня есть микросервис на основе Spring Reactive, который использует Netty Server версии 4.1.101.Final. Он работал совершенно нормально и обрабатывал все отправленные мной HTTP-запросы. Теперь я хочу перехватить идентификаторы соединений, чтобы выяснить, какое соединение используется, а какое закрывается. Для этого я написал собственный обработчик ведения журнала подключений. Ниже приведен мой ConnectionLoggingHandler:
public class ConnectionLoggingHandler extends ChannelInboundHandlerAdapter{
private static final AtomicInteger connectionIdCounter = new AtomicInteger(0);
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception{
int connectionId=connectionIdCounter.incrementAndGet();
System.out.println("Connection established : ID = " + connectionId);
ctx.channel().attr(AttributeKey.valueOf("connectionId")).set(connectionId);
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception{
//retrieve connectionID
Integer connectionId = (Integer) ctx.channel().attr(AttributeKey.valueOf("connectionId")).get();
if (connectionId!=null){
System.out.println("Connection closed : ID = " + connectionId);
}
super.channelInactive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception{
//retrieve connectionID
Integer connectionId = (Integer) ctx.channel().attr(AttributeKey.valueOf("connectionId")).get();
if (connectionId!=null){
System.out.println("Exception in Connection: ID = " + connectionId);
}
cause.printStackTrace();
ctx.close();
}
}
Теперь я хочу добавить этот обработчик на свой сервер Netty. Я попробовал это двумя способами.
Код для пользовательского сервера Netty:
public class CustomNettyServer{
private int port =8080;
@PostConstruct
public void startServer() throws InterruptedException{
EventLoopGroup boss= new NioEventLoopGroup();
EventLoopGroup worker=new NioEventLoopGroup();
try{
ServerBootstrap bootstrap new ServerBootstrap();
bootstrap.group(boss,worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<Channel>(){
@Override
protected void initChannel(Channel ch) throws Exception{
ch.pipeline().addLast(new HttpServerCodec(),new HttpRequestDecoder(),new HttpContentDecompressor(),new HttpResponseEncoder(),new HttpContentCompressor(),new HttpObjectAggregator(512*1024),new ConnectionLoggingHandler());
}
});
Channel channel = bootstarp.bind(port).sync().channel();
channel.closeFuture().sync();
}
finally{
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
Когда я использую этот сервер, я могу видеть журналы, присутствующие в моем пользовательском обработчике, когда я отправляю через Postman запрос на установление соединения. Однако мой HTTP-запрос не обрабатывается.
Пишет: входящее сообщение отброшено. Пожалуйста, проверьте конфигурацию вашего конвейера. Я не знаю, чего мне здесь не хватает.
Затем я попробовал еще один способ настройки моего сервера Netty. Ниже приведен мой код для CustomNettyServer.
public class CustomNettyServer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory>{
@Override
public void customize(NettyReactiveWebServerFactory factory){
factory.addServerCustomizers(server->
server.tcpConfiguration(tcp->
tcp.doOnConnection(connection->
connection.addHandlerLast(new ConnectionLoggingHandler()))));
}
}
Используя этот метод, я не вижу печати журналов из-за моего ConnectionLoggingHandler всякий раз, когда я нажимаю на запрос и запрос успешно обрабатывается. Я вижу, как обработчик добавляется в конвейер при отправке запроса и удаляется при его обслуживании. Но логи не генерируются. Я также сохранил уровень ведения журнала DEBUG.
Я не знаю, что я делаю не так в этих двух отношениях, и застрял здесь.
Поэтому я хочу знать, как получить журналы идентификаторов соединений, которые открываются, используются и закрываются.
Спасибо.
Reactor Netty (среда выполнения по умолчанию, используемая Spring WebFlux) не использует классы Bootstrap, предоставляемые Netty.
Правильный способ расширения Reactor Netty — через обратные вызовы жизненного цикла .
В вашем примере вы использовали doOnConnection
, но на данный момент канал уже активен, и ваша реализация под ConnectionLoggingHandler#channelActive
никогда не будет вызываться (проверьте javadoc на предмет addHandlerLast).
Вам лучше использовать обратный вызов doOnChannelInit
, как показано ниже.
@Component
public class CustomNettyServer
implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
@Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(server ->
server.doOnChannelInit((obs, ch, addr) ->
ch.pipeline().addAfter(NettyPipeline.HttpCodec, "test", new ConnectionLoggingHandler())));
}
}
Пожалуйста, проверьте это projectreactor.io/docs/netty/release/reference/…
Это заставило мой ConnectionLoggingHanlder работать. Но я не могу понять одного.... Когда я нажимаю на запрос, я вижу, что два соединения устанавливаются, а затем одно закрывается, а другое используется для обслуживания запроса. Почему он устанавливает два соединения? и когда будет закрыто другое соединение? Есть ли по умолчанию тайм-аут простоя, тайм-аут соединения или тайм-аут поддержания активности в netty?