Nio概念
NIO:异步非阻塞,服务器实现模式为一个请求一个线程,客户端发送的连接请求都会注册到多路复用器(Seclector)上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
面向缓冲;读写同时;只需一个线程负责Selector轮询。
Netty核心组件
- Channel
它代表一个到实体的开放连接,如读操作和写操作。
可看做是传入或者传出数据的载体,可以被打开或者关闭,连接或者断开。 - 回调
一个回调及一个方法,Netty在内部使用了回调来处理事件;当一个回调被触发时,相关的事件可以被一个ChannelHandler的实现处理。(ChannelHandler负责接收并响应事件通知)。
- Future
提供了另一种在操作完成时通知应用程序的方式。可以看做一个异步操作的结果的占位符,将在未来的某个时刻完成,并提供对结果的访问。
每个Netty的出站IO操作都将会返回一个ChannelFuture,都不会阻塞。
代码练习
用SpringBoot模仿写一个简单的服务器:
-
添加依赖并消除Tomcat
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.31.Final</version> </dependency>
-
建一个EchoServer类:
public class EchoServer { private final int port; private Logger log = LoggerFactory.getLogger(this.getClass()); public EchoServer(int port) { this.port=port; } public void start() throws Exception{ final EchoServerHandler serverHandler = new EchoServerHandler(); //1. 创建NioEventLoopGroup EventLoopGroup group = new NioEventLoopGroup(); try { //2. 创建ServerBootstrap ServerBootstrap serverBootstrap=new ServerBootstrap(); serverBootstrap.group(group) //3. 指定端口 .localAddress(new InetSocketAddress(port)) //4. 指定使用的NIo传输Channel .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline().addLast(serverHandler); } //@Override protected void initchannel(SocketChannel ch) throws Exception { //5. 添加一个Handler到Pipeline ch.pipeline().addLast(serverHandler); } }); ChannelFuture f=serverBootstrap.bind().sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } }
-
建EchoServerHandler类
@ChannelHandler.Sharable public class EchoServerHandler extends ChannelInboundHandlerAdapter { //消息读取有两个方法,channelRead和channelRead0,其中channelRead0可以读取泛型 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{ ByteBuf in = (ByteBuf) msg; System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8)); ctx.write(in); } @Override public void channelReadComplete(ChannelHandlerContext ctx){ ctx.writeAndFlush(ChannelFutureListener.CLOSE); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){ cause.printStackTrace(); ctx.close(); } }
-
Application
@SpringBootApplication public class PracticeApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(PracticeApplication.class, args); } @Override public void run(String... strings) throws Exception { EchoServer echoServer = new EchoServer(7000); echoServer.start(); } }
-
验证结果:
回顾总结
回顾一下重要步骤:
1.创建一个ServerBootsrtap的实例以引导和绑定服务器
2. 创建并分配一个NioEventLoopGroup实例以进行事物的处理,如接收新连接以及读写数据。
3. 指定服务器绑定地址。
4. 初始化每一个新的Chanel。