Netty框架详解

news2024/12/24 11:37:10

一、Netty简介

Netty是一款基于Java NIO的网络编程、高性能、异步事件驱动的网络应用框架。它的设计目标是提供简单易用、高性能、可扩展的网络编程框架。

二、Netty主要特点

  1. 高并发:Netty使用异步的、非阻塞的I/O模型,通过事件驱动的方式处理网络操作,能够高效地处理并发连接和大量的并发请求。
  2. 高性能:Netty采用了一系列优化策略,如零拷贝技术、内存池和可定制的线程模型等,以提供出色的性能和吞吐量。
  3. 多协议支持:Netty提供了丰富的协议支持,包括常用的网络协议(如HTTP、WebSocket、TCP和UDP)以及自定义协议。具备灵活的编解码器和处理器,简化了协议的实现和交互。
  4. 可扩展和灵活:Netty的架构和组件设计具有高度的可扩展性和灵活性。它提供了一组可重用的组件,可以根据应用需求进行定制和扩展。
  5. 安全性:Netty提供了一系列的安全性功能,包括SSL/TLS支持、加密和身份验证等。
  6. 简单易用:Netty提供了简洁的API和丰富的文档,使得开发人员可以快速上手,快速开发高质量的网络应用程序。
  7. 跨平台:Netty可以在多种操作系统平台上运行,包括Windows、Linux、Mac OS X等。

三、Netty使用场景

  1. 服务器开发:Netty可以用于开发高性能、可靠的服务器应用程序,如Web服务器、游戏服务器、消息服务器等。
  2. 客户端开发:Netty可以用于开发高性能、可靠的客户端应用程序,如聊天软件、文件传输软件等。
  3. 分布式系统:Netty可以用于开发分布式系统,如RPC框架、消息队列等。

四、Netty服务端工作架构流程

在这里插入图片描述
(1)服务端工作流程:

  1. 服务端初始化时会创建2个NioEventLoopGroup,BoosGroup用于Accept连接建立事件并分发请求,WokerGroup用于处理读写事件和业务逻辑。
  2. 服务端启动时创建 ServerBootstrap 实例,并配置 EventLoopGroup、Channel 类型和处理器,通过调用 serverBootstrap.bind() 绑定服务器端口。
  3. BoosGroup中的NioEventLoop不断轮询注册在其Selector上的ServerSocketChannel的 Accept 事件。
  4. 接收到Accept 事件后,再交由processSelectedKeys处理 accept 事件,与客户端建立连接,生成一个 NioSocketChannel,并将其注册到WokerGroup的某个 NioEventLoop 的 Selector 上。
  5. WokerGroup的 NioEventLoop 不断轮询注册在其Selector上的NioSocketChannel的 read/write 事件。
  6. 接收到read/write 事件后,再交由processSelectedKeys处理 read/write 事件,并调用ChannelPipeline中的相关的ChannelHandler 的channelRead()方法接收并处理客户端发送的数据。
  7. 处理完数据后,通过ChannelHandlerContext 的writeAndFlush()方法发送响应结果消息给客户端。

(2)客户端工作流程:

  1. 客户端初始化时会创建1个NioEventLoopGroup 用于处理客户端的I/O操作。
  2. 客户端启动时创建 Bootstrap 实例,并配置 EventLoopGroup、Channel 类型和处理器,通过调用 bootstrap.connect() 发起连接请求交给 NioEventLoopGroup 处理。
  3. NioEventLoopGroup 选择一个 NioEventLoop 来处理连接请求,并与服务端建立连接。
  4. 当客户端与服务端成功建立连接时,NioEventLoop 会创建一个 Channel 对象来表示连接,该对象维护了与连接相关的状态和属性。同时客户端和服务端的 ChannelHandler 中的 channelActive() 方法会被调用。
  5. 再通过调用ChannelHandlerContext 的writeAndFlush()方法向服务端发送消息。
  6. 发送成功后,服务端接收到消息并返回处理结果时,ChannelHandler 的channelRead()方法能接收到服务端返回的响应结果消息。

五、Netty的核心组件

  1. Bootstrap:用于启动和配置网络应用程序配置类,ServerBootstrap用于服务端,Bootstrap用于客户端。
  2. NioEventLoopGroup:相当于一个事件循环组,这个组中含有多个事件循环,每个事件循环就是一个 NioEventLoop。在服务端会创建BossEventLoopGroup 和WorkerEventLoopGroup两个NioEventLoopGroup实例。BossEventLoopGroup 只负责处理连接事件,WorkerEventLoopGroup则负责read/write 事件。客户端通常只有一个NioEventLoopGroup来发起连接处理I/O任务。
  3. NioEventLoop:表示一个不断循环的执行事件处理的线程,每个 NioEventLoop 都包含一个 Selector和一个TaskQueue,Selector用于监听注册在其上的 SocketChannel 上的I/O事件,如:read,write,accept,connect等;TaskQueue用于存放一些非I/O任务,如:register,bind等任务。
  4. Channel:Netty 对网络操作抽象类,它除了包括基本的 I/O 操作,如 bind()、connect()、read()、write() 等。不同协议、不同的阻塞类型的连接都有不同的 Channel 类型与之对应。常用的类型有:NioSocketChannel(客户端 TCP Socket 连接)、NioServerSocketChannel(服务器端 TCP Socket 连接)。
  5. ChannelHandler:是消息的具体处理器,负责处理输入输出数据的逻辑。可以接收入站事件(如数据接收)和出站事件(如数据发送),并执行相应的处理逻辑。
  6. ChannelHandlerContext:是 ChannelHandler 的上下文环境。包含与ChannelHandler相关联的各种信息,如Channel、EventLoop、ChannelPipeline等。ChannelHandlerContext还提供了丰富的方法,以便于ChannelHandler与其他组件进行交互。
  7. ChannelPipeline:是一个双向链表,拦截和处理事件的链式结构,主要负责管理ChannelHandler并协调它们的处理顺序。ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表,并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler。
  8. ChannelFuture:主要用于接收异步I/O操作返回的执行结果。ChannelFuture提供了丰富的方法,用于检查操作的状态、添加监听器以便在操作完成时接收通知,并对操作的结果进行处理。

六、TCP 粘包与拆包

  • 拆包:是指发送方发送的一条完整的数据放入缓冲区后,接收端从缓冲区每次只读取到数据这条完整数据的一部分。
  • 粘包:缓冲区存放着发送方发送的多条完整的数据,接收端从缓冲区每次读取到的数据是多条数据拼在一起。
    之所以会产生粘包与拆包主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。可以使用 Netty 自带的解码器来解决粘包与拆包问题。
    (1)LineBasedFrameDecoder : 发送端发送数据包的时候,每个数据包之间以换行符作为分隔,LineBasedFrameDecoder 的工作原理是它依次遍历 ByteBuf 中的可读字节,判断是否有换行符,然后进行相应的截取。
    (2)DelimiterBasedFrameDecoder : 可以自定义分隔符解码器,LineBasedFrameDecoder 实际上是一种特殊的 DelimiterBasedFrameDecoder 解码器。
    (3)FixedLengthFrameDecoder: 固定长度解码器,它能够按照指定的长度对消息进行相应的拆包。

七、使用实例

  1. 添加 Netty 依赖
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.97.Final</version>
</dependency>
  1. 编写Server服务端启动类
public class NettyServer {
	 /**
     * 创建服务端实例并绑定端口
     * @throws InterruptedException
     */
    public static void bind() throws InterruptedException {
        // 创建boss线程组,用于接收连接
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // 创建worker线程组,用于处理连接上的I/O操作,含有子线程NioEventGroup个数为CPU核数大小的2倍
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // 创建ServerBootstrap实例,服务器启动对象
            ServerBootstrap bootstrap = new ServerBootstrap();

            // 使用链式编程配置参数
            // 将boss线程组和worker线程组暂存到ServerBootstrap
            bootstrap.group(bossGroup, workerGroup);
            // 设置服务端Channel类型为NioServerSocketChannel作为通道实现
            bootstrap.channel(NioServerSocketChannel.class);

            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    // 添加ServerHandler到ChannelPipeline,对workerGroup的SocketChannel(客户端)设置处理器
                    pipeline.addLast(new NettyServerHandler());
                }
            });
            // 设置启动参数,初始化服务器连接队列大小。服务端处理客户端连接请求是顺序处理,一个时间内只能处理一个客户端请求
            // 当有多个客户端同时来请求时,未处理的请求先放入队列中
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            // 绑定端口并启动服务器,bind方法是异步的,sync方法是等待异步操作执行完成,返回ChannelFuture异步对象
            ChannelFuture channelFuture = bootstrap.bind(8888).sync();
            // 等待服务器关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 优雅地关闭boss线程组
            bossGroup.shutdownGracefully();
            // 优雅地关闭worker线程组
            workerGroup.shutdownGracefully();
        }
    }
}
  1. 编写服务端处理器handler
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    /**
     * 当 Channel 已经注册到它的 EventLoop 并且能够处理 I/O 时被调用
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("执行 channelRegistered");
    }

    /**
     * 当 Channel 从它的 EventLoop 注销并且无法处理任何 I/O 时被调
     * 用
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("执行 channelUnregistered");
    }

    /**
     * 当 Channel 处于活动状态时被调用;Channel 已经连接/绑定并且已经就绪
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("执行 channelActive");
    }

    /**
     * 当 Channel 离开活动状态并且不再连接它的远程节点时被调用
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("执行 channelInactive");
    }

    /**
     * 当从 Channel 读取数据时被调用
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        System.out.println("执行 channelRead");
        // 处理接收到的数据
        ByteBuf byteBuf = (ByteBuf) msg;
        try {
            // 将接收到的字节数据转换为字符串
            String message = byteBuf.toString(CharsetUtil.UTF_8);
            // 打印接收到的消息
            System.out.println("接收到客户端消息为: " + message);
            // 发送响应消息给客户端
            ctx.writeAndFlush(Unpooled.copiedBuffer("我是服务端,我收到你的消息啦~", CharsetUtil.UTF_8));
        } finally {
            // 释放ByteBuf资源
            ReferenceCountUtil.release(byteBuf);
        }

    }

    /**
     * 当 Channel 上的一个读操作完成时被调用,对通道的读取完成的事件或通知。当读取完成可通知发送方或其他的相关方,告诉他们接受方读取完成
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("执行 channelReadComplete");
    }

    /**
     * 当 ChannelnboundHandler.fireUserEventTriggered()方法被调用时被
     * 调用
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("执行 userEventTriggered");
    }

    /**
     * 当 Channel 的可写状态发生改变时被调用。可以通过调用 Channel 的 isWritable()方法
     * * 来检测 Channel 的可写性。与可写性相关的阈值可以通过
     * * Channel.config().setWriteHighWaterMark()和 Channel.config().setWriteLowWaterMark()方法来
     * * 设置
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("执行 channelWritabilityChanged");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("执行 exceptionCaught");
        // 异常处理
        cause.printStackTrace();
        ctx.close();
    }
}
  1. 编写客户端启动类
public class NettyClient {
    /**
     * 创建客户端实例并向服务端发送连接请求
     */
    public static void start() {

        // 创建EventLoopGroup,用于处理客户端的I/O操作
        EventLoopGroup groupThread = new NioEventLoopGroup();

        try {
            // 创建Bootstrap实例,客户端启动对象
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(groupThread);
            // 设置服务端Channel类型为NioSocketChannel作为通道实现
            bootstrap.channel(NioSocketChannel.class);
            // 设置客户端处理
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    socketChannel.pipeline().addLast(new NettyClientHandler());
                }
            });
            // 向服务端发送连接请求
            ChannelFuture channelFuture = bootstrap.connect("localhost", 8888).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 优雅地关闭线程
            groupThread.shutdownGracefully();
        }
    }
}
  1. 编写客户端处理器handler
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 连接建立时的处理,发送请求消息给服务器
        ctx.writeAndFlush(Unpooled.copiedBuffer("我是客户端,连接建立成功!", CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 处理接收到的数据
        ByteBuf byteBuf = (ByteBuf) msg;
        try {
            // 将接收到的字节数据转换为字符串
            String message = byteBuf.toString(CharsetUtil.UTF_8);
            // 打印接收到的消息
            System.out.println("收到服务端响应的消息为: " + message);

            // TODO: 对数据进行业务处理
        } finally {
            // 释放ByteBuf资源
            ReferenceCountUtil.release(byteBuf);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 异常处理
        cause.printStackTrace();
        ctx.close();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1129111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

回归预测 | MATLAB实现BO-LSTM贝叶斯优化长短期神经网络多输入单输出回归预测

回归预测 | MATLAB实现BO-LSTM贝叶斯优化长短期神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现BO-LSTM贝叶斯优化长短期神经网络多输入单输出回归预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-LSTM贝叶斯优化长短期神经网络多输入…

潮流玩具演绎城市文化,泡泡玛特入选2023“上海礼物”

每一座城市都有其独特的文化氛围和历史背景&#xff0c;“城市礼物”承载着地域特色、文化内涵和人文精神&#xff0c;不断复制和传递着城市文化。近年来&#xff0c;上海市文旅局会同有关各方&#xff0c;从旅游商品的研发设计、品牌塑造、展售渠道等方面&#xff0c;创建“上…

【软件教程】如何用C++交叉编译出能在Android运行的ELF程序或so动态库

一、配置NDK交叉编译平台 1. 打开Android的官方ndk下载链接https://developer.android.com/ndk/downloads?hlzh-cn&#xff0c;下载windows 64位ndk环境包。 2. 解压后将具有以下文件的路径加入到系统环境变量。 3. 配置好环境变量&#xff0c;如下图所示&#xff0c;Path中存…

mysql 数据库 表结构生成word文档

1、背景 我们在做项目时&#xff0c;表设计文档都是非常重要的&#xff0c;可以让开发人员快速了解表与业务的关系、表之间的关系。 产品在不停迭代的过程中&#xff0c;表的结构也会有相应的变化&#xff0c;我们需要将变化更新的表设计文档中。以前我们是人工方式更新文档&…

C++ 虚函数详解:多态性实现原理及其在面向对象编程中的应用

在面向对象的编程中&#xff0c;多态性是一个非常重要的概念。多态性意味着在不同的上下文中使用同一对象时&#xff0c;可以产生不同的行为。C是一种面向对象的编程语言&#xff0c;在C中&#xff0c;虚函数是实现多态性的关键 什么是虚函数 虚函数是一个在基类中声明的函数&…

基于SpringBoot的时间管理系统

基于SpringBoot的时间管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 管理员界面 用户界面 摘要 基于Spring Boot的时间管理系统是一款功能丰富…

【Redis安装】Ubuntu和Centos

此处安装的是 Redis5 在 Ubuntu 系统上 切换到 root 用户下&#xff0c;su 命令切换使用 apt 可以搜索 redis 相关软件包 apt search redis使用 apt 命令安装 redis apt install redis手动修改配置文件 redis.conf cd /etc/redis/ vim redis.conf修改以下两处 重启服务器 …

2021年下半年 软件设计师 上午试卷(1-28)

计算机指令系统采用多种寻址方式。立即寻址是指操作数包含在指令中&#xff0c;寄存器寻址是指操作数在寄存器中&#xff0c;直接寻址是指操作数的地址在指令中。这三种寻址方式获取操作数的速度 &#xff08;1&#xff09; 。 &#xff08;1&#xff09; A. 立即寻址最快&am…

【JAVA学习笔记】43 - 枚举类

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter11/src/com/yinhai/enum_ 〇、创建时自动填入版权 作者等信息 如何在每个文件创建的时候打入自己的信息以及版权呢 菜单栏-File-setting-Editor-File and Code Templaters -Includes-输入信…

SpringBoot整合Activiti7——任务监听器(七)

文章目录 一、任务监听器事件类型配置方式(选)代码实现xml文件创建监听器class方式expression方式delegateExpression 测试流程部署流程启动流程完成任务 一、任务监听器 任务监听器可以在任务创建、任务分配、任务完成、任务删除发生时触发&#xff0c;从而执行相应的逻辑。 事…

rust学习——方法 Method

文章目录 方法 Method定义方法self、&self 和 &mut self方法名跟结构体字段名相同 带有多个参数的方法关联函数多个 impl 定义为枚举实现方法 rust 结构体与枚举的区别回答1回答2 方法 Method 从面向对象语言过来的同学对于方法肯定不陌生&#xff0c;class 里面就充斥…

nginx 动静分离 nginx防盗链

一、动静分离环境准备静态资源配置(10.36.192.169)安装nginx修改配置文件重启nginx 动态资源配置(192.168.20.135)yum安装php修改nginx配置文件重启nginx nginx代理机配置&#xff08;192.168.20.134&#xff09;修改nginx子自配置文件重启nginx 客户端访问 二、防盗链nginx防止…

【proteus】8086仿真/汇编:创建项目并添加汇编代码文件

1.创建好新项目 2.点击source code 弹出VSM 3. 4.注意两个都不勾选 可以看到schematic有原理图出现 5. 再次点击source code 6.project/project settings&#xff0c;取消勾选embed 7. add 8.输入文件名保存后&#xff1a; 注意&#xff1a;proteus不用写dos的相关语句 。

内存CACHE同步引起OSD时间戳显示异常

目前在用的这款芯片&#xff0c;图像翻转有专门的一个图像处理IP来完成&#xff0c;同时这个IP又支持叠加OSD的功能&#xff0c;但是在设计的时候叠加OSD的功能单元又在图像翻转单元的前面&#xff0c;导致了开启了图像翻转功能后&#xff0c;OSD就倒着显示、位置不在原来的坐标…

vue3 computed 和 watch 的差异

目录 前言 用法 computed watch 代码 理解 高质量的使用 Vue.js作为一种现代化的前端框架&#xff0c;提供了丰富的特性来帮助开发者构建高效和响应式的用户界面。在这其中&#xff0c;computed 和 watch 是两个非常重要的选项&#xff0c;它们都用于处理数据的变化&…

VUE到底有什么好处?

网上有许多前端开发框架的对比&#xff0c;相对的&#xff0c;VUE在综合评分方面还是优秀的。以下是一些State of JavaScript调查数据结果&#xff1a; 使用率&#xff1a;VUE使用者在调研开发者中占比 51%&#xff1b; 开发者满意度&#xff1a;VUE的综合开发者满意度达到64%…

【算法练习Day27】买卖股票的最佳时机 II跳跃游戏跳跃游戏 II

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 买卖股票的最佳时机 II跳跃…

智能视频监控平台EasyCVR接口调用注意事项汇总!

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

汽车4S店如何在数字化管理下,提高市场竞争力

在所有人都认为疫情过后&#xff0c;经济形势会一路向阳&#xff0c;但是&#xff0c;实际情况出乎所有人的意料&#xff0c;各行各业举步维艰。 新闻爆出的各大房地产&#xff0c;恒大的2.4万亿让人瞠目结舌&#xff0c;还有碧桂园和融创&#xff0c;也是债台高筑了&#xff…

Access denied for user ‘root‘@‘localhost‘ (using password:YES) 解决方案(禅道相关)

如果是忘记Mysql密码或更改权限后访问不了的问题请直接跳转以下链接&#xff1a; MySQL登录时出现Access denied for user ‘root‘‘localhost‘ (using password: YES)无法打开的解决方法 关于这个问题&#xff0c;网上查到的解决方法基本都是因为忘记Mysql密码或者用户权限问…