概述
Channel–Socket
EventLoop–控制流,多线程处理,并发
ChannelFuture --异步通知
Channel
基本的I/O 操作(bind()、connect()、read()和write())依赖于底层网络传输所提供的原语。在基于Java 的网络编程中,其基本的构造是class Socket。Netty 的Channel 接口所提供的API,大大地降低了直接使用Socket 类的复杂性。
Netty自己定义的类有以下几种:
EmbeddedChannel;
LocalServerChannel;
NioDatagramChannel;
NioSctpChannel;
NioSocketChannel。
EventLoop接口
EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的时间。
上图中的关系是:
一个EventLoopGroup 包含一个或者多个EventLoop;
一个EventLoop 在它的生命周期内只和一个Thread 绑定;
所有由EventLoop 处理的I/O 事件都将在它专有的Thread 上被处理;
一个Channel 在它的生命周期内只注册于一个EventLoop;
一个EventLoop 可能会被分配给一个或多个Channel。
ChannelFuture接口
Netty所有的I/O都是异步的,因为一个操作不会立即返回,所以我们需要一种用于在这之后的某个时间点确定其结果的方法。实例代码如下所示:
package com.bobo.netty.chapter01;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
/**
* netty服务器端
*
* @author [email protected]
* @create 2018-12-19 19:55
**/
public class HelloServer {
private Logger logger = LoggerFactory.getLogger(HelloServer.class);
private final int port = 8080;
public static void main(String[] args) throws Exception {
new HelloServer().start();
}
public void start() throws Exception {
final HelloServerHandler serverHandler = new HelloServerHandler();
// 创建eventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();
try {
// 创建ServerBootstrap
ServerBootstrap bootstrap = new ServerBootstrap();
// 指定所使用的NIO传输Channel,指定端口套接字
bootstrap.group(group).channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port)).
childHandler(new ChannelInitializer<SocketChannel>() {
// 添加一个serverHandler到子Channel的ChannelPipeLine
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(serverHandler);
}
});
// 异步绑定服务器,调用sync方法阻塞等待直到绑定成功
ChannelFuture future = bootstrap.bind().sync();
// 获取Channel的CloseFuture,并且阻塞当前线程直到它完成
future.channel().closeFuture().sync();
}catch (Exception e) {
logger.error("server error");
//关闭EventLoopGroup,释放掉所有的资源
group.shutdownGracefully().sync();
}
}
}
ChannelHander
ChannelHandler经常用于做业务逻辑处理,,有许多不同类型的ChannelHandler,它们各自的功能主要取决于它们的超类。Netty 以适配器类的形式提供了大量默认的ChannelHandler 实现,其旨在简化应用程序处理逻辑的开发过程。你已经看到了,ChannelPipeline中的每个ChannelHandler将负责把事件转发到链中的下一个ChannelHandler。这些适配器类(及它们的子类)将自动执行这个操作,所以你可以只重写那些你想要特殊处理的方法和事件。常用的适配类有以下几种:
ChannelHandlerAdapter
ChannelInboundHandlerAdapter
ChannelOutboundHandlerAdapter
ChannelDuplexHandler
编码器和解码器
概述
当你通过Netty发送或者是接受一个消息的时候,就会发生一次数据转换。入站消息会被解码,从自己转化为另外一种形式,通常是一个java对象。如果是出站消息,则会发横相反方向的转化,它会从当前格式编码为字节。所有由Netty 提供的编码器/解码器适配器类都实现ChannelOutboundHandler 或者ChannelInboundHandler 接口。
你将会发现对于入站数据来说,channelRead 方法/事件已经被重写了。对于每个从入站Channel 读取的消息,这个方法都将会被调用。随后,它将调用由预置解码器所提供的decode()方法,并将已解码的字节转发给ChannelPipeline 中的下一个ChannelInboundHandler。
ChannelPipeline 接口
ChannelPipeline提供 了ChannelHandler链的容器,并且定义了用于在该链上传播入站和出站时间流的API,当Channel被创建的时候,它会被自动的分配到它专属的ChannelPipeLine。
ChannelHandler 安装到ChannelPipeline 中的过程如下所示:
一个ChannelInitializer的实现被注册到了ServerBootstrap中;
当ChannelInitializer.initChannel()方法被调用时,ChannelInitializer
将在ChannelPipeline 中安装一组自定义的ChannelHandler;
ChannelInitializer 将它自己从ChannelPipeline 中移除。
这里讲述一下Netty应用程序中入站和出站数据流之间的区别,从一个客户端应用程序的角度来讲,如果事件的运动方向是从客户端到服务器daunt,那么我们成这些事件为出站的。反之是入站。
上图显示了入站和出站ChannelHandler 可以被安装到同一个ChannelPipeline中。如果一个消息或者任何其他的入站事件被读取,那么它会从ChannelPipeline 的头部开始流动,并被传递给第一个ChannelInboundHandler。这个ChannelHandler 不一定会实际地修改数据,具体取决于它的具体功能,在这之后,数据将会被传递给链中的下一个ChannelInboundHandler。最终,数据将会到达ChannelPipeline 的尾端,届时,所有处理就都结束了。
数据的出站运动(即正在被写的数据)在概念上也是一样的。在这种情况下,数据ChannelOutboundHandler 链的尾端开始流动,直到它到达链的头部为止。在这之后,出站数据将会到达网络传输层,这里显示为Socket。通常情况下,这将触发一个写操作。
注意:Netty能区分ChannelInboundHandler实现和ChannelOutboundHandler 实现,并确保数据只会在具有相同定向类型的两个ChannelHandler 之间传递。
当ChannelHandler 被添加到ChannelPipeline 时,它将会被分配一个ChannelHandler-Context,其代表了ChannelHandler 和ChannelPipeline 之间的绑定。虽然这个对象可以被用于获取底层的Channel,但是它主要还是被用于写出站数据。
在Netty 中,有两种发送消息的方式。你可以直接写到Channel 中,也可以写到和Channel-Handler相关联的ChannelHandlerContext 对象中。前一种方式将会导致消息从Channel-Pipeline 的尾端开始流动,而后者将导致消息从ChannelPipeline 中的下一个Channel-Handler 开始流动。
抽象类SimpleChannelInboundHandler
引导
Netty的引导类有两种,一种用于客户端(简单的称为BootStrap),而另一猴子那个(ServerBootstrap用于服务器,引导类的作用是为应用程序的网络层配置提供了容器。
这两者的区别在于引导一个客户端只需要一个EventLoopGroup,但是一个ServerBootstrap 则需要两个(也可以是同一个实例。
因为服务器需要两组不同的Channel。第一组将只包含一个ServerChannel,代表服务器自身的已绑定到某个本地端口的正在监听的套接字。而第二组将包含所有已创建的用来处理传入客户端连接(对于每个服务器已经接受的连接都有一个)的Channel。图3-4 说明了这个模型,并且展示了为何需要两个不同的EventLoopGroup。