Netty

Netty is an asynchronous event-driven network application framework

for rapid development of maintainable high performance protocol servers & clients.

  • Unified API for various transport types - blocking and non-blocking socket

  • Based on a flexible and extensible event model which allows clear separation of concerns

  • Highly customizable thread model - single thread, one or more thread pools such as SEDA

  • True connectionless datagram socket support (since 3.1)

user-guide-for-4.x

Hello World

从最简单的 Discard 开始。

这里查看完整示例代码

  • jar 引入
  [xml]
1
2
3
4
5
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.17.Final</version> </dependency>

Server

  • DiscardServerHandler.java

服务端的处理器。直接忽略信息。

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * Handles a server-side channel. */ public class DiscardServerHandler extends SimpleChannelInboundHandler<Object> { @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { // discard } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // Close the connection when an exception is raised. cause.printStackTrace(); ctx.close(); } }
  • DiscardServer.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.SelfSignedCertificate; /** * Discards any incoming data. */ public final class DiscardServer { static final boolean SSL = System.getProperty("ssl") != null; static final int PORT = Integer.parseInt(System.getProperty("port", "8009")); public static void main(String[] args) throws Exception { // Configure SSL. final SslContext sslCtx; if (SSL) { SelfSignedCertificate ssc = new SelfSignedCertificate(); sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); } else { sslCtx = null; } EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc())); } p.addLast(new DiscardServerHandler()); } }); // Bind and start to accept incoming connections. ChannelFuture f = b.bind(PORT).sync(); // Wait until the server socket is closed. // In this example, this does not happen, but you can do that to gracefully // shut down your server. f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } }

运行 main() 可以启动服务。日志如下:

  [plaintext]
1
2
3
4
5
6
十一月 16, 2017 8:19:59 下午 io.netty.handler.logging.LoggingHandler channelRegistered 信息: [id: 0xbd9cae9f] REGISTERED 十一月 16, 2017 8:19:59 下午 io.netty.handler.logging.LoggingHandler bind 信息: [id: 0xbd9cae9f] BIND: 0.0.0.0/0.0.0.0:8009 十一月 16, 2017 8:19:59 下午 io.netty.handler.logging.LoggingHandler channelActive 信息: [id: 0xbd9cae9f, L:/0:0:0:0:0:0:0:0:8009] ACTIVE

Client

  • DiscardClientHandler.java

客户端处理器代码。

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * Handles a client-side channel. */ public class DiscardClientHandler extends SimpleChannelInboundHandler<Object> { private ByteBuf content; private ChannelHandlerContext ctx; @Override public void channelActive(ChannelHandlerContext ctx) { this.ctx = ctx; // Initialize the message. content = ctx.alloc().directBuffer(DiscardClient.SIZE).writeZero(DiscardClient.SIZE); // Send the initial messages. generateTraffic(); } @Override public void channelInactive(ChannelHandlerContext ctx) { content.release(); } @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { // Server is supposed to send nothing, but if it sends something, discard it. } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // Close the connection when an exception is raised. cause.printStackTrace(); ctx.close(); } long counter; private void generateTraffic() { // Flush the outbound buffer to the socket. // Once flushed, generate the same amount of traffic again. ctx.writeAndFlush(content.retainedDuplicate()).addListener(trafficGenerator); } private final ChannelFutureListener trafficGenerator = new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { if (future.isSuccess()) { generateTraffic(); } else { future.cause().printStackTrace(); future.channel().close(); } } }; }
  • DiscardClient.java

客户端代码。

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; /** * Keeps sending random data to the specified address. */ public final class DiscardClient { static final boolean SSL = System.getProperty("ssl") != null; static final String HOST = System.getProperty("host", "127.0.0.1"); static final int PORT = Integer.parseInt(System.getProperty("port", "8009")); static final int SIZE = Integer.parseInt(System.getProperty("size", "256")); public static void main(String[] args) throws Exception { // Configure SSL. final SslContext sslCtx; if (SSL) { sslCtx = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); } else { sslCtx = null; } EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); } p.addLast(new DiscardClientHandler()); } }); // Make the connection attempt. ChannelFuture f = b.connect(HOST, PORT).sync(); // Wait until the connection is closed. f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } }

运行 main() 方法,此时在服务器端会新增日志如下:

  [plaintext]
1
2
3
4
十一月 16, 2017 8:22:17 下午 io.netty.handler.logging.LoggingHandler channelRead 信息: [id: 0xbd9cae9f, L:/0:0:0:0:0:0:0:0:8009] READ: [id: 0xb998910b, L:/127.0.0.1:8009 - R:/127.0.0.1:64593] 十一月 16, 2017 8:22:17 下午 io.netty.handler.logging.LoggingHandler channelReadComplete 信息: [id: 0xbd9cae9f, L:/0:0:0:0:0:0:0:0:8009] READ COMPLETE

netty 学习路线

netty 是进阶的必备技能。

java nio

java nio

java 并发

java 并发

jmm

jmm

实战

比如手写 grpc/dubbo 的等框架,深入理解实现机制。

参考资料

Netty学习和进阶策略