Мы только что закончили создание сервера для хранения данных на диск и вышли с ним Netty. Во время тестирования нагрузки мы наблюдали масштабирование Netty до 8000 сообщений в секунду. Учитывая наши системы, это выглядело очень низким. Для эталона мы написали интерфейс Tomcat и выполнили те же тесты нагрузки. С помощью этих тестов мы получали примерно 25 000 сообщений в секунду.
Вот спецификации для нашей машины тестирования нагрузки:
- MacBook Pro Quad core
- 16 ГБ оперативной памяти
- Java 1.6
Вот настройка загрузки для Netty:
- 10 потоков
- 100 000 сообщений на поток
- Код сервера Netty (довольно стандартный) - наш конвейер Netty на сервере - это два обработчика: FrameDecoder и SimpleChannelHandler, которые обрабатывают запрос и ответ.
- Клиентский JIO с использованием Commons Pool для объединения и повторного использования соединений (пул был таким же, как и количество потоков)
Вот тестовая установка загрузки для Tomcat:
- 10 потоков
- 100 000 сообщений на поток
- Tomcat 7.0.16 с настройкой по умолчанию с использованием сервлета для вызова кода сервера
- Клиентская сторона использует URLConnection без объединения.
Мой главный вопрос - почему такие огромные различия в производительности? Есть ли что-то очевидное в отношении Netty, которое может заставить его работать быстрее, чем Tomcat?
Изменить: Вот главный код сервера Netty:
NioServerSocketChannelFactory factory = new NioServerSocketChannelFactory();
ServerBootstrap server = new ServerBootstrap(factory);
server.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
RequestDecoder decoder = injector.getInstance(RequestDecoder.class);
ContentStoreChannelHandler handler = injector.getInstance(ContentStoreChannelHandler.class);
return Channels.pipeline(decoder, handler);
}
});
server.setOption("child.tcpNoDelay", true);
server.setOption("child.keepAlive", true);
Channel channel = server.bind(new InetSocketAddress(port));
allChannels.add(channel);
Наши обработчики выглядят так:
public class RequestDecoder extends FrameDecoder {
@Override
protected ChannelBuffer decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) {
if (buffer.readableBytes() < 4) {
return null;
}
buffer.markReaderIndex();
int length = buffer.readInt();
if (buffer.readableBytes() < length) {
buffer.resetReaderIndex();
return null;
}
return buffer;
}
}
public class ContentStoreChannelHandler extends SimpleChannelHandler {
private final RequestHandler handler;
@Inject
public ContentStoreChannelHandler(RequestHandler handler) {
this.handler = handler;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer in = (ChannelBuffer) e.getMessage();
in.readerIndex(4);
ChannelBuffer out = ChannelBuffers.dynamicBuffer(512);
out.writerIndex(8); // Skip the length and status code
boolean success = handler.handle(new ChannelBufferInputStream(in), new ChannelBufferOutputStream(out), new NettyErrorStream(out));
if (success) {
out.setInt(0, out.writerIndex() - 8); // length
out.setInt(4, 0); // Status
}
Channels.write(e.getChannel(), out, e.getRemoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
Throwable throwable = e.getCause();
ChannelBuffer out = ChannelBuffers.dynamicBuffer(8);
out.writeInt(0); // Length
out.writeInt(Errors.generalException.getCode()); // status
Channels.write(ctx, e.getFuture(), out);
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
NettyContentStoreServer.allChannels.add(e.getChannel());
}
}
ОБНОВЛЕНИЕ:
Мне удалось получить решение Netty в пределах 4000/сек. Несколько недель назад я тестировал клиентскую сторону PING в моем пуле соединений как безопасную защиту от простаивающих сокетов, но я забыл удалить этот код, прежде чем начать нагрузочное тестирование. Этот код эффективно PINGed сервер каждый раз, когда Socket был извлечен из пула (используя Commons Pool). Я прокомментировал этот код, и теперь я получаю 21 000/секунд с Netty и 25 000 в секунду с Tomcat.
Хотя это отличная новость на стороне Netty, я по-прежнему получаю 4,000/second меньше с Netty, чем с Tomcat. Я могу опубликовать свою клиентскую сторону (что, по-моему, я исключаю, но, по-видимому, нет), если кто-то заинтересован в этом.