Netty

  1. Netty
    1. Netty简介
    2. Netty组成
      1. Channel
      2. Callback (回调)
      3. Future
      4. Event 和 Handler
      5. Netty异步实现 Handler
      6. 多路复用 Selector
      7. 事件循环 EventLoop

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

文章标题:Netty

字数:1.6k

本文作者:Os467

发布时间:2023-04-22, 20:55:26

最后更新:2023-04-22, 20:56:00

原始链接:https://os467.github.io/2023/04/22/Netty/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏