概述

官方地址https://netty.io/
Github:https://github.com/netty/netty
图片.png

什么是netty

Netty is an asynchronous event-driven network application framework
for rapid development of maintainable high performance protocol servers & clients.
本质: 网络应用框架
实现: 异步,事件驱动
特性: 快速开发,可维护的,高性能
用途: 服务器和客户端开发

echo example

  1. import io.netty.bootstrap.ServerBootstrap;
  2. import io.netty.channel.ChannelFuture;
  3. import io.netty.channel.ChannelInitializer;
  4. import io.netty.channel.EventLoopGroup;
  5. import io.netty.channel.nio.NioEventLoopGroup;
  6. import io.netty.channel.socket.SocketChannel;
  7. import io.netty.channel.socket.nio.NioServerSocketChannel;
  8. public class NettyServer {
  9. public static void main(String[] args) {
  10. //配置服务端NIO线程组
  11. EventLoopGroup bossGroup = new NioEventLoopGroup();
  12. EventLoopGroup workGroup = new NioEventLoopGroup();
  13. try {
  14. ServerBootstrap b = new ServerBootstrap();
  15. b.group(bossGroup, workGroup)
  16. .channel(NioServerSocketChannel.class)
  17. .childHandler(new ChannelInitializer<SocketChannel>() {
  18. @Override
  19. protected void initChannel(SocketChannel channel) throws Exception {
  20. System.out.println("有客户端链接到本服务端");
  21. System.out.println("IP:" + channel.localAddress().getHostString());
  22. System.out.println("Port:" + channel.localAddress().getPort());
  23. }
  24. });
  25. ChannelFuture f = b.bind(8080).sync();
  26. f.channel().closeFuture().sync();
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. } finally {
  30. workGroup.shutdownGracefully();
  31. bossGroup.shutdownGracefully();
  32. }
  33. }
  34. }

官方案例https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/echo

1.声明线程池

  1. EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  2. EventLoopGroup workerGroup = new NioEventLoopGroup();

声明两个 Group,一个是 bossGroup,用于处理 Accept 事件,一个是 workerGroup,用于处理消息的读写事件。

2.创建服务端引导类

引导类,是用来集成所有配置,引导程序加载的,分成两种,一种是客户端引导类 Bootstrap(个人觉得叫 ClientBootstrap 可能更贴切),另一种是服务端引导类 ServerBootstrap,我们这里编写的是服务端程序,创建的当然是服务端引导类。
注意,Bootstrap 和 ServerBootstrap 之间并不是继承关系,他们是平等的,都继承了 AbstractBootstrap 抽象类。

  1. ServerBootstrap serverBootstrap = new ServerBootstrap();

3.设置线程池

把第一步声明的线程池设置到 ServerBootstrap 中,它说明了 Netty 应用程序以什么样的线程模型运行,正如前面所说 bossGroup 负责接受(Accept)连接,workerGroup 负责读写数据。

  1. serverBootstrap.group(bossGroup, workerGroup);

4.设置 ServerSocketChannel 类型

如果您需要使用阻塞型 IO 模型,直接把 Nio 改成 Oio 就可以了,即 OioServerSocketChannel,不过它已经废弃了,所以不建议。
另外,如果您的程序运行在 Linux 系统上,还可以使用一种更高效的方式,即 EpollServerSocketChannel,它使用的是 Linux 系统上的 epoll 模型,比 select 模型更高效,可见 Netty 把性能优化做到了极致。

  1. serverBootstrap.channel(NioServerSocketChannel.class);

5.设置参数(可选)

设置 Netty 中可以使用的任何参数,这些参数都在 ChannelOption 及其子类中,后面我们会详细介绍各个参数的含义,不过,很遗憾地告诉你,大多数情况下并不需要修改 Netty 的默认参数,这就是 Netty 比较牛的地方。

  1. serverBootstrap.option(ChannelOption.SO_BACKLOG, 100);

serverBootstrap.option(ChannelOption.SO_BACKLOG, 100);
我们这里设置了一个 SO_BACKLOG 系统参数,它表示的是最大等待连接数量。

6. 设置 Handler(可选)

设置 ServerSocketChannel 对应的 Handler,注意只能设置一个,它会在 SocketChannel 建立起来之前执行,等我们看源码的时候会详细介绍它的执行时机。

  1. serverBootstrap.handler(new LoggingHandler(LogLevel.INFO))

我们这里简单地设置一个打印日志的 Handler。

7. 编写并设置子 Handler(必须)

Netty 中的 Handler 分成两种,一种叫做 Inbound,一种叫做 Outbound。我们这里简单地写一个 Inbound 类型的 Handler,它接收到数据后立即写回客户端。

  1. public class EchoServerHandler extends ChannelInboundHandlerAdapter {
  2. @Override
  3. public void channelRead(ChannelHandlerContext ctx, Object msg) {
  4. // 读取数据后写回客户端
  5. ctx.write(msg);
  6. }
  7. @Override
  8. public void channelReadComplete(ChannelHandlerContext ctx) {
  9. ctx.flush();
  10. }
  11. @Override
  12. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
  13. cause.printStackTrace();
  14. ctx.close();
  15. }
  16. }

设置子 Handler 设置的是 SocketChannel 对应的 Handler,注意也是只能设置一个,它用于处理 SocketChannel 的事件。

  1. serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
  2. @Override
  3. public void initChannel(SocketChannel ch) throws Exception {
  4. ChannelPipeline p = ch.pipeline();
  5. // 可以添加多个子Handler
  6. p.addLast(new LoggingHandler(LogLevel.INFO));
  7. p.addLast(new EchoServerHandler());
  8. }
  9. });

虽然只能设置一个,但是 Netty 的提供了一种可以设置多个 Handler 的途径,即使用 ChannelInitializer 方式,当然,第六步的设置 Handler 也可以使用这种方式设置多个 Handler。
这里,我们设置了一个打印的 Handler 和一个自定义的 EchoServerHandler。

8. 绑定端口(必须)

绑定端口,并启动服务端程序,sync () 会阻塞直到启动完成才执行后面的代码。

  1. ChannelFuture f = serverBootstrap.bind(PORT).sync();

9. 等待服务端端口关闭(必须)

等待服务端监听端口关闭,sync () 会阻塞主线程,内部调用的是 Object 的 wait () 方法。

  1. f.channel().closeFuture().sync();

10. 优雅地关闭线程池(建议)

最后,是在 finally 中调用 shutdownGracefully () 方法优雅地关闭线程池,优雅停机。

  1. bossGroup.shutdownGracefully();
  2. workerGroup.shutdownGracefully();