netty入门-3 EventLoop和EventLoopGroup,简单的服务器实现
文章目录
- EventLoop和EventLoopGroup
- 服务器与客户端基本使用
- 增加非NIO工人
- NioEventLoop 处理普通任务与定时任务
- 结语
EventLoop和EventLoopGroup
二者大概是什么这里不再赘述,前一篇已简述过。
不理解也没关系。
下面会简单使用,看了就能明白是什么
这篇文章只说NioEventLoopGroup
后续文章会有服务器创建类BootStrap的方法总结。
服务器与客户端基本使用
EventLoopGroup的常用实现类是NioEventLoopGroup
就以此为例,我们来看这两者,在使用netty创建一个服务器的过程中处于什么位置。
PS:我觉得既然是专注于这个东西怎么用,就不要太纠结于底层。
我当时看到childHandler,就想弄明白这个到底是怎么把Handler加到新Channel的Pipeline中的。这就要涉及源码。下面这个链式调用其他部分也是一样。想搞明白每个调用的底层还是需要看源码。会给初学者带来没必要的精力耗费。我觉得需要收住好奇心,先会简单使用,再说底层实现。
//服务端
//ServerBootstrap相当于提供了创建服务器的辅助类。允许通过链式调用更优雅的启动一个服务器。
//我们通过一系列链式调用完成了服务器的创建。
new ServerBootstrap()//group相当于我们配置EventLoopGroup的地方,它将用于后续的事件处理.group(new NioEventLoopGroup(1), new NioEventLoopGroup(2))//channel指定通道类型,NioServerSocketChannel是netty的封装类,即NIO中的升级版.channel(NioServerSocketChannel.class)//这个名字child代表子通道,什么意思?即我们有一个NioServerSocketChannel来处理Accept请求。接受的每个连接都会创建自己的SocketChannel用于通信,可当作child,即子通道。//所以顾名思义子通道处理器。即为新创建的每一个子通道都会绑定这个ChannelInitializer,那么它又是做什么的?//ChannelInitializer会在每个新Channel被注册到EventLoopGroup时执行内部我们重写的initChannel方法,将我们在initChannel方法中自定义的处理器添加到我们这个channel的channelpipeline中。即为我们新连接的Channel添加一个handler。关于handler和pipeline,在上篇中简单讲述了是什么。//简单来说,它是对每个新建立的连接Channel,指定我们写好的处理逻辑,之后Channel的读写操作都会经过这些逻辑。.childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) {//这里的参数我猜测就是我们新连接的channel//这里对新channel添加我们自定义的handlerch.pipeline().addLast(new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf byteBuf = msg instanceof ByteBuf ? ((ByteBuf) msg) : null;if (byteBuf != null) {byte[] buf = new byte[16];ByteBuf len = byteBuf.readBytes(buf, 0, byteBuf.readableBytes());log.debug(new String(buf));}}});}//绑定端口,并sync阻塞住}).bind(8080).sync();
这里还有一些理解
channel()方法,将我们指定的NioServerSocketChannel 注册到group中的第一个group内的某个EventLoop,由该EventLoop监听。第一个group中的EventLoop被称为Boss,专门处理连接请求。
所有连接由NioServerSocketChannel处理,同时新连接产生的SocketChannel会注册到第二个group内的某个EventLoop,由该EventLoop监听。这些EventLoop被称为Worker。处理读写请求。
上面程序中相当于有两个Worker,因为创建EventLoopGroup时参数为2。
所以小结一下。每个Channel都会由一个EventLoop监听。并且在事件发生时调用对应的处理器进行处理。即完成对事件的监听和处理。
//客户端
//通过链式调用,拿到连接完毕的Channel
Channel channel = new Bootstrap().group(new NioEventLoopGroup(1)).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {System.out.println("init...");ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));}}).channel(NioSocketChannel.class).connect("localhost", 8080).sync().channel();// 发送消息
channel.writeAndFlush(ByteBufAllocator.DEFAULT.buffer().writeBytes("wangwu".getBytes()));Thread.sleep(2000);channel.writeAndFlush(ByteBufAllocator.DEFAULT.buffer().writeBytes("wangwu".getBytes()));
这里多创建几个客户端发消息,会发现服务器中的两个Worker会分别处理属于自己管理Channel的事件。
增加非NIO工人
下面程序有两个handler
LoggingHandler和"myhandler"
引入了一个我们自己创建的DefaultEventLoopGroup 去处理"myhandler"的任务,即非NIO工人
//额外的工程组
//非NIO工人2个
DefaultEventLoopGroup normalWorkers = new DefaultEventLoopGroup(2);
new ServerBootstrap().group(new NioEventLoopGroup(1), new NioEventLoopGroup(2)).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) {ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));ch.pipeline().addLast(normalWorkers,"myhandler",new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf byteBuf = msg instanceof ByteBuf ? ((ByteBuf) msg) : null;if (byteBuf != null) {byte[] buf = new byte[16];ByteBuf len = byteBuf.readBytes(buf, 0, byteBuf.readableBytes());log.debug(new String(buf));}}});}}).bind(8080).sync();
看完这个代码,我们看添加handler时的参数,我们发现"myhandler"被交给了我们自己一开始创建的DefaultEventLoopGroup normalWorkers 来执行。
关系见下图
head和tail看做pipeline中handler链的头尾,可视为无意义。
h1代表LoggingHandler
h2代表"myhandler"

那么这里老师巧妙的引出了pipeline处理过程中的handler的切换,因为两个handler分别是两个不同的EventLoop去执行,就需要一个执行完后交给另一个EventLoop去执行。然后引出了源码,比较容易让人接受,理解。
//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() {@Overridepublic void run() {next.invokeChannelRead(m);}});}
}
- 如果两个 handler 绑定的是同一个线程(EventLoop),那么就直接调用
- 否则,把要调用的代码封装为一个任务对象,由下一个 handler 的线程来调用
NioEventLoop 处理普通任务与定时任务
NioEventLoop还可以执行普通任务和定时任务
//普通任务
NioEventLoopGroup nioWorkers = new NioEventLoopGroup(2);
log.debug("server start...");
Thread.sleep(2000);
nioWorkers.execute(()->{log.debug("normal task...");
});//定时任务
NioEventLoopGroup nioWorkers = new NioEventLoopGroup(2);
log.debug("server start...");
Thread.sleep(2000);
nioWorkers.scheduleAtFixedRate(() -> {log.debug("running...");
}, 0, 1, TimeUnit.SECONDS);
结语
仅仅说使用的话内容好像不多。
api使用的话还要之后自己进行网络程序的编写,学习过程中估计很难记住。还是要学完去实践才能熟练使用。
下篇应该是Netty的Future与Promise,或者Netty Channel相比于NIO的有什么不同(这块估计要看下书,课上没咋提)。
后续会有文章单独说明ServerBootstrap类的一些方法。
感谢阅读,欢迎批评指正。
相关文章:
netty入门-3 EventLoop和EventLoopGroup,简单的服务器实现
文章目录 EventLoop和EventLoopGroup服务器与客户端基本使用增加非NIO工人NioEventLoop 处理普通任务与定时任务 结语 EventLoop和EventLoopGroup 二者大概是什么这里不再赘述,前一篇已简述过。 不理解也没关系。 下面会简单使用,看了就能明白是什么 这…...
通信原理-思科实验五:家庭终端以太网接入Internet实验
实验五 家庭终端以太网接入Internet实验 一实验内容 二实验目的 三实验原理 四实验步骤 1.按照上图选择对应的设备,并连接起来 为路由器R0两个端口配置IP 为路由器R1端口配置IP 为路由器设备增加RIP,配置接入互联网的IP的动态路由项 5.为路由器R1配置静…...
【Vue】vue概述
1、简介 Vue.js(简称Vue)是一款用于构建用户界面的渐进式JavaScript框架。由前Google高级软件工程师尤雨溪(Evan You)于2014年创建,是一个独立且社区驱动的开源项目。Vue.js基于标准的HTML、CSS和JavaScriptÿ…...
Docker use experience
#docker command docker load -i <镜像文件.tar> docker run -it -d --name 容器名 -p 宿主机端口:容器端口 -v 宿主机文件存储位置:容器内文位置 镜像名:Tag /bin/bash docker commit -m"提交的描述信息" -a"作者" 容器ID 要…...
Android平台RTSP|RTMP直播播放器技术接入说明
技术背景 大牛直播SDK自2015年发布RTSP、RTMP直播播放模块,迭代从未停止,SmartPlayer功能强大、性能强劲、高稳定、超低延迟、超低资源占用。无需赘述,全自研内核,行业内一致认可的跨平台RTSP、RTMP直播播放器。本文以Android平台…...
数据结构——栈(顺序结构)
一、栈的定义 栈是一种数据结构,它是一种只能在一端进行插入和删除操作的特殊线性表。这一端被称为栈顶,另一端被称为栈底。栈按照后进先出(LIFO)的原则进行操作(类似与手枪装弹后射出子弹的顺序)。在计算…...
速盾:cdn能防御ddos吗?
CDN(内容分发网络)是一种广泛应用于互联网中的技术,它通过将内容分发到全球各地的服务器上,以提高用户在访问网站时的加载速度和稳定性。然而,CDN是否能够有效防御DDoS(分布式拒绝服务)攻击是一…...
分享 2 个 .NET EF 6 只更新某些字段的方法
前言 EF 更新数据时,通常情况下,是更新全部字段的,但实际业务中,更新全部字段的情况其实很少,一般都是修改其中某些字段,所以为了实现这个目标,很多程序员通常会这样作: 先从数据库…...
vs code解决报错 (c/c++的配置环境 远端机器为Linux ubuntu)
参考链接:https://blog.csdn.net/fightfightfight/article/details/82857397 https://blog.csdn.net/m0_38055352/article/details/105375367 可以按照步骤确定那一步不对,如果一个可以就不用往下看了 目录 一、检查一下文件扩展名 二、安装扩展包并…...
08 字符串和字节串
使用单引号、双引号、三单引号、三双引号作为定界符(delimiter)来表示字符串,并且不同的定界符之间可以相互嵌套。 很多内置函数和标准库对象也都支持对字符串的操作。 x hello world y Python is a great language z Tom said, "Le…...
vue使用mavonEditor(流程图、时序图、甘特图实现)
mavonEditor 安装mavonEditor $ npm install mavon-editor --save使用 // 全局注册import Vue from vueimport mavonEditor from mavon-editorimport mavon-editor/dist/css/index.css// useVue.use(mavonEditor)new Vue({el: #main,data() {return { value: }}})//局部使用…...
Java实现短信验证码服务
1.首先这里使用的是阿里云的短信服务。 package com.wzy.util;; import cn.hutool.captcha.generator.RandomGenerator; import com.aliyun.dysmsapi20170525.Client; import com.wzy.entity.Ali; import org.springframework.stereotype.Component;/*** Author: 顾安* Descri…...
python中的线程
线程 线程概念 线程 在一个进程的内部, 要同时干多件事, 就需要同时运行多个"子任务", 我们把进程内的这些"子任务"叫做线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中, 是进程的实际运作单位。一条线程指的是进程中一个单一顺序的控制流…...
hcip学习 多实例生成树,VRRP工作原理
一、STP 和 RSTP 解决了什么问题 1、STP:解决了在冗余的二层网络中所出现的环路问题 2、RSTP:在 STP 的基础上,解决了 STP 收敛速度慢的问题,引入了一些 STP 保护机制,使其网络更加稳定 二、MSTP 针对 RSTP 的改进 …...
Docker搭建群晖
Docker搭建群晖 本博客介绍在docker下搭建群晖 1.编辑docker-compose.yml文件 version: "3" services:dsm:container_name: dsmimage: vdsm/virtual-dsm:latestenvironment:DISK_SIZE: "16G"cap_add:- NET_ADMIN ports:- 8080:50…...
【java】BIO,NIO,多路IO复用,AIO
在Java中,处理I/O操作的模型主要有四种:阻塞I/O (BIO), 非阻塞I/O (NIO), 异步I/O (AIO), 以及IO多路复用。下面详细介绍这四种I/O模型的工作原理和应用场景。 1. 阻塞I/O (BIO) 工作原理 阻塞I/O是最传统的I/O模型。在这种模型中,当一个线…...
服务器怎样减少带宽消耗的问题?
择业在使用服务器的过程中会消耗大量的带宽资源,而减少服务器的带宽消耗则可以帮助企业降低经济成本,同时还能够提高用户的访问速度,那么服务器怎样能减少带宽的消耗呢?本文就来带领大家一起来探讨一下吧! 企业可以选择…...
linux 报错:bash: /etc/profile: 行 32: 语法错误:未预期的文件结束符
目录 注意错误不一定错在最后一行 i进入编辑 esc退出编辑 :wq 保存编辑退出 :q!不保存退出 if [ $# -eq 3 ] then if [ ! -e "$1" ]; then miss1 $1 elif [ ! -e "$2" -a ! -e "$3" ]; then miss2and3…...
MySQL练习(5)
作业要求: 实现过程: 一、触发器 (1)建立两个表:goods(商品表)、orders(订单表) (2)在商品表中导入商品记录 (3)建立触发…...
泛型新理解
1.创建三个类,并写好对应关系 package com.jmj.gulimall.study;public class People { }package com.jmj.gulimall.study;public class Student extends People{ }package com.jmj.gulimall.study;public class Teacher extends People{ }2.解释一下这三个方法 pub…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
Xcode 16 集成 cocoapods 报错
基于 Xcode 16 新建工程项目,集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...
react更新页面数据,操作页面,双向数据绑定
// 路由不是组件的直接跳转use client,useEffect,useRouter,需3个结合, use client表示客户端 use client; import { Button,Card, Space,Tag,Table,message,Input } from antd; import { useEffect,useState } from react; impor…...
