常见面试题-Netty专栏(一)
typora-copy-images-to: imgs
Netty 是什么呢?Netty 用于做什么呢?
答:
Netty 是一个 NIO 客户服务端框架,可以快速开发网络应用程序
,如协议服务端和客户端,极大简化了网络编程,如 TCP 和 UDP 套接字服务(来自官网)
热门开源项目如 Dubbo、RocketMQ 底层都是用了 Netty
Netty怎么实现高性能设计?
答:
Netty 高性能的三个方面:
- 传输:用什么样的通道将数据发送给对方,BIO、NIO 或者 AIO,IO 模型在很大程度上决定了框架的性能。
IO模型的选择
- 协议:采用什么样的通信协议,HTTP 或者内部私有协议。协议的选择不同,性能模型也不同。相比于公有协议,内部私有协议的性能通常可以被设计的更优。
协议的选择
- 线程:数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派发,Reactor 线程模型的不同,对性能的影响也非常大。
线程模型的选择
介绍一下 AIO、BIO 和 NIO?
答:
- AIO:
从 Java.1.7 开始,Java 提供了 AIO(异步IO),Java 的 AIO 也被称为 “NIO.2”
Java AIO 采用订阅-通知
模式,应用程序向操作系统注册 IO 监听,之后继续做自己的事情,当操作系统发生 IO 事件并且已经准备好数据时,主动通知应用程序,应用程序再进行相关处理
(Linux 平台没有这种异步 IO 技术,而是使用 epoll 对异步 IO 进行模拟)
- BIO:
BIO 即同步阻塞 IO,服务端实现模式为一个连接对应一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理
BIO简单工作流程:
- 服务器端启动一个 ServerSocket
- 客户端启动 Socket 对服务器进行通信,默认情况下服务器端需要对每个客户端建立一个线程与之通讯
- 客户端发出请求后, 先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝
- 如果有响应,客户端线程会等待请求结束后,再继续执行
BIO存在问题:
- 当并发量较大时,需要创建大量线程来处理连接,比较占用系统资源
- 连接建立之后,如果当前线程暂时没有数据可读,则线程会阻塞在 Read 操作上,造成线程资源浪费
- NIO:
从 Java1.4 开始,Java 提供了 NIO,NIO 即 “Non-blocking IO”(同步非阻塞IO)
NIO 的几个核心概念:
-
Channel、Buffer:BIO是基于字节流或者字符流的进行操作,而NIO 是基于
缓冲区
和通道
进行操作的,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中 -
Selector:选择器用于监听多个通道的事件(如,连接打开,数据到达),因此,单个线程可以监听多个数据通道,极大提升了单机的并发能力
当 Channel 上的 IO 事件未到达时,线程会在 select 方法被挂起,让出 CPU 资源,直到监听到 Channel 有 IO 事件发生,才会进行相应的处理
- NIO和BIO有什么区别?
- NIO是以
块
的方式处理数据,BIO是以字节流或者字符流
的形式去处理数据。 - NIO是通过
缓存区和通道
的方式处理数据,BIO是通过InputStream和OutputStream流
的方式处理数据。 - NIO的通道是双向的,BIO流的方向只能是单向的。
- NIO采用的多路复用的同步非阻塞IO模型,BIO采用的是普通的同步阻塞IO模型。
- NIO的效率比BIO要高,NIO适用于网络IO,BIO适用于文件IO。
NIO如何实现了同步非阻塞?
通过 Selector 和 Channel 来进行实现,一个线程使用一个 Selector 监听多个 Channel 上的 IO 事件,通过配置监听的通道Channel为非阻塞,那么当Channel上的IO事件还未到达时,线程会在select方法被挂起,让出CPU资源。直到监听到Channel有IO事件发生时,才会进行相应的响应和处理。
介绍一下 Netty 使用的线程模型?
答:
Netty 主要基于主从 Reactor 多线程模型,其中主从 Reactor 多线程模型将 Reactor 分为两部分:
- mainReactor:监听 Server Socket,用来处理网络 IO 连接建立操作,将建立的 SocketChannel 指定注册给 subReactor
- subReactor:和建立起来的 socket 做数据交互和业务处理操作
因为客户端的连接数量相对来说比较少,而数据的读和写会比较多一点,使用 mainReactor 只接受客户端连接,由其他线程 subReactor 负责读和写,将业务处理剥离出,让线程池来处理,降低了 Reactor 的性能开销
扩展:单 Reactor 单线程模型、单 Reactor 多线程模型
-
单 Reactor 单线程模型
通过 1 个线程负责客户端连接、网络数据的读写、业务处理
缓存 Redis 就是单 Reactor 单线程模型
-
单 Reactor 多线程模型
通过 1 个线程负责客户端的连接、网络数据的读写,将业务处理剥离出去,通过线程池来进行处理
三种 Reactor 模型的优缺点:
- 单 Reactor 单线程模型是单线程进行业务处理,当负载过重时,处理速度将会变慢,影响系统性能,因此引出单 Reactor 多线程模型
- 单 Reactor 多线程模型时多个线程处理业务,业务处理速度上来了,但是单 Reactor 承担了所有时间的监听和响应,可能存在性能问题。当有数百万客户端进行连接或者服务端需要对客户端握手进行安全认证,认证本身非常消耗性能,因此出现了主从 Reactor 多线程模型
- 主从 Reactor 多线程模型中 1 个主 Reactor 只用来处理网络 IO 的连接建立操作,而对于接入认证、IP 黑白名单过滤、握手等操作由从 Reactor 进行处理,这样进一步提升性能,在主从 Reactor 多线程模型中,从 Reactor 有多个,可以与 CPU 个数相同
TCP 粘包、拆包是什么?如何解决?
答:
TCP本身的机制决定了一定会有粘包、拆包,因为 TCP 传输协议时基于数据流传输的,而流化的数据没有界限,因此 TCP 作为传输层协议并不了解上层业务数据的具体含义,会根据 TCP 缓冲区的实际情况进行数据包的划分,所以业务上认为的一个完整的包,可能被 TCP 拆成多个包或者把多个小的包封装成一个大的包进行发送。
产生原因:
- 粘包:客户端发送的包的大小比socket的缓存小或者接收方读取socket缓存不及时,因此多个包一起发送了
- 拆包:客户端发送的包的大小比socket的缓存大或者发送的数据大于协议的MTU(最大传输单元)必须拆包,那么这个包就被拆分成了多个包进行发送
解决方法:
有三种方式:
- 通过指定分隔符来进行分割
- 通过指定固定长度来进行分割
- 上边两种方式灵活性不好,因此常用的是通过指定接收数据的长度来解决,也就是
LengthFieldBasedFrameDecoder()
这个类
Netty 中常用组件?
答:
Channel
:Netty 网络操作抽象类,包括了基本的 IO 操作,如 bind、connect、read、write 等等EventLoop
:主要是配合 Channel 处理 IO 操作,用来处理连接的生命周期中所发生的事件ChannelFuture
:Netty 中的所有 IO 操作都是异步的,我们通过 ChannelFuture 的 addListener() 注册一个 ChannelFutureListener 监听事件,当操作执行完毕后,监听就会返回结果ChannelHandler
:作为处理器,用于处理入站和出战的数据ChannelHandlerContext
:用于包裹 ChannelHandler,维护了 pipeline 这个双向链表中的 pre 和 next 指针,这样可以方便的找到与其相邻的 ChannelHandler,并且过滤出一些符合执行条件的 ChannelHandler,Netty 的异步事件在 pipeline 中传播就是依靠 ChannelHandlerContextChannelPipeline
:每一个 Channel 都会分配一个 ChannelPipeline,pipeline 是一个双向链表的结构,Netty 中产生的 IO 异步事件都会在这个 pipeline 中传播
Netty 如何发送消息?
答:
有两种发送消息的方式:
- 直接写入 Channel,消息从 ChannelPipeline 的尾节点开始向前传播至头节点,代码
channelHandlerContext.write()
- 使 write 事件从当前 ChannelHandler 开始沿着 pipeline 向前传播,代码
channelHandlerContext.channel().write()
这里解释一下,上边发送消息为什么是向前传播:
在 Netty 中,IO 异步事件基本上分为两类:inbound(入站) 事件、outbound(出站) 事件,那么入站事件是沿着 pipeline 的头结点一直向后传播,因此出站事件就是沿着 pipeline 的尾结点一直向前传播,而上边发送消息也就是出站事件,因此是沿着 pipeline 从后向前
进行传播
直接内存比堆内存快在了哪里?
答:
首先直接内存不是 Java 虚拟机中的内存,是直接向系统内存申请的空间,来源于 NIO,通过 Java 堆中的 DirectByteBuffer 来进行操作。
直接内存相比于堆内存,避免了数据的二次拷贝。
-
我们先来分析
不使用直接内存
的情况,我们发送数据需要将数据先写入 Socket 的缓冲区内,那么如果数据存储在 JVM 的堆内存中的话,会先将堆内存中的数据复制一份到直接内存中,再将直接内存中的数据写入到 Socket 缓冲区中,之后进行数据的发送-
为什么不能直接将 JVM 堆内存中的数据写入 Socket 缓冲区中呢?
在 JVM 堆内存中有 GC 机制,GC 后可能会导致堆内存中数据位置发生变化,那么如果直接将 JVM 堆内存中的数据写入 Socket 缓冲区中,如果写入过程中发生 GC,导致我们需要写入的数据位置发生变化,就会将错误的数据写入 Socket 缓冲区
-
-
那么如果使用直接内存的时候,我们将
数据直接存放在直接内存中
,在堆内存中只存放了对直接内存中数据的引用,这样在发送数据时,直接将数据从直接内存取出,放入 Socket 缓冲区中即可,减少了一次堆内存到直接内存的拷贝
什么是 Netty 的零拷贝?什么是 TCP 缓冲区?
答:
Netty 的零拷贝主要包含三个方面:
-
Netty 的接收和发送使用堆外(直接内存)进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。
-
Netty 提供 CompositeByteBuf 组合缓冲区类,可以将多个 ByteBuf合并为一个逻辑上的ByteBufer,避免了各个ByteBufer之间的拷贝,将几个小buffer合并成一个大buffer的繁琐操作。
-
Netty 的文件传输使用了
FileChannel
的transferTo
方法,该方法底层使用了sendfile
函数实现了 cpu 零拷贝。sendfile
函数通过网络发送数据的流程为:磁盘
----DMA拷贝---->
文件读取缓冲区----CPU拷贝---->
套接字发送缓冲区(SO_SNDBUF)----DMA拷贝---->
网络设备缓冲区(网卡)图片流程如下:(3次拷贝、2次上下文切换,这里上下文切换是在用户空间发起write操作,此时用户态切换为内核态,write调用完毕后又会从内核态切换回用户态)
什么是TCP 缓冲区?
每个 TCP 的 Socket 的内核中都有一个发送缓冲区(SO_SNDBUF)和一个接收缓冲区(SO_RECVBUF)(在通过 TCP 需要进行网络数据传输时,数据都是会写入 Socket 的缓冲区中的,之后再通过网络协议发送出去)
了解 Netty 中的 ByteBuf 类吗?
答:
在 Java NIO 编程中,Java 提供了 ByteBuffer 作为字节缓冲区类型(缓冲区可以理解为一段内存区域),来表示一个连续的字节序列。
Netty 中并没有使用 Java 的 ByteBuffer,而是使用了新的缓冲类型 ByteBuf,特性如下:
-
允许自定义缓冲类型
-
复合缓冲类型中内置的透明的零拷贝实现
-
开箱即用的动态缓冲类型,具有像 StringBuffer 一样的动态缓冲能力
-
不再需要调用 flip() 方法
Java 的 ByteBuffer 类中,需要使用 flip() 来进行读写两种模式的切换
-
正常情况下具有比 ByteBuffer 更快的响应速度
Java 中的 ByteBuffer:
主要需要注意有 3 个属性:position、limit、capacity
- capacity:当前数组的容量大小
- position:写入模式的可写入数据的下标,读取模式的可读取数据下标
- limit:写入模式的可写入数组大小,读取模式的最多可以读取数据的下标
假如说数组容量是 10,那么三个值初始值为:
position = 0
limit = 10
capacity = 10
假如写入 4 个字节的数据,此时三个值如下:
position = 4
limit = 10
capacity = 10
如果切换到读取数据模式(使用 flip()
),会改变上边的三个值,会从 position 的位置开始读取数据到 limit 的位置
position = 0
limit = 4
capacity = 10
Netty 中的 ByteBuf:
ByteBuf 主要使用两个指针来完成缓冲区的读写操作,分别是: readIndex
和 writeIndex
- 当写入数据时,writeIndex 会增加
- 当读取数据时,readIndex 会增加,但不会超过 writeIndex
ByteBuf 的使用:
public static void main(String[] args) {ByteBuf buffer = Unpooled.buffer(10);System.out.println("----------初始化ByteBuf----------");printByteBuffer(buffer);System.out.println("----------ByteBuf写入数据----------");String str = "hello world!";buffer.writeBytes(str.getBytes());printByteBuffer(buffer);System.out.println("----------ByteBuf读取数据----------");while (buffer.isReadable()) {System.out.print((char)buffer.readByte());}System.out.println();printByteBuffer(buffer);System.out.println("----------ByteBuf释放无用空间----------");buffer.discardReadBytes();printByteBuffer(buffer);System.out.println("----------ByteBuf清空----------");buffer.clear();printByteBuffer(buffer);
}
private static void printByteBuffer(ByteBuf buffer) {System.out.println("readerIndex:" + buffer.readerIndex());System.out.println("writerIndex:" + buffer.writerIndex());System.out.println("capacity:" + buffer.capacity());
}
/**输出**/
----------初始化ByteBuf----------
readerIndex:0
writerIndex:0
capacity:10
----------ByteBuf写入数据----------
readerIndex:0
writerIndex:12
capacity:64
----------ByteBuf读取数据----------
hello world!
readerIndex:12
writerIndex:12
capacity:64
----------ByteBuf释放无用空间----------
readerIndex:0
writerIndex:0
capacity:64
----------ByteBuf清空----------
readerIndex:0
writerIndex:0
capacity:64
ByteBuf 的 3 种使用模式:
ByteBuf 共有 3 种使用模式:
-
堆缓冲区模式(Heap Buffer)
堆缓冲区模式又称为 “支撑数据”,其数据存放在 JVM 的
堆空间
优点:
- 数据在 JVM 堆中存储,可以快速创建和释放,并且提供了数组直接快速访问的方法
缺点:
- 每次数据与 IO 进行传输时,都需要将数据复制到直接缓冲区(这里为什么要将数据复制到直接缓冲区的原因在上边的
直接内存比堆内存快在了哪里?
问题中已经讲过)
创建代码:
ByteBuf buffer = Unpooled.buffer(10);
-
直接缓冲区模式(Direct Buffer)
直接缓冲区模式属于堆外分配的直接内存,不占用堆的容量
优点:
- 使用 socket 传输数据时性能很好,避免了数据从 JVM 堆内存复制到直接缓冲区
缺点:
- 相比于堆缓冲区,直接缓冲区分配内存空间和释放更为昂贵
创建代码:
ByteBuf buffer = Unpooled.directBuffer(10);
-
复合缓冲区模式(Composite Buffer)
本质上类似于提供一个或多个 ByteBuf 的组合视图
优点:
- 提供一种方式让使用者自由组合多个 ByteBuf,避免了复制和分配新的缓冲区
缺点:
- 不支持访问其支撑数据,如果要访问,需要先将内容复制到堆内存,再进行访问
创建代码:
public static void main(String[] args) { // AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);// 创建一个堆缓冲区ByteBuf heapBuf = Unpooled.buffer(2);String str1 = "hi";heapBuf.writeBytes(str1.getBytes());// 创建一个直接缓冲区ByteBuf directBuf = Unpooled.directBuffer(5);String str2 = "nihao";directBuf.writeBytes(str2.getBytes());// 创建一个复合缓冲区CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer(10);compositeByteBuf.addComponents(heapBuf, directBuf);// 检查是否支持支撑数组,发现并不支持if (!compositeByteBuf.hasArray()) {for (ByteBuf buf : compositeByteBuf) {// 第一个字节偏移量int offset = buf.readerIndex();// 总共数据长度int length = buf.readableBytes();byte[] bytes = new byte[length];// 不支持访问支撑数组,需要将内容复制到堆内存中,即 bytes 数组中,才可以进行访问buf.getBytes(offset, bytes);printByteBuffer(bytes, offset, length);}} }private static void printByteBuffer(byte[] array, int offset, int length) {System.out.println("array:" + array);System.out.println("array->String:" + new String(array));System.out.println("offset:" + offset);System.out.println("len:" + length); } /**输出**/ array:[B@4f8e5cde array->String:hi offset:0 len:2 array:[B@504bae78 array->String:nihao offset:0 len:5
Netty 中 ByteBuf 如何分配?有池化的操作吗?
答:
ByteBuf 的分配接口定义在了 ByteBufAllocator
中,他的直接抽象类是 AbstracByteBufAllocator
,而 AbstractByteBufAllocator
有两种实现:PooledByteBufAllocator
和 UnpooledByteBufAllocator
- PooledByteBufAllocator 提供了池化的操作,将 ByteBuf 实例放入池中,提升了性能,将内存碎片化减到了最小UnpooledByteBufAllocator。(这个实现采用了一种内存分配的高效策略,成为 jemalloc,已经被好几种现代操作系统所采用)
- UnpooledByteBufAllocator 在每次创建缓冲区时,都会返回一个新的 ByteBuf 实例,这些实例由 JVM 负责 gc 回收
NioEventLoopGroup 默认启动了多少线程?
答:
NioEventLoopGroup 是一个多线程的事件循环器,默认启动了电脑可用线程数的两倍,在调用 NioEventLoopGroup 的构造方法之后,如果不传入线程数,最后启动的默认线程数的计算公式为:
Netty 如何解决 Selector 空轮询 Bug 的策略
答:
空轮询 Bug 也就是网络上发生了唤醒 Selector 的事件,但是 Selector 去取事件取不到,就一直去取,发生了空轮询,导致 CPU 使用率达到 100%
Netty解决机制:
判断如果发生了 N(默认是512次) 次空轮询,就新建一个Selector,把原来Selector事件都迁移过来
为什么没有使用 Netty 5?
答:
Netty版本分别是netty3.x、netty4.x、netty5.x
Netty5出现重大bug,已经被官网废弃,目前推荐使用Netty4.x稳定版本
相关文章:

常见面试题-Netty专栏(一)
typora-copy-images-to: imgs Netty 是什么呢?Netty 用于做什么呢? 答: Netty 是一个 NIO 客户服务端框架,可以快速开发网络应用程序,如协议服务端和客户端,极大简化了网络编程,如 TCP 和 UDP …...

【iOS】JSONModel的基本使用
文章目录 前言一、导入JSONModel二、JSONModel的基本使用1.基本用法2.模型集合3.模型导出为NSDictionary或JSON4.设置所有属性可选(所有属性值可以为空)5.下划线(蛇式)转驼峰命名法 前言 JSONModel 是一个用于 Objective-C 的开源库,它用于简…...

imu预积分学习(更新中)
imu预积分学习(更新中) IMU预积分可以做什么? 以上面那个经典图片为例子,IMU可以通过六轴数据,拿到第i帧和第j帧之间的相对位姿,这样不就可以去用来添加约束了吗 但是有一个比较大的问题是: I…...

算法刷题-链表
算法刷题-链表 203. 移除链表元素 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 示例 1: 输入:head [1,2,6,3,4,5,6], val 6 输出:[1,2,3,4,5]…...

Linux 挂载磁盘到指定目录
问题:公司分配了数据磁盘,但是分区也没有挂载到目录 首先 df -h 查看一下挂载点的情况 查看服务器上未挂载的磁盘 fdisk -l 注:图中sda、sdb (a、b指的是硬盘的序号) 分区操作 我们可以看到b硬盘有536G未分区&…...

ZYNQ linux调试LCD7789
一,硬件管脚 1,参数解释和实物 LVGL是一个开源的图形库,主要用于MCU上屏幕UI的部署,功能完善,封装合理,可裁切性强,也可以实现Linux上fbx的部署。LVGL官网LVGL - Light and Versatile Embedded Graphics Library 每根线的作用...

【双向链表的插入和删除】
文章目录 双向链表双向链表的插入双向链表的删除操作 双向链表 双向链表的结构定义如下: //双向链表的结构定义 typedef struct DuLNode {ElemType data;struct DuLNode* prior, * next; }DuLNode,*DuLinkList;双向链表的结点有两个指针域:prior&#…...

【Android知识笔记】Webview专题
WebView 核心组件 类名作用常用方法WebView创建对象加载URL生命周期管理状态管理loadUrl():加载网页 goBack():后退WebSettings配置&管理 WebView缓存:setCacheMode() 与JS交互:setJavaScriptEnabled()WebViewClient处理各种通知&请求事件should...
Leetcode第 368 场周赛
元素和最小的山形三元组 II 预处理前缀和后缀最小值,记为pre[i]和sa[i] 对于当前编号i,如果前面的最小值和后面的最大值都小于nums[i],则记录ans[i] nums[i]pre[i-1]sa[i1] 结果输出最小的ans[i]即可。 合法分组的最少组数 统计每一个数字出现的次数。将每一个数…...

Mysql数据库 3.SQL语言 DML数据操纵语言 增删改
DML语句:用于完成对数据表中数据的插入、删除、修改操作 一.表数据插入 插入数据语法: 步骤例: 1.声明数据库:use 数据库名; 2.删除操作:drop table if exists 表名; 3.创建数据库中的表:create table 表…...

Java中,如何去掉字符串中前面所有的0
大家好,我是三叔,这期主要给大家分享下在开发中使用的字符串的一些常见方法。 例如:00000000110,现在需要去掉前面所有补的0,得到110,相信大家在开发中肯定有遇到过类似的开发需求,如何做&…...
数组能开空间大小
奈何辰星无可奈_leetcode,中等难度,算法-CSDN博客 这个博客介绍的很好,可以参考下...
Python 数据类 - dataclass 的作用与不足
https://docs.python.org/zh-cn/3/library/dataclasses.html https://peps.python.org/pep-0526/ https://peps.python.org/pep-0557/ dataclass 简单示例 from dataclasses import dataclassdataclass class User:name: strage: intif __name__ __main__:response_json {na…...

【C++初阶】类与对象(一)
目录 1、初识面向对象思想2、类 struct2.1 C中的struct及使用 3、类 class3.1 类的定义3.2 类的访问限定符3.2.1 访问限定符是什么3.2.2 访问限定符的使用3.2.3 访问限定符的使用规范3.2.4 访问限定符与封装 3.3 类做声明和定义分离3.3.1 声明和定义分离3.3.2 在函数声明的地方…...
thinkPHP框架详解+部署
目录 什么是ThinkPHP: ThinkPHP的主要特性: 什么是ThinkPHP: ThinkPHP是一个快速、兼容而且简单的轻量级国产PHP开发框架,诞生于2006年初,由国内的技术爱好者创建,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和…...

Java拦截器(Interceptor)和过滤器(Filter)实例详解
一、Java过滤器和拦截器 1.1、过滤器(Filter) Filter过滤器,是Servlet(Server Applet)技术中的技术,开发人员可以通过Filter技术,管理web资源,可以对指定的一些行为进行拦截,例如URL级别的权限…...

通过热敏电阻计算温度(二)---ODrive实现分析
文章目录 通过热敏电阻计算温度(二)---ODrive实现分析测量原理图计算分析计算拟合的多项式系数根据多项式方程计算温度的函数温度计算调用函数 通过热敏电阻计算温度(二)—ODrive实现分析 ODrive计算热敏电阻的温度采用的时B值的…...
基于typescript+express实现一个简单的接口权限验证
package.json "scripts": {"start": "nodemon src/main.ts","start:a": "nodemon src/a.ts","build": "tsc","build:dev": "tsc src/main.ts"}, express服务器文件 import * as…...

yolov7改进优化之蒸馏(二)
续yolov7改进优化之蒸馏(一)-CSDN博客 上一篇已经基本写出来yolov7/v5蒸馏的整个过程,不过要真的训起来我们还需要进行一些修改。 Model修改 蒸馏需要对teacher和student网络的特征层进行loss计算,因此我们forward时要能够返回需…...
生产与作业管理(POM)的历史
1800年,惠特尼:零件标准化、质量管理。 1881年,泰勒:人员选拔、计划和时程安排、动作研究。管理与劳动分开。 - 使雇员与工作相适应。 - 提供适当的训练。 - 提供正确的工作方法和工具。 - 建立适当的激励机制促使工作得以完成。 …...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...