【Netty】 工作原理详解(十一)

news2024/11/29 0:52:47

文章目录

  • 前言
  • 一、Netty 模型
  • 二、代码示例
    • 2.1、引入Maven依赖
    • 2.2、服务端的管道处理器
    • 2.3、服务端主程序
    • 2.4、客户端管道处理器
    • 2.5、客户端主程序
    • 2.6、测试运行
  • 总结

前言

回顾Netty系列文章:

  • Netty 概述(一)
  • Netty 架构设计(二)
  • Netty Channel 概述(三)
  • Netty ChannelHandler(四)
  • ChannelPipeline源码分析(五)
  • 字节缓冲区 ByteBuf (六)(上)
  • 字节缓冲区 ByteBuf(七)(下)
  • Netty 如何实现零拷贝(八)
  • Netty 程序引导类(九)
  • Reactor 模型(十)

一、Netty 模型

下图就是 Netty 的工作原理图:
在这里插入图片描述

执行流程如下:

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

  • BossGroup 和 WorkerGroup 类型都是 NioEventLoopGroup。

  • NioEventLoopGroup 相当于一个事件循环组,这个组中含有多个事件循环 ,每一个事件循环是 NioEventLoop。

  • NioEventLoop 表示一个不断循环的执行处理任务的线程, 每个NioEventLoop 都有一个selector ,用于监听绑定在其上的 socket 的网络通讯。

  • NioEventLoopGroup 可以有多个线程,即可以含有多个 NioEventLoop。

  • 每个Boss NioEventLoop 循环执行的步骤有3步 :

    1. 轮询accept 事件 ;
    2. 处理accept 事件 ,与 client 建立连接 ,生成 NioScocketChannel,并将其注册到某个 worker NIOEventLoop 上的 selector;
    3. 处理任务队列的任务 , 即 runAllTasks。
  • 每个 Worker NIOEventLoop 循环执行的步骤 :

    1. 轮询read,write 事件 处理 I/O 事件, 即 read , write 事件;
    2. 在对应NioScocketChannel处理;
    3. 处理任务队列的任务 , 即 runAllTasks;
  • 每个Worker NIOEventLoop 处理业务时,会使用pipeline(管道),pipeline 中包含了channel ,即通过pipeline 可以获取到对应通道,管道中维护了很多的 处理器(ChannelHandler)。

二、代码示例

2.1、引入Maven依赖

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

2.2、服务端的管道处理器

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    //读取数据实际(这里我们可以读取客户端发送的消息)
    /*
    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
    2. Object msg: 就是客户端发送的数据 默认Object
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("server ctx =" + ctx);
        Channel channel = ctx.channel();
        //将 msg 转成一个 ByteBuf
        //ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer.
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8));
        System.out.println("客户端地址:" + channel.remoteAddress());
    }


    //数据读取完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //writeAndFlush 是 write + flush
        //将数据写入到缓存,并刷新
        //一般讲,我们对这个发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer("公司最近账户没啥钱,再等几天吧!", CharsetUtil.UTF_8));
    }

    //处理异常, 一般是需要关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

2.3、服务端主程序

public class NettyServer {

    public static void main(String[] args) throws Exception {
        //创建BossGroup 和 WorkerGroup
        //说明
        //1. 创建两个线程组 bossGroup 和 workerGroup
        //2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
        //3. 两个都是无限循环
        //4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
        //   默认实际 cpu核数 * 2
        //
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //8
        try {
            //创建服务器端的启动对象,配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            //使用链式编程来进行设置
            bootstrap.group(bossGroup, workerGroup) //设置两个线程组
                    .channel(NioServerSocketChannel.class) //bossGroup使用NioSocketChannel 作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数 option主要是针对boss线程组,
                    .childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态 child主要是针对worker线程组
                    .childHandler(new ChannelInitializer<SocketChannel>() {//workerGroup使用 SocketChannel创建一个通道初始化对象																														(匿名对象)
                        //给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //可以使用一个集合管理 SocketChannel, 再推送消息时,可以将业务加入到各个channel 对应的 NIOEventLoop 的 									taskQueue 或者 scheduleTaskQueue
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

            System.out.println(".....服务器 is ready...");
            //绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
            //启动服务器(并绑定端口)
            ChannelFuture cf = bootstrap.bind(7788).sync();
            //给cf 注册监听器,监控我们关心的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess()) {
                        System.out.println("服务已启动,端口号为7788...");
                    } else {
                        System.out.println("服务启动失败...");
                    }
                }
            });
            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

NioEventLoopGroup是用来处理I/O操作的多线程事件循环器。Netty 提供了许多不同的EventLoopGroup的实现来处理不同的传输。

上面的服务端应用中,有两个NioEventLoopGroup被使用。第一个叫作bossGroup,用来接收进来的连接。第二个叫作workerGroup,用来处理已经被接收的连接,一旦 bossGroup接收连接,就会把连接的信息注册到workerGroup上。

ServerBootstrap是一个NIO服务的引导启动类。可以在这个服务中直接使用Channel。

  • group方法用于 设置EventLoopGroup。
  • 通过Channel方法,可以指定新连接进来的Channel类型为NioServerSocketChannel类。
  • childHandler用于指定ChannelHandler,也就是前面实现的NettyServerHandler。
  • 可以通过option设置指定的Channel来实现NioServerSocketChannel的配置参数。
  • childOption主要设置SocketChannel的子Channel的选项。
  • bind用于绑定端口启动服务。

2.4、客户端管道处理器

public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    //当通道就绪就会触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client ctx =" + ctx);
        ctx.writeAndFlush(Unpooled.copiedBuffer("老板,工资什么时候发给我啊?", CharsetUtil.UTF_8));
    }

    //当通道有读取事件时,会触发
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器的地址: "+ ctx.channel().remoteAddress());
    }

    //处理异常, 一般是需要关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

2.5、客户端主程序

public class NettyClient {

    public static void main(String[] args) throws Exception {
        //客户端需要一个事件循环组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            //创建客户端启动对象
            //注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
            Bootstrap bootstrap = new Bootstrap();
            //设置相关参数
            bootstrap.group(group) //设置线程组
                    .channel(NioSocketChannel.class) // 设置客户端通道的实现类(反射)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器
                        }
                    });
            System.out.println("客户端 ok..");
            //启动客户端去连接服务器端
            //关于 ChannelFuture 要分析,涉及到netty的异步模型
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 7788).sync();
            //给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

客户端只需要一个NioEventLoopGroup就可以了。

2.6、测试运行

分别启动服务器 NettyServer 和客户端 NettyClient程序

服务端控制台输出内容:

服务器 is ready...
服务已启动,端口号为7788...
server ctx =ChannelHandlerContext(NettyServerHandler#0, [id: 0xa1b2233c, L:/127.0.0.1:7788 - R:/127.0.0.1:63239])
客户端发送消息是:老板,工资什么时候发给我啊?
客户端地址:/127.0.0.1:63239

客户端控制台输出内容:

客户端 ok..
client ctx =ChannelHandlerContext(NettyClientHandler#0, [id: 0x21d6f98e, L:/127.0.0.1:63239 - R:/127.0.0.1:7788])
服务器回复的消息:公司最近账户没啥钱,再等几天吧!
服务器的地址: /127.0.0.1:7788

至此,一个简单的基于Netty开发的服务端和客户端就完成了。

总结

本篇文章主要讲解了 Netty 的工作原理及简单应用。下节我们来讲解 Netty 的编解码。

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

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

相关文章

借军工经验开拓消费市场,三星显示收购eMagin浅析

前不久三星显示&#xff08;Samsung Display&#xff09;宣布&#xff0c;拟支付2.18亿美元收购微显示方案商eMagin全部普通股&#xff0c;收购完成后eMagin将并入三星显示&#xff0c;以加速XR显示业务发展。 据青亭网了解&#xff0c;eMagin成立于1996年&#xff0c;该公司多…

《Spring Guides系列学习》guide11 - guide15

要想全面快速学习Spring的内容&#xff0c;最好的方法肯定是先去Spring官网去查阅文档&#xff0c;在Spring官网中找到了适合新手了解的官网Guides&#xff0c;一共68篇&#xff0c;打算全部过一遍&#xff0c;能尽量全面的了解Spring框架的每个特性和功能。 接着上篇看过的gu…

JVM调优实战

1、当项目运行一段时间以后&#xff0c;产生了OOM的问题&#xff0c;我们该如何排查问题呢&#xff1f; 用top命令&#xff0c;看看是哪个进程CPU占用率高&#xff0c;获取它的进程ID&#xff0c;再根据具体的进程id&#xff0c;执行 top -HP 进程id号 命令,看看哪个线程的CP…

复制架构,Redis Sentinel分析

存储高可用&#xff0c;一般采用复制架构&#xff0c;复制架构&#xff0c;需要关注故障架构和状态决策2个要点 复制架构通用关注点 数据复制 复制格式 格式优点缺点举例命令数据量小可能存在数据不一致Mysql 的statement同步方式&#xff0c;按commit顺序同步&#xff0c;…

文件系统考古:1974-Unix V7 File System

有时&#xff0c;进步难以察觉&#xff0c;特别是当你正身处其中时。而对比新旧资料之间的差异&#xff0c;寻找那些推动变革的信息源&#xff0c;我们就可以清晰地看到进步的发生。在Linux&#xff08;以及大部分Unix系统&#xff09;中&#xff0c;都可以印证这一点。 Unix …

淘宝层级改版,五力模型上线!

最近临近618&#xff0c;淘系又各种功能改版&#xff0c;现在店铺层级也开始陆续改版了。从原来的只需要提升销售金额&#xff0c;到现在的五力模型。我们先说一下原来的店铺层级考核&#xff0c;跟现在的区别主要是哪&#xff1f; 1.原来店铺层级的考核升级特别简单&#xff0…

推荐一款神级编程工具[Cursor]自带gpt而且免费!

今天推荐一款非常好用开发工具&#xff0c;一个智能编程助手。 官网&#xff1a;https://www.cursor.so/ 适用平台&#xff1a;Mac、Windows、Linux。 助手的样子 可以做哪些事情&#xff1f; 编程语言&#xff1a;我可以回答关于各种编程语言&#xff08;如Python、Java、Ja…

测试老鸟总结,从功能到自动化测试详细整理,测试之路广阔无垠...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

黑客零基础入门教程,从入门到精通学习路线规划,看完这篇就够了

很多人上来就说想学习黑客&#xff0c;但是连方向都没搞清楚就开始学习&#xff0c;最终也只是会无疾而终&#xff01;黑客是一个大的概念&#xff0c;里面包含了许多方向&#xff0c;不同的方向需要学习的内容也不一样。 想要成为黑客&#xff0c;却苦于没有方向&#xff0c;…

戏曲APP软件开发需具备哪些功能呢?

戏曲是我国的国粹&#xff0c;传统戏曲文化源远流长&#xff0c;博大精深&#xff0c;数千年以来一直都是深受大众喜欢的文化生活的重要环节。随着时代的推进&#xff0c;娱乐形式更加多样化&#xff0c;传统的剧场演出形式的戏曲传播方式已经跟不上时代发展以及人们的需求了。…

python+Django+vue高校体育足球篮球联赛网站系统

系统主要是以后台管理员管理为主。管理员需要先登录系统然后才可以使用本系统&#xff0c;管理员可以对个人中心、用户管理、赛事信息管理、球队信息管理、球员信息管理、比赛分值板管理、系统管理等进行添加、查询、修改、删除&#xff0c;以保障足球联赛管理系统的正常运行。…

es elasticsearch 十 中文分词器ik分词器 Mysql 热更新词库

目录 中文分词器ik分词器 介绍 安装 使用分词器 Ik分词器配置文件 Mysql 热更新词库 中文分词器ik分词器 介绍 中文分词器按照中文进行分词&#xff0c;中文应用最广泛的是ik分词器 安装 官网下载对应版本zip 下载 放到 plugins 目录 新建 ik文件夹 考入解析zip 重…

es Elasticsearch 十一 Java api实现 索引管理

目录 Java api实现 索引管理 Pom 测试联通 增加索引 打印结果 异步执行 删除索引 查看索引是否存在 关闭索引 开启索引 Java api实现 索引管理 Pom <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-re…

C嘎嘎~~[谈谈C++的一些优化]

C的一些优化 匿名对象引用引用作形参引用作返回值 编译器优化构造 拷贝构造 ⇒ 构造拷贝构造 拷贝构造 ⇒ 一个拷贝构造 匿名对象 通过以前C语言的学习, 我们知道了有一种 具有临时性的, 没有名字的变量 — — 匿名变量. 那么我们的对象应该也有这个特性 — — 匿名对象 匿名…

如何在 Linux 中创建非登录用户?

在 Linux 系统中&#xff0c;用户账户的管理是一个重要的任务。除了常规的登录用户&#xff0c;有时候我们需要创建一些非登录用户&#xff0c;这些用户通常用于运行服务、执行特定任务或限制访问权限。 本文将详细介绍如何在 Linux 中创建非登录用户&#xff0c;并提供一些相关…

linuxOPS基础_linux系统注意事项

Linux严格区分大小写 Linux 和Windows不同&#xff0c;Linux严格区分大小写的&#xff0c;包括文件名和目录名、命令、命令选项、配置文件设置选项等。 例如&#xff0c;Win7 系统桌面上有文件夹叫做Test&#xff0c;当我们在桌面上再新建一个名为 test 的文件夹时&#xff0c…

Java之旅(四)

Java变量 变量是存储数据值的容器&#xff0c;用于在内存中保存数据。成员变量 作用范围是整个类 类的成员变量的作用范围同类的实例化对象的作用范围相同。当类被实例化的时候&#xff0c;成员变量就会在内存中分配空间&#xff0c;并初始化 实例变量&#xff08;不以 static…

如何在华为OD机试中获得满分?Java实现【基站维修工程师】一文详解!

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述4. Java算法源码5. 测试6.解题思路1. 题目描述 小陈是一名

Canvas实现连线动态效果

前言 这段时间一直在研究 Canvas 的动画&#xff0c;本文将带大家基于 Canvas 封装的 ZRender 库&#xff0c;了解ZRender 库中提供的 animate 绘制动画的方法&#xff0c;并且使用 animate 方法实现一个带有箭头流动效果的连线。 效果 ZRender 在介绍 ZRender 的动画之前&a…

java+iClientOpenlayers实现污水排放扩散模拟(湖库污染排放扩散模拟)

软件实现效果 一、应用背景 湖库污染是一个日益严峻的环境问题。随着城市化和工业化的加速发展&#xff0c;越来越多的有害物质被排放入湖库中&#xff0c;导致湖库污染加剧并扩散到周围地区。本文将探讨湖库污染扩散的原因、影响和解决方法。 首先&#xff0c;湖库污染扩散的…