Я написал код клиента netty для отправки обработанных данных нескольким клиентам. После работы в течение 3-4 часов я исчерпываю все розетки, и соединения невозможны. Также, когда я проверяю состояния сокетов в ОС, большое количество сокетов находится в состоянии TIME_WAIT.
public class NettyClient {
private static LogHelper logger = new LogHelper(NettyClient.class);
private static EventLoopGroup workerGroup = new NioEventLoopGroup();
private static Bootstrap nettyClient = new Bootstrap()
.group(workerGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
private URL url;
private RequestVo Req;
private ChannelFuture chFuture;
private Object ReportData;
private JAXBContext jbContext;
private static final int CHANNEL_READ_TIMEOUT = 5;
public NettyClient() {
// TODO Auto-generated constructor stub
}
public NettyClient(RequestVo Req, JAXBContext jbCtx,Object data) {
this.Req = Req;
this.ReportData = data;
this.jbContext = jbCtx;
}
public void sendRequest() {
logger.debug("In sendRequest()");
//ChannelFuture chFuture = null;
try {
this.url = new URL(Req.getPushAddress());
//add handlers
nettyClient.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast("timeout",
new ReadTimeoutHandler(CHANNEL_READ_TIMEOUT, TimeUnit.SECONDS));
ch.pipeline()
.addLast("codec", new HttpClientCodec());
ch.pipeline()
.addLast("inbound",
new NettyClientInBoundHandler(Req, jbContext, ReportData));
}
});
//make a connection to the Client
int port = url.getPort() == -1? url.getDefaultPort():url.getPort();
chFuture = nettyClient.connect(url.getHost(), port);
chFuture.addListener(new NettyClientConnectionListener(this.Req.getRequestId()));
} catch (Exception e) {
logger.error("Exception: Failed to connect to Client ", e);
} finally {
}
}
}
Вот методы из класса ChannelInBoundHandler
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
{
Map<String, String> props = new HashMap<String, String>();
if (msg instanceof HttpResponse) {
logger.debug("channelRead()");
HttpResponse httpRes = (HttpResponse) msg;
HttpResponseStatus httpStatus = httpRes.status();
props.put(REQUEST_ID, this.Request.getRequestId());
props.put(CLIENT_RESPONSE_CODE, String.valueOf(httpStatus.code()));
JmsService.getInstance(DESTINATION).sendTextMessage(props, "");
logger.debug("channelRead() HttpResponse Code: " + httpStatus.code());
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
Map<String, String> props = new HashMap<String, String>();
logger.error("exceptionCaught()", cause);
if (cause instanceof ReadTimeoutException) {
//If read-timeout, send back the response
props.put(REQUEST_ID, this.Request.getRequestId());
props.put(CLIENT_RESPONSE_CODE,
String.valueOf(HttpResponseStatus.REQUEST_TIMEOUT.code()));
JmsService.getInstance(DESTINATION).sendTextMessage(props, "");
ctx.close();
}
else {
logger.error("Exception: ", cause);
}
}
Любая идея, что не так в коде, мне очень поможет. Спасибо
Также вы уверены, что правильно закрываете все Channel
?
Да, я закрываю канал после исключения тайм-аута чтения или после получения ответа.
Можно ли повторно использовать открытое соединение вместо того, чтобы каждый раз открывать новое?
Вы создаете исходящие соединения быстрее, чем можно освободить их локальные порты. Вам следует попытаться сохранить исходящие соединения, по возможности используя HTTP keepalive.
Я не знаком с нетти, но думаю, что смогу объяснить часть вашей проблемы и, надеюсь, помочь вам на этом пути:
Когда вы используете порт, а затем закрываете его, порт не будет автоматически доступен для использования другими процессами сразу. Вместо этого он перейдет в состояние TIME_WAIT
на определенный период времени. Для Windows это будет 240 секунд (четыре минуты).
Я предполагаю, что ваш код медленно использует все доступные порты в вашей системе, так как выход портов из состояния TIME_WAIT
происходит слишком медленно.
Мне не совсем понятно, откуда берутся фактические номера портов (возможно, они автоматически генерируются url.getDefaultPort()
?), Но, возможно, вы сможете найти способ их повторно использовать? Если вы можете сохранить одно или несколько открытых соединений и как-то повторно использовать их, то вы сможете уменьшить частоту запросов на новые порты настолько, чтобы закрытые порты вышли из своего состояния TIME_WAIT
.
Порты создаются путем создания новых исходящих подключений. url.getDefaultPort()
тут ни при чем.
вы можете попробовать bootstrap.setOption ("reuseAddress", true);