Netty初识Hello World 事件循环对象(EventLoop) 事件循环组 (EventLoopGroup)

初始Netty-HelloWorld

Netty在网络通信中的地位就如同Spring框架在JavaEE开发中的地位。
基于Netty网络通信开发简易的服务端、客户端,以实现客户端向服务端发送hello world,服务端仅接收不返回数据。
服务端代码:

@Slf4j
public class HelloServer {
    public static void main(String[] args) {
        new ServerBootstrap() // 服务器端的启动器,负责组装netty组件,启动服务器
                // Group组,BossEventLoop,WorkerEventLoop(selector, thread)
                .group(new NioEventLoopGroup()) // 1
                .channel(NioServerSocketChannel.class) // 2
                // boss 负责处理连接,worker(child)负责处理读写,决定了worker(child)能执行哪些操作(handler)
                .childHandler(
                        // channel代表和客户端进行数据读写的通道 Initializer初始化,负责添加别的handler
                        new ChannelInitializer<NioSocketChannel>() { // 3
                            protected void initChannel(NioSocketChannel ch) {
                                // 添加具体的handler, StringDecoder字符串解码,将传输的ByteBuf转换为字符串
                                ch.pipeline().addLast(new StringDecoder()); // 5
                                ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() { // 6,自定义handler
                                    @Override	// 读事件
                                    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                                        // 打印上一步转换的字符串
                                        System.out.println(msg);
                                    }
                                });
                            }
                        })
                .bind(8080); // 4,绑定监听端口
    }
}

客户端代码:

public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        new Bootstrap() // 创建启动器
                // 添加EventLoop
                .group(new NioEventLoopGroup()) // 1
                // 选择客户端的channel
                .channel(NioSocketChannel.class) // 2
                // 添加处理器
                .handler(new ChannelInitializer<Channel>() { // 3
                    @Override // 在连接建立后被调用
                    protected void initChannel(Channel ch) {
                        // 对发送的数据进行编码
                        ch.pipeline().addLast(new StringEncoder()); // 8
                    }
                })
                // 连接到服务器
                .connect("127.0.0.1", 8080) // 4
                //
                .sync() // 5
                .channel() // 6
                .writeAndFlush(new Date() + ": hello world!"); // 7 向服务器发送数据
    }
}

主要执行流程:
图片
可以理解如下:

  • 把 channel 理解为数据的通道
  • 把 msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline (流水线)的加工,会变成其它类型对象,最后输出又变成 ByteBuf
  • 把 handler 理解为数据的处理工序
    • 工序有多道,合在一起就是 pipeline,pipeline 负责发布事件(读、读取完成…)传播给每个 handler, handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)
    • handler 分 Inbound 和 Outbound 两类
  • 把 eventLoop 理解为处理数据的工人
    • 工人可以管理多个 channel 的 io 操作,并且一旦工人负责了某个 channel,就要负责到底(绑定)
    • 工人既可以执行 io 操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个 channel 的待处理任务,任务分为普通任务、定时任务
    • 工人按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每道工序指定不同的工人

EventLoop详解

EventLoop基础知识

事件循环对象

EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件。

它的继承关系比较复杂

  • 一条线是继承自 j.u.c.ScheduledExecutorService 因此包含了线程池中所有的方法
  • 另一条线是继承自 netty 自己的 OrderedEventExecutor,
    • 提供了 boolean inEventLoop(Thread thread) 方法判断一个线程是否属于此 EventLoop
    • 提供了 parent 方法来看看自己属于哪个 EventLoopGroup

事件循环组

EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)

  • 继承自 netty 自己的 EventExecutorGroup
    • 实现了 Iterable 接口提供遍历 EventLoop 的能力
    • 另有 next 方法获取集合中下一个 EventLoop

EventLoop使用示例

// 创建事件循环组
// NioEventLoopGroup可以处理io事件、普通任务、定时任务
EventLoopGroup eventLoopGroup = new NioEventLoopGroup(2);
// DefaultEventLoopGroup可以处理普通任务、定时任务   
EventLoopGroup eventLoopGroup = new DefaultEventLoopGroup();

创建事件循环组时,若不指定线程个数,则创建个数为机器可用CPU * 2,若指定个数,则按照指定个数进行创建。
在这里插入图片描述

        // 处理普通任务
        eventLoopGroup.next().submit(()->{ // 或者使用.execute()
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("ok");
        });
        // 执行定时任务
        eventLoopGroup.next().scheduleAtFixedRate(()->{
            log.info("test schedule task ...");
        }, 0, 1, TimeUnit.SECONDS);

使用NioEventLoopGroup处理IO事件

采用多个NioEventLoopGroup来分别处理不同的事件,如boss专门处理accept事件,worker专门处理read事件。并且对于耗时较长的handler交给专门的EventLoopGroup来处理,从而不阻塞原有的NioEventLoopGroup监听事件的正常运行。
在这里插入图片描述
优化一:

上述图片中第23行为初始代码,一个NioEventLoopGroup处理所有的事件,包括accept、read
等,显然不符合各司其职的功能,将其优化为第24行所示,明确NioEventLoopGroup负责处理的
事件类别。
1.boss只负责NioServerSocketChannel上的accept事件。
2.worker负责NioSocketChannel上的read事件。

在这里插入图片描述
优化二:

对于比较耗时的handler,可以将其将给其他EventLoopGroup创建handler来执行。

服务端代码:

@Slf4j
public class EventLoopServer {

    public static void main(String[] args) {
        // 2.细分:如果某个handler执行事件比较长,可以独立出一个eventloopgroup进行处理
        EventLoopGroup group = new DefaultEventLoopGroup();
        new ServerBootstrap()
                // 1.进行优化,明确eventloop负责处理的事件类别,boss和worker
                // boss只负责NioServerSocketChannel上的accept事件,worker负责NioSocketChannel上的read事件
                // 是否需要指定第一个负责accept的NioEventLoopGroup的适量,不需要,只会有一个NioServerSocketChannel
//                .group(new NioEventLoopGroup())
                .group(new NioEventLoopGroup(), new NioEventLoopGroup(2))
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast("handler1", new ChannelInboundHandlerAdapter(){
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                ByteBuf buf = (ByteBuf) msg;
                                // 转为toString的时候需要指定字符集,此处选择使用默认字符集
                                // 若在网络通信中,需要客户端和服务器协商字符集的使用,使用同一个标准进行处理
                                log.info(buf.toString(Charset.defaultCharset()));
                                ctx.fireChannelRead(msg);   // 将消息传递给下一个handler
                            }
                        }).addLast(group, "handler2", new ChannelInboundHandlerAdapter(){
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                ByteBuf buf = (ByteBuf) msg;
                                log.info(buf.toString(Charset.defaultCharset()));
                            }
                        });

                    }
                })
                .bind(8080);
    }
}

客户端代码:

@Slf4j
public class EventLoopClient {
    public static void main(String[] args) throws InterruptedException {
        Channel channel = new Bootstrap() // 创建启动器
                // 添加EventLoop
                .group(new NioEventLoopGroup()) // 1
                // 选择客户端的channel
                .channel(NioSocketChannel.class) // 2
                // 添加处理器
                .handler(new ChannelInitializer<Channel>() { // 3
                    @Override // 在连接建立后被调用
                    protected void initChannel(Channel ch) {
                        // 对发送的数据进行编码
                        ch.pipeline().addLast(new StringEncoder()); // 8
                    }
                })
                // 连接到服务器
                .connect("127.0.0.1", 8080) // 4
                //
                .sync() // 5
                .channel();// 6
//        for (int i = 0; i < 5; i++) {
//            channel.writeAndFlush("Hello eventloop_" + i + "   ");
//        }
        log.info("{}",channel);
        System.out.println("");
    }
}

EventLoop、Channel和Handler之间的关系

建立连接之后,channel和一个EventLoop进行绑定,并且一个线程可以管理多个Channel。
图片

同一个Channel绑定多个不同的EventLoop(也就不同EventLoopGroup中对应的EventLoop),此时如何进行不同handler之间的切换。

图片
  • 如果两个 handler 绑定的是同一个线程,那么就直接调用
  • 否则,把要调用的代码封装为一个任务对象,由下一个 handler 的线程来调用
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
    final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
    // 下一个 handler 的事件循环是否与当前的事件循环是同一个线程
    EventExecutor executor = next.executor();
    
    // 是,直接调用
    if (executor.inEventLoop()) {
        next.invokeChannelRead(m);
    } 
    // 不是,将要执行的代码作为任务提交给下一个事件循环处理(换人)
    else {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                next.invokeChannelRead(m);
            }
        });
    }}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ICML2024 定义新隐私保护升级:DP-BITFIT新型微调技术让AI模型学习更安全

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; 引言&#xff1a;差分隐私在大模型微调中的重要性和挑战 在当今的深度学习领域&#xff0c;大型预训练模型的微调已成为提高各种任务性能的关键技术。然而&am…

开放式耳机哪个品牌音质好用又实惠耐用?五大公认卷王神器直入!

​在现今耳机市场&#xff0c;开放式耳机凭借其舒适的佩戴体验和独特的不入耳设计&#xff0c;备受消费者追捧。它们不仅让你在享受音乐时&#xff0c;仍能察觉周围的声音&#xff0c;确保与人交流无障碍&#xff0c;而且有利于耳朵的卫生与健康。对于运动爱好者和耳机发烧友而…

AI大模型探索之路-实战篇7:Function Calling技术实战:自动生成函数

系列篇章&#x1f4a5; AI大模型探索之路-实战篇4&#xff1a;深入DB-GPT数据应用开发框架调研 AI大模型探索之路-实战篇5&#xff1a;探索Open Interpreter开放代码解释器调研 AI大模型探索之路-实战篇6&#xff1a;掌握Function Calling的详细流程 目录 系列篇章&#x1f4a…

Centos修改系統語言

一、使用命令行修系统语言 1、显示系统当前语言环 [rootkvm-suma ~]# localectl System Locale: LANGen_US.utf8 VC Keymap: cn X11 Layout: cn 2、查看系统支持字符集 [rootkvm-suma ~]# locale -a 2、设置系统语言环境 [rootkvm-suma ~]# localectl set-locale LANGz…

Gradio 搭建yolov8 分类系统

代码 import gradio as gr import pandas as pd from ultralytics import YOLO from skimage import data from PIL import Imagemodel YOLO(yolov8n-cls.pt) def predict(img):logging.info("Gradio 调用开始")result model.predict(sourceimg)logging.info("…

机器学习预测-CNN手写字识别

介绍 这段代码是使用PyTorch实现的卷积神经网络&#xff08;CNN&#xff09;&#xff0c;用于在MNIST数据集上进行图像分类。让我一步步解释&#xff1a; 导入库&#xff1a;代码导入了必要的库&#xff0c;包括PyTorch&#xff08;torch&#xff09;、神经网络模块&#xff0…

【Linux】Linux的安装

文章目录 一、Linux环境的安装虚拟机 镜像文件云服务器&#xff08;可能需要花钱&#xff09; 未完待续 一、Linux环境的安装 我们往后的学习用的Linux版本为——CentOs 7 &#xff0c;使用 Ubuntu 也可以 。这里提供几个安装方法&#xff1a; 电脑安装双系统&#xff08;不…

LeetCode热题100——矩阵

73.矩阵清零 题目 给定一个 *m* x *n* 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 示例…

OpenAI撤回有争议的决定:终止永久性非贬损协议

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Docker提示某网络不存在如何解决,添加完网络之后如何删除?

Docker提示某网络不存在如何解决&#xff1f; 创建 Docker 网络 假设现在需要创建一个名为my-mysql-network的网络 docker network create my-mysql-network运行容器 创建网络之后&#xff0c;再运行 mysqld_exporter 容器。完整命令如下&#xff1a; docker run -d -p 9104…

力扣刷题---2283. 判断一个数的数字计数是否等于数位的值【简单】

题目描述 给你一个下标从 0 开始长度为 n 的字符串 num &#xff0c;它只包含数字。 如果对于 每个 0 < i < n 的下标 i &#xff0c;都满足数位 i 在 num 中出现了 num[i]次&#xff0c;那么请你返回 true &#xff0c;否则返回 false 。 示例 1&#xff1a; 输入&a…

机器人物理引擎

机器人物理引擎是用于计算并模拟机器人及其交互环境在虚拟世界中运动轨迹的组件。 MuJoCo&#xff08;Multi-Joint Dynamics with Contact&#xff09;&#xff1a; 基于广义坐标和递归算法&#xff0c;专注于模拟多关节系统如人形机器人。采用了速度相关的算法来仿真连接点力…

AI菜鸟向前飞 — LangChain系列之十四 - Agent系列:从现象看机制(上篇)

上一篇介绍了Agent与LangGraph的基础技能Tool的必知必会 AI菜鸟向前飞 — LangChain系列之十三 - 关于Tool的必知必会 前面已经详细介绍了Promp、RAG&#xff0c;终于来到Agent系列&#xff08;别急后面还有LangGraph&#xff09;&#xff0c;大家可以先看下这张图&#xff1…

网络模型-路由策略

一、路由策略 路由策略(Routing Policy)作用于路由&#xff0c;主要实现了路由过滤和路由属性设置等功能&#xff0c;它通过改变路由属性(包括可达性)来改变网络流量所经过的路径。目的:设备在发布、接收和引入路由信息时&#xff0c;根据实际组网需要实施一些策略&#xff0c…

C++:关联容器及综合运用:

关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。关联容器因此相比与顺序容器支持高效的关键字查找和访问。 其底层数据结构&#xff1a;顺序关联容器 ->红黑树&#xff0c;插入…

Redis离线安装(单机)

目录 1-环境准备1-1下载redis-4.0.11.tar.gz1-2gcc环境 2-上传解压3-编译安装(需要gcc环境)4-配置redis5-启动Redis6-开启防火墙(root)7-添加开机启动脚本8-设置权限9-设置开机启动10-测试redis服务11-检查是否安装成功12-创建redis命令软连接13-测试redis14-必要时设置防火墙 …

禅道密码正确但是登录异常处理

禅道密码正确&#xff0c;但是登录提示密码错误的异常处理 排查内容 # 1、服务器异常&#xff0c;存储空间、数据库异常 # 2、服务异常&#xff0c;文件丢失等异常问题定位 # 1、df -h 排查服务器存储空间 # 2、根据my.php排查数据库连接是否正常 # 3、修改my.pho,debugtrue…

外企也半夜发布上线吗?

0 别把问题想得太复杂 如果有灰度发布的能力&#xff0c;最好白天发布&#xff1b;如果没有灰度发布&#xff0c;只能在半夜发布。 即使有灰度发布能力&#xff0c;也不要沾沾自喜&#xff0c;好好反思一下你们的灰度发布是否真的经得起考验&#xff0c;还是仅仅是装装样子。…

区块链技术和应用二

前言 学习长安链的一些基本原理 官网&#xff1a;长安链开源文档 b站课程&#xff1a;区块链基础与应用 一、共识算法 1.1 POW工作量证明 最长链共识&#xff0c;没听明白 1.2 51%攻击 二、区块链的发展 2.1 区块链1.0到3.0 2.2 共有链、联盟链、私有链 2.3 发展趋势 2.4 扩…