Netty学习(Netty入门)

概述

Netty是什么

在这里插入图片描述

Netty的地位

在这里插入图片描述

Netty的优势

在这里插入图片描述

HelloWorld

public class HelloClient {public static void main(String[] args) throws InterruptedException {// 1. 启动类new Bootstrap()// 2. 添加 EventLoop.group(new NioEventLoopGroup())// 3. 选择客户端 channel 实现.channel(NioSocketChannel.class)// 4. 添加处理器.handler(new ChannelInitializer<NioSocketChannel>() {@Override // 在连接建立后被调用protected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new StringEncoder());}})// 5. 连接到服务器.connect(new InetSocketAddress("localhost", 8080)).sync().channel()// 6. 向服务器发送数据.writeAndFlush("hello, world");}
}public class HelloServer {public static void main(String[] args) {// 1. 启动器,负责组装 netty 组件,启动服务器new ServerBootstrap()// 2. BossEventLoop, WorkerEventLoop(selector,thread), group 组.group(new NioEventLoopGroup())// 3. 选择 服务器的 ServerSocketChannel 实现.channel(NioServerSocketChannel.class) // OIO BIO// 4. boss 负责处理连接 worker(child) 负责处理读写,决定了 worker(child) 能执行哪些操作(handler).childHandler(// 5. channel 代表和客户端进行数据读写的通道 Initializer 初始化,负责添加别的 handlernew ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {// 6. 添加具体 handlerch.pipeline().addLast(new LoggingHandler());ch.pipeline().addLast(new StringDecoder()); // 将 ByteBuf 转换为字符串ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { // 自定义 handler@Override // 读事件public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println(msg); // 打印上一步转换好的字符串}});}})// 7. 绑定监听端口.bind(8080);}
}

流程分析

在这里插入图片描述

正确理解

正确理解 Netty中各个组件的功能和职责
在这里插入图片描述

组件

EventLoop

在这里插入图片描述

普通任务和定时任务


@Slf4j
public class TestEventLoop {public static void main(String[] args) {// 1. 创建事件循环组EventLoopGroup group = new NioEventLoopGroup(2); // io 事件,普通任务,定时任务
//        EventLoopGroup group = new DefaultEventLoopGroup(); // 普通任务,定时任务// 2. 获取下一个事件循环对象System.out.println(group.next());System.out.println(group.next());System.out.println(group.next());System.out.println(group.next());// 3. 执行普通任务group.next().execute(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.debug("ok");});// 4. 执行定时任务group.next().scheduleAtFixedRate(() -> {log.debug("ojbk");}, 0, 1, TimeUnit.SECONDS);log.debug("main");}
}

IO任务

Netty客户端是多线程程序,idea debug 默认断点模式为ALL,即会停止主线程以及守护线程,所以当客户端断点自定义Evaluate发送数据时,守护线程的发送数据Channel也被断点停止,所以无法发送数据

选择Thread只停止当前线程,守护线程仍然可以运行
在这里插入图片描述

一个客户端的NIO线程跟Channel建立链接就会建立一个绑定关系,后续客户端的Channel上的IO事件都由一个EventLoop处理,
客户端-Channel-EventLoop绑定关系

在这里插入图片描述

EventLoop的分工细化

第一次细分,Netty建议将EventLoop职责细分,分为boss和worker
group中传入两个EventLoop,那么boss只负责accept事件,worker负责read事件

在这里插入图片描述

上诉优化,worker中的NIOEventLoopGroup除了要负责SocketChannel的NIO连接操作还要负责连接后的读写操作,如果读写较长较重,那么会阻塞影响到worker其他的连接或读写操作,所以,
再次细分,EventLoop有两种实现,NIOEventLoopGroup 能处理IO事件普通任务和定时任务,DefaultEventLoopGroup只能处理普通任务和定时任务,将读写操作交给它去处理耗时较长的读写操作。

在这里插入图片描述

作为对比,第一个没有指定group,默认使用了worker的NIOEventLoopGroup来处理读写操作,而第二则使用了DefaultEventLoop来处理读写操作

切换线程

在这里插入图片描述

Channel

在这里插入图片描述

正确的链接建立:ChannelFuture

处理异步连接

由于连接的建立是耗时的,所以Channel必须等到连接建立完成再执行获取,否则是无效的

在这里插入图片描述
connect方法返回的ChannelFuture若没有阻塞等待连接,那么接下来获取到的Channel是没有建立好连接的Channel

在这里插入图片描述
如上两种方法异步等待NIO线程建立完毕

谁发起的调用谁等待链接结果

正确的链接关闭:CloseFuture的关闭

在这里插入图片描述

不能直接在主线程或其他线程中直接处理关闭操作,因为nioEventLoopGroup-2-1属于异步线程,此处close方法非阻塞,有可能在关闭操作还未完成就执行了关闭后操作

解决方法:
使用阻塞关闭方法,只有当channel真的关闭了才执行后面的方法

在这里插入图片描述
优雅的关闭:等待还未执行完的操作执行完后再关闭
在这里插入图片描述

为什么Netty是异步设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Future & Promise

概述

在这里插入图片描述
在这里插入图片描述

Future

jdk中的Future

Future就是在线程之间传递结果的一个容器,是被动的获取结果,由执行完任务的线程给予的结果,没有暴露主动赋予结果的方法


@Slf4j
public class TestJdkFuture {public static void main(String[] args) throws ExecutionException, InterruptedException {//1.创建线程池ExecutorService service = Executors.newFixedThreadPool(2);//2.提交任务Future<Object> future = service.submit(new Callable<Object>() {@Overridepublic Object call() throws Exception {log.debug("执行计算");Thread.sleep(1000);return 50;}});//3.祝线程通过future获取结果,get是阻塞等待方法log.debug("等待结果");log.debug("结果{}",future.get());}
}
Netty 中的 Future

与jdk中的差不多,继承至jdk的Future,做了增强


@Slf4j
public class TestNettyFuture {public static void main(String[] args) throws ExecutionException, InterruptedException {NioEventLoopGroup group = new NioEventLoopGroup();EventLoop eventLoop = group.next();//提交任务Future<Integer> future = eventLoop.submit(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {log.debug("执行计算");System.out.println("执行计算");Thread.sleep(1000);return 50;}});//通过future获取结果,get是阻塞等待方法log.debug("等待结果");log.debug("结果{}",future.get());//异步方式获取结果future.addListener(new GenericFutureListener<Future<? super Integer>>() {@Overridepublic void operationComplete(Future<? super Integer> future) throws Exception {//getNow非阻塞等待 立即获取结果log.debug("结果{}",future.getNow());}});}
}

Promise

Promise又继承至Netty的Future,功能更强大,可以主动填充结果,对于网络通信非常有用

@Slf4j
public class TestNettyPromise {public static void main(String[] args) throws ExecutionException, InterruptedException {// 1. 准备 EventLoop 对象EventLoop eventLoop = new NioEventLoopGroup().next();// 2. 可以主动创建 promise, 结果容器DefaultPromise<Integer> promise = new DefaultPromise<>(eventLoop);new Thread(() -> {// 3. 任意一个线程执行计算,计算完毕后向 promise 填充结果log.debug("开始计算...");try {int i = 1 / 0;Thread.sleep(1000);promise.setSuccess(80);} catch (Exception e) {e.printStackTrace();promise.setFailure(e);}}).start();// 4. 接收结果的线程log.debug("等待结果...");log.debug("结果是: {}", promise.get());}}

Handler & Pipeline

Pipeline

在这里插入图片描述

Inbound

在这里插入图片描述
入栈是按入栈顺序出,出栈是按入栈顺序返出

channelRead 是一个调用链,如果中间没调用,那么后面的handler 则调用不到

Outbound

注意ctx.writeAndFlush和 ch.writeAndFlush
ctx.writeAndFlush 是从当前调用的 handler 往后寻找 OutboundHandler,若之前没有执行到OutboundHandler 那么找不到OutboundHandler执行

而ch.writeAndFlush则是从整个调用链的最前端 tail 处理开始往后寻找OutboundHandler
而且先执行调用链中的InboundHandler输入,中间的 OutboundHandler 被跳过不影响正常输入执行
在这里插入图片描述
如图ch.writeAndFlush 的调用执行流程
在这里插入图片描述

ByteBuffer

netty 中 ByteBuf容量动态扩容,netty 中 ByteBuffer 固定容量
在这里插入图片描述
netty 中 ByteBuffer 默认使用直接内存(系统内存、内存条)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

例如 扩容 2 的整数倍 2^9=512 扩容至 2^10 =1024

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
tail 只能处理原始 ByteBuf 如果中途 ByteBuf 被转换成其他数据类型,则 tail 无法自动release

零拷贝 slice

slice 是 netty 中对于零拷贝的体现之一
在这里插入图片描述
在这里插入图片描述

切片后生成的对象,实际上还是操作原始bytebuf 的内容
在这里插入图片描述

使用习惯,切片自己增加引用计数,避免被其他调用者释放
在这里插入图片描述
在这里插入图片描述

component 组合零拷贝

writeBytes 会发生真正的数据复制,每次writeBytes都会发生数据复制
addComponents 是使逻辑上连续,没有发生复制

在这里插入图片描述

在这里插入图片描述

双向通信

实现一个 echo server

编写 server

new ServerBootstrap().group(new NioEventLoopGroup()).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) {ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buffer = (ByteBuf) msg;System.out.println(buffer.toString(Charset.defaultCharset()));// 建议使用 ctx.alloc() 创建 ByteBufByteBuf response = ctx.alloc().buffer();response.writeBytes(buffer);ctx.writeAndFlush(response);// 思考:需要释放 buffer 吗// 思考:需要释放 response 吗}});}}).bind(8080);

编写 client

NioEventLoopGroup group = new NioEventLoopGroup();
Channel channel = new Bootstrap().group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new StringEncoder());ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buffer = (ByteBuf) msg;System.out.println(buffer.toString(Charset.defaultCharset()));// 思考:需要释放 buffer 吗}});}}).connect("127.0.0.1", 8080).sync().channel();channel.closeFuture().addListener(future -> {group.shutdownGracefully();
});new Thread(() -> {Scanner scanner = new Scanner(System.in);while (true) {String line = scanner.nextLine();if ("q".equals(line)) {channel.close();break;}channel.writeAndFlush(line);}
}).start();

💡 读和写的误解

我最初在认识上有这样的误区,认为只有在 netty,nio 这样的多路复用 IO 模型时,读写才不会相互阻塞,才可以实现高效的双向通信,但实际上,Java Socket 是全双工的:在任意时刻,线路上存在A 到 B 和 B 到 A 的双向信号传输。即使是阻塞 IO,读和写是可以同时进行的,只要分别采用读线程和写线程即可,读不会阻塞写、写也不会阻塞读

例如

public class TestServer {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(8888);Socket s = ss.accept();new Thread(() -> {try {BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));while (true) {System.out.println(reader.readLine());}} catch (IOException e) {e.printStackTrace();}}).start();new Thread(() -> {try {BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));// 例如在这个位置加入 thread 级别断点,可以发现即使不写入数据,也不妨碍前面线程读取客户端数据for (int i = 0; i < 100; i++) {writer.write(String.valueOf(i));writer.newLine();writer.flush();}} catch (IOException e) {e.printStackTrace();}}).start();}
}

客户端

public class TestClient {public static void main(String[] args) throws IOException {Socket s = new Socket("localhost", 8888);new Thread(() -> {try {BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));while (true) {System.out.println(reader.readLine());}} catch (IOException e) {e.printStackTrace();}}).start();new Thread(() -> {try {BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));for (int i = 0; i < 100; i++) {writer.write(String.valueOf(i));writer.newLine();writer.flush();}} catch (IOException e) {e.printStackTrace();}}).start();}
}

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

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

相关文章

如何恢复未保存的 Excel 文件

您是否曾经在处理 Excel 工作表时&#xff0c;电脑突然崩溃&#xff1f;您首先想到的是“进度保存了吗&#xff1f;”或“我是否按了 CtrlS 来保存文件&#xff1f;”这种压力是难以想象的&#xff0c;因为意外断电或电脑崩溃可能会让您所有的辛苦工作付诸东流。 无论对于学生…

前端技术(三)—— javasctipt 介绍:jQuery方法和点击事件介绍(补充)

6. 常用方法 ● addClass() 为jQuery对象添加一个或多个class <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&…

Educational Codeforces Round 167 (Rated for Div. 2)(A~C)题解

A. Catch the Coin 解题思路: 最终&#x1d465;一定会相等&#xff0c;我们考虑直接到下面接住他。 #include<bits/stdc.h> using namespace std; typedef long long ll; #define N 1000005 ll dp[N], w[N], v[N], h[N]; ll dis[1005][1005]; ll a, b, c, n, m, t; ll…

反编译kasada

继续研究反编译 这次的网站是 一个航司网站 他有 akamai和 kasada 两种防护 akamai 没啥好说的 结构分析 最开始有个长字符串 处理成 一个十几万的数组 通过 r.W[0] 走什么分支 还有数据的存取 M是一个98个函数组成的数组 代表不同的执行逻辑 这里给他转成了 switch case…

pygame 音乐粒子特效

代码 import pygame import numpy as np import pymunk from pymunk import Vec2d import random import librosa import pydub# 初始化pygame pygame.init()# 创建屏幕 screen pygame.display.set_mode((1920*2-10, 1080*2-10)) clock pygame.time.Clock()# 加载音乐文件 a…

RAID的实现

软RAID&#xff0c;在实际工作中使用较少&#xff0c;性能太次。 mdadm工具&#xff0c;主要在虚拟机上使用&#xff0c; 硬RAID 用一个单独的芯片&#xff0c;这个芯片的名字叫做RAID卡&#xff0c;数据在RAID中进行分散的时候&#xff0c;用的就是RAID卡。 模拟RAID-5工作…

麦蕊智数,,另外一个提供免费的股票数据API,可以通过其提供的接口获取实时和历史的股票数据。

麦蕊智数&#xff0c;&#xff0c;提供免费的股票数据API&#xff0c;可以通过其提供的接口获取实时和历史的股票数据。 API接口&#xff1a;http://api.mairui.club/hslt/new/您的licence 备用接口&#xff1a;http://api1.mairui.club/hslt/new/您的licence 请求频率&#x…

element-plus的文件上传组件el-upload

el-upload组件 支持多种风格&#xff0c;如文件列表&#xff0c;图片&#xff0c;图片卡片&#xff0c;支持多种事件&#xff0c;预览&#xff0c;删除&#xff0c;上传成功&#xff0c;上传中等钩子。 file-list&#xff1a;上传的文件集合&#xff0c;一定要用v-model:file-…

stm32按键检测+光敏+蜂鸣器

按键检测 按键注意消抖&#xff0c;机械按下和松开时均伴随有一连串的抖动&#xff0c;一般为5ms&#xff5e;10ms。可通过软件或硬件消抖。 void Key_Init() {//开启时钟,GPIOBRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//定义结构体变量GPIO_InitTypeDef GPIO_…

080、类与类之间的关系

类与类之间主要存在以下几种关系&#xff1a; 继承&#xff08;Inheritance&#xff09;&#xff1a; 继承是面向对象编程的一个基本特征&#xff0c;它允许我们定义一个类&#xff08;子类或派生类&#xff09;来继承另一个类&#xff08;父类或基类&#xff09;的属性和方法…

孟德尔随机化与痛风3

写在前面 检索检索&#xff0c;刚好发现一篇分区还挺高&#xff0c;但结果内容看上去还挺熟悉的文章&#xff0c;特记录一下。 文章 Exploring the mechanism underlying hyperuricemia using comprehensive research on multi-omics Sci Rep IF:3.8中科院分区:2区 综合性期…

刷题——重建二叉树

重建二叉树_牛客题霸_牛客网 TreeNode* reConstructBinaryTree(vector<int>& preOrder, vector<int>& vinOrder) {// write code hereint n preOrder.size();int m vinOrder.size();if(n 0 || m 0) return NULL;TreeNode* root new TreeNode(preOrde…

【排序算法】—— 快速排序

快速排序的原理是交换排序&#xff0c;其中qsort函数用的排序原理就是快速排序&#xff0c;它是一种效率较高的不稳定函数&#xff0c;时间复杂度为O(N*longN)&#xff0c;接下来就来学习一下快速排序。 一、快速排序思路 1.整体思路 以升序排序为例&#xff1a; (1)、首先随…

web缓存代理服务器

一、web缓存代理 web代理的工作机制 代理服务器是一个位于客户端和原始&#xff08;资源&#xff09;服务器之间的服务器&#xff0c;为了从原始服务器取得内容&#xff0c;客户端向代理服务器发送一个请求&#xff0c;并指定目标原始服务器&#xff0c;然后代理服务器向原始…

启动外部EXE参数

QString rootDir1 QApplication::applicationDirPath(); QString filePathExe1 QString(“%1/run/xxx.exe”).arg(rootDir1); QString fileConfigPath1 QString(“%1/run/”).arg(rootDir1); std::string stdStr filePathExe1.toStdString(); LPCSTR lpcStr stdStr.c_str(…

xxxxxxx.jar中没有主清单属性

写了一个小的springboot demo&#xff0c;构建时由于没加构建的maven插件,导致使用java -jar命令运行时报错xxxxxxx.jar中没有主清单属性。 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boo…

使用Spring Boot和HBase实现大数据存储

使用Spring Boot和HBase实现大数据存储 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 一、引言 随着大数据技术的发展&#xff0c;处理和存储海量数据成为许…

2-27 基于matlab的一种混凝土骨料三维随机投放模型

基于matlab的一种混凝土骨料三维随机投放模型&#xff0c;为混凝土细观力学研究提供一种快捷的三维建模源代码。可设置骨料数量&#xff0c;边界距离、骨料大小等参数。程序已调通&#xff0c;可直接运行。 2-27 matlab 混凝土骨料三维随机投放模型 - 小红书 (xiaohongshu.com)…

CDNOW_master.txt数据分析实战

一、数据详情 该数据集是常见的销售数据集&#xff0c;数据展示的是美国1997后的商品销售数据。包含四个字段&#xff0c;分别是用户id,购买时间&#xff0c;销售量&#xff0c;与销售金额。 二、数据读取与数据清洗 导入必要的包 \s代表的许多空格作为分割&#xff0c;names重…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【明文导入密钥(C/C++)】

明文导入密钥(C/C) 以明文导入ECC密钥为例。具体的场景介绍及支持的算法规格 在CMake脚本中链接相关动态库 target_link_libraries(entry PUBLIC libhuks_ndk.z.so)开发步骤 指定密钥别名keyAlias。 密钥别名的最大长度为64字节。 封装密钥属性集和密钥材料。通过[OH_Huks_I…