Série Java Networking · 04
Selector: milhares de conexões numa thread só
new Thread(() -> handle(server.accept())).start(); // uma thread por cliente01 / 06
Bloqueante: cada conexão prende uma thread
No servidor da prancha 02, cada cliente ganha uma thread que passa quase o tempo todo parada no read(). Com 10.000 clientes são 10.000 threads, cada uma com a sua stack, e a CPU gasta tempo trocando de uma pra outra. Mude o número de conexões e veja a conta.
Conexões simultâneas
Use ← e → pra trocar de cena, espaço pra pausar.
Pra fixar
- Bloqueante: uma thread por conexão, quase sempre parada no read(). Com milhares de clientes, a memória das stacks e a troca de contexto pesam.
- configureBlocking(false) faz accept(), read() e write() voltarem na hora com o que tiver; read() pode devolver 0.
- O canal se registra no Selector com os eventos de interesse (OP_ACCEPT, OP_CONNECT, OP_READ, OP_WRITE). Cada registro é uma SelectionKey.
- select() bloqueia até algum canal ficar pronto e devolve quantos estão; selectedKeys() traz só esses.
- Tire a chave do selectedKeys() depois de atender (it.remove()), senão ela volta no próximo select().
- É a base do Netty, do conector NIO do Tomcat e do cliente do Kafka: poucas threads, cada uma com o seu Selector.
O loop de eventos
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // sleeps until some socket is ready
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove(); // or it comes back on the next select()
if (key.isAcceptable()) {
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
if (client.read((ByteBuffer) key.attachment()) == -1) client.close();
}
}
}Próxima prancha: UDP (em breve).
Caramelo