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

news2024/9/23 13:22:54

初始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);
            }
        });
    }}

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

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

相关文章

HCIP-Datacom-ARST自选题库__MPLS多选【25道题】

1.下列描述中关于MPLS网络中配置静态LSP正确的是 当某一台LSR为Egress LSR时&#xff0c;1仅需配置In Label&#xff0c;范围为16~1023 当某一台LSR为Transit LSR时&#xff0c;需要同时配置In Label和Out label&#xff0c;In Label范围为16~1023&#xff0c;0utLabel范围为…

【C语言】C语言-学生选修课程系统(源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

牛客NC302 环形数组的连续子数组最大和【中等 动态规划 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/e9f3282363844355aa51497c5410beee 思路 动态规划 两种情况&#xff08;首位相连的&#xff09;和首位不相连的 首尾相连的可以算最小的连续子数组得出&#xff0c;sum-就是。Java代码 import java.util.*;pub…

Echarts - 多个页面内有N个 echarts 图表,封装组件 CommonEcharts 快捷实现

目录 子组件父组件使用注意 option 文件效果展示相关数据处理&#xff08;代码备份 - 可不看&#xff09;数据处理后页面展示 子组件 CommonEcharts.vue <template><div><div v-for"id in domId" :id"id" :key"id" class"…

springboot+vue+mybatis校园兼职平台+PPT+论文+讲解+售后

社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个学生的使用。互联网具有便利性&#xff0c;速度快&#xff0c;效率高&#xff0c;成本低等优点。 因此&#xff0c;构建符合自己要…

十、通配符和正则表达式

10.1 通配符 通配符是由shell处理的, 它只会出现在 命令的“参数”里。当shell在“参数”中遇到了通配符 时&#xff0c;shell会将其当作路径或文件名去在磁盘上搜寻可能的匹配&#xff1a;若符合要求的匹配存在&#xff0c;则进 行代换(路径扩展)&#xff1b;否则就将该通配…

基于springboot+vue的致远汽车租赁系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

新兴勒索软件组织不断涌现:他们是谁,如何运作

新兴的勒索软件团伙正采取不同的策略和目标&#xff0c;填补大公司关闭和执法中断所留下的空白。 3 月份 BlackCat (ALPHV) 的关闭和 2 月份执法部门对 LockBit 基础设施的干扰导致勒索软件生态系统出现空白&#xff0c;而这一空白正迅速被经验不足的团体所填补。 今年到目前…

Flask实现文件上传/下载【基础版】

目录 前言 一.文件上传 1.1一些<input>相关上传属性 1.1.1multiple 1.1.2accept 1.2Flask后台接收文件提交 1.3Flask后台接收多个文件 二.保护文件上传 2.1限制文件上传大小 2.2验证文件名 2.3验证文件内容 三.文件下载 3.1使用send_file()方法下载文件 前言…

SwiftUI中的Stepper(系统Stepper以及自定义Stepper)

本篇文章主要介绍一下Stepper&#xff0c;这个组件在UIKit中也已经有较长的历史了&#xff0c;下面看看在SwiftUI中如何使用&#xff0c;有哪些更加便捷的方法呢&#xff1f; Stepper减号(-)和加号()按钮&#xff0c;可以点击后以指定的数值进行加减。 基础初始化方法 Stepp…

【SpringCloud】服务注册与发现

目录 Eureka/注册中心简介模式 使用Eureka实现注册中心1.创建一个名称为demo-eureka-server的Spring Boot项目2.添加项目依赖3. 在启动类添加启动注解4.添加配置信息Eureka的自我保护机制为Eureka Server添加用户认证1.添加依赖2. 添加配置信息3.添加放行代码4.启动服务&#x…

NASA数据集——阿尔法喷气式大气实验甲醛(HCHO)数据

Alpha Jet Atmospheric eXperiment Formaldehyde Data 简介 阿尔法喷气式大气实验甲醛数据 阿尔法喷气式大气实验&#xff08;AJAX&#xff09;是美国国家航空航天局艾姆斯研究中心与 H211, L.L.C. 公司的合作项目&#xff0c;旨在促进对加利福尼亚、内华达和太平洋沿岸地区的…

春秋云境CVE-2018-7422

简介 WordPress Plugin Site Editor LFI 正文 1.进入靶场 2.漏洞利用 /wp-content/plugins/site-editor/editor/extensions/pagebuilder/includes/ajax_shortcode_pattern.php?ajax_path/../../../../../../flag看别人wp做的。不懂怎么弄的&#xff0c;有没有大佬讲一下的

谈谈你对 vue 的理解 ?

1.谈谈你对 vue 的理解 ? 官方: Vue是一套用于构建用户界面的渐进式框架,Vue 的核心库只关注视图层 2. 声明式框架 Vue 的核心特点,用起来简单。那我们就有必要知道命令式和声明式的区别! 早在 JQ 的时代编写的代码都是命令式的,命令式框架重要特点就是关注过程 声明…

【Chrono Engine学习总结】6-创建自定义场景-6.1-3D场景获取

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 Chrono可以导入自定义的三维模型&#xff0c;所以想自己搭建一个3D仿真环境。过程中遇到了一些问题&#xff0c;记录与整理。 1、3D环境的创建方法 Chrono的Irrlich…

如何从头搭建一个自己的java库并上传到maven官方仓库

创建代码 在代码库根目录执行maven命令&#xff0c;用于快速生成一个基础的Maven项目 mvn archetype:generate \-DgroupIdcom.mycompany \-DartifactIdmy-maven-project \-Dversion1.0.0 \-DarchetypeArtifactIdmaven-archetype-quickstart \-DinteractiveModefalse 这个命令…

初学Echart

创建一个html文件 1.引入 点击链接----快速上手网址&#xff1a;快速上手 - 使用手册 - Apache ECharts 复制这一串【这个是引入echart路径】 引入到这里 2.使用 我们在上一步---点击返回--往下翻---找到完整代码--复制黏贴 复制粘贴后--总体长这样 <!DOCTYPE html> &…

【YOLOv10训练教程】如何使用YOLOv10训练自己的数据集并且推理使用

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

Oracle Graph 入门 - RDF 知识图谱

Oracle Graph 入门 - RDF 知识图谱 0. 引言1. 查看 RDF Semantic Graph 安装情况2. 创建一个语义网络4. 创建一个模型5. 加载 RDF 文件6. 配置 W3C 标准的 SPARQL 端点 0. 引言 Oracle Graph 的中文资料太少了&#xff0c;只能自己参考英文资料整理一篇吧。 Oracle 数据库包括…