1. Netty核心功能与线程模型详解

本文介绍了Netty的基础知识,包括Netty的核心组件如EventLoop和EventLoopGroup,Channel及其生命周期,ChannelPipeline和ChannelHandlerContext的作用。Netty作为高性能的网络通信框架,因其简单易用、功能强大和高度定制的特点被广泛应用。文中还展示了如何创建第一个Netty程序,包括服务端和客户端的实现,并强调了ChannelHandler在处理事件和数据流中的关键角色。


本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。

课程内容:

01、Netty核心组件快速了解
02、Hello,Netty!
03、深入理解Channel、EventLoop(Group)
04、深入理解EventLoop和EventLoopGroup
05、ChannelHandler和它的适配器
06、ChannelPipeline辨析
07、ChannelHandlerContext辨析
08、用Netty解决TCP粘包/半包
09、Netty中的编解码器
10、实战Netty快速实现Web服务器
11、序列化框架Netty集成实战
12、Netty中的单元测试

1. 认识Netty

Netty 4.1.42.Final版本进行

<dependency>
   <groupId>io.netty</groupId>
   <artifactId>netty-all</artifactId>
   <version>4.1.42.Final</version>
   <scope>compile</scope>
</dependency>

基于Netty的知名项目

  • 数据库:Cassandra
  • 大数据处理:Spark、Hadoop
  • 消息中间件:RocketMQ
  • 检索:Elasticsearch
  • 框架:gRPC、Apache Dubbo、Spring5 WebFlux
  • 分布式协调器:ZooKeeper

Netty的优势

  1. API使用简单,开发门槛低;
  2. 功能强大,预置了多种编解码功能,支持多种主流协议;
  3. 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
  4. 性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优;
  5. 成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦恼;
  6. 社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会加入;
  7. 经历了大规模的商业应用考验,质量得到验证。

为什么不用Netty5 ?
Netty5有两个重大改变:支持AIO(异步编程,就是要回调),将线程模型改成了favk/join。通过测试发现其实对性能的提高不是很大,几乎不明显。

因为Netty5还处于Alpha1版本,几乎停止维护了

为什么Netty使用NIO而不是AIO?
Linux下的AIO是一个伪AIO,Linux下的AIO性能也不如NIO的强。

为什么不用Mina ?

其实根据诞生关系是Netty的诞生稍微比Mina早了那么一点,这个Netty与Mina是同一个作者,这个作者在完成了Mina之后又投身于Netty种了,相当于Mina处于缺少维护的状态。并且Netty之后发布的版本的性能都比Mina优秀,因此就没有使用Mina

2. 第一个Netty程序

Netty的核心组件初步认识

  • Bootstrap、EventLoop(Group)、Channel
  • 事件和ChannelHandler、ChannelPipeline
  • ChannelFuture

Bootstrap是Netty框架启动类和主入口类。Bootstrap分为服务端的与客户端的,

在 Netty 中,Channel 是网络通信的基本概念和抽象。它代表着一个开放的连接,可以进行数据的读写和事件的处理。
具体来说,Channel 可以理解为应用程序与网络套接字之间的通道,它提供了以下主要功能:

  1. 数据的读写:通过 Channel,应用程序可以从网络中读取数据或将数据写入网络。它提供了高效的读写操作,支持异步非阻塞的 I/O 操作。可以通过 Channel 的读写方法来实现数据的传输和交换。
  2. 事件的处理:Channel 是一个事件驱动的实体,它可以处理各种网络事件,例如连接建立、数据到达、写就绪等。通过注册不同类型的事件处理器(ChannelHandler),应用程序可以根据需要处理这些事件,执行相应的逻辑处理。
  3. 状态的管理:Channel 提供了连接的状态管理,包括打开、关闭、活动状态等。应用程序可以通过 Channel 的状态变化事件进行相应的处理,例如连接建立或断开时的处理操作。
  4. 通道的配置:Channel 可以进行各种配置,如设置缓冲区大小、接收和发送缓冲区的设置、超时设置等。通过配置 Channel,可以调整网络通信的性能和行为。
    总之,Channel 在 Netty 中扮演着非常重要的角色,它是应用程序与网络之间的桥梁,提供了数据读写、事件处理和状态管理等功能。通过使用 Channel,开发者可以方便地进行网络编程,实现高性能和可靠的网络通信。

EventLoop(Group)
在 Netty 中,EventLoop 是用于处理 I/O 事件和任务的线程。它是 Netty 异步事件驱动的核心组件,负责管理 Channel 的生命周期、处理事件和执行任务。
EventLoop 在 EventLoopGroup 中扮演着重要角色,而 EventLoopGroup 则是一组维护和管理 EventLoop 的容器。一般情况下,一个应用程序只需要一个 EventLoopGroup,而其中的多个 EventLoop 可以同时处理多个 Channel 的 I/O 操作。
EventLoop 的主要功能包括:

  1. I/O 事件的处理:EventLoop 负责处理 Channel 的 I/O 事件,如数据的读写、连接的建立和关闭等。当一个 I/O 事件发生时,EventLoop 会调用相应的 ChannelHandler 处理事件,并触发相应的回调方法。通过事件驱动的方式,实现了高效的异步非阻塞 I/O。
  2. 任务的执行:除了处理 I/O 事件外,EventLoop 还可以执行用户自定义的任务。这些任务可以是一些耗时的计算、定时任务、事件调度等。通过将任务提交给 EventLoop,可以在事件循环中异步执行,避免了阻塞主线程的情况。
  3. 定时任务的处理:EventLoop 提供了定时任务的调度和执行,支持在一定的时间间隔或固定的时间点执行任务。这对于需要定时执行一些操作的应用场景非常有用,例如心跳检测、定时数据刷新等。

需要注意的是,EventLoop 在内部维护一个事件循环(Event Loop),它会不断地轮询 I/O 事件和任务,以便及时处理。每个 EventLoop 都有一个独立的线程来执行这个循环,确保了并发的安全性。

总之,EventLoop 是 Netty 中负责处理 I/O 事件和任务的线程,通过事件驱动的方式实现了高效的异步非阻塞 I/O。它在 EventLoopGroup 中起着重要的作用,为应用程序提供了高性能和可扩展的网络编程能力。

在 Netty 中,事件(Event)、ChannelHandler 和 ChannelPipeline 是实现高效网络通信的重要组件。

  1. 事件(Event):事件是 Netty 中的核心概念,它代表了网络通信过程中发生的各种状态和操作,如连接建立、数据到达、数据写就绪等。Netty 使用事件来驱动整个网络通信过程,通过触发和处理事件来实现异步的、非阻塞的网络通信。
  2. ChannelHandler:ChannelHandler 是用于处理事件和执行业务逻辑的组件。它定义了一组回调方法,如 channelRead()、channelActive()、channelInactive() 等,用于处理不同类型的事件。开发者可以实现自己的 ChannelHandler,并将其注册到 ChannelPipeline 中,以便在特定事件发生时执行相应的逻辑处理。
  3. ChannelPipeline:ChannelPipeline 是 ChannelHandler 的容器,它负责管理和执行 ChannelHandler 的调用顺序。每个 Channel 都会有一个对应的 ChannelPipeline,用于处理该 Channel 上的事件和数据。当一个事件被触发时,ChannelPipeline 会按照预定的顺序依次调用注册的 ChannelHandler 来处理事件。通过配置不同的 ChannelHandler,可以实现数据的编解码、数据处理、安全认证等功能。

ChannelPipeline 的设计遵循了责任链模式,每个 ChannelHandler 只负责自己关心的事件,通过调用下一个 ChannelHandler 来传递事件和数据。这样可以实现组件的解耦和灵活的功能扩展。

通过将不同的 ChannelHandler 注册到 ChannelPipeline 中,可以构建一个处理器链,每个处理器负责特定的功能。数据在经过 ChannelPipeline 时,会按照处理器链的顺序依次经过每个处理器,最终被发送到合适的目标。

总结来说,事件、ChannelHandler 和 ChannelPipeline 是 Netty 中实现高效网络通信的核心组件。事件用于驱动整个网络通信过程,ChannelHandler 用于处理事件和执行业务逻辑,而 ChannelPipeline 则是管理和执行 ChannelHandler 的调用顺序,实现了组件的解耦和灵活的功能扩展。通过合理配置和使用这些组件,可以实现高性能、可扩展的网络应用。

在 Netty 中,ChannelFuture 是用于异步操作结果的表示和处理的对象。它表示一个尚未完成的 I/O 操作,可以通过 ChannelFuture 来获取操作的结果、添加监听器以及执行后续的操作。
ChannelFuture 提供了以下主要功能:

  1. 异步操作的结果:ChannelFuture 可以获取异步操作的结果,如数据的发送、连接的建立等。它提供了方法来判断操作是否完成,获取操作的成功或失败状态,以及获取操作返回的结果对象。
  2. 添加操作完成的监听器:可以通过 ChannelFuture 添加一个或多个监听器,以便在操作完成后执行相应的操作。例如,可以添加一个监听器来处理操作完成时的逻辑,或者添加一个监听器来处理操作失败时的错误处理。通过监听器,可以对异步操作的结果进行处理。
  3. 等待和同步操作:可以通过 ChannelFuture 进行等待和同步操作,即阻塞当前线程,直到操作完成。这对于需要等待操作结果并进行后续处理的情况非常有用。可以使用 ChannelFuture 的一些方法,如 await()、sync() 等来实现等待和同步操作。
  4. 链式操作:ChannelFuture 支持链式操作,可以在一个操作完成后继续执行下一个操作。通过调用 ChannelFuture 的一些方法,如 addListener()、addListener(ChannelFutureListener) 等,可以实现链式操作的编写,简化了代码的编写。

总之,ChannelFuture 在 Netty 中用于表示和处理异步操作的结果。通过它,可以获取操作的结果、添加监听器来处理操作完成后的逻辑,进行等待和同步操作,以及实现链式操作。它提供了灵活的方式来处理和管理异步操作,使得网络编程更加简洁和高效。

在这里插入图片描述

Hello,Netty! 我们的第一个Netty程序

首先导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ding</groupId>
    <artifactId>netty</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>netty</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.6.2</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

只要使用Netty,下面这个代码是必写的,唯一不同的是每个应用的handler不太一样。

package com.ding.nettybasic;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

public class EchoServer {
    private static final Logger LOG = LoggerFactory.getLogger(EchoServer.class);

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        int port = 9999;
        EchoServer echoServer = new EchoServer(port);
        LOG.info("服务器即将启动");
        echoServer.start();
        LOG.info("服务器关闭");
    }

    private void start() throws InterruptedException {
        /*线程组*/
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            /*服务端启动必备*/
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                    .channel(NioServerSocketChannel.class)/*指定使用NIO的通信模式*/
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            /*异步绑定到服务器,sync()会阻塞到完成*/
            ChannelFuture f = b.bind().sync();
            /*阻塞当前线程,知道服务器的ServerChannel被关闭*/
            f.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully().sync();
        }
    }
}

SocketChannel在与业务流转之间还有一个Buffer,因此这个里面也要有一个Buffer

在这里插入图片描述

package com.ding.nettybasic;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        ByteBuf in = (ByteBuf)msg;
        System.out.println("server accept:"+in.toString(CharsetUtil.UTF_8));
        ctx.writeAndFlush(in);
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("连接已建立");
        super.channelActive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
package com.ding.nettybasic;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

public class EchoClient {
    private static final Logger LOG = LoggerFactory.getLogger(EchoClient.class);

    private final int port;
    private final String host;

    public EchoClient(int port, String host) {
        this.port = port;
        this.host = host;
    }
    public void start() throws InterruptedException {
        /*线程组*/
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            /*服务端启动必备*/
            Bootstrap b = new Bootstrap();
            b.group(group)
                .channel(NioSocketChannel.class)/*指定使用NIO的通信模式*/
                .remoteAddress(new InetSocketAddress(host,port))
                .handler(new ChannelInitializer<SocketChannel>() {
                     @Override
                     protected void initChannel(SocketChannel ch) throws Exception {
                         ch.pipeline().addLast(new EchoClientHandler());
                     }
                 });
            /*异步绑定到服务器,sync()会阻塞到完成*/
            ChannelFuture f = b.connect().sync();
            /*阻塞当前线程,知道服务器的ServerChannel被关闭*/
            f.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient(9999,"127.0.0.1").start();
    }
}
package com.ding.nettybasic;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        /*读取网络数据后进行业务处理,并关闭连接*/
        System.out.println("client Accept" + msg.toString(CharsetUtil.UTF_8));
        /*关闭连接*/
        ctx.close();
    }

    /*channel活跃后,做业务处理*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Netty",CharsetUtil.UTF_8));
    }
}

在这里插入图片描述
在这里插入图片描述

3. Netty组件

3.1 EventLoop和EventLoopGroup

关系说明

  • 一个EventLoopGroup包含一个或者多个EventLoop;
  • 一个EventLoop在它的生命周期内只和一个Thread 绑定;
  • 所有由EventLoop 处理的I/O事件都将在它专有的Thread上被处理;

在这里插入图片描述

在这里插入图片描述

Channel

  • Channel-Socket;
  • EventLoop—控制、多线程处理、并发;

在这里插入图片描述

ChannelPipeline和ChannelHandlerContext

ChannelPipeline 提供了ChannelHandler链的容器,并定义了用于在该链上传播入站和出站事件流的APl。

ChannelHandler 的生命周期

在这里插入图片描述
ChannelHandler 的生命周期

在ChannelHandler被添加到ChannelPipeline 中或者被从ChannelPipeline中移除时会调用下面这些方法。这些方法中的每一个都接受一个ChannelHandlerContext参数。

handlerAdded当把 ChannelHandler 添加到ChannelPipeline 中时被调用
handlerRemoved当从ChannelPipeline中移除ChannelHandler时被调用
exceptionCaught当处理过程中在 ChannelPipeline中有错误产生时被调用

ChannelPipeline中ChannelHandler

Netty会把出站Handler和入站Handler放到一个Pipeline中,数据结构上是一个双向链表

在这里插入图片描述

ChannelPipeline

在这里插入图片描述

那么分属出站和入站不同的Handler,在业务没要求的情况下是可以不考虑顺序的。
而同属一个方向的Handler则是有顺序的,因为上一个Handler处理的结果往往是下一个Handler的要求的输入。

下载前可以先看下教程 https://pan.quark.cn/s/16a53f4bd595 小天才电话手表刷机教程 — 基础篇 我们将为您简单的介绍小天才电话手表新机型的简单刷机以及玩法,如adb工具的使用,magisk的刷入等等。 我们会确保您看完此教程后能够对Android系统有一个最基本的认识,以及能够成功通过magisk root您的手表,并安装您需要的第三方软件。 ADB Android Debug Bridge,简称,在android developer的adb文档中是这么描述它的: 是一种多功能命令行工具,可让您设备进行通信。 该命令有助于各种设备操作,例如安装和调试应用程序。 提供对 Unix shell 的访问,您可以使用它在设备上运行各种命令。 它是一个客户端-服务器程序。 这听起来有些难以理解,因为您也没有必要去理解它,如果您对本文中的任何关键名词产生疑惑或兴趣,您都可以在搜索引擎中去搜索它,当然,我们会对其进行简单的解释:是一款在命令行中运行的,用于对Android设备进行调试的工具,并拥有比一般用户以及程序更高的权限,所以,我们可以使用它对Android设备进行最基本的调试操作。 而在小天才电话手表上启用它,您只需要这么做: - 打开拨号盘; - 输入; - 点按打开adb调试选项。 其次是电脑上的Android SDK Platform-Tools的安装,此工具是 Android SDK 的组件。 它包括 Android 平台交互的工具,主要由和构成,如果您接触过Android开发,必然会使用到它,因为它包含在Android Studio等IDE中,当然,您可以独立下载,在下方选择对应的版本即可: - Download SDK Platform...
已经博主授权,源码转载自 https://pan.quark.cn/s/b24469074755 SmartDNS English SmartDNS SmartDNS 是一个运行在本地的 DNS 服务器,它接受来自本地客户端的 DNS 查询请求,然后从多个上游 DNS 服务器获取 DNS 查询结果,并将访问速度最快的结果返回给客户端,以此提高网络访问速度。 SmartDNS 同时支持指定特定域名 IP 地址,并高性匹配,可达到过滤广告的效果; 支持DOT,DOH,DOQ,DOH3,更好的保护隐私。 DNSmasq 的 all-servers 不同,SmartDNS 返回的是访问速度最快的解析结果。 支持树莓派、OpenWrt、华硕路由器原生固件和 Windows 系统等。 使用指导 SmartDNS官网:https://pymumu..io/smartdns 软件效果展示 仪表盘 SmartDNS-WebUI 速度对比 阿里 DNS 使用阿里 DNS 查询百度IP,并检测结果。 SmartDNS 使用 SmartDNS 查询百度 IP,并检测结果。 从对比看出,SmartDNS 找到了访问 最快的 IP 地址,比阿里 DNS 速度快了 5 倍。 特性 多虚拟DNS服务器 支持多个虚拟DNS服务器,不同虚拟DNS服务器不同的端口,规则,客户端。 多 DNS 上游服务器 支持配置多个上游 DNS 服务器,并同时进行查询,即使其中有 DNS 服务器异常,也不会影响查询。 支持每个客户端独立控制 支持基于MAC,IP地址控制客户端使用不同查询规则,可实现家长控制等功能。 返回最快 IP 地址 支持从域名所属 IP 地址列表中查找到访问速度最快的 IP 地址,并返回给客户端,提高...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值