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.writeAndFlushch.writeAndFlush
ctx.writeAndFlush 是从当前调用的 handler 往后寻找 OutboundHandler,若之前没有执行到OutboundHandler 那么找不到OutboundHandler执行

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

ByteBuffer

nettyByteBuf容量动态扩容,nettyByteBuffer 固定容量
在这里插入图片描述
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 到 BB 到 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.mzph.cn/pingmian/42604.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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-…

孟德尔随机化与痛风3

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

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

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

web缓存代理服务器

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

Word文档中公式的常用操作

一、参考资料 二、常用操作 插入公式 Alt 多行公式 Shift Enter 多行公式对齐 WORD Tips: 多行公式编辑及对齐 word自带公式等号对齐&#xff08;可任意符号处对齐&#xff09; 多行公式按照 为基准对齐。 拖动鼠标选中整个公式点击右键&#xff0c;选择【对齐点(…

计算机系统简述

目标 计算机世界并非如此神秘。相反&#xff0c;计算机是非常“确定”的一个系统&#xff0c;即在任何时候&#xff0c;在相同的方法、相同的状态下&#xff08;当然还包括相同的起始条件&#xff09;&#xff0c;同样的问题必然获得相同的结果。其实&#xff0c;计算机并不是…

数据库的学习(4)

一、题目 1、创建数据表qrade: CREATE TABLE grade(id INT NOT NULL,sex CHAR(1),firstname VARCHAR(20)NOT NULL,lastname VARCHAR(20)NOT NULL,english FLOAT,math FLOAT,chinese FLOAT ); 2、向数据表grade中插入几条数据: (3,mAllenwiiliam,88.0,92.0 95.0), (4,m,George&…

【数据结构与算法】快速排序双指针法

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​

【js基础巩固】深入理解作用域与作用域链

作用域链 先看一段代码&#xff0c;下面代码输出的结果是什么&#xff1f; function bar() {console.log(myName) } function foo() {var myName "极客邦"bar() } var myName "极客时间" foo()当执行到 console.log(myName) 这句代码的时候&#xff0c…

nullptr和NULL

nullptr 既不是整型类型&#xff0c;也不是指针类型&#xff0c;nullptr 的类型是 std::nullptr_t&#xff08;空指针类型&#xff09;&#xff0c;能转换成任意的指针类型。 NULL是被定义为0的常量&#xff0c;当遇到函数重载时&#xff0c;就会出现问题。避免歧义 函数重载…