Netty 的概念及体系结构
Netty–异步和事件驱动
Netty 提供了极丰富的网络编程工具集,但其只是一个框架,它的架构方法和设计原则是:每一个笑点都和它的技术内容一样重要。
- 关注点分离—-业务和网络逻辑解耦
- 模块化和可复用性
- 可测试性味首要的要求
Java 网络编程
早期的 Java API(java.net)只支持本地系统的套接字库来提供所谓的阻塞函数。
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| package nia.chapter1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class BlockingIoExample {
public void serve(int portNumber) throws IOException {
ServerSocket serverSocket = new ServerSocket(portNumber);
Socket clientSocket = serverSocket.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out =
new PrintWriter(clientSocket.getOutputStream(), true);
String request, response;
while ((request = in.readLine()) != null) {
if ("Done".equals(request)) {
break;
}
response = processRequest(request);
out.println(response);
}
}
private String processRequest(String request){
return "Processed";
}
}
|
Java NIO
Java 对非阻塞I/O的支持在JDK1.4的 java.nio包中。
选择器
java.nio.channels.Selector 是Java的非阻塞I/O实现的关键。使用了事件通知API以确定一组非阻塞套接字中农有哪些已经就绪能够进行I/O相关操作。
- 使用较少的县城可以处理许多连接,因此也减少了内存管理和上下文切换带来的开销
- 当没有I/O操作需要的时候,线程可以用于其他任务
Netty 简介
想要实现更高的吞吐量和可扩展性,在更低的成本基础上进行交付。如果直接使用底层的API,显得较为复杂。在网络编程领域,Netty是 Java的卓越框架。它驾驭了Java高级API的能力,并将其隐藏在一个易于使用的API之后。其特性主要体现在以下。
- 设计:统一的API,支持多种传输类型,阻塞和非阻塞,简单而强大的线程模型,真正的无连接数据报套接字支持,链接逻辑组建以支持复用
- 易于使用:详实的文档和示例集
- 性能:拥有比Java的核心API更高的吞吐量以及更低的延迟,得益于池化和复用,拥有更低的资源消耗,最少的内存复制
- 健壮性:不会因为慢速、快速或者超载的连接导致OutOfMemoryError
- 安全性:完整的SSL/TLS支持
- 社区驱动:发布快速且频繁
异步和事件驱动
异步和可伸缩性之间的联系:
- 非阻塞网络调用使我们可以不必等待一个操作的完成。完全异步的I/O正是基于这个特性构建的,异步方法会立即返回,并且在它完成时,会直接或者稍后的某个时间通知用户。
- 选择器是的我们能够通过较少的线程见识许多连接上的事件
Netty 的核心组件
- Channel
- 回调
- Future
- 事件和ChannelHandler
这些构建块代表了不同类型的构造:资源、逻辑以及通知。你的应用将使用它们来访问网络以及流经网络的数据。
Channel
Channel 是Java NIO 的一个基本构造。代表了一个实体的开放连接,如读操作和写操作。
回调
回调其实就是一个方法,一个指向已经被提供给另一个方法的方法的引用。回调在广泛的编程场景中都有应用,而且是在操作完成后通知相关方最常见的方式。在Netty内部使用了回调来处理事件,当一个回调被触发时,相关事件可以被 ChannelHandler 接口实现来处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ConnectHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx)
throws Exception {
System.out.println(
"Client " + ctx.channel().remoteAddress() + " connected");
}
}
|
Future
Future 提供了另一种在操作完成时通知应用程序的方式。可以看做是异步操作的结果占位符;它将在未来的某个时刻完成,并提供对其结果的访问。每个 Netty 的出站I/O操作都返回一个ChannelFuture,也就是说都不会阻塞。
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
public class ConnectExample {
private static final Channel CHANNEL_FROM_SOMEWHERE = new NioSocketChannel();
public static void connect() {
Channel channel = CHANNEL_FROM_SOMEWHERE;
ChannelFuture future = channel.connect(
new InetSocketAddress("192.168.0.1", 25));
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
ByteBuf buffer = Unpooled.copiedBuffer(
"Hello", Charset.defaultCharset());
ChannelFuture wf = future.channel()
.writeAndFlush(buffer);
} else {
Throwable cause = future.cause();
cause.printStackTrace();
}
}
});
}
}
|
事件和 ChannelHandler
Netty 使用不同的事件通知我们状态的改变或者是操作的改变。使我们能够基于已经发生的事件来触发适当的动作。如:
Netty 入站数据或者相关的状态变更而触发的事件包括:
- 连接已被激活或连接失活
- 数据读取
- 用户事件
- 错误事件
Netty 出站事件是未来将会触发的某个动作的操作结果,如:
- 打开或关闭到远程节点的连接
- 将数据写到或者冲刷到套接字