【基础】Netty 的基础概念及使用

news2024/11/23 2:24:30

Netty

  • 基本概念理解
    • 阻塞与非阻塞
    • 同步与异步
    • BIO 与 NIO
    • Reactor 模型
  • Netty 基本概念
    • Netty 的执行流程
    • Netty 的模块组件
    • Netty 工作原理
  • Netty 的基本使用
    • Netty Server
    • Netty Client
  • 参考文章

基本概念理解

阻塞与非阻塞

阻塞与非阻塞是进程访问数据时的处理方式,根据数据是否准备完成,划分为:

  • 阻塞:进程将一直等待数据准备完成;

  • 非阻塞:若进程发现数据未准备完成,则立即返回,期间不断的进行数据请求,直到数据准备完成,直接返回;

例如烧水,我们站在水壶旁边一直等待水烧开即为阻塞;而我们在将水烧上之后,就去处理别的事情,并按照一定的频率来看水是否烧开,这个过程即为非阻塞。阻塞与非阻塞的区分在于“人”。

在这里插入图片描述

同步与异步

同步与异步是应用程序和操作系统处理 IO 时的处理方式,根据数据是否准备完成,划分为:

  • 同步:应用程序直接参与 IO 的操作;

  • 异步:应用程序将 IO 操作完全交由操作系统处理,应用程序只等待通知;

还是烧水的例子,我们烧水并一直阻塞在水壶旁,等待水开的过程即为同步。我们需要参与整个水烧开的过程;而如果有一个智能水壶,当水烧开时会发出提示声,这样我们就可以打开烧水开关后继续去完成其他任务,等待水壶提醒水已烧开即可,该过程即为异步。同步与异步的区分在于“水壶”。

在这里插入图片描述

BIO 与 NIO

  • BIO 是一种同步阻塞的模型,对应传统的java.io包,其通信基于流模型实现,在 IO 操作完成之前,对应线程会一直阻塞;

  • NIO 是一种同步非阻塞的模型,对应java.nio包,其通信基于缓冲实现,对于高负载、高并发的网络应用,适合使用 NIO 的非阻塞模式来开发;

BIO 的 Server 通信模型:

在这里插入图片描述

NIO 的 Server 通信模型:

在这里插入图片描述

Reactor 模型

单线程的 Reactor 模型

在这里插入图片描述

多线程的 Reactor 模型

在这里插入图片描述

多线程的主从 Reactor 模型

在这里插入图片描述

Netty 基本概念

Netty 的执行流程

在这里插入图片描述

Netty 的模块组件

Bootstrap/ServerBootstrap

Bootstrap 是 Netty 应用的引导类,其作用是配置整个 Netty 程序,并串联各个组件。

Future/ChannelFuture

Netty 中的所有 IO 操作都是异步的。消息被处理后不会立刻得到处理结果,此时就需要通过该组件注册监听以追踪后续结果,当消息的处理操作产生结果相应的监听会自动触发监听事件。

Channel

Netty 中负责网络通信的组件,用于处理网络 IO 操作。

Selector

Netty 基于 Selector 实现 IO 多路复用,通过该组件,一个线程可以监听多个注册的 Channel 事件。

NioEventLoop

NioEventLoop 中维护了一个线程和任务队列,支持异步提交执行任务,线程启动时会调用其中的 run 方法,执行 IO 或非 IO 的任务。

IO 任务即 selectionKey 中 ready 的事件,如 accept、connect、read、write 等,由 processSelectedKeys 方法触发;非 IO 任务即添加到 taskQueue 中的任务,如 register0、bind0 等任务,由 runAllTasks 方法触发。

NioEventLoopGroup

该模块管理 eventLoop 的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程 NioEventLoop 负责处理多个 Channel 上的事件,而一个 Channel 只对应于一个线程。

ChannelHandler

ChannelHandler 是一个接口,处理 IO 事件或拦截 IO 操作,并将其转发到其 ChannelPipeline 中的下一个处理程序。

ChannelHandlerContext

保存Channel相关的所有上下文信息,同时关联一个ChannelHandler对象。

ChannelPipline

保存 ChannelHandler 的 List,用于处理或拦截 Channel 的入站事件和出站操作。

Netty 工作原理

Server 端工作原理

在这里插入图片描述

server 端启动时,会绑定本地的端口,并将自己的 NioServerSocketChannel 注册到某一个 bossNioEventLoop 的 selector 中。server 端包含 1 个 boss NioEventLoopGroup 和 1 个 worker NioEventLoopGroup。

每个 boss NioEventLoop 循环执行的任务包含 3 步:

  1. 轮询 accept 事件;
  2. 处理 io 任务,即 accept 事件,与 client 建立连接,生成 NioSocketChannel,并将 NioSocketChannel 注册到某个 worker NioEventLoop 的 selector 上;
  3. 处理任务队列中的任务,runAllTasks。任务队列中的任务包括用户调用 eventloop.execute 或 schedule 执行的任务,或者其它线程提交到该 eventloop 的任务。

每个 worker NioEventLoop 循环执行的任务包含 3 步:

  1. 轮询 read、write 事件;
  2. 处理 io 任务,即 read、write 事件,在 NioSocketChannel 可读、可写事件发生时进行处理;
  3. 处理任务队列中的任务,runAllTasks。

Client 端工作原理

在这里插入图片描述

client 端启动时 connect 到 server,建立 NioSocketChannel,并注册到某个 NioEventLoop 的 selector 上。

client 端只包含 1 个 NioEventLoopGroup,每个 NioEventLoop 循环执行的任务包含 3 步:

  1. 轮询 connect、read、write 事件;
  2. 处理 io 任务,即 connect、read、write 事件,在 NioSocketChannel 连接建立、可读、可写事件发生时进行处理;
  3. 处理非 io 任务,runAllTasks。

Netty 的基本使用

首先引入依赖

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.51.Final</version>
        </dependency>

Netty Server

Netty Server 端代码:

public class Server {

    /**
     * 服务端端口
     */
    private int port;

    /**
     * 构造方法:需指定服务器端口号
     */
    public Server(int port) {
        this.port = port;
    }

    /**
     * 服务器启动方法
     */
    public void start() {
        System.out.println("服务器启动中...");

        // 配置服务端线程组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        // 服务器启动引导类
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                // 设置存放已完成 tcp 三次握手的请求的等待队列的最大长度
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 设置允许复用 host/port,默认 false
                .option(ChannelOption.SO_REUSEADDR, true)
                // 指定客户端使用的 handler,同样可以配置多个
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new ServerHandler());
                    }
                });

        try {
            // 启动服务器,绑定端口,同步等待绑定成功
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            System.out.println("服务端启动成功...");
            // 等待服务器监听端口关闭
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            System.out.println("服务器启动失败!");
            e.printStackTrace();
        } finally {
            // 退出,释放线程池
            workGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        // 启动服务端
        new Server(8000).start();
    }
}

ServerHandler 代码:

/**
 * ServerHandler
 * @ChannelHandler.Sharable 允许多个线程使用此 handler
 */
@ChannelHandler.Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 建立连接时 channel 被激活,将自动调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    /**
     * 接收到客户端信息时调用
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf data = (ByteBuf) msg;
        System.out.println("服务器接收到数据>>>" + data.toString(CharsetUtil.UTF_8));
        ctx.writeAndFlush(data);
    }

    /**
     * 发生异常时调用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

Netty Client

Netty Client 端代码:

public class Client {

    /**
     * 要连接的服务器主机地址
     */
    private String host;

    /**
     * 要连接的服务器端口号
     */
    private int port;

    /**
     * 构造方法,指定服务器的主机地址和端口号
     */
    public Client(String host, int port) {
        this.host = host;
        this.port = port;
    }

    /**
     * 客户端启动方法
     */
    public void start() {
        System.out.println("客户端启动中...");
        // EventLoop可以看做线程,EventLoopGroup可以看做线程组?
        NioEventLoopGroup group = new NioEventLoopGroup();
        // 实例化启动引导类
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                // 指定服务端主机地址和端口
                .remoteAddress(new InetSocketAddress(host, port))
                // 设置允许复用 host/port,默认 false
                .option(ChannelOption.SO_REUSEADDR, true)
                // 指定客户端使用的 handler
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // 此处可以指定多个 handler,各个 handler 按照添加的顺序进行处理
                        socketChannel.pipeline().addLast(new ClientHandler());
                    }
                });

        try {
            // 连接到服务器,connect() 是异步连接,需要调用 sync() 同步等待连接成功
            ChannelFuture channelFuture = bootstrap.connect().sync();
            System.out.println("客户端启动成功...");
            // 阻塞线程直到客户端 channel 关闭
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            System.out.println("客户端启动失败!");
            e.printStackTrace();
        } finally {
            // 执行退出,释放 NIO 线程组
            group.shutdownGracefully();
        }
    }

    @SuppressWarnings("all")
    public static void main(String[] args) throws InterruptedException {
        // 启动客户端,因为启动后会阻塞所在线程,所以单独用一个线程来启动
        new Thread(() -> {
            new Client("localhost", 8000).start();
        }).start();

        Thread.sleep(1000);
        ClientHandler.sendMsg("Hello, Netty...");
    }
}

ClientHandler 代码:

public class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    private static ChannelHandlerContext context;

    /**
     * 建立连接时 channel 被激活,将自动调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ClientHandler.context = ctx;
    }

    /**
     * 接收到服务器返回信息时调用
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
        System.out.println("服务端返回的数据>>>" + byteBuf.toString(CharsetUtil.UTF_8));
    }

    /**
     * 处理完服务器返回的数据时调用
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    /**
     * 发生异常时调用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 暴露方法供外部调用,向服务器发送消息
     */
    public static void sendMsg(String msg) {
        ByteBuf byteBuf = Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8);
        ClientHandler.context.writeAndFlush(byteBuf);
    }
}

参考文章

  1. 【硬核】肝了一月的Netty知识点
  2. 再有人问你Netty是什么,就把这篇文章发给他
  3. 万字长文带你深入理解netty,史上最强详解!

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

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

相关文章

系分 - 案例分析 - 系统维护与设计模式

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录系分 - 案例分析 - 系统维护与设计模式典型例题 1题目描述参考答案典型例题 2题目描述参考答案系分 - 案例分析 - 系统维护与设计模式 典型例题 1 题目描述 某企业两年前自主研发的消防集中控制软件系统…

05-requests添加Cookies与正则表达式

第5讲 requests添加Cookies与正则表达式 整体课程知识点查看 &#xff1a;https://blog.csdn.net/j1451284189/article/details/128713764 本讲总结 request代理使用 request SSL request添加Cookies 数据解析方法简介 数据解析&#xff1a;正则表达式讲解 一、requests 代理 …

【23种设计模式】学习汇总(未完结+思维导图)

获取思维导图翻至底部底部&#xff0c;基本概览博客内容&#xff08;暂未完全完善&#xff0c;期待你的持续关注&#xff09; 写作不易&#xff0c;如果您觉得写的不错&#xff0c;欢迎给博主来一波点赞、收藏~让博主更有动力吧&#xff01; 一.相关内容 在软件工程中&#xf…

关系型数据库RDBMS | 字节青训营笔记

一、经典案例 1、红包雨案例 每年春节&#xff0c;抖音都会有红包雨获得 2、事务 事务(Transaction): 是由一组SQL语句组成的一个程序执行单元(Unit)&#xff0c;它需要满足ACID特性 BEGIN; UPDATE account table SET balance balance - 小目标 WHERE name “抖音; UPDATE…

指数加权平均、动量梯度下降法

目录1.指数加权平均(exponentially weighted averages)这里有一年的温度数据。如果想计算温度的趋势&#xff0c;也就是局部平均值(local average)&#xff0c;或者说移动平均值(moving average)&#xff0c;怎么做&#xff1f;&#xff1a;当天的温度&#xff0c;&#xff1a;…

交换机的基本原理(特别是动态ARP、静态ARP、代理ARP)

第六章&#xff1a;交换机的基本配置 二层交换设备工作在OSI模型的第二层&#xff0c;即数据链路层&#xff0c;它对数据包的转发是建立在MAC&#xff08;Media Access Control &#xff09;地址基础之上的。二层交换设备不同的接口发送和接收数据独立&#xff0c;各接口属于不…

esxi宿主机进入维护模式虚拟机不会自动释放【不会自动迁移出去】解决方法、查看辨别宿主机本地空间和存储池、esxi进入存储内部清理空间

文章目录说明虚拟机不自动释放处理过程报错说明宿主机进入维护模式说明手动迁移报错说明直接启动虚拟机报错说明解决方法报错原因分析解决方法查看辨别宿主机本地空间esxi进入存储内部清理空间进入存储池内存储内部空间清理及原则存储空间说明说明 我当前的esxi主机版本为5.5 …

7亿人养活的眼镜行业,容不下一家县城小店

文|螳螂观察 作者| 青月 如果要盘点那些被暴利眷顾的行业&#xff0c;眼镜零售肯定榜上有名。 从上市企业的财报数据来看&#xff0c;国内眼镜零售行业的首家上市公司——博士眼镜&#xff0c;2021年前三季度的平均毛利率超过60%&#xff1b;国内镜片第一股明月眼镜在2021年…

【C进阶】文件操作

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C语言进阶 ⭐代码仓库&#xff1a;C Advanced 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们…

小程序应用生命周期

小程序应用生命周期生命周期介绍应用生命周期钩子函数参数对象页面生命周期页面生命周期-页面参数组件生命周期生命周期介绍 定义 一个组件或者页面生老病死的过程一堆会在特定时期触发的函数 分类 应用生命周期页面生命周期组件生命周期 应用生命周期钩子函数 属性说明onL…

Xpath Helper 在新版Edge中的安装及解决快捷键冲突问题

&#x1f935;‍♂️ 个人主页老虎也淘气 个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f44d;&#x1f3fb; 收藏…

vue2源码分析-keep-alive组件

简介 keep-alive是Vue.js的一个内置组件。它能够将指定的组件实例保存在内存中&#xff0c;而不是直接将其销毁&#xff0c;它是一个抽象组件&#xff0c;不会被渲染到真实DOM中&#xff0c;也不会出现在父组件链中。 具体用法咱们这里就不再细说了&#xff0c;今天主要是探讨…

JavaEE day2 初识web与HTML

初步了解相关知识 关于端口&#xff08;port&#xff09;&#xff1a;一个端口同一时间只能被一个进程监听&#xff0c;但是一个进程可以监听多个端口 URL的标准格式&#xff1a;协议名称&#xff1a;//主机/资源路径&#xff1f;查询字符串#文档片段 一般协议最常见的为htt…

Java基础之《netty(25)—handler链调用机制》

一、netty的handler的调用机制 1、使用自定义的编码器和解码器来说明netty的handler调用机制。 客户端发送long -> 服务器 服务端发送long -> 客户端 2、案例 二、客户端发送给服务端 1、服务端 NettyServer.java package netty.inboundhandlerAndOutboundhandler;i…

【C++】从0到1入门C++编程学习笔记 - 基础入门篇:程序流程结构

文章目录一、选择结构1.1 if 语句1.2 三目运算符1.3 switch语句二、循环结构2.1 while 循环语句2.2 do...while 循环语句2.3 for 循环语句2.4 嵌套循环三、跳转语句3.1 break 语句3.2 continue 语句3.3 goto 语句C/C支持最基本的三种程序运行结构&#xff1a;顺序结构、选择结构…

MySQL进阶——优化

1、选择最合适的字段属性 Mysql是一种关系型数据库&#xff0c;可以很好地支持大数据量的存储&#xff0c;但是一般来说&#xff0c;数据库中的表越小&#xff0c;在它上面执行的查询也就越快。因此&#xff0c;在创建表的时候&#xff0c;为了获得更好的性能&#xff0c;我们…

腾讯云HiFlow场景连接器 联动对象存储企业网盘,打通数据分发“最后一公里”

对云厂商和企业用户来说&#xff0c;随着数据规模的快速增长&#xff0c;企业除了对存储功能和性能的要求不断增加&#xff0c;也越来越注重数据分发的效率。在传统数据分发的过程中&#xff0c;数据管理员往往需要先在存储桶下载对应的客户方案/交付资料&#xff0c;再使用微信…

LINUX软中断-softirq

前言 关于linux的软中断的文章&#xff0c;在网上可以找到很多&#xff0c;但总觉着讲的都不够深入&#xff0c;打算自己写一下 软中断的感性认识 中断一旦被触发&#xff0c;本地cpu正在运行的不管是什么程序都要让路&#xff0c;让中断程序执行并且执行过程中不能被打断。…

分布式事务问题

4.2 分布式事务问题 4.2.1 什么是分布式事务 一次课程发布操作需要向数据库、redis、elasticsearch、MinIO写四份数据&#xff0c;这里存在分布式事务问题。 什么是分布式事务&#xff1f; 首先理解什么是本地事务&#xff1f; 平常我们在程序中通过spring去控制事务是利用…

Linux---进程优先级

目录 基本概念 查看系统进程 PRI and NI 用top命令更改已存在进程的nice&#xff1a; 其他概念 基本概念 cpu资源分配的先后顺序&#xff0c;就是指进程的优先权&#xff08;priority&#xff09;。 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很…