六个月离职空档期获得足够时间反思自我+常见Netty面试题详解

五、Netty核心技术之线程模型讲解 一、线程模型基本介绍      不同的线程模式,对程序的性能有很大的影响,为了搞清Netty线程模式,我们来系统的讲解下各个线程模式,最后看看Netty线程模型有什么优越性。      目前存在的线程模型有: 传统阻塞IO服务模型 Reactor模式      根据Reator的数量和处理资源线程池线程的数量不同,有三种典型的实现 单Reactor 单线 阅读详情

前言

疫情期我离职了,在这个时间离职也是很无奈。但这次的经历却让我有了一个认真反思的机会。

回顾前两年的工作经历,每天忙忙碌碌,看似解决了很多问题,也积累了很多经验,但实际都是一些浅层次的东西,只不过是缩短了开发时间完成需求而已,在性能优化以及代码的严谨度、高可用性上和大厂真没法比。

小公司不要求代码有多么的优雅,只要能按时保证在不出BUG的情况下上线就可以。所以在时间紧急的情况下,不会对代码进行高度的封装重构。还有就是团队人员上可能较少,一个人要负责多种人的角色。不能够专心的去完成开发工作。久而久之就失去了创造优雅代码的思维。

大厂就不一样了,团队人员齐全,每个人只需要负责自己的业务就可以,那做出来的东西肯定就会更精致。而且技术上还能得到大牛的指点,会少走很多弯路。

所以鼓励大家能进大厂一定要进大厂。

两天面了4家都给了offer,但薪资却都给不到位。 他们的共同点是都是小公司,希望花更少的钱招到更全面一些的人。

也有收到大厂的面试,但总在2轮面试的时候就被刷掉。有两个原因,第一个是大厂的面试问到的问题更具有深度一些,看似平常很常见的东西,会用却讲不出原理来。第二个是学历(好吧,我承认也是我技术不行,专科技术到位也可以进去)

这也让我深刻的反思了一下,前两年只注重了技术的广度,却忽视了技术的深度。在某一领域不是最优,具有可替代性。

需求的完成度再好,也只是个干活的。同样能完成需求的人一大把,总会有更便宜的人选,那为什么还会选择你。所以得培养自己的核心竞争力,让自己能够有拥有别人替代不了的价值。 还有就是对行业的选择。 什么样的行业可持续性会更久。 这次是入职了,但下一次再遇到倒闭,遇到裁员呢? 又要反反复复的面试。年纪小一些还行,到了30岁还再去面试就有问题了。 再有一个就是一定要找机会拓展自己的业务能力,不能单单只是一个技术员。

我已经走了一些弯路,希望那些快要毕业,即将踏入社会的大学生们,在毕业选择就业单位的时候一定要谨慎,不是说随便找一个单位就可以,一定要想清楚自己未来的发展方向。

下面分享下近段时间经常会在面试中碰到 Netty 相关的问题。

采用大家喜欢的与面试官对话的形式展开。 如果大家觉得总结的不错的话,不妨点一个赞鼓励一下!这是我继续坚持很重要的动力来源。

概览:

  • Netty 是什么?
  • 为什么要用 Netty?
  • Netty 应用场景了解么?
  • Netty 核心组件有哪些?分别有什么作用?
  • EventloopGroup 了解么?和 EventLoop 啥关系?
  • Bootstrap 和 ServerBootstrap 了解么?
  • NioEventLoopGroup 默认的构造函数会起多少线程?
  • Netty 线程模型了解么?
  • Netty 服务端和客户端的启动过程了解么?
  • Netty 长连接、心跳机制了解么?
  • Netty 的零拷贝了解么?

Netty 是什么?

面试官 :介绍一下自己对 Netty 的认识吧!小伙子。

我 :好的!那我就简单用 3 点来概括一下 Netty 吧!

  1. Netty 是一个 基于 NIO 的 client-server(客户端服务器)框架,使用它可以快速简单地开发网络应用程序。
  2. 它极大地简化并优化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面甚至都要更好。
  3. 支持多种协议 如 FTP,SMTP,HTTP 以及各种二进制和基于文本的传统协议。

用官方的总结就是:Netty 成功地找到了一种在不妥协可维护性和性能的情况下实现易于开发,性能,稳定性和灵活性的方法。

除了上面介绍的之外,很多开源项目比如我们常用的 Dubbo、RocketMQ、Elasticsearch、gRPC 等等都用到了 Netty。

网络编程我愿意称 Netty 为王 。

为什么要用 Netty?

面试官 :为什么要用 Netty 呢?能不能说一下自己的看法。

我 :因为 Netty 具有下面这些优点,并且相比于直接使用 JDK 自带的 NIO 相关的 API 来说更加易用。

  • 统一的 API,支持多种传输类型,阻塞和非阻塞的。
  • 简单而强大的线程模型。
  • 自带编解码器解决 TCP 粘包/拆包问题。
  • 自带各种协议栈。
  • 真正的无连接数据包套接字支持。
  • 比直接使用 Java 核心 API 有更高的吞吐量、更低的延迟、更低的资源消耗和更少的内存复制。
  • 安全性不错,有完整的 SSL/TLS 以及 StartTLS 支持。
  • 社区活跃
  • 成熟稳定,经历了大型项目的使用和考验,而且很多开源项目都使用到了 Netty, 比如我们经常接触的 Dubbo、RocketMQ 等等。
  • ......

Netty 应用场景了解么?

面试官 :能不能通俗地说一下使用 Netty 可以做什么事情?

我 :凭借自己的了解,简单说一下吧!理论上来说,NIO 可以做的事情 ,使用 Netty 都可以做并且更好。Netty 主要用来做网络通信 :

  1. 作为 RPC 框架的网络通信工具 :我们在分布式系统中,不同服务节点之间经常需要相互调用,这个时候就需要 RPC 框架了。不同服务节点之间的通信是如何做的呢?可以使用 Netty 来做。比如我调用另外一个节点的方法的话,至少是要让对方知道我调用的是哪个类中的哪个方法以及相关参数吧!
  2. 实现一个自己的 HTTP 服务器 :通过 Netty 我们可以自己实现一个简单的 HTTP 服务器,这个大家应该不陌生。说到 HTTP 服务器的话,作为 Java 后端开发,我们一般使用 Tomcat 比较多。一个最基本的 HTTP 服务器可要以处理常见的 HTTP Method 的请求,比如 POST 请求、GET 请求等等。
  3. 实现一个即时通讯系统 :使用 Netty 我们可以实现一个可以聊天类似微信的即时通讯系统,这方面的开源项目还蛮多的,可以自行去 Github 找一找。
  4. 实现消息推送系统 :市面上有很多消息推送系统都是基于 Netty 来做的。
  5. ......

Netty 核心组件有哪些?分别有什么作用?

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

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

1.Channel

Channel 接口是 Netty 对网络操作抽象类,它除了包括基本的 I/O 操作,如 bind()、connect()、read()、write() 等。

比较常用的Channel接口实现类是NioServerSocketChannel(服务端)和NioSocketChannel(客户端),这两个 Channel 可以和 BIO 编程模型中的ServerSocket以及Socket两个概念对应上。Netty 的 Channel 接口所提供的 API,大大地降低了直接使用 Socket 类的复杂性。

2.EventLoop

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

《Netty 实战》这本书是这样介绍它的:(可关注后私信小编“pdf”免费获取这本书)

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

 

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

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

那 Channel 和 EventLoop 直接有啥联系呢?

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

3.ChannelFuture

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

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

并且,你还可以通过ChannelFuture 的 channel() 方法获取关联的Channel


 
public interface ChannelFuture extends Future<Void> {     Channel channel();     ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> var1);      ......     ChannelFuture sync() throws InterruptedException; }

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

4.ChannelHandler 和 ChannelPipeline

下面这段代码使用过 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 是消息的具体处理器。他负责处理读写操作、客户端连接等事情。

ChannelPipeline 为 ChannelHandler 的链,提供了一个容器并定义了用于沿着链传播入站和出站事件流的 API 。当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。

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

EventloopGroup 了解么?和 EventLoop 啥关系?

面试官 :刚刚你也介绍了 EventLoop。那你再说说 EventloopGroup 吧!和 EventLoop 啥关系?

我 :

 

EventLoopGroup 包含多个 EventLoop(每一个 EventLoop 通常内部包含一个线程),上面我们已经说了 EventLoop 的主要作用实际就是负责监听网络事件并调用事件处理器进行相关 I/O 操作的处理。

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

上图是一个服务端对 EventLoopGroup 使用的大致模块图,其中 Boss EventloopGroup 用于接收连接,Worker EventloopGroup 用于具体的处理(消息的读写以及其他逻辑处理)。

从上图可以看出:当客户端通过 connect 方法连接服务端时,bossGroup 处理客户端连接请求。当客户端处理完成后,会将这个连接提交给 workerGroup 来处理,然后 workerGroup 负责处理其 IO 相关操作。

Bootstrap 和 ServerBootstrap 了解么?

面试官 :你再说说自己对 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 ,一个用于接收连接,一个用于具体的处理。

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

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

我 :嗯嗯!看过部分。

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


 
// 1.bossGroup 用于接收连接,workerGroup 用于具体的处理 EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup();

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


 
    /**      * 无参构造函数。      * nThreads:0      */     public NioEventLoopGroup() {         //调用下一个构造方法         this(0);     }     /**      * Executor:null      */     public NioEventLoopGroup(int nThreads) {         //继续调用下一个构造方法         this(nThreads, (Executor) null);     }     //中间省略部分构造函数     /**      * RejectedExecutionHandler():RejectedExecutionHandlers.reject()      */     public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {        //开始调用父类的构造函数         super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());     } 

一直向下走下去的话,你会发现在 MultithreadEventLoopGroup 类中有相关的指定线程数的代码,如下:


 
    // 从1,系统属性,CPU核心数*2 这三个值中取出一个最大的     //可以得出 DEFAULT_EVENT_LOOP_THREADS 的值为CPU核心数*2     private static final int DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));     // 被调用的父类构造函数,NioEventLoopGroup 默认的构造函数会起多少线程的秘密所在     // 当指定的线程数nThreads为0时,使用默认的线程数DEFAULT_EVENT_LOOP_THREADS     protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {         super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);     }

综上,我们发现 NioEventLoopGroup 默认的构造函数实际会起的线程数为 CPU核心数*2。

另外,如果你继续深入下去看构造函数的话,你会发现每个NioEventLoopGroup对象内部都会分配一组NioEventLoop,其大小是 nThreads, 这样就构成了一个线程池, 一个NIOEventLoop 和一个线程相对应,这和我们上面说的 EventloopGroup 和 EventLoop关系这部分内容相对应。

Netty 线程模型了解么?

面试官 :说一下 Netty 线程模型吧!

我 :大部分网络框架都是基于 Reactor 模式设计开发的。

“Reactor 模式基于事件驱动,采用多路复用将事件分发给相应的 Handler 处理,非常适合处理海量 IO 的场景。

在 Netty 主要靠 NioEventLoopGroup 线程池来实现具体的线程模型的 。

我们实现服务端的时候,一般会初始化两个线程组:

  1. bossGroup :接收连接。
  2. workerGroup :负责具体的处理,交由对应的 Handler 处理。

下面我们来详细看一下 Netty 中的线程模型吧!

1、单线程模型 :

一个线程需要执行处理所有的 accept、read、decode、process、encode、send 事件。对于高负载、高并发,并且对性能要求比较高的场景不适用。

对应到 Netty 代码是下面这样的

“使用 NioEventLoopGroup 类的无参构造函数设置线程数量的默认值就是 CPU 核心数 *2 。


 
  //1.eventGroup既用于处理客户端连接,又负责具体的处理。   EventLoopGroup eventGroup = new NioEventLoopGroup(1);   //2.创建服务端启动引导/辅助类:ServerBootstrap   ServerBootstrap b = new ServerBootstrap();             boobtstrap.group(eventGroup, eventGroup)             //......

2、多线程模型

一个 Acceptor 线程只负责监听客户端的连接,一个 NIO 线程池负责具体处理:accept、read、decode、process、encode、send 事件。满足绝大部分应用场景,并发连接量不大的时候没啥问题,但是遇到并发连接大的时候就可能会出现问题,成为性能瓶颈。

对应到 Netty 代码是下面这样的:


 
// 1.bossGroup 用于接收连接,workerGroup 用于具体的处理 EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try {   //2.创建服务端启动引导/辅助类:ServerBootstrap   ServerBootstrap b = new ServerBootstrap();   //3.给引导类配置两大线程组,确定了线程模型   b.group(bossGroup, workerGroup)     //......

 

3、主从多线程模型

从一个 主线程 NIO 线程池中选择一个线程作为 Acceptor 线程,绑定监听端口,接收客户端连接的连接,其他线程负责后续的接入认证等工作。连接建立完成后,Sub NIO 线程池负责具体处理 I/O 读写。如果多线程模型无法满足你的需求的时候,可以考虑使用主从多线程模型 。


 
// 1.bossGroup 用于接收连接,workerGroup 用于具体的处理 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try {   //2.创建服务端启动引导/辅助类:ServerBootstrap   ServerBootstrap b = new ServerBootstrap();   //3.给引导类配置两大线程组,确定了线程模型   b.group(bossGroup, workerGroup)     //......

 

Netty 服务端和客户端的启动过程了解么?

服务端


 
        // 1.bossGroup 用于接收连接,workerGroup 用于具体的处理         EventLoopGroup bossGroup = new NioEventLoopGroup(1);         EventLoopGroup workerGroup = new NioEventLoopGroup();         try {             //2.创建服务端启动引导/辅助类:ServerBootstrap             ServerBootstrap b = new ServerBootstrap();             //3.给引导类配置两大线程组,确定了线程模型             b.group(bossGroup, workerGroup)                     // (非必备)打印日志                     .handler(new LoggingHandler(LogLevel.INFO))                     // 4.指定 IO 模型                     .channel(NioServerSocketChannel.class)                     .childHandler(new ChannelInitializer<SocketChannel>() {                         @Override                         public void initChannel(SocketChannel ch) {                             ChannelPipeline p = ch.pipeline();                             //5.可以自定义客户端消息的业务处理逻辑                             p.addLast(new HelloServerHandler());                         }                     });             // 6.绑定端口,调用 sync 方法阻塞知道绑定完成             ChannelFuture f = b.bind(port).sync();             // 7.阻塞等待直到服务器Channel关闭(closeFuture()方法获取Channel 的CloseFuture对象,然后调用sync()方法)             f.channel().closeFuture().sync();         } finally {             //8.优雅关闭相关线程组资源             bossGroup.shutdownGracefully();             workerGroup.shutdownGracefully();         }

简单解析一下服务端的创建过程具体是怎样的:

1、首先你创建了两个 NioEventLoopGroup 对象实例:bossGroup 和 workerGroup。

  • bossGroup : 用于处理客户端的 TCP 连接请求。
  • workerGroup :负责每一条连接的具体读写数据的处理逻辑,真正负责 I/O 读写操作,交由对应的 Handler 处理。

举个例子:我们把公司的老板当做 bossGroup,员工当做 workerGroup,bossGroup 在外面接完活之后,扔给 workerGroup 去处理。一般情况下我们会指定 bossGroup 的 线程数为 1(并发连接量不大的时候) ,workGroup 的线程数量为 CPU 核心数 *2 。另外,根据源码来看,使用 NioEventLoopGroup 类的无参构造函数设置线程数量的默认值就是 CPU 核心数 *2 。

2、接下来 我们创建了一个服务端启动引导/辅助类:ServerBootstrap,这个类将引导我们进行服务端的启动工作。

3、通过 .group() 方法给引导类 ServerBootstrap 配置两大线程组,确定了线程模型。

通过下面的代码,我们实际配置的是多线程模型,这个在上面提到过。


 
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);     EventLoopGroup workerGroup = new NioEventLoopGroup();

4、通过channel()方法给引导类 ServerBootstrap指定了 IO 模型为NIO

  • NioServerSocketChannel :指定服务端的 IO 模型为 NIO,与 BIO 编程模型中的ServerSocket对应
  • NioSocketChannel : 指定客户端的 IO 模型为 NIO, 与 BIO 编程模型中的Socket对应5.通过 .childHandler()给引导类创建一个ChannelInitializer ,然后制定了服务端消息的业务处理逻辑 HelloServerHandler 对象6.调用 ServerBootstrap 类的 bind()方法绑定端口

客户端


 
        //1.创建一个 NioEventLoopGroup 对象实例         EventLoopGroup group = new NioEventLoopGroup();         try {             //2.创建客户端启动引导/辅助类:Bootstrap             Bootstrap b = new Bootstrap();             //3.指定线程组             b.group(group)                     //4.指定 IO 模型                     .channel(NioSocketChannel.class)                     .handler(new ChannelInitializer<SocketChannel>() {                         @Override                         public void initChannel(SocketChannel ch) throws Exception {                             ChannelPipeline p = ch.pipeline();                             // 5.这里可以自定义消息的业务处理逻辑                             p.addLast(new HelloClientHandler(message));                         }                     });             // 6.尝试建立连接             ChannelFuture f = b.connect(host, port).sync();             // 7.等待连接关闭(阻塞,直到Channel关闭)             f.channel().closeFuture().sync();         } finally {             group.shutdownGracefully();         }

继续分析一下客户端的创建流程:

1.创建一个 NioEventLoopGroup 对象实例

2.创建客户端启动的引导类是 Bootstrap

3.通过 .group() 方法给引导类 Bootstrap 配置一个线程组

4.通过channel()方法给引导类 Bootstrap指定了 IO 模型为NIO

5.通过 .childHandler()给引导类创建一个ChannelInitializer ,然后制定了客户端消息的业务处理逻辑 HelloClientHandler 对象

6.调用 Bootstrap 类的 connect()方法进行连接,这个方法需要指定两个参数:

  • inetHost : ip 地址
  • inetPort : 端口号

 
    public ChannelFuture connect(String inetHost, int inetPort) {         return this.connect(InetSocketAddress.createUnresolved(inetHost, inetPort));     }     public ChannelFuture connect(SocketAddress remoteAddress) {         ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");         this.validate();         return this.doResolveAndConnect(remoteAddress, this.config.localAddress());     }

connect 方法返回的是一个 Future 类型的对象


 
public interface ChannelFuture extends Future<Void> {   ...... }

也就是说这个方是异步的,我们通过 addListener 方法可以监听到连接是否成功,进而打印出连接信息。具体做法很简单,只需要对代码进行以下改动:


 
ChannelFuture f = b.connect(host, port).addListener(future -> {   if (future.isSuccess()) {     System.out.println("连接成功!");   } else {     System.err.println("连接失败!");   } }).sync();

什么是 TCP 粘包/拆包?有什么解决办法呢?

面试官 :什么是 TCP 粘包/拆包?

我 :TCP 粘包/拆包 就是你基于 TCP 发送数据的时候,出现了多个字符串“粘”在了一起或者一个字符串被“拆”开的问题。比如你多次发送:“你好,你真帅啊!哥哥!”,但是客户端接收到的可能是下面这样的:

 

面试官 :那有什么解决办法呢?

我 :

1.使用 Netty 自带的解码器

  • LineBasedFrameDecoder : 发送端发送数据包的时候,每个数据包之间以换行符作为分隔,LineBasedFrameDecoder 的工作原理是它依次遍历 ByteBuf 中的可读字节,判断是否有换行符,然后进行相应的截取。
  • DelimiterBasedFrameDecoder : 可以自定义分隔符解码器,LineBasedFrameDecoder 实际上是一种特殊的 DelimiterBasedFrameDecoder 解码器。
  • FixedLengthFrameDecoder: 固定长度解码器,它能够按照指定的长度对消息进行相应的拆包。
  • LengthFieldBasedFrameDecoder:

2.自定义序列化编解码器

在 Java 中自带的有实现 Serializable 接口来实现序列化,但由于它性能、安全性等原因一般情况下是不会被使用到的。

通常情况下,我们使用 Protostuff、Hessian2、json 序列方式比较多,另外还有一些序列化性能非常好的序列化方式也是很好的选择:

  • 专门针对 Java 语言的:Kryo,FST 等等
  • 跨语言的:Protostuff(基于 protobuf 发展而来),ProtoBuf,Thrift,Avro,MsgPack 等等

由于篇幅问题,这部分内容会在后续的文章中详细分析介绍~~~

Netty 长连接、心跳机制了解么?

面试官 :TCP 长连接和短连接了解么?

我 :我们知道 TCP 在进行读写之前,server 与 client 之间必须提前建立一个连接。建立连接的过程,需要我们常说的三次握手,释放/关闭连接的话需要四次挥手。这个过程是比较消耗网络资源并且有时间延迟的。

所谓,短连接说的就是 server 端 与 client 端建立连接之后,读写完成之后就关闭掉连接,如果下一次再要互相发送消息,就要重新连接。短连接的有点很明显,就是管理和实现都比较简单,缺点也很明显,每一次的读写都要建立连接必然会带来大量网络资源的消耗,并且连接的建立也需要耗费时间。

长连接说的就是 client 向 server 双方建立连接之后,即使 client 与 server 完成一次读写,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。长连接的可以省去较多的 TCP 建立和关闭的操作,降低对网络资源的依赖,节约时间。对于频繁请求资源的客户来说,非常适用长连接。

面试官 :为什么需要心跳机制?Netty 中心跳机制了解么?

我 :在 TCP 保持长连接的过程中,可能会出现断网等网络异常出现,异常发生的时候, client 与 server 之间如果没有交互的话,它们是无法发现对方已经掉线的。为了解决这个问题, 我们就需要引入 心跳机制 。

心跳机制的工作原理是: 在 client 与 server 之间在一定时间内没有数据交互时, 即处于 idle 状态时, 客户端或服务器就会发送一个特殊的数据包给对方, 当接收方收到这个数据报文后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互。所以, 当某一端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性.

TCP 实际上自带的就有长连接选项,本身是也有心跳包机制,也就是 TCP 的选项:SO_KEEPALIVE。但是,TCP 协议层面的长连接灵活性不够。所以,一般情况下我们都是在应用层协议上实现自定义心跳机制的,也就是在 Netty 层面通过编码实现。通过 Netty 实现心跳机制的话,核心类是 IdleStateHandler 。

Netty 的零拷贝了解么?

面试官 :讲讲 Netty 的零拷贝?

我 :维基百科是这样介绍零拷贝的:

“零复制(英语:Zero-copy;也译零拷贝)技术是指计算机执行操作时,CPU 不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省 CPU 周期和内存带宽。

在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据。而在 Netty 层面 ,零拷贝主要体现在对于数据操作的优化。

Netty 中的零拷贝体现在以下几个方面:

  • 使用 Netty 提供的 CompositeByteBuf 类, 可以将多个ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝。
  • ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝。
  • 通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题.

最后

这本312页的netty实战书籍免费送给粉丝们,转发加关注后私信“学习”免费获取;

AXI Timer IP核使用说明 AXI Timer是提供定时技术功能的集成IP核,具有时间生成、事件捕获、产生PWM波以及产生中断的功能,下面具体讲述Timer IP核的使用说明。 阅读详情

相关推荐

Matlab TreeBagger实战:用随机森林预测房价的5个关键步骤(附完整代码)

本文详细介绍了使用Matlab的TreeBagger工具包构建随机森林模型预测房价的完整流程。通过波士顿房价数据集实战,涵盖数据预处理、模型构建、参数调优、特征重要性分析及高级应用(如分位数回归)等5个关键步骤,并附有完整代码,帮助读者快速掌握这一强大的回归分析工具。

ol789012的博客 444

35岁整,失业空窗6个月,终于接offer上岸,虽然降薪到四年前水平,但能继续养家糊口,也已经很好了。分享一点个人经验!...

????????关注后回复“进群”,拉你进程序员交流群????????作者丨行者来源丨架构师社区(ID:devabc)被裁后为了生存,人需要做出什么改变?一位35岁网友在失业6个月后终于拿到offer,虽然降薪到四年前的水平,但能继续养家糊口,楼主已经很满意了,并分享了自己的个人经验:1.挖掘历史项目经验,能沾边的都投,不拘泥于行业,有面则面,薪酬不设限。2.第一轮就强制要求现场面试的公司,如果不是很知名,建议慎重。...

<sdffdsfsdfdfs>sfsfsfsdfsdffds</sdfsDS>Fsd 442

C语言面试,8道一定会遇到的----关于指针的笔试题详解

C语言面试,8道一定会遇到的----关于指针的笔试题详解

m0_55322924的博客 463

【Netty】线程模型

目录1. 单Reactor单线程2. 单Reactor多线程3. Reactor主从模型 单线程模型 (单Reactor单线程) 多线程模型 (单Reactor多线程) 主从多线程模型 (多Reactor多线程) 1. 单Reactor单线程 所有操作都在同一个NIO线程处理,在这个单线程中要负责接收请求,处理IO,编解码所有操作,相当于一个饭馆只有一个人,同时负责前台和后台服务,效率低。 一个NIO线程同时处理成百上千的连接,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息

记录各种Bug解决案例及日常学习内容 2012

【课程设计】TCP/IP 应用环境中服务器的部署与实现

本项目旨在通过搭建一个虚拟网络环境,实现软路由系统的配置和服务器的搭建与测试。具体包括安装虚拟机 VMWare Workstation,安装 Windows Server 2003 操作系统,配置软路由系统(包括双网卡安装、协议配置和测试),以及搭建并测试 DHCP 服务、DNS 服务、WWW 服务、FTP 服务和 SMTP/POP3 服务。

weixin_74907236的博客 1524

PCI Express 权威指南:从协议原理到芯片实现

目录 全文总览第一部分:PCIe体系结构概览第1章 互连技术的演进与PCIe的诞生1.1 从ISA到PCI:并行总线的辉煌与瓶颈1.2 PCI-X的尝试与局限1.3 串行革命:从AGP、PCI到PCIe的范式转移1.4 PCIe的核心优势第2章 PCIe系统全局视角2.1 系统拓扑:Root Complex, Switch, Endpoint的职能与关系2.2 拓扑结构实例:服务器、工作站、嵌入式系统2.3 枚举与配置:系统如何发现和组织PCIe设备2.4 地址空间第3章 PCIe协议分层架构3.1 分层模

qq_42882296的博客 2894

【Netty】模型篇一:Netty 线程模型架构 & 工作原理 解读

本文通过与传统I/O模型以及三种Reactor模型进行对比,详细分析了Netty的线程模型是怎么设计的,以及它的工作流程。

正在努力工作的搬砖人 1万+

从零到一:在RK3588 Android12上实战RTL8723DU WiFi蓝牙双模驱动移植

本文详细介绍了在RK3588 Android12系统上实战RTL8723DU WiFi蓝牙双模驱动移植的全过程。从驱动源码获取与修改、系统级集成与编译,到调试与性能优化,提供了完整的解决方案。特别针对USB接口适配、电源管理和蓝牙共存等关键问题给出了具体代码示例,帮助开发者快速完成驱动适配并提升设备稳定性。

weixin_28703489的博客 325

Netty的线程模型

Netty是一个基于NIO(非阻塞I/O)的客户端-服务器框架,它使用Reactor模式来实现高并发处理。Netty的线程模型是其高性能、高并发处理能力的重要支撑。

猪猪 2250

YOLO26 实例分割技术:Mask 分支与特征融合

YOLO26实例分割技术研究摘要:本文提出了一种基于YOLO架构的高效实例分割方法,通过创新设计Mask分支和特征融合策略解决像素级分割精度、多尺度目标处理等挑战。系统采用共享Backbone提取特征,检测分支输出边界框和类别,Mask分支生成原型Mask和组合系数。核心创新包括:1)原型Mask生成与线性组合机制;2)检测与Mask特征共享;3)结合BCE和Dice的多任务损失函数。实验表明该方法在保持实时性的同时实现了高质量分割,为轻量级实例分割提供了新思路。

一键难忘的博客 248

ADS基础教程20 - 电磁仿真(EM)参数化仿真

一、引言二、参数化设置1.参数定义2.参数赋值3.创建EM模型和符号四、总结1)在Layout视图,菜单栏中选中EMComponent2)在弹出的参数编辑框中,采用标称/扰动()设计。使用子网(Subnetwork)参数。这些方法是互斥的,在为单个组件定义参数时不能将它们组合起来。标称/扰动(Nominal/Perturbed)参数使用平面化布局场景,并且只包含端口和原始图形形状(如多边形、矩形和圆形)时定义。

XZ_ZC的博客 4029

Netty线程模型(图解秒懂)

Netty是基于异步的事件驱动的网络框架和工具,常见的分布式中间件底层都有涉及到Netty。 Netty基本组件:Channel、Bootstrap 、EventLoop、ChannelPipeline、ChannelHandler、Future Netty的线程模型为三种:单Reactor单线程,单Reactor多线程、主从Reactor多线程 Reactor的作用相当于一个分发器,可以分发连接请求和处理IO请求;相当于一个饭店的服务员,是可以异步非阻塞地进行工作 这里的线程一般指的是处理IO请求的线程数

weixin_47257050的博客 2505

【unity知识】最新的虚拟相机Cinemachine3简单使用介绍

本文介绍了Unity中Cinemachine 3虚拟相机系统的使用方法。相比旧版本,新版功能更强大且参数分类更直观。文章详细讲解了摄像机镜头参数、旋转控制(包括None、目标跟随、样条线推拉、旋转追随等6种模式)和位置控制(跟随、样条轨道、硬锁定等5种模式)。重点演示了样条轨道摄像机的创建与配置,以及如何实现第一人称和第三人称视角。通过丰富的GIF图示,展示了各种模式的运行效果,帮助用户快速掌握Cinemachine 3的核心功能。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 866

Netty线程模型

Netty抽象出两组线程池,BossGroup专门负责接收客户端的链接,WorkerGroup专门负责网络的读写

不积跬步无以至千里,不积小流无以成江海。一个喜欢学习分享的程序员 1445

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

我为 Netty 贡献源码 | 且看 Netty 如何应对 TCP 连接的正常关闭,异常关闭,半关闭场景

Netty 关于处理 TCP 连接关闭时所需要面对的所有场景,你想知道吗?本文会全面清晰地告诉你答案!!!

bin的技术小屋 3797

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Netty线程模型详解

Netty的线程模型采用了Reactor模式,即一个或多个EventLoop轮询各自的任务队列,当发现有任务时,就处理它们。Netty支持单线程模型、多线程模型和混合线程模型等多种线程模型。Reactor其实是在NIO多路复用的基础上提出的一个高性能IO设计模式。它的核心思想是把响应IO事件和业务处理进行分离,通过一个或者多个线程来处理IO事件。然后把就绪的事件分发给业务线程进行异步处理。Reactor模型有三个重要的组件:Reactor :把I/O事件分发给对应的Handler。

刘皇叔说Java的博客 2418

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

在Netty中,如何优雅地关闭一个连接?你能解释一下Netty的Channel、EventLoop和EventLoopGroup的区别和联系吗?

Channel是Netty网络抽象的核心,它代表了一个到实体(如硬件设备、文件、网络套接字或可以执行I/O操作的程序组件)的开放连接,如TCP/IP套接字连接。EventLoop是一个线程驱动的,它在其生命周期内只绑定一个线程,处理所有分配给它的Channel的事件。总的来说,Channel、EventLoop和EventLoopGroup是Netty框架中的核心组件,它们共同协作,使得Netty能够进行高效、异步、事件驱动的网络通信和数据处理。需要注意的是,优雅关闭并不意味着立即关闭。

Aaron的博客 2708

netty 关闭连接_netty入门知识点整理

最近笔者在研究Dubbo的网络层设计,Dubbo的网络层使用的netty框架,笔者在学校的时候也有接触过,但是时间长了,很多知识点也已忘记,最近两天,笔者重新学习了netty框架,实现了一个带心跳的Echo案例,将知识点整理成文章,以便日后回顾,同时希望能帮助到有需要的同学1. ChannelChannel代表了netty对网络连接的抽象,Channel是线程安全的,它提供了一些重要信息,比如当...

weixin_39832643的博客 1544

Netty——I/O 线程模型

本文讲解了几种常见的 I/O 线程模型,并且介绍了 Redis 和 Netty 使用的 I/O 线程模型。

qq_61350148的博客 1427

netty 关闭连接_Netty原理和基础(二)

《高并发实战》之Netty基础和原理前言 这周末去参加了个音乐节玩的比较嗨,文章也没做整理,加上最近阿里云主机被攻击,搞得很烦,这几天在处理云主机的问题,后续也会把遇到的木马、挖矿程序整理出来。这篇接着上一篇,对netty基础和原理做个收尾。五、详解Channel通道通过上面的学习,通道是Netty网络中最重要的核心,代表着网络连接。通道是通信的主题,负责同对端进行网络通信,可以写...

weixin_39786141的博客 1823

netty关闭连接_Netty网络编程

引言:本文主要介绍netty特性,netty的核心组件以及netty使用,建议配合netty源码学习Netty核心组件Netty的主要构件块包括:1 .Channel 2.回调 3.Future 4.事件和ChannelHandler Channel :是Java NIO 的一个基本构造 ,它代表一个硬件设备,一个文件,一个网络套接字或者一个能够执行一个或者多个不同的IO操作的程序组件)的开放连...

weixin_39654245的博客 556

彻底搞懂Netty的线程模型

前言 Netty是基于异步的事件驱动的高性能网络框架和工具,常见的分布式中间件底层都有涉及到Netty。 要学习netty线程模型,先了解三种IO模型,这样理解netty线程模型和IO模型更轻松。 BIO 同步阻塞IO模型 一个线程负责连接,多线程则为每一个接入开启一个线程一个请求一个应答请求之后应答之前客户端会一直阻塞 NIO 同步非阻塞I/O 基于IO多路复用技术的“非阻塞同步”IO模型。简单来说,内核将可读可写事件通知应用,由应用主动发起读写操作; AIO 异步非阻塞IO,AIO 引入异步通道的

4112

Netty核心源码剖析

为什么要看源码: 1、提升技术功底:学习源码里的优秀设计思想,比如一些疑难问题的解决思路,还有一些优秀的设计模式,整体提升自己的技术功底 2、深度掌握技术框架:源码看多了,对于一个新技术或框架的掌握速度会有大幅提升,看下框架demo大致就能知道底层的实现,技术框架更新再快也不怕 3、快速定位线上问题:遇到线上问题,特别是框架源码里的问题(比如bug),能够快速定位,这就是相比其他没看过源码的人的优势 4、对面试大有裨益:面试一线互联网公司对于框架技术一般都会问到源码级别的实现 5、知其然知其所以然

1 1163

四、Java NIO 深入解读--Netty模型

这样线程模型就实现了统一。从调度层面看,也不存在从EventLoop线程中再启动其他类型的线程用于异步执行另外的任务,这样就避免了多线程并发操作和锁竞争,提升了IO线程的处理和调度性能。

qq_41925134的博客 318

公司 CTO:高性能开发,你不会 Netty,怎么好意思拿 20K?

主管:这个版块用 Netty 框架就可以了呀,不会吗?(此时,公司 CTO 路过)某程序员:这个我真不会...主管:好了好了,那这一块我交给别人去做,这个也不难啊?!某程序员:大佬,我一定会好好学习,早日把这一块弄懂!主管:行了,你先去忙吧!CTO:慢着!这是什么情况?项目分配下去了,怎么能又安排其他同事来加班做呢?主管:总监,项目这个版块涉及到高性能的开发,这个同事可能搞不定,眼看着项目要上线了,还是让经验丰富的工程师来做吧!CTO(凶狠的对着程序员):那也不行,拿着这么高的工资,这都做不了,Netty

m0_74931226的博客 338
上一篇: 80%的人都回答不上来的一道Java面试题
下一篇: 备战两个月成功拿阿里offer
Java斗帝之路
博客等级 码龄7年 2392粉丝 687原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值