netty的简介
- API使用简单
- 功能强大
- 定制能力强
- 性能高
- 社区活跃
- 有成熟的商业应用
netty为什么采用NIO?
因为NIO具有异步、高性能的特点。
Hello Netty
示例程序分为服务端与客户端。每一个端都包括了三个主要类,分别为业务处理类(Handler)、channel初始化类(Initializer)、启动类(Run)
服务端启动类
public class Server {
public static void main(String[] args) throws InterruptedException {
//Configure the server
//创建两个EventLoopGroup对象
//创建boss线程组 用于服务端接受客户端的连接
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
/// 创建 worker 线程组 用于进行 SocketChannel 的数据读写
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ServerInitializer())
;
ChannelFuture f = b.bind(8080);
f.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
服务端初始化器
public class ServerInitializer extends ChannelInitializer {
private static final StringDecoder DECODER = new StringDecoder();
private static final StringEncoder ENCODER = new StringEncoder();
private static final ServerHandler SERVER_HANDLER = new ServerHandler();
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(DECODER);
pipeline.addLast(ENCODER);
pipeline.addLast(SERVER_HANDLER);
}
}
服务端业务处理类
public class ServerHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.write("Welcome to " + InetAddress.getLocalHost().getHostName() + "!\r\n");
ctx.write("It is " + new Date() + " now.\r\n");
ctx.flush();
}
@Override
public void channelRead0(ChannelHandlerContext ctx, String request) throws Exception {
String response = null;
boolean close = false;
if (request.isEmpty()) {
response = "Please type something.\r\n";
}else if ("bye".equals(request)){
response = "Have a good day!\r\n";
close = true;
}else {
response = "Did you say '" + request + "'?\r\n";
}
ChannelFuture future = ctx.writeAndFlush(response);
if (close){
future.addListener(ChannelFutureListener.CLOSE);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客户端启动类
public class Client {
public static void main(String[] args) throws InterruptedException, IOException {
NioEventLoopGroup loopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(loopGroup)
.channel(NioSocketChannel.class)
.handler(new ClientInitializer());
Channel ch = bootstrap.connect("127.0.0.1", 8080).sync().channel();
ChannelFuture lastWriteFuture = null;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
String line = in.readLine();
if (line == null) {
break;
}
// Sends the received line to the server.
lastWriteFuture = ch.writeAndFlush(line + "\r\n");
// If user typed the 'bye' command, wait until the server closes
// the connection.
if ("bye".equals(line.toLowerCase())) {
ch.closeFuture().sync();
break;
}
// Wait until all messages are flushed before closing the channel.
if (lastWriteFuture != null) {
lastWriteFuture.sync();
}
}
}finally {
loopGroup.shutdownGracefully();
}
}
}
客户端初始化器
public class ClientInitializer extends ChannelInitializer<SocketChannel> {
private static final StringDecoder DECODER = new StringDecoder();
private static final StringEncoder ENCODER = new StringEncoder();
private static final ClientHandler CLIENT_HANDLER = new ClientHandler();
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(DECODER);
pipeline.addLast(ENCODER);
pipeline.addLast(CLIENT_HANDLER);
}
}
客户端业务处理类
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.err.println(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
基本组件的说明
channel
channel为NIO的最基本组件,表示着每一个客户端的连接。它所处的状态以及产生的事件,就是我们在每一个完成客户端连接生命周期中的不同阶段的处理。
生命周期:
- unregistered: channel已经创建,但为分配EventLoop
- registered: channel分配到EventLoop
- active: 连接成功
- inactive: 未连接
channelPipeline
pipeline就是包含了这些个handler,并按照一定的顺序去依次执行。存储这些handler的是双向链表,且是入站与出站两个工作流混在一起。
重要方法:
- addFirst 添加到开头
- addBefore 添加当前位置之后
- addAfter 添加当前位置之前
- addLast 添加到末尾
- remove 移除一个handler
- replace 替换一个handler
- get 获取一个handler
- context 返回上下文
- names 返回所有handler的名称
channelHandler
在netty中就如上述所说,每一次连接的不同阶段都会产生对应的事件和状态的改变,它们可以分为两个工作流,一个为入站一个为出站。其中入站操作就包括:连接被激活、连接失效、数据读取、用户相应事件和错误事件等等。而出站操作就包括:打开或关闭连接、讲数据冲刷到套接字中等等。当然,netty官方提供了很多的预制handler可以调用。
重要回调方法
- handlerAdded 被添加到pipeline调用
- handlerRemoved 被删除到pipeline调用
- exceptionCaught 执行出错调用
- channelRead 读事件回调
channelFuture
Netty中所有的I/O操作都是异步的,我们知道“异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等”,那就是说至少我们需要一种获得异步执行结果的手段。JDK预置了interface java.util.concurrent.Future,Future提供了一种在操作完成时通知应用程序的方式。这个对象可以看作是一个异步操作的结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。但是其所提供的实现,只允许手动检查对应的操作是否已经完成,或者一直阻塞直到它完成。这是非常繁琐的,所以Netty提供了它自己的实现ChannelFuture,用于在执行异步操作的时候使用。一般来说,每个Netty的出站I/O操作都将返回一个ChannelFuture。