### 01, Kažite princip i proces Netty-ja
01, Kažite princip i proces Netty-ja
Netty je visokoperformansni asinhroni događaj-no pogon framework za mrežne aplikacije baziran na Java NIO, u velikoj meri pojednostavljuje kompleksnost mrežnog programiranja.
Često se koristi za izgradnju RPC okvira, kako bi se poboljšala efikasnost komunikacije između distribuiranih servisa. Kao što se mrežni sloj Dubbo-a može implementirati na osnovu Netty-ja.

Netty podržava nulto kopiranje, proširiv model događaja; podržava TCP, UDP, HTTP, WebSocket i druge protokole; pruža siguran prenos, kompresiju, velike fajlove, enkoding/dekodiranje i druge funkcionalnosti.

Netty je implementiran na osnovu Master-Reactor obrasca, uglavnom podeljen u dve grupe niti:
①, glavna Reactor nit grupa (Boss Group)
Odgovorna je za obradu novih zahteva za povezivanje klijenata. Interno održava jednu ili više niti, svaka nit sadrži jedan Selector.
ServerSocketChannel se registruje na Selector-u BossGroup-a, samo obraća pažnju na OP_ACCEPT događaje, to jest nove zahteve za uspostavljanje veze.
Kada Selector BossGroup-a primi zahtev za povezivanje, koristi ServerSocketChannel.accept() metodu da prihvati novu vezu.
Prihvaćena nova veza se enkapsulira kao NioSocketChannel, i registruje se na Selector-u Worker Group-a.
②, podređena Reactor nit grupa (Worker Group)
WorkerGroup može imati više niti koje upravlja, svaka nit takođe održava sopstveni Selector. Netty će obično prema određenoj strategiji (kao što je rotaciono) izabrati jednog Selector-a da balansira opterećenje.
Svaki Selector je odgovoran za praćenje i obradu IO događaja svih registrovanih NioSocketChannel, kao što je čitanje (OP_READ), pisanje (OP_WRITE) događaja itd.
Kada se događaj desi, odgovarajući ChannelHandler se poziva da obradi te događaje. Ovi Handler-i mogu biti korisnički definisani procesori, korišćeni za implementaciju konkretne poslovne logike.

Molim vas kažite tok rada Netty-ja?
Ispod je jednostavan primer Netty servera i klijenta, pokazuje osnovni tok rada. U ovom primeru, server prima string poruke, pretvara ih u velika slova i vraća ih klijentu.
NettyServer:
public class NettyServer {
private final int port;
public NettyServer(int port) {
this.port = port;
}
public void start() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new ServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture future = bootstrap.bind(port).sync();
System.out.println("Server pokrenut na portu " + port);
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyServer(8080).start();
}
static class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String input = (String) msg;
System.out.println("Primljeno: " + input);
ctx.writeAndFlush(input.toUpperCase());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
}①, BossGroup i WorkerGroup:
Pri inicijalizaciji servera, prvo se kreiraju dve instance NioEventLoopGroup.
BossGroup se koristi za prihvatanje veza klijenata, WorkerGroup se koristi za obradu prenosa podataka nakon uspostavljanja veze.
- BossGroup prati zahteve za povezivanje na portu, kad god primi novu vezu, BossNioEventLoop će obraditi zahtev za povezivanje, prihvatiti vezu, i registrovati novi SocketChannel na jednom NioEventLoop-u WorkerGroup-a.
- Kad NioEventLoop WorkerGroup-a otkrije IO događaj (kao što je čitanje podataka),će prema registrovanom ChannelPipeline-u ChannelHandlers obrađivati te događaje. U primeru, nakon što server primi podatke, kroz ServerHandler pretvara podatke u velika slova i vraća ih klijentu.
②, ServerBootstrap: pomoćna klasa za pokretanje konfiguracije servera. Postavlja tip kanala koji server koristi na NioServerSocketChannel.
I za novooformirane veze definiše ChannelInitializer, u ovom inicijalizatoru konfiguriše ChannelPipeline, uključujući enkoder/dekoder i procesore poslovne logike.
NettyClient:
public class NettyClient {
private final String host;
private final int port;
public NettyClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new ClientHandler());
}
});
Channel channel = bootstrap.connect(host, port).sync().channel();
Scanner scanner = new Scanner(System.in);
while (true) {
String line = scanner.nextLine();
if ("quit".equalsIgnoreCase(line)) {
channel.close();
break;
}
channel.writeAndFlush(line);
}
channel.closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyClient("localhost", 8080).start();
}
static class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println("Primljeno sa servera: " + msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
}①, EventLoopGroup: klijent zahteva samo jedan NioEventLoopGroup da obradi sve operacije, uključujući uspostavljanje veze, slanje podataka i primanje podataka.
②, Bootstrap: pomoćna klasa za pokretanje konfiguracije klijenta. Postavlja tip kanala koji klijent koristi na NioSocketChannel.
Klijent koristi Scanner da čita korisnički unos iz komandne linije, šalje ga na server; istovremeno može primati podatke koje server vratio, i kroz ClientHandler ispisivati na konzolu.
