netty入门-3 EventLoop和EventLoopGroup,简单的服务器实现

文章目录

  • EventLoop和EventLoopGroup
    • 服务器与客户端基本使用
    • 增加非NIO工人
    • NioEventLoop 处理普通任务与定时任务
  • 结语

EventLoop和EventLoopGroup

二者大概是什么这里不再赘述,前一篇已简述过。
不理解也没关系。
下面会简单使用,看了就能明白是什么
这篇文章只说NioEventLoopGroup
后续文章会有服务器创建类BootStrap的方法总结。

服务器与客户端基本使用

EventLoopGroup的常用实现类是NioEventLoopGroup
就以此为例,我们来看这两者,在使用netty创建一个服务器的过程中处于什么位置。

PS:我觉得既然是专注于这个东西怎么用,就不要太纠结于底层。
我当时看到childHandler,就想弄明白这个到底是怎么把Handler加到新ChannelPipeline中的。这就要涉及源码。下面这个链式调用其他部分也是一样。想搞明白每个调用的底层还是需要看源码。会给初学者带来没必要的精力耗费。我觉得需要收住好奇心,先会简单使用,再说底层实现。

//服务端
//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 来执行。
关系见下图
headtail看做pipelinehandler链的头尾,可视为无意义。
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类的一些方法。

感谢阅读,欢迎批评指正。

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

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

相关文章

第124天:内网安全-代理 Sockets协议路由不出网后渗透通讯CS-MSF 控制上线

目录 思维导图 环境配置 案例一&#xff1a;网络通讯&控制上线--CS-路由添加&节点建立&协议生成&正反连接 案例二&#xff1a;网络通讯&控制上线--MSF-路由添加&节点建立&协议生成&正反连接 思维导图 环境配置 这里由于系统内存问题我只设…

Python的人脸识别程序

1.录入人脸&#xff0c;输入ID号 haarcascade_frontalface_default.xml # 导入模块 import os import numpy as np import cv2 as cv import cv2face_detector cv2.CascadeClassifier(rD:\Automation_All_Files\OCR\haarcascade_frontalface_default.xml) # 待更改# 为即将…

Windows10+vs 2017中创建WEB API教程

我们如果需要用到web api怎么办&#xff1f;一般来说可以自己开发和去使用别人开发好的api&#xff0c;今天我们来讲一下Windows10vs 2017中创建web Api的教程。目前本教程当中的方法在Win10 VS2017&#xff08;MVC5&#xff09;win server2016vs2017&#xff0c;vs2013 vs201…

网安人必须人手一份的《Linux私房教程》,GitHub星标286K!

Linux是一套免费使用和自由传播的操作系统内核&#xff0c;是一个基于POSIX和Unix的多用户、多任务支持多线程和多CPU的操作系统内核。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想&#xff0c;是一个性能稳…

【iOS】GCD

参考文章&#xff1a;GCD函数和队列原理探索 之前写项目的时候&#xff0c;进行耗时的网络请求使用GCD处理过异步请求&#xff0c;但对一些概念都很模糊&#xff0c;这次就来系统学习一下GCD相关 相关概念 什么是GCD&#xff1f; Grand Center Dispatch简称GCD&#xff0c;是…

ChatTTS真人文本转语音模型,富有韵律与情感,且免费开源

上期图文教程&#xff0c;我们分享了微软TTS真人转语音大模型&#xff0c;但是微软的TTS模型只有针对新用户免费一年&#xff0c;其他用户都是收费的&#xff0c;虽然微软开源了部分TTS的功能&#xff0c;但是针对真人类似的富有情感的TTS模型并没有进行开源&#xff0c;本期介…

软件测试基础1--功能测试

1、什么是软件测试&#xff1f; 软件是控制计算机硬件运行的工具。 软件测试&#xff1a;使用技术手段验证软件是否满足使用需求&#xff0c;为了发现软件功能和需求不相符合的地方&#xff0c;或者寻找实际输出和预期输出之间的差异。 软件测试的目的&#xff1a;减少软件缺陷…

学习笔记之JAVA篇(0724)

p 方法 方法声明格式&#xff1a; [修饰符1 修饰符2 ...] 返回值类型 方法名&#xff08;形式参数列表&#xff09;{ java语句;......; } 方法调用方式 普通方法对象.方法名&#xff08;实参列表&#xff09;静态方法类名.方法名&#xff08;实参列表&#xff09; 方法的详…

【YashanDB知识库】YashanDB的JDBC/OCI驱动如何设置字符编码

问题现象 Oracle、Mysql数据库链接串&#xff0c;JDBC驱动连接串可以指定客户端的编码格式&#xff1a; jdbc:mysql://hostname:port/database_name?useUnicodetrue&characterEncodingutf8mb4 jdbc:oracle:thin://hostname:port/service_name?NLS_LANGUAGEAMERICAN&am…

【SQL语句大全(MySQL)】

SQL语法 添加删除修改查询基本查询条件查询分组函数/聚合函数分组查询排序分页查询&#xff08;限制查询&#xff09;多表查询连接查询根据年代分类连接查询根据连接方式分类1、内连接2、左外连接3、右外连接 多张表连接的语法格式 嵌套查询 SQL语句书写顺序 添加 INSERT INTO…

构建生成工具cmake的使用(1)

ps:本文是对cmake的基础讲解&#xff0c;掌握后解决70-80%情况是足以应对的&#xff0c;后续会对cmake有进阶内容。 一 前言 CMake 是一个工具&#xff0c;帮助开发者管理和自动化软件项目的构建过程。它使用一个叫做CMakeLists.txt 的文本文件来描述项目的组织结构、编译选项…

h5图片压缩后变为base64格式的流地址上传服务器

**操作逻辑&#xff1a;前端把图片压缩变为base64格式的流地址&#xff0c;然后把base64格式的流地址传输到后端进行上传到cos服务器 操作流程** 1.前端&#xff1a;我这里是通过uniapp开发h5的&#xff0c;upLoadPutBase64是封装好的上传后端服务器的接口 div写上 <helan…

oracle数据库下的定时任务,如何创建Jobs

oarcle中写存储过程&#xff0c;上面的文章中已经介绍过了&#xff0c;这次是写好存储过程后&#xff0c;在Jobs文件夹中新建job,达到定时执行任务的目的。 1、在plsql数据库中&#xff0c;找到左边的Jobs,右键点击新建&#xff0c;如下图&#xff1a; 2、按照下图将相应的项添…

【Gitlab】SSH配置和克隆仓库

生成SSH Key ssh-keygen -t rsa -b 4096 私钥文件: id_rsa 公钥文件:id_rsa.pub 复制生成的ssh公钥到此处 克隆仓库 git clone repo-address 需要进行推送和同步来更新本地和服务器的文件 推送更新内容 git push <remote><branch> 拉取更新内容 git pull &…

探索 IT 新边疆:元宇宙的无限可能与挑战

目录 元宇宙的基本概念 元宇宙的无限可能 元宇宙与艺术创作 元宇宙中的社交互动 不同文化在元宇宙中的融合与冲突 文化的融合&#xff1a;元宇宙的桥梁作用 文化的冲突&#xff1a;价值观与认同的碰撞 应对策略&#xff1a;促进融合&#xff0c;化解冲突 元宇宙面临的…

LINUX高性能服务器框架

1.服务器模型&#xff1a; 1). C/S模型 &#xff1a; 此模型就是一个服务器能给多个客户端提供服务&#xff0c;但所以资源都被服务端所占有&#xff0c;客户端想要获取只能通过请求连接服务端去获取。由于客户端的请求访问是异步的&#xff0c;所以需要一个手段进行此类事件的…

android user 版本如何手动触发dump

项目需要在android user版本增加手动触发dump方法&#xff0c;用以确认user版本发生dump后系统是重启还是真正发生dump卡机&#xff01; 本文以qcom平台项目为例描述所做的修改&#xff0c;留下足迹以备后忘。 闲言少叙&#xff0c;开整上干货&#xff1a; 一、修改bin文件 …

Typescript学习笔记(2.0)

ts编译选项 tsc app.ts -w 参数-w&#xff0c;就是对app.ts进行监视&#xff0c;每次该文件改变时就会自动编译 **:任意目录 *&#xff1a;任意文件 接口 接口用来定义一个类结构&#xff0c;用来定义一个类中应该包含哪些属性和方法&#xff0c;同时接口也可以当做类型…

Profinet与以太网Ethernet的区别

在工业自动化和网络通信领域&#xff0c;Profinet和以太网Ethernet是两种常见的通信协议。尽管它们都基于以太网技术&#xff0c;但在技术规范、应用领域、通信速度和实时性、系统兼容性和互操作性以及数据处理和网络管理等方面存在显著差异。 一、技术规范和应用领域 以太网 …

爬虫基础之Web网页基础

在探讨爬虫技术的深入应用之前&#xff0c;了解Web网页的基础知识是至关重要的。Web网页是互联网上的基本构成单元&#xff0c;而爬虫则是与这些网页交互的自动化程序。本文将介绍Web网页的基础知识&#xff0c;包括HTML、CSS、JavaScript以及它们在爬虫技术中的角色。 HTML&a…