Netty
Netty简介
Netty是一个异步的,基于事件驱动的网络应用框架,可用于开发可维护,高性能的网络服务器和客户端
采用的是Selector模式(多路复用),这里的异步表示的是负责不同工作的线程使用的是不同线程
Netty在java网络应用框架中的地位就好比:Spring在javaEE开发中的地位
用到netty的常见框架:
- Spark
- Hadoop
- ElasticSearch
- Zookeeper
引入依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
</dependency>
Netty组成
Channel
Channel 是 NIO 基本的结构。它代表了一个用于连接到实体如硬件设备、文件、网络套接字或程序组件,能够执行一个或多个不同的 I/O 操作(例如读或写)的开放连接。
现在,把 Channel 想象成一个可以“打开”或“关闭”,“连接”或“断开”和作为传入和传出数据的运输工具。
Callback (回调)
callback (回调)是一个简单的方法,提供给另一种方法作为引用,这样后者就可以在某个合适的时间调用前者。这种技术被广泛使用在各种编程的情况下,最常见的方法之一通知给其他人操作已完成。
Netty 内部使用回调处理事件时。一旦这样的回调被触发,事件可以由接口 ChannelHandler 的实现来处理。
当连接建立时,将会调用channelActive方法
class ConnectHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("remoteAddress: "+ctx.channel().remoteAddress()+" connect success!");
}
}
Future
Future 提供了另外一种通知应用操作已经完成的方式。这个对象作为一个异步操作结果的占位符,它将在将来的某个时候完成并提供结果。
JDK 附带接口 java.util.concurrent.Future ,但所提供的实现只允许手动检查操作是否完成或阻塞了。这是很麻烦的,所以 Netty 提供自己了的实现ChannelFuture(实现Future),用于在执行异步操作时使用。
每个 Netty 的 outbound I/O 操作都会返回一个 ChannelFuture,这样就不会阻塞。这就是 Netty 所谓的“自底向上的异步和事件驱动”。
//不会阻塞
ChannelFuture future = channel.connect(new InetSocketAddress("localhost",8080));
ChannelFuture 提供多个附件方法来允许一个或者多个ChannelFutureListener 实例。这个回调方法operationComplete()
会在操作完成时调用。事件监听者能够确认这个操作是否成功或者是错误。如果是后者,我们可以检索到产生的 Throwable。简而言之, ChannelFutureListener 提供的通知机制不需要手动检查操作是否完成的。
当调用 connect() 将会直接是非阻塞的,并且调用在背后完成。由于线程是非阻塞的,所以无需等待操作完成,而可以去干其他事,因此这令资源利用更高效。
public class ChannelFutureDemo {
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
ChannelFuture channelFuture = bootstrap
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringEncoder(Charset.defaultCharset()));
}
})
.connect("localhost", 8080);
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()){
//成功建立连接
ByteBuf byteBuf = Unpooled.copiedBuffer("hello", Charset.defaultCharset());
future.channel().writeAndFlush(byteBuf);
}else {
Throwable cause = future.cause();
cause.printStackTrace();
}
}
});
}
}
连接建立后就会通过监听器触发回调函数
Event 和 Handler
Netty 使用不同的事件来通知我们更改的状态或操作的状态。
状态变化通常有
- 活动或非活动连接
- 数据的读取
- 用户事件
- 错误
Netty 的 ChannelHandler 是各种处理程序的基本抽象。想象下,每个处理器实例就是一个回调,用于执行对各种事件的响应。
Netty异步实现 Handler
Netty 的异步编程模型是建立在 future 和 callback 的概念上的。所有这些元素的协同为自己的设计提供了强大的力量。
拦截操作和转换入站或出站数据只需要您提供回调或利用 future 操作返回的。这使得链操作简单、高效,促进编写可重用的、通用的代码。
一个 Netty 的设计的主要目标是促进“关注点分离”:业务逻辑从网络基础设施应用程序中分离。
下面是多线程的异步实现
使用Callable + Future实现异步任务(多线程)基于接口回调技术
public class CallBackTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callback callback = new Callback();
FutureTask futureTask = new FutureTask<>(callback);
//执行异步任务
Thread thread = new Thread(futureTask);
thread.start();
Thread.sleep(200);
System.out.println("主函数任务");
System.out.println("获取回调结果"+futureTask.get());
}
}
class Callback implements Callable {
@Override
public Object call() throws Exception {
System.out.println("执行异步任务");
Thread.sleep(1000);
System.out.println("异步任务结束");
return "异步任务完成";
}
}
多路复用 Selector
Netty的多路复用是通过引入Selector来实现的,Selector是一个用于注册各种事件的选择器机构。
在工人线程中,Netty通过为每个channel在Selector中注册与之绑定的SelectedKeys来实现关注事件的注册。这样就能在一个线程中注册多个与之关联的channel,同时可以通过Selecotr来监视不同channel的不同事件,实现了多路复用,解决了原来网络编程中只能一个线程负责一个channel造成的资源浪费问题。
事件循环 EventLoop
EventLoop 分配给每个 Channel 来处理所有的事件,包括
- 注册感兴趣的事件
- 调度事件到 ChannelHandler
- 安排进一步行动
因此,我们只需要在ChannelHandler中关注具体的事件逻辑实现即可
EventLoop使用到了反应器模型Reactor。
select事件轮询
Netty 也同样遵循 Reactor 线程模型的运行机制,下面我们来了解一下 Netty 是如何实现 Reactor 线程模型的。
- 连接注册:Channel 建立后,注册至 Reactor 线程中的 Selector 选择器。
- 事件轮询:轮询 Selector 选择器中已注册的所有 Channel 的 I/O 事件。
- 事件分发:为准备就绪的 I/O 事件分配相应的处理线程。
- 任务处理:Reactor 线程还负责任务队列中的非 I/O 任务,每个 Worker 线程从各自维护的任务队列中取出任务异步执行。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com