Netty实现通信框架

一、LengthFieldBasedFrameDecoder的参数解释

1、LengthFieldBasedFrameDecoder的构造方法参数

看下最多参数的构造方法

/*** Creates a new instance.** @param byteOrder*        the {@link ByteOrder} of the length field* @param maxFrameLength*        the maximum length of the frame.  If the length of the frame is*        greater than this value, {@link TooLongFrameException} will be*        thrown.* @param lengthFieldOffset*        the offset of the length field* @param lengthFieldLength*        the length of the length field* @param lengthAdjustment*        the compensation value to add to the value of the length field* @param initialBytesToStrip*        the number of first bytes to strip out from the decoded frame* @param failFast*        If <tt>true</tt>, a {@link TooLongFrameException} is thrown as*        soon as the decoder notices the length of the frame will exceed*        <tt>maxFrameLength</tt> regardless of whether the entire frame*        has been read.  If <tt>false</tt>, a {@link TooLongFrameException}*        is thrown after the entire frame that exceeds <tt>maxFrameLength</tt>*        has been read.*/public LengthFieldBasedFrameDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,int lengthAdjustment, int initialBytesToStrip, boolean failFast) {this.byteOrder = checkNotNull(byteOrder, "byteOrder");checkPositive(maxFrameLength, "maxFrameLength");checkPositiveOrZero(lengthFieldOffset, "lengthFieldOffset");checkPositiveOrZero(initialBytesToStrip, "initialBytesToStrip");if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {throw new IllegalArgumentException("maxFrameLength (" + maxFrameLength + ") " +"must be equal to or greater than " +"lengthFieldOffset (" + lengthFieldOffset + ") + " +"lengthFieldLength (" + lengthFieldLength + ").");}this.maxFrameLength = maxFrameLength;this.lengthFieldOffset = lengthFieldOffset;this.lengthFieldLength = lengthFieldLength;this.lengthAdjustment = lengthAdjustment;this.lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;this.initialBytesToStrip = initialBytesToStrip;this.failFast = failFast;}
  1. byteOrder:表示字节顺序,有两个常量分别是BIG_ENDIAN(多字节值的字节从最高有效到最低有效排序)和LITTLE_ENDIAN(多字节值的字节从最低有效到最高有效排序)
  2. maxFrameLength:包的最大长度,字面意思是最大帧的长度
  3. lengthFieldOffset:指的是长度域的偏移量,表示跳过指定个数字节之后的才是长度域
  4. lengthFieldLength:记录该帧数据长度的字段,也就是长度域本身的长度
  5. lengthAdjustment:长度的一个修正值,可正可负,Netty在读取到数据包的长度值N后, 认为接下来的N个字节都是需要读取的,但是根据实际情况,有可能需要增加N的值,也有可能需要减少N的值,具体增加多少,减少多少,写在这个参数里
  6. initialBytesToStrip:从数据帧中跳过的字节数,表示得到一个完整的数据包之后,扔掉这个数据包中多少字节数,才是后续业务实际需要的业务数据
  7. failFast:如果为 true,则表示读取到长度域,TA 的值的超过 maxFrameLength,就抛出 一个TooLongFrameException,而为false表示只有当真正读取完长度域的值表示的字节之后,才会抛出TooLongFrameException,默认情况下设置为 true,建议不要修改,否则可能会造成内存溢出

2、LengthFieldBasedFrameDecoder的构造方法参数对应数据包

在LengthFieldBasedFrameDecoder上也有对构造方法主要参数的解释,下面表示从解码前到解码后的字节参数分别对应

lengthFieldOffset   = 0
lengthFieldLength   = 2
lengthAdjustment    = 0
initialBytesToStrip = 0 (= do not strip header)BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
+--------+----------------+      +--------+----------------+
| Length | Actual Content |----->| Length | Actual Content |
| 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |
+--------+----------------+      +--------+----------------+
// lengthFieldOffset长度存在位置的偏移量是0
// lengthFieldLength,一个十六进制4位,000C是16位,所以是占两个字节是2
// lengthAdjustment,000C就是12,"HELLO, WORLD"正好是12个字节,所以是0
// initialBytesToStrip没有丢数据,所以是0
lengthFieldOffset   = 0
lengthFieldLength   = 2
lengthAdjustment    = 0
initialBytesToStrip = 2 (= the length of the Length field)BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)
+--------+----------------+      +----------------+
| Length | Actual Content |----->| Actual Content |
| 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |
+--------+----------------+      +----------------+
// lengthFieldOffset长度存在位置的偏移量是0
// lengthFieldLength,一个十六进制4位,000C是16位,所以是占两个字节是2
// lengthAdjustment,000C就是12,"HELLO, WORLD"正好是12个字节,所以是0
// initialBytesToStrip丢弃了长度两个字节,所以是2
lengthFieldOffset   =  0
lengthFieldLength   =  2
lengthAdjustment    = -2 (= the length of the Length field)
initialBytesToStrip =  0BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
+--------+----------------+      +--------+----------------+
| Length | Actual Content |----->| Length | Actual Content |
| 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |
+--------+----------------+      +--------+----------------+
// lengthFieldOffset长度存在位置的偏移量是0
// lengthFieldLength,一个十六进制4位,000C是16位,所以是占两个字节是2
// lengthAdjustment,000E就是14,而"HELLO, WORLD"是12个字节,少了两个字节所以是-2
// initialBytesToStrip丢弃了长度两个字节,所以是2
lengthFieldOffset   = 2 (= the length of Header 1)
lengthFieldLength   = 3
lengthAdjustment    = 0
initialBytesToStrip = 0BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
+----------+----------+----------------+      +----------+----------+----------------+
| Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content |
|  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |
+----------+----------+----------------+      +----------+----------+----------------+
// lengthFieldOffset,Header占用了两个字节,所以长度存在位置的偏移量是2
// lengthFieldLength,一个十六进制4位,00000C是24位,所以是占两个字节是3
// lengthAdjustment,00000C就是12,"HELLO, WORLD"正好是12个字节,所以是0
// initialBytesToStrip没有丢数据,所以是0
lengthFieldOffset   = 0
lengthFieldLength   = 3
lengthAdjustment    = 2 (= the length of Header 1)
initialBytesToStrip = 0BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
+----------+----------+----------------+      +----------+----------+----------------+
|  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content |
| 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |
+----------+----------+----------------+      +----------+----------+----------------+
// lengthFieldOffset长度存在位置的偏移量是0
// lengthFieldLength,一个十六进制4位,00000C是24位,所以是占两个字节是3
// lengthAdjustment,00000C就是12,"HELLO, WORLD"正好是12个字节,而Header多占用了两个字节,所以是2
// initialBytesToStrip没有丢数据,所以是0
lengthFieldOffset   = 1 (= the length of HDR1)
lengthFieldLength   = 2
lengthAdjustment    = 1 (= the length of HDR2)
initialBytesToStrip = 3 (= the length of HDR1 + LEN)BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
+------+--------+------+----------------+      +------+----------------+
| HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
| 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
+------+--------+------+----------------+      +------+----------------+
// lengthFieldOffset,HDR1占用了一个字节,所以长度存在位置的偏移量是1
// lengthFieldLength,一个十六进制4位,000C是16位,所以是占两个字节是2
// lengthAdjustment,000C就是12,"HELLO, WORLD"正好是12个字节,而HDR2多占用了一个字节,所以是1
// initialBytesToStrip丢弃了HDR1和Length共三个字节,所以是3
lengthFieldOffset   =  1
lengthFieldLength   =  2
lengthAdjustment    = -3 (= the length of HDR1 + LEN, negative)
initialBytesToStrip =  3BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
+------+--------+------+----------------+      +------+----------------+
| HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
| 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
+------+--------+------+----------------+      +------+----------------+
// lengthFieldOffset,HDR1占用了一个字节,所以长度存在位置的偏移量是1
// lengthFieldLength,一个十六进制4位,0010是16位,所以是占两个字节是2
// lengthAdjustment,0010就是16,"HELLO, WORLD"加上HDR2是13个字节,所以是-3
// initialBytesToStrip丢弃了HDR1和Length共三个字节,所以是3

EmbeddedChannel的单元测试暂时略过,后面有空再看

三、手写Netty大体结构

1、功能描述

基于 Netty 的 NIO 通信框架

提供消息的编解码框架,可以实现 POJO 的序列化和反序列化(【编解码】与【序列化】一块)

消息内容防篡改机制(就跟我们web开发的鉴权一样,在处理之前先校验一下内容合法性)

提供基于 IP 地址的白名单接入认证机制

断线重连机制

2、通信模型

(1)客户端发送应用握手请求消息,携带节点ID等有效身份认证信息;

(2)服务端对应用握手请求消息进行合法性校验,包括节点ID有效性校验、节点重复 登录校验和IP地址合法性校验,校验通过后,返回登录成功的应用握手应答消息;

(3)链路建立成功之后,客户端发送业务消息;

(4)链路成功之后,服务端发送心跳消息;

(5)链路建立成功之后,客户端发送心跳消息;

(6)链路建立成功之后,服务端发送业务消息;

(7)服务端退出时,服务端关闭连接,客户端感知对方关闭连接后,被动关闭客户端连接。

协议通信双方链路建立成功之后,双方可以进行全双工通信,无 论客户端还是服务端,都可以主动发送请求消息给对方,通信方式可以是 TWO WAY 或者 ONE WAY。双方之间的心跳采用 Ping-Pong 机制,当链路处于空闲状态时,客户端主动发送 Ping 消息给服务端,服务端接收到 Ping 消息后发送应答消息 Pong 给客户端,如果客户端连 续发送 N 条 Ping 消息都没有接收到服务端返回的 Pong 消息,说明链路已经挂死或者对方处 于异常状态,客户端主动关闭连接,间隔周期 T 后发起重连操作,直到重连成功。

3、消息体定义

消息定义包含两部分:

消息头;消息体。

在消息的定义上,因为是同步处理模式,不考虑应答消息需要填入请求消息 ID,所以 消息头中只有一个消息的 ID。如果要支持异步模式,则请求消息头和应答消息头最好分开 设计,应答消息头中除了包括本消息的 ID 外,还应该包括请求消息 ID,以方便请求消息的 发送方根据请求消息 ID 做对应的业务处理。

消息体则支持 Java 对象类型的消息内容。

Netty 消息定义表

名称

类型

长度

描述

header

Header

变长

消息头定义

body

Object

变长

消息的内容

消息头定义(Header)

名称

类型

长度

描述

md5

String

变长

消息体摘要,缺省 MD5 摘要

msgId

Long

64

消息的ID

Type

Byte

8

0:业务请求消息 1:业务响应消息 2:业务one way消息

3:握手请求消息 4:握手应答消息 5:心跳请求消息 6:心跳应答消息

Priority

Byte

8

消息优先级:0~255

Attachment

Map<String, Object>

变长

可选字段,用于扩展消息头

4、链路的建立

客户端的说明如下:如果 A 节点需要调用 B 节点的服务,但是 A 和 B 之间还没有建立 物理链路,则有调用方主动发起连接,此时,调用方为客户端,被调用方为服务端。 考虑到安全,链路建立需要通过基于 Ip 地址或者号段的黑白名单安全认证机制,作为 样例,本协议使用基于 IP 地址的安全认证,如果有多个 Ip,通过逗号进行分割。在实际的 商用项目中,安全认证机制会更加严格,例如通过密钥对用户名和密码进行安全认证。

客户端与服务端链路建立成功之后,由客户端发送业务握手请求的认证消息,服务端接 收到客户端的握手请求消息之后,如果 IP 校验通过,返回握手成功应答消息给客户端,应 用层链路建立成功。握手应答消息中消息体为 byte 类型的结果,0:认证成功;-1 认证失败; 服务端关闭连接。

链路建立成功之后,客户端和服务端就可以互相发送业务消息了,在客户端和服务端的 消息通信过程中,业务消息体的内容需要通过 MD5 进行摘要防篡改。

5、可靠性设计

1)、心跳机制

在凌晨等业务低谷时段,如果发生网络闪断、连接被 Hang 住等问题时,由于没有业务 消息,应用程序很难发现。到了白天业务高峰期时,会发生大量的网络通信失败,严重的会 导致一段时间进程内无法处理业务消息。为了解决这个问题,在网络空闲时采用心跳机制来 检测链路的互通性,一旦发现网络故障,立即关闭链路,主动重连。

当读或者写心跳消息发生 I/O 异常的时候,说明已经中断,此时需要立即关闭连接,如 果是客户端,需要重新发起连接。如果是服务端,需要清空缓存的半包信息,等到客户端重连。

空闲的连接和超时

检测空闲连接以及超时对于及时释放资源来说是至关重要的。由于这是一项常见的任务, Netty 特地为它提供了几个 ChannelHandler 实现。

IdleStateHandler 当连接空闲时间太长时,将会触发一个 IdleStateEvent 事件。然后,可 以通过在 ChannelInboundHandler 中重写 userEventTriggered()方法来处理该 IdleStateEvent 事件。

ReadTimeoutHandler 如果在指定的时间间隔内没有收到任何的入站数据,则抛出一个 ReadTimeoutException 并关闭对应的 Channel。可以通过重写你的 ChannelHandler 中的 exceptionCaught()方法来检测该 Read-TimeoutException。

2)、重连机制

如果链路中断,等到 INTEVAL 时间后,由客户端发起重连操作,如果重连失败,间隔周 期 INTERVAL 后再次发起重连,直到重连成功。

为了保持服务端能够有充足的时间释放句柄资源,在首次断连时客户端需要等待 INTERVAL 时间之后再发起重连,而不是失败后立即重连。

为了保证句柄资源能够及时释放,无论什么场景下重连失败,客户端必须保证自身的资 源被及时释放,包括但不现居 SocketChannel、Socket 等。

重连失败后,可以打印异常堆栈信息,方便后续的问题定位。

3)、重复登录保护

当客户端握手成功之后,在链路处于正常状态下,不允许客户端重复登录,以防止客户 端在异常状态下反复重连导致句柄资源被耗尽。

服务端接收到客户端的握手请求消息之后,对 IP 地址进行合法性校验,如果校验成功, 在缓存的地址表中查看客户端是否已经登录,如果登录,则拒绝重复登录,同时关闭 TCP 链路,并在服务端的日志中打印握手失败的原因。

客户端接收到握手失败的应答消息之后,关闭客户端的 TCP 连接,等待 INTERVAL 时间 之后,再次发起 TCP 连接,直到认证成功。

6、实现

Handler示意图如下:

其中认证申请和认证检查可以在完成后移除。

7、前期准备

定义了消息有关的实体类,为了防篡改,消息体需要进行摘要, vo 包下提供了 EncryptUtils 类,可以对消息体进行摘要,目前支持 MD5、SHA-1 和 SHA-256 这 三种,缺省为 MD5,其中 MD5 额外提供了加盐摘要。 同时定义了有关序列化和反序列化的工具类和Handler, 本项目中序列化使用了 Kryo 序列化框架。

8、服务端

服务端中 NettyServe 类是服务端的主入口,内部使用了 ServerInit 类进行 Handler 的安装。

最先安装的当然是解决粘包和半包问题的 Handler,很自然,这里应该用 LengthFieldBasedFrameDecoder 进行解码,为了实现方便,我们也没有在消息报文中附带消 息的长度,由 Netty 帮我们在消息报文的最开始增加长度,所以编码器选择了 LengthFieldPrepender。

接下来,自然就是序列化和反序列化,直接使用我们在 kryocodec 下已经准备好的 KryoDecoder 和 KryoEncoder 即可。

服务端需要进行登录检查、心跳应答、业务处理,对应着三个 handler,于是我们分别 安装了 LoginAuthRespHandler、HeartBeatRespHandler、ServerBusiHandler。

为了节约网络和服务器资源,如果客户端长久没有发送业务和心跳报文,我们认为客户 端出现了问题,需要关闭这个连接,我们引入 Netty 的 ReadTimeoutHandler,当一定周期内 (默认值 50s,我们设定为 15s)没有读取到对方任何消息时,会触发一个 ReadTimeouttException,这时我们检测到这个异常,需要主动关闭链路,并清除客户端登录 缓存信息,等待客户端重连。

9、客户端

客户端的主类是 NettyClient,并对外提供一个方法 send,供业务使用内部使用了 ClientInit 类进行 Handler 的安装。

最先安装的当然是解决粘包和半包问题的 Handler,同样这里应该用 LengthFieldBasedFrameDecoder 进行解码,编码器选择了 LengthFieldPrepender。

接下来,自然就是序列化和反序列化,依然使用 KryoDecoder 和 KryoEncoder 即可。

客户端需要主动发出认证请求和心跳请求。

在 TCP 三次握手,链路建立后,客户端需要进行应用层的握手认证,才能使用服务,这 个功能由 LoginAuthReqHandler 负责,而这个 Handler 在认证通过后,其实就没用了,所以 在认证通过后,可以将这个 LoginAuthReqHandler 移除(其实服务端的认证应答 LoginAuthRespHandler 同样也可以移除)。

对于发出心跳请求有两种实现方式,一是定时发出,本框架的第一个版本就是这种实现 方式,但是这种方式其实有浪费的情况,因为如果客户端和服务器正在正常业务通信,其实 是没有必要发送心跳的;所以第二种方式就是,当链路写空闲时,为了维持通道,避免服务 器关闭链接,发出心跳请求。为了实现这一点,我们首先在整个 pipeline 的最前面安装一个 CheckWriteIdleHandler进行写空闲检测,空闲时间定位8S,取服务器读空闲时间15S的一半, 然后再安装一个 HearBeatReqHandler,因为写空闲会触发一个 FIRST_WRITER_IDLE_STATE_EVENT 入站事件,我们在 HearBeatReqHandler 的 userEventTriggered 方法中捕捉这个事件,并发出心跳请求报文。

考虑到在我们的实现中并没有双向心跳(即是客户端向服务器发送心跳请求,是服务器 也向客户端发送心跳请求),客户端这边同样需要检测服务器是否存活,所以我们客户端这 边安装了一个 ReadTimeoutHandler,捕捉 ReadTimeoutException 后提示调用者,并关闭通信 链路,触发重连机制。

为了测试,单独建立一个 BusiClient,模拟业务方的调用。因为客户端的网络通信代 码是在一个线程中单独启动的,为了协调主线程和通信线程的工作,我们引入了线程中的等 待通知机制。

Netty对于我来说过于复杂,后面再深究细节吧

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

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

相关文章

Redis快速入门(基础篇)

简介&#xff1a; 是一个高性能的 key-value数据库。 存在内存中 与其他 key-value 缓存产品有以下三个特点&#xff1a; Redis支持数据的持久化&#xff0c;可以将内存中的数据保持在磁盘中&#xff0c;重启的时候可以再次加载进行使用。 Redis不仅仅支持简单的key-value类…

nodejs+vue+python+PHP+微信小程序-安卓- 电影在线订票系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

uniapp: 实现pdf预览功能

目录 第一章 实现效果 第二章 了解并解决需求 2.1 了解需求 2.2 解决需求 2.2.1 方法一 2.2.2 方法二 第三章 资源下载 第一章 实现效果 第二章 了解并解决需求 2.1 了解需求 前端需要利用后端传的pdf临时路径实现H5端以及app端的pdf预览首先我们别像pc端一样&#…

分类预测 | Matlab实现PSO-BiLSTM粒子群算法优化双向长短期记忆神经网络的数据多输入分类预测

分类预测 | Matlab实现PSO-BiLSTM粒子群算法优化双向长短期记忆神经网络的数据多输入分类预测 目录 分类预测 | Matlab实现PSO-BiLSTM粒子群算法优化双向长短期记忆神经网络的数据多输入分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现PSO-BiLSTM粒子…

详解IP安全:IPSec协议簇 | AH协议 | ESP协议 | IKE协议

目录 IP安全概述 IPSec协议簇 IPSec的实现方式 AH&#xff08;Authentication Header&#xff0c;认证头&#xff09; ESP&#xff08;Encapsulating Security Payload&#xff0c;封装安全载荷&#xff09; IKE&#xff08;Internet Key Exchange&#xff0c;因特网密钥…

【java:牛客每日三十题总结-7】

java:牛客每日三十题总结 总结如下 总结如下 执行流程如下&#xff1a;创建HttpServlet时需要覆盖doGet()和doPost请求 2. request相关知识 request.getParameter()方法传递的数据&#xff0c;会从Web客户端传到Web服务器端&#xff0c;代表HTTP请求数据&#xff1b;request.…

【电路笔记】-戴维南定理(Thevenin‘s Theorem)

戴维南定理&#xff08;Thevenin’s Theorem&#xff09; 文章目录 戴维南定理&#xff08;Thevenins Theorem&#xff09;1、概述与定义2、戴维南模型确定3、一些线性电路的戴维南模型3.1 单电压源3.2 单电流源3.3 多电流/电压源 4、结论 在本文中&#xff0c;我们将介绍一种强…

开发vue3 UI组件库,并且发布到NPM

目录 1.创建vue3工程 2.创建package文件 3.编写组件&#xff0c;并且导出 4.编写package.json 5.npm账号注册登录并发布 6.从npm安装使用 7.注意事项 1.创建vue3工程 &#xff08;1&#xff09;初始化Vue项目 cnpm create vite &#xff08;2&#xff09;进入文件夹…

设计模式之原型模式(Prototype)

原型模式 如果已经有一个对象了&#xff0c;你想创建一个对象&#xff0c;而且对象里面的属性和已经存在的对象的属性差不多&#xff0c;就可以使用clone方法 克隆一个出来 实现原型模式需要实现标记型接口Cloneable -->标记型接口 : 里面没有需要实现的方法(空接口) 一般…

数据运营基础:用户场景营销

一、概述 场景营销模型是顶层模型&#xff0c;是站在用户经营和用户场景角度来制定经营策略的模型。本质上&#xff0c;场景营销模型是在用户使用产品的每个细分场景中通过分析用户需求整合功能、实体和体验等为用户提供服务的模型。 二、场景的起源和特点 数据运营体系在发展…

RHCE第四次作业

题目 架设一台NFS服务器&#xff0c;并按照以下要求配置 1、开放/nfs/shared目录&#xff0c;供所有用户查询资料 2、开放/nfs/upload目录&#xff0c;为192.168.100.0/24网段主机可以上传目录&#xff0c; 并将所有用户及所属的组映射为nfs-upload,其UID和GID均为210 3、将…

ChatGPT重磅升级 奢侈品VERTU推出双模型AI手机

2023年11月7日,OpenAI举办了首届开发者大会,CEO Sam Altman(山姆奥尔特曼)展示了号称“史上最强”AI的GPT-4 Turbo。它支持长达约10万汉字的输入,具备前所未有的长文本处理能力,使更复杂的互动成为可能。此外,GPT-4 Turbo还引入了跨模态API支持,可以同时处理图片、视频和声音,从…

Three.js——基于原生WebGL封装运行的三维引擎

文章目录 前言一、什么是WebGL&#xff1f;二、Three.js 特性 前言 Three.js中文官网 Three.js是基于原生WebGL封装运行的三维引擎&#xff0c;在所有WebGL引擎中&#xff0c;Three.js是国内文资料最多、使用最广泛的三维引擎。既然Threejs是一款WebGL三维引擎&#xff0c;那么…

基于YOLOv8的输电线路异物识别算法应用

基于 YOLOv8 的输电线路异物识别算法应用 输电线路作为电力系统的重要一环&#xff0c;保证其安全稳定运行是十分必要的。由于长期暴露于室外&#xff0c;线路所面临的不安全因素繁多&#xff0c;异物入侵便是其中之一。异物可能会引起线路短路甚至诱发火灾&#xff0c;因此要加…

Unity中雾效的实现方法二

文章目录 前言一、声明雾效所需要的内置变体二、在 v2f 中声明顶点传入片段中的雾效插值器三、 在顶点着色器中计算雾效采样四、在片元着色器中进行雾效颜色混合在这里插入图片描述 五、最终效果 前言 Unity中雾效的实现方法二&#xff0c;使用 Unity 自带的方法实现&#xff…

如何将系统盘MBR转GPT?无损教程分享!

什么是MBR和GPT&#xff1f; MBR和GPT是磁盘的两种分区形式&#xff1a;MBR&#xff08;主引导记录&#xff09;和GPT&#xff08;GUID分区表&#xff09;。 新硬盘不能直接用来保存数据。使用前应将其初始化为MBR或GPT分区形式。但是&#xff0c;如果您在MBR时需…

关于我在配置zookeeper出现,启动成功,进程存在,但是查看状态却没有出现Mode:xxxxx的问题和我的解决方案

在我输入:zkServer.sh status 之后出现报错码. 报错码&#xff1a; ZooKeeper JMX enabled by default Using config: /opt/software/zookeeper/bin/../conf/zoo.cfgClient port found: 2181. Client address: localhost. Error contacting service. It is probably not runni…

基于SpringBoot的SSMP整合案例(开启日志与分页查询条件查询功能实现)

开启事务 导入Mybatis-Plus框架后&#xff0c;我们可以使用Mybatis-Plus自带的事务&#xff0c;只需要在配置文件中配置即可 使用配置方式开启日志&#xff0c;设置日志输出方式为标准输出mybatis-plus:global-config:db-config:table-prefix: tb_id-type: autoconfiguration:…

【自动化测试】Pytest框架 —— 跳过测试和失败重试

1、Pytest跳过测试用例 自动化测试执行过程中&#xff0c;我们常常出现这种情况&#xff1a;因为功能阻塞&#xff0c;未实现或者环境有问题等等原因&#xff0c;一些用例执行不了&#xff0c; 如果我们注释掉或删除掉这些测试用例&#xff0c;后面可能还要进行恢复操作&#…

ObRegisterCallbacks()返回0xC0000022(拒绝访问)解决办法

在开发测试环境下&#xff0c;没有打签名的驱动调用ObRegisterCallbacks会返回0xC0000022&#xff08;拒绝访问&#xff09;的错误码。这是由于该函数内部会进行驱动的签名校验。 具体位置在 因此可以用以下代码绕过该检查 // 以下代码放在DriverEntry中 ULONG_PTR pDrvSectio…