原理解析!「Java面试必看

实际上还有很多很多优秀的项目用到了 Netty,Netty 官方也做了统计,统计结果在这里:https://netty.io/wiki/related-projects.html

介绍一下Netty的核心组件?


👨‍💻面试官 :Netty 核心组件有哪些?分别有什么作用?

🙋 :表面上,嘴上开始说起 Netty 的核心组件有哪些,实则,内心已经开始 mmp 了,深度怀疑这面试官是存心搞我啊!

简单介绍 Netty 最核心的一些组件(对于每一个组件这里不详细介绍)。通过下面这张图你可以将我提到的这些 Netty 核心组件串联起来。

Bytebuf(字节容器)

网络通信最终都是通过字节流进行传输的。 ByteBuf 就是 Netty 提供的一个字节容器,其内部是一个字节数组。 当我们通过 Netty 传输数据的时候,就是通过 ByteBuf 进行的。

我们可以将 ByteBuf 看作是 Netty 对 Java NIO 提供了 ByteBuffer 字节容器的封装和抽象。

有很多小伙伴可能就要问了 : 为什么不直接使用 Java NIO 提供的 ByteBuffer 呢?

因为 ByteBuffer 这个类使用起来过于复杂和繁琐。

Bootstrap 和 ServerBootstrap(启动引导类)

Bootstrap 是客户端的启动引导类/辅助类,具体使用方法如下:


 EventLoopGroup group = new NioEventLoopGroup();

        try {

            //创建客户端启动引导/辅助类:Bootstrap

            Bootstrap b = new Bootstrap();

            //指定线程模型

            b.group(group).

                    ......

            // 尝试建立连接

            ChannelFuture f = b.connect(host, port).sync();

            f.channel().closeFuture().sync();

        } finally {

            // 优雅关闭相关线程组资源

            group.shutdownGracefully();

        } 

ServerBootstrap 客户端的启动引导类/辅助类,具体使用方法如下:


 // 1.bossGroup 用于接收连接,workerGroup 用于具体的处理

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {

            //2.创建服务端启动引导/辅助类:ServerBootstrap

            ServerBootstrap b = new ServerBootstrap();

            //3.给引导类配置两大线程组,确定了线程模型

            b.group(bossGroup, workerGroup).

                   ......

            // 6.绑定端口

            ChannelFuture f = b.bind(port).sync();

            // 等待连接关闭

            f.channel().closeFuture().sync();

        } finally {

            //7.优雅关闭相关线程组资源

            bossGroup.shutdownGracefully();

            workerGroup.shutdownGracefully();

        }

    } 

从上面的示例中,我们可以看出:

  1. Bootstrap 通常使用 connet() 方法连接到远程的主机和端口,作为一个 Netty TCP 协议通信中的客户端。另外,Bootstrap 也可以通过 bind() 方法绑定本地的一个端口,作为 UDP 协议通信中的一端。

  2. ServerBootstrap通常使用 bind() 方法绑定本地的端口上,然后等待客户端的连接。

  3. Bootstrap 只需要配置一个线程组— EventLoopGroup ,而 ServerBootstrap需要配置两个线程组— EventLoopGroup ,一个用于接收连接,一个用于具体的 IO 处理。

Channel(网络操作抽象类)

Channel 接口是 Netty 对网络操作抽象类。通过 Channel 我们可以进行 I/O 操作。

一旦客户端成功连接服务端,就会新建一个 Channel 同该用户端进行绑定,示例代码如下:


 //  通过 Bootstrap 的 connect 方法连接到服务端

   public Channel doConnect(InetSocketAddress inetSocketAddress) {

        CompletableFuture<Channel> completableFuture = new CompletableFuture<>();

        bootstrap.connect(inetSocketAddress).addListener((ChannelFutureListener) future -> {

            if (future.isSuccess()) {

                completableFuture.complete(future.channel());

            } else {

                throw new IllegalStateException();

            }

        });

        return completableFuture.get();

    } 

比较常用的Channel接口实现类是 :

  • NioServerSocketChannel(服务端)

  • NioSocketChannel(客户端)

这两个 Channel 可以和 BIO 编程模型中的ServerSocket以及Socket两个概念对应上。

EventLoop(事件循环)

EventLoop 介绍

这么说吧!EventLoop(事件循环)接口可以说是 Netty 中最核心的概念了!

《Netty 实战》这本书是这样介绍它的:

EventLoop 定义了 Netty 的核心抽象,用于处理连接的生命周期中所发生的事件。

是不是很难理解?说实话,我学习 Netty 的时候看到这句话是没太能理解的。

说白了,EventLoop 的主要作用实际就是责监听网络事件并调用事件处理器进行相关 I/O 操作(读写)的处理。

Channel 和 EventLoop 的关系

ChannelEventLoop 直接有啥联系呢?

Channel 为 Netty 网络操作(读写等操作)抽象类,EventLoop 负责处理注册到其上的Channel 的 I/O 操作,两者配合进行 I/O 操作。

EventloopGroup 和 EventLoop 的关系

EventLoopGroup 包含多个 EventLoop(每一个 EventLoop 通常内部包含一个线程),它管理着所有的 EventLoop 的生命周期。

并且,EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理,即 ThreadEventLoop 属于 1 : 1 的关系,从而保证线程安全。

下图是 Netty NIO 模型对应的 EventLoop 模型。通过这个图应该可以将EventloopGroupEventLoopChannel三者联系起来。

ChannelHandler(消息处理器) 和 ChannelPipeline(ChannelHandler 对象链表)

下面这段代码使用过 Netty 的小伙伴应该不会陌生,我们指定了序列化编解码器以及自定义的 ChannelHandler 处理消息。


 b.group(eventLoopGroup)

                .handler(new ChannelInitializer<SocketChannel>() {

                    @Override

                    protected void initChannel(SocketChannel ch) {

                        ch.pipeline().addLast(new NettyKryoDecoder(kryoSerializer, RpcResponse.class));

                        ch.pipeline().addLast(new NettyKryoEncoder(kryoSerializer, RpcRequest.class));

                        ch.pipeline().addLast(new KryoClientHandler());

                    }

                }); 

ChannelHandler 是消息的具体处理器,主要负责处理客户端/服务端接收和发送的数据。

Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。 一个Channel包含一个 ChannelPipelineChannelPipelineChannelHandler 的链,一个 pipeline 上可以有多个 ChannelHandler

我们可以在 ChannelPipeline 上通过 addLast() 方法添加一个或者多个ChannelHandler一个数据或者事件可能会被多个 Handler 处理) 。当一个 ChannelHandler 处理完之后就将数据交给下一个 ChannelHandler

ChannelHandler 被添加到的 ChannelPipeline 它得到一个 ChannelHandlerContext,它代表一个 ChannelHandlerChannelPipeline 之间的“绑定”。 ChannelPipeline 通过 ChannelHandlerContext来间接管理 ChannelHandler

ChannelFuture(操作执行结果)


public interface ChannelFuture extends Future<Void> {

    Channel channel();



    ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> var1);

     ......



    ChannelFuture sync() throws InterruptedException;

} 

Netty 是异步非阻塞的,所有的 I/O 操作都为异步的。

因此,我们不能立刻得到操作是否执行成功,但是,你可以通过 ChannelFuture 接口的 addListener() 方法注册一个 ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。


ChannelFuture f = b.connect(host, port).addListener(future -> {

  if (future.isSuccess()) {

    System.out.println("连接成功!");

  } else {

    System.err.println("连接失败!");

  }

}).sync(); 

并且,你还可以通过ChannelFuturechannel() 方法获取连接相关联的Channel


Channel channel = f.channel(); 

另外,我们还可以通过 ChannelFuture 接口的 sync()方法让异步的操作编程同步的。


//bind()是异步的,但是,你可以通过 `sync()`方法将其变为同步。

ChannelFuture f = b.bind(port).sync(); 

Bootstrap 和 ServerBootstrap 了解么?


👨‍💻面试官 :你再说说自己对 BootstrapServerBootstrap 的了解吧!

🙋

Bootstrap 是客户端的启动引导类/辅助类,具体使用方法如下:


 EventLoopGroup group = new NioEventLoopGroup();

        try {

            //创建客户端启动引导/辅助类:Bootstrap

            Bootstrap b = new Bootstrap();

            //指定线程模型

            b.group(group).

                    ......

            // 尝试建立连接

            ChannelFuture f = b.connect(host, port).sync();

            f.channel().closeFuture().sync();

        } finally {

            // 优雅关闭相关线程组资源

            group.shutdownGracefully();

        } 

ServerBootstrap 客户端的启动引导类/辅助类,具体使用方法如下:


 // 1.bossGroup 用于接收连接,workerGroup 用于具体的处理

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {

            //2.创建服务端启动引导/辅助类:ServerBootstrap

            ServerBootstrap b = new ServerBootstrap();

            //3.给引导类配置两大线程组,确定了线程模型

            b.group(bossGroup, workerGroup).

                   ......

            // 6.绑定端口

            ChannelFuture f = b.bind(port).sync();

            // 等待连接关闭

            f.channel().closeFuture().sync();

        } finally {

            //7.优雅关闭相关线程组资源

            bossGroup.shutdownGracefully();

            workerGroup.shutdownGracefully();

        }

    } 

从上面的示例中,我们可以看出:

  1. Bootstrap 通常使用 connet() 方法连接到远程的主机和端口,作为一个 Netty TCP 协议通信中的客户端。另外,Bootstrap 也可以通过 bind() 方法绑定本地的一个端口,作为 UDP 协议通信中的一端。

  2. ServerBootstrap通常使用 bind() 方法绑定本地的端口上,然后等待客户端的连接。

  3. Bootstrap 只需要配置一个线程组— EventLoopGroup ,而 ServerBootstrap需要配置两个线程组— EventLoopGroup ,一个用于接收连接,一个用于具体的处理。

NioEventLoopGroup 默认的构造函数会起多少线程?


👨‍💻面试官 :看过 Netty 的源码了么?NioEventLoopGroup 默认的构造函数会起多少线程呢?

🙋 :嗯嗯!看过部分。

回顾我们在上面写的服务器端的代码:


// 1.bossGroup 用于接收连接,workerGroup 用于具体的处理

EventLoopGroup bossGroup = new NioEventLoopGroup(1);

EventLoopGroup workerGroup = new NioEventLoopGroup(); 

为了搞清楚NioEventLoopGroup 默认的构造函数 到底创建了多少个线程,我们来看一下它的源码。


 /**

     * 无参构造函数。

     * nThreads:0

     */

    public NioEventLoopGroup() {

        //调用下一个构造方法


# 最后总结

## ActiveMQ+Kafka+RabbitMQ学习笔记PDF

![image.png](https://img-blog.csdnimg.cn/img_convert/a571de6e16a673744c81f4d5a9ff13d2.png)


* ### RabbitMQ实战指南

![image.png](https://img-blog.csdnimg.cn/img_convert/7c25c504f090c3248a54e76e3c87081f.png)


* ### 手写RocketMQ笔记

![image.png](https://img-blog.csdnimg.cn/img_convert/f1c25f3af6b2e3e4ccbe7acb34d65a58.png)


* ### 手写“Kafka笔记”

![image](https://img-blog.csdnimg.cn/img_convert/0bdb34f66f242377af352f1789bba63e.png)

关于分布式,限流+缓存+缓存,这三大技术(包含:ZooKeeper+Nginx+MongoDB+memcached+Redis+ActiveMQ+Kafka+RabbitMQ)等等。这些相关的面试也好,还有手写以及学习的笔记PDF,都是啃透分布式技术必不可少的宝藏。以上的每一个专题每一个小分类都有相关的介绍,并且小编也已经将其整理成PDF啦

**

     * 无参构造函数。

     * nThreads:0

     */

    public NioEventLoopGroup() {

        //调用下一个构造方法


# 最后总结

## ActiveMQ+Kafka+RabbitMQ学习笔记PDF

[外链图片转存中...(img-joT4kYg2-1628146786833)]


* ### RabbitMQ实战指南

[外链图片转存中...(img-oS3XizXj-1628146786836)]


* ### 手写RocketMQ笔记

[外链图片转存中...(img-T89zcDkf-1628146786838)]


* ### 手写“Kafka笔记”

[外链图片转存中...(img-HtoQxKwe-1628146786840)]

关于分布式,限流+缓存+缓存,这三大技术(包含:ZooKeeper+Nginx+MongoDB+memcached+Redis+ActiveMQ+Kafka+RabbitMQ)等等。这些相关的面试也好,还有手写以及学习的笔记PDF,都是啃透分布式技术必不可少的宝藏。以上的每一个专题每一个小分类都有相关的介绍,并且小编也已经将其整理成PDF啦

**[资料领取方式:戳这里免费领取](https://gitee.com/vip204888/java-p7)**
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值