终结全网!手写Netty面试题答案(上)

简介: 终结全网!手写Netty面试题答案

1 最原始架构

一个线程负责处理连接、读写等各种请求。

创建一个线程,注册到 Selector,将 serversocketchannel 注册到Selector

selectionKey 里就有具体的事件


image.png


对应代码

package io.netty.example.helloworld;
import io.netty.channel.EventLoopGroup;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.Set;
/**
 * @author JavaEdge
 * @date 2021/5/17
 */
public class NioServer {
    public static void main(String[] args) throws Exception {
        // 创建一个 ServerSocketChannel
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(8080));
        // 设置为非阻塞模式
        serverChannel.configureBlocking(false);
        // 创建一个事件查询器
        Selector selector = SelectorProvider.provider().openSelector();
        // 把 ServerSocketChannel 注册到 selector,并且感兴趣 OP_ACCEPT 事件
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // 阻塞方法,等待系统有I/O事件发生
            int eventNum = selector.select();
            System.out.println("系统发生IO事件 数量->" + eventNum);
            Set<SelectionKey> keySet = selector.selectedKeys();
            Iterator<SelectionKey> iterable = keySet.iterator();
            while (iterable.hasNext()) {
                // 拿到该 key
                SelectionKey key = iterable.next();
                // 拿到后就移除它,否则后面遍历还会重复拿到它
                iterable.remove();
                // 连接事件
                if (key.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    // 接受客户端的连接,一个 SocketChannel 代表一个TCP连接
                    SocketChannel socketChannel = ssc.accept();
                    // 把SocketChannel设置为非阻塞模式
                    socketChannel.configureBlocking(false);
                    System.out.println("服务器接受了一个新的连接 " + socketChannel.getRemoteAddress());
                }
            }
        }
    }
}


package io.netty.example.helloworld;
import io.netty.channel.EventLoopGroup;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
 * @author JavaEdge
 * @date 2021/5/17
 */
public class NioServer {
    public static void main(String[] args) throws Exception {
        //创建一个ServerSocket
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(8089));
        //设置为非阻塞模式
        serverChannel.configureBlocking(false);
        // 创建一个事件查询器
        Selector selector = SelectorProvider.provider().openSelector();
        // 把 ServerSocketChannel 注册到事件查询器上,并且感兴趣 OP_ACCEPT  事件
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
//
//        //创建一组事件查询器
//        EventLoopGroup eventLoopGroup = new EventLoopGroup();
        while (true) {
            // 阻塞方法,等待系统有I/O事件发生
            int eventNum = selector.select();
            System.out.println("系统发生IO事件 数量->" + eventNum);
            Set<SelectionKey> keySet = selector.selectedKeys();
            Iterator<SelectionKey> iterable = keySet.iterator();
            while (iterable.hasNext()) {
                // 拿到该 key
                SelectionKey key = iterable.next();
                // 拿到后就移除它,否则后面遍历还会重复拿到它
                iterable.remove();
                // 连接事件
                if (key.isAcceptable()) {
                    // 因为只有 ServerSocketChannel 有接收事件,所以可直接强转
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    // 接受客户端的连接,一个 SocketChannel 代表一个TCP连接
                    // 事件如果发生了,就肯定有新的连接
                    SocketChannel socketChannel = ssc.accept();
                    // 把SocketChannel设置为非阻塞模式
                    socketChannel.configureBlocking(false);
                    System.out.println("服务器接受了一个新的连接 " + socketChannel.getRemoteAddress());
                    // 把SocketChannel注册到Selector,并关注OP_READ事件
                    socketChannel.register(selector, SelectionKey.OP_READ);
//                    eventLoopGroup.register(socketChannel, SelectionKey.OP_READ);
                }
                // 可读事件
                if (key.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    try {
                        int readNum = socketChannel.read(buffer);
                        if (readNum == -1) {
                            System.out.println("读取结束,关闭 socket");
                            key.channel();
                            socketChannel.close();
                            break;
                        }
                        // 将Buffer从写模式切到读模式
                        buffer.flip();
                        byte[] bytes = new byte[readNum];
                        buffer.get(bytes, 0, readNum);
                        System.out.println(new String(bytes));
/*                        byte[] response = "client hello".getBytes();
                        // 清理了才可以重新使用
                        buffer.clear();
                        buffer.put(response);
                        buffer.flip();
                        // 该方法非阻塞的,如果此时无法写入也不会阻塞在此,而是直接返回 0 了
                        socketChannel.write(buffer);
                        */
                        // 在 key 上附加一个对象
                        key.attach("hello client".getBytes());
                        // 把 key 关注的事件切换为写
                        key.interestOps(SelectionKey.OP_WRITE);
                    } catch (IOException e) {
                        System.out.println("读取时发生异常,关闭 socket");
                        // 取消 key
                        key.channel();
                    }
                }
                if (key.isWritable()) {
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    // 可写时再将那个对象拿出来
                    byte[] bytes = (byte[]) key.attachment();
                    key.attach(null);
                    System.out.println("可写事件发生 写入消息" + Arrays.toString(bytes));
                    if (bytes != null) {
                        socketChannel.write(ByteBuffer.wrap(bytes));
                    }
                    // 写完后,就不需要写了,就切换为读事件   如果不写该行代码就会死循环
//                    key.interestOps(SelectionKey.OP_READ);
                }
            }
        }
    }
}



image.png

目录
相关文章
|
Dubbo 网络协议 NoSQL
阿里巴巴的Netty面试题到底有多难,这些知识你能掌握多少?
Netty 是一个可以快速开发网络应用程序的 NIO 框架,它大大简化了 TCP 或者 UDP 服务器的网络编程。Netty 的简易和快速开发并不意味着由它开发的程序将失去可维护性或者存在性能问题,它的设计参考了许多协议的实现,比如 FTP,SMTP,HTTP 和各种二进制和基于文本的传统协议,因此 Netty 成功的实现了兼顾快速开发,性能,稳定性,灵活性为一体,不需要为了考虑一方面原因而妥协其他方面。Netty 的应用还是比较广泛的,比如阿里巴巴开源的 Dubbo 和 Sofa-Bolt 框架底层网络通讯都是基于 Netty 来实现的。
|
存储 缓存 算法
这些年背过的面试题——Netty篇
本文是技术人面试系列Netty篇,面试中关于Netty都需要了解哪些基础?一文带你详细了解,欢迎收藏!
|
消息中间件 监控 Java
滴滴面试:谈谈你对Netty线程模型的理解?
Netty 线程模型是指 Netty 框架为了提供高性能、高并发的网络通信,而设计的管理和利用线程的策略和机制。 **Netty 线程模型被称为 Reactor(响应式)模型/模式,它是基于 NIO 多路复用模型的一种升级,它的核心思想是将 IO 事件和业务处理进行分离,使用一个或多个线程来执行任务的一种机制。** ## 1.**Reactor三大组件** Reactor 包含以下三大组件: ![image.png](https://cdn.nlark.com/yuque/0/2024/png/92791/1717079218890-89000a00-48bc-4a1a-b87e-e1b6
557 2
|
消息中间件 存储 网络协议
拼多多面试:Netty如何解决粘包问题?
粘包和拆包问题也叫做粘包和半包问题,**它是指在数据传输时,接收方未能正常读取到一条完整数据的情况(只读取了部分数据,或多读取到了另一条数据的情况)就叫做粘包或拆包问题。** 从严格意义上来说,粘包问题和拆包问题属于两个不同的问题,接下来我们分别来看。 ## 1.粘包问题 粘包问题是指在网络通信中,发送方连续发送的多个小数据包被接收方一次性接收的现象。这可能是因为底层传输层协议(如 TCP)会将多个小数据包合并成一个大的数据块进行传输,导致接收方在接收数据时一次性接收了多个数据包,造成粘连。 例如以下案例,正常情况下客户端发送了两条消息,分别为“ABC”和“DEF”,那么接收端也应该收到两
287 0
拼多多面试:Netty如何解决粘包问题?
|
设计模式 前端开发 网络协议
面试官:说说Netty的核心组件?
Netty 核心组件是指 Netty 在执行过程中所涉及到的重要概念,这些核心组件共同组成了 Netty 框架,使 Netty 框架能够正常的运行。 Netty 核心组件包含以下内容: 1. 启动器 Bootstrap/ServerBootstrap 2. 事件循环器 EventLoopGroup/EventLoop 3. 通道 Channel 4. 通道处理器 ChannelHandler 5. 通道管道 ChannelPipeline 这些组件的交互流程如下: ![image.png](https://cdn.nlark.com/yuque/0/2024/png/92791/1716
364 0
面试官:说说Netty的核心组件?
|
消息中间件 存储 Java
美团面试:说说Netty的零拷贝技术?
零拷贝技术(Zero-Copy)是一个大家耳熟能详的技术名词了,它主要用于提升 IO(Input & Output)的传输性能。 那么问题来了,为什么零拷贝技术能提升 IO 性能? ## 1.零拷贝技术和性能 在传统的 IO 操作中,当我们需要读取并传输数据时,我们需要在用户态(用户空间)和内核态(内核空间)中进行数据拷贝,它的执行流程如下: ![](https://cdn.nlark.com/yuque/0/2024/png/92791/1706491312473-52f5904a-2742-4e99-9b78-995e9a8b9696.png?x-oss-process=image%2F
517 0
|
安全 IDE Java
【2024java面试题无需C币下载】终结空指针异常:Java开发者的生存指南
【2024java面试题无需C币下载】终结空指针异常:Java开发者的生存指南
361 1
|
存储 设计模式 缓存
面试官:说说Netty对象池的实现原理?
Netty 作为一个高性能的网络通讯框架,它内置了很多恰夺天工的设计,目的都是为了将网络通讯的性能做到极致,其中「对象池技术」也是实现这一目标的重要技术。 ## 1.什么是对象池技术? 对象池技术是一种重用对象以减少对象创建和销毁带来的开销的方法。在对象池中,只有第一次访问时会创建对象,并将其维护在内存中,当再次需要使用对象时,会直接从对象池中获取对象,并在使用完毕后归还给对象池,而不是频繁地创建和销毁对象。 使用对象池技术的优点有以下几个: 1. **提高性能**:复用对象可以减少对象的创建和销毁次数,降低系统开销,提高系统性能和吞吐量。 2. **减少内存碎片**:对象池可以避免
225 0
|
设计模式 监控 网络协议
Netty面试题
Netty面试题
|
XML JSON 网络协议
Netty相关面试题汇总
Netty相关面试题汇总
415 0