2015-03-02 20 views
8

Mam problem z zasobami w mojej aplikacji serwera netty.Serwer Netty nie zamyka/zwalnia gniazda

[io.netty.channel.DefaultChannelPipeline] An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.: java.io.IOException: Too many open files 
    at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) [rt.jar:1.7.0_60] 
    at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:241) [rt.jar:1.7.0_60] 
    at io.netty.channel.socket.nio.NioServerSocketChannel.doReadMessages(NioServerSocketChannel.java:135) [netty-all-4.0.25.Final.jar:4.0.25.Final] 
    at io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe.read(AbstractNioMessageChannel.java:69) [netty-all-4.0.25.Final.jar:4.0.25.Final] 
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511) [netty-all-4.0.25.Final.jar:4.0.25.Final] 
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468) [netty-all-4.0.25.Final.jar:4.0.25.Final] 
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382) [netty-all-4.0.25.Final.jar:4.0.25.Final] 
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354) [netty-all-4.0.25.Final.jar:4.0.25.Final] 
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116) [netty-all-4.0.25.Final.jar:4.0.25.Final] 
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137) [netty-all-4.0.25.Final.jar:4.0.25.Final] 
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_60] 

Jako obejście I zwiększyła maksymalnie otwarte pliki z ulimit -n, ale nadal mogę whatch liczba plików/gniazd rosnącą:

lsof -p 5604 | grep socket | wc -l 

teraz ponad 3000 ...

nie widać żadnych otwartych lub wiszące połączenia z netstat ...

używam ReadTimeoutHandler zamknąć nieużywane połączenia z następującym kodem exceptionHandler:

@Override 
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
    if (cause instanceof ReadTimeoutException) { 
    logger.debug("Read timeout - close connection"); 
    } else { 
    logger.info(cause.getMessage()); 
    } 
    ctx.close(); 
} 

Server bootstrap wygląda następująco:

ServerBootstrap b = new ServerBootstrap(); 
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() { 
@Override 
public void initChannel(SocketChannel ch) throws Exception { 
    ch.pipeline().addLast(new ReadTimeoutHandler(60)); 
    ch.pipeline().addLast(new LoggingHandler(mySpec.getPortLookupKey().toLowerCase())); 
    ch.pipeline().addLast(new RawMessageEncoder()); 
    ch.pipeline().addLast(new RawMessageDecoder()); 
    ch.pipeline().addLast(new RequestServerHandler(ctx.getWorkManager(), factory)); 
} 
}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true); 

ChannelFuture channelFuture = b.bind(port).sync(); 

Coś mnie ominęło? Czy liczba otwartych plików nie powinna spadać po zamknięciu połączenia (przez zdalny host lub przez funkcję timeout)?

Co muszę zmienić, aby zapisać zasoby tutaj?

Aktualizacja: Używam netty 4.0.25

Aktualizacja 2: Zgodnie z wnioskiem przeniosłem obsługi rejestrowania przed ReadTimeouthandler, oto logi. sytuację, w której klient normalnie rozłącza:

09:41:39,755 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 => /127.0.0.1:4300] REGISTERED 
09:41:39,756 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 => /127.0.0.1:4300] ACTIVE 
09:41:39,810 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 => /127.0.0.1:4300] RECEIVED(1024B) 
09:41:39,813 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 => /127.0.0.1:4300] RECEIVED(1024B) 
09:41:39,814 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 => /127.0.0.1:4300] RECEIVED(150B) 
09:41:40,854 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 => /127.0.0.1:4300] WRITE(1385B) 
09:41:40,855 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 => /127.0.0.1:4300] FLUSH 
09:41:40,861 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 :> /127.0.0.1:4300] INACTIVE 
09:41:40,864 [3-1] [id: 0xca6601a2, /127.0.0.1:64258 :> /127.0.0.1:4300] UNREGISTERED 

sytuacja, w której klient nie odłączać:

10:04:24,104 [3-1] [id: 0x48076684, /127.0.0.1:50525 => /127.0.0.1:4300] REGISTERED 
10:04:24,107 [3-1] [id: 0x48076684, /127.0.0.1:50525 => /127.0.0.1:4300] ACTIVE 
10:04:24,594 [3-1] [id: 0x48076684, /127.0.0.1:50525 => /127.0.0.1:4300] RECEIVED(1024B) 
10:04:24,597 [3-1] [id: 0x48076684, /127.0.0.1:50525 => /127.0.0.1:4300] RECEIVED(1024B) 
10:04:24,598 [3-1] [id: 0x48076684, /127.0.0.1:50525 => /127.0.0.1:4300] RECEIVED(150B) 
10:04:25,638 [3-1] [id: 0x48076684, /127.0.0.1:50525 => /127.0.0.1:4300] WRITE(1383B) 
10:04:25,639 [3-1] [id: 0x48076684, /127.0.0.1:50525 => /127.0.0.1:4300] FLUSH 
10:05:25,389 [3-1] [id: 0x48076684, /127.0.0.1:50525 => /127.0.0.1:4300] CLOSE() 
10:05:25,390 [3-1] [id: 0x48076684, /127.0.0.1:50525 :> /127.0.0.1:4300] CLOSE() 
10:05:25,390 [3-1] [id: 0x48076684, /127.0.0.1:50525 :> /127.0.0.1:4300] INACTIVE 
10:05:25,394 [3-1] [id: 0x48076684, /127.0.0.1:50525 :> /127.0.0.1:4300] UNREGISTERED 

Więc nie ma 60s przerwa przed zamknięciem (jak oczekiwano od ReadTimeoutHandler)

Po trochę więcej analiz, mam wrażenie, że nawet przy normalnym rozłączeniu z klientem rośnie liczba otwartych plików! Ponadto, nie ma ZAMKNIJ() w tej sytuacji ...

+0

której używasz wersji Netty? – HCarrasko

+0

Czy można wstawić 'LoggingHandler' z wystarczająco wysokim poziomem dziennika przed' ReadTimeoutHandler' w potoku i zaktualizować swoje pytania za pomocą dziennika? – trustin

+0

Być może jest to coś więcej związanego z SO, te połączenia wydają mi się całkiem normalne i nigdy nie widziałem takiego problemu. Jest nowa wersja Netty 4.0.28, czy możesz spróbować z nią? – crigore

Odpowiedz

1

Być może jest to związane z tym Netty emisji https://github.com/netty/netty/issues/1731

Jest to oczekiwane zachowanie, a to nic nie można zmienić. JVM sygnalizuje, że nie może zaakceptować kanału - dlatego też nie można zainicjować żadnego połączenia i nie można wysłać żadnej odpowiedzi. Klient zobaczy błąd połączenia. Jeśli masz moduł równoważenia obciążenia, powinien on ponowić próbę na alternatywnym hoście lub zwrócić 503 w imieniu aplikacji użytkownika.