分布式系统通信解决方案:Netty Marshalling 全面解析
分布式系统通信解决方案:Netty Marshalling 全面解析
一、引言
在现代网络编程中,Netty 作为一款高性能、异步事件驱动的网络应用框架,因其强大的功能和灵活的扩展性,备受开发者青睐。Netty 广泛应用于分布式系统、RPC 框架以及高并发场景中,是构建高效网络服务的利器。
在我之前的博客《构建高性能网络服务:从 Socket 原理到 Netty 应用实践》中,详细探讨了从传统的 Socket 通信原理到 Netty 应用的基本实践内容,包括:
- Socket 编程的局限性:传统阻塞式 IO 在高并发场景中的瓶颈。
- NIO 的优势与挑战:通过多路复用和非阻塞 IO 提升效率,但编程复杂度较高。
- Netty 的解决方案:屏蔽底层复杂性,提供直观 API,支持高性能网络通信。
在实际开发中,数据的编解码 是网络传输中的关键环节。Netty 提供了强大的工具和机制,如 ByteBuf
、编码解码器,以及更高级的序列化方案,极大简化了开发工作。
本文将重点介绍 Netty 的高级编解码技术之一: Marshalling。这是基于 JBoss Marshalling 项目实现的一种高效序列化机制。通过本文,你将学习如何利用 Marshalling 实现 Java 对象的高效传输,同时结合其他编解码工具构建稳定、高性能的网络应用。
如果你还不熟悉 Netty 的基础知识,可以先参考我的博客《构建高性能网络服务:从 Socket 原理到 Netty 应用实践》,获取必要的背景知识。
二、什么是 Marshalling
Marshalling 是 Netty 提供的一种序列化机制,基于 JBoss 的 Marshalling 项目实现。它用于将 Java 对象转换为字节流以便传输,以及将字节流反序列化回 Java 对象。
相比于 Java 自带的 ObjectInputStream
和 ObjectOutputStream
,Marshalling 具有以下优势:
- 性能更优:序列化和反序列化的效率更高。
- 可扩展性强:支持自定义序列化策略。
- 更易集成:与 Netty 无缝集成,提供专用的编解码器。
三、Marshalling 的核心组件
在 Netty 中,Marshalling 的主要职责是高效地完成 Java 对象与字节流之间的转换,适用于需要序列化复杂对象的网络传输场景。Netty 提供了两大核心组件与其相关联:
1. MarshallingEncoder
功能:
负责将 Java 对象序列化为字节流并写入到 ByteBuf
中,便于通过网络传输。它是一个 MessageToByteEncoder
的实现类,专为对象序列化设计。
关键特性:
- 高效性:通过底层优化的序列化机制,提升对象转换的性能。
- 流式操作:可以将复杂的对象轻松转换为网络流中的字节表示。
典型使用场景:
- 在客户端或服务端发送包含复杂对象的数据包时,利用
MarshallingEncoder
将对象序列化为可传输的字节流。 - 适用于传输包含嵌套结构的对象,如 JSON、XML 或复杂 POJO。
2. MarshallingDecoder
功能:
负责将接收到的字节流反序列化为 Java 对象。它是一个 ByteToMessageDecoder
的实现类,能够从 ByteBuf
中读取字节流并还原为原始对象。
关键特性:
- 自动拆包:根据字节流解析完整对象,避免由于 TCP 拆包或黏包导致的数据不完整问题。
- 灵活性:可结合其他解码器(如
DelimiterBasedFrameDecoder
或LengthFieldBasedFrameDecoder
)实现定制化数据解析。
典型使用场景:
- 在服务端或客户端接收包含序列化对象的数据包时,使用
MarshallingDecoder
将其反序列化为具体的 Java 对象。 - 适用于需要还原复杂对象的场景,如 RPC 调用、分布式系统通信等。
3. Marshalling 编解码器的协作工作流程
为了让 MarshallingEncoder
和 MarshallingDecoder
正常协作工作,我们需要将它们添加到 Netty 的 ChannelPipeline 中。整个工作流程如下:
- 发送端:将 Java 对象交由
MarshallingEncoder
序列化为字节流,再通过其他编码器(如长度字段或分隔符解码器)添加传输标识。 - 接收端:接收到字节流后,
MarshallingDecoder
将其反序列化为 Java 对象。
示意图:
lua复制编辑+-----------------------+ +-----------------------+
| MarshallingEncoder | -----> | 网络传输(字节流) | -----> | MarshallingDecoder |
+-----------------------+ +-----------------------+(对象序列化) (传输数据) (对象反序列化)
4. 配合定制化工具的使用
MarshallingEncoder 和 MarshallingDecoder 可与其他 Netty 编解码器(如 DelimiterBasedFrameDecoder
或 LengthFieldBasedFrameDecoder
)结合使用,以解决 TCP 拆包黏包问题并优化网络传输。具体例子将在后续示例中详细展示。
通过对 MarshallingEncoder 和 MarshallingDecoder 的合理应用,我们可以高效、安全地实现对象的序列化与反序列化操作,为构建复杂的网络传输方案提供坚实基础。
四、快速入门
场景描述
我们实现一个简单的库存管理系统,服务端接收客户端发送的库存变动请求,并返回处理结果。每次请求和响应都包含复杂的 Java 对象,例如 InventoryRequest
和 InventoryResponse
。
这些对象包含多层嵌套结构和集合数据,适合展示 Marshalling 的序列化能力。
4.1 引入依赖
在 Maven 项目中添加以下依赖:
<dependency><groupId>org.jboss.marshalling</groupId><artifactId>jboss-marshalling</artifactId><version>2.0.12.Final</version>
</dependency>
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.97.Final</version>
</dependency>
4.2 初始化 Marshalling 编解码器
示例代码
1. 创建复杂的传输对象
定义两个对象:InventoryRequest
和 InventoryResponse
,并实现 Serializable
接口以支持序列化。
创建一个工具类 MarshallingCodeCFactory
来初始化编解码器:
import java.io.Serializable;
import java.util.List;// 库存请求对象
public class InventoryRequest implements Serializable {private static final long serialVersionUID = 1L;private String productId; // 产品 IDprivate int quantity; // 请求的数量private String operation; // 操作类型:增加(add) 或 减少(remove)// 构造方法、getter 和 setterpublic InventoryRequest(String productId, int quantity, String operation) {this.productId = productId;this.quantity = quantity;this.operation = operation;}@Overridepublic String toString() {return "InventoryRequest{" +"productId='" + productId + '\'' +", quantity=" + quantity +", operation='" + operation + '\'' +'}';}
}
// 库存响应对象
public class InventoryResponse implements Serializable {private static final long serialVersionUID = 1L;private String productId; // 产品 IDprivate boolean success; // 是否成功private String message; // 响应消息// 构造方法、getter 和 setterpublic InventoryResponse(String productId, boolean success, String message) {this.productId = productId;this.success = success;this.message = message;}@Overridepublic String toString() {return "InventoryResponse{" +"productId='" + productId + '\'' +", success=" + success +", message='" + message + '\'' +'}';}
}
4.3 服务端代码
2. 服务端实现
服务端处理逻辑(Handler):
服务端接收 InventoryRequest
对象,解析后根据操作类型更新库存,并返回 InventoryResponse
对象。
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;public class InventoryServerHandler extends SimpleChannelInboundHandler<InventoryRequest> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, InventoryRequest request) {System.out.println("收到客户端请求:" + request);// 模拟库存处理逻辑boolean success = "add".equals(request.getOperation()) || "remove".equals(request.getOperation());String message = success ? "操作成功!" : "操作失败,未知操作类型:" + request.getOperation();// 构造响应对象InventoryResponse response = new InventoryResponse(request.getProductId(), success, message);// 发送响应ctx.writeAndFlush(response);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}
服务端启动类
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class InventoryServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());ch.pipeline().addLast(new InventoryServerHandler());}});ChannelFuture future = bootstrap.bind(8080).sync();System.out.println("服务端启动,端口:8080");future.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
4.4 客户端代码
客户端发送逻辑(Handler):
客户端发送一个 InventoryRequest
,并接收服务端返回的 InventoryResponse
。
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;public class InventoryClientHandler extends SimpleChannelInboundHandler<InventoryResponse> {@Overridepublic void channelActive(ChannelHandlerContext ctx) {// 构造一个请求对象并发送InventoryRequest request = new InventoryRequest("P12345", 10, "add");ctx.writeAndFlush(request);System.out.println("客户端已发送请求:" + request);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, InventoryResponse response) {// 打印服务端响应System.out.println("收到服务端响应:" + response);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}
客户端启动类
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class InventoryClient {public static void main(String[] args) throws Exception {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());ch.pipeline().addLast(new InventoryClientHandler());}});ChannelFuture future = bootstrap.connect("localhost", 8080).sync();future.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}
}
五、TCP 拆包与黏包问题
虽然 Marshalling 解决了数据的序列化与反序列化问题,但在基于 TCP 的网络通信中,可能会遇到拆包和黏包现象。
5.1 什么是拆包和黏包?
拆包和黏包是 TCP 协议的常见问题,其本质原因在于 TCP 是一种流式传输协议,不保证消息边界:
- 拆包: 发送的数据包过大,接收端一次读取不完整,导致数据被分割成多个包。
- 黏包: 发送的数据包较小,多个消息被拼接在一个 TCP 包中,接收端无法区分边界。
在我之前的博客《构建高性能网络服务:从 Socket 原理到 Netty 应用实践》中,已详细分析了 TCP 拆包与黏包的成因和常见处理方法,建议读者参考相关内容。
5.2 Netty 提供的解决方案
针对 TCP 拆包和黏包问题,Netty 提供了以下通用解码器,可以与 Marshalling 协作使用:
-
定长帧解码器:
使用FixedLengthFrameDecoder
,通过指定消息的固定长度,强制按照长度切分数据包。适用场景: 消息格式固定、长度已知的简单协议。
ch.pipeline().addLast(new FixedLengthFrameDecoder(1024)); // 每次读取固定 1024 字节
-
特殊分隔符解码器:
使用DelimiterBasedFrameDecoder
,指定特殊字符(如\n
或自定义符号)作为消息边界。适用场景: 消息中含有明确的结束符,如基于文本的协议。
ByteBuf delimiter = Unpooled.copiedBuffer("_$".getBytes()); ch.pipeline().addLast(new DelimiterBasedFrameDecoder(8192, delimiter));
-
长度字段解码器:
使用LengthFieldBasedFrameDecoder
,通过消息头携带的长度信息解析数据包。适用场景: 自定义协议中包含消息长度字段。
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536, // 最大帧长度0, // 长度字段的偏移4, // 长度字段的字节数0, // 长度调整值4 // 跳过的初始字节数 ));
5.3 Marshalling 的协作
在基于 Marshalling 的对象传输中,消息往往是复杂 Java 对象,直接使用 MarshallingDecoder
和 MarshallingEncoder
可能会因为拆包和黏包问题导致数据不完整或出错。为避免此问题,需结合上述解码器解决。
典型组合示例:
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4, 0, 4)); // 处理拆包黏包
ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder()); // 反序列化
ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder()); // 序列化
小结:
- 通过明确消息边界,可以有效避免拆包和黏包问题。
- 在本博客的复杂对象传输示例中,
Marshalling
通常结合LengthFieldBasedFrameDecoder
使用,确保消息的完整性和可靠性。
六、总结
在本篇博客中,我们探讨了 Netty 提供的高级编解码工具 Marshalling,并结合实际示例展示了如何通过它高效地实现 Java 对象的序列化与反序列化。以下是关键点回顾:
1. 核心内容回顾
- Marshalling 的作用:
- 提供了一种高效的序列化机制,适用于 Java 对象的网络传输。
- 基于 JBoss Marshalling 项目实现,性能优于传统的
ObjectInputStream
和ObjectOutputStream
。
- Marshalling 的核心组件:
MarshallingEncoder
:负责对象序列化为字节流。MarshallingDecoder
:负责字节流反序列化为 Java 对象。
- 典型使用场景:
- 复杂 Java 对象的网络传输,如分布式系统通信、RPC 框架中数据的序列化与反序列化。
2. 实践中的重要建议
- 结合解码器处理拆包与黏包问题:
在 TCP 网络传输中,结合 Netty 提供的LengthFieldBasedFrameDecoder
等解码器使用,可以有效避免因拆包或黏包导致的传输错误。 - 性能优化:
- 针对性能需求较高的场景,可通过自定义序列化器(如实现
Externalizer
接口)优化特定对象的序列化效率。 - 对序列化后的数据进行压缩,进一步减少传输开销。
- 针对性能需求较高的场景,可通过自定义序列化器(如实现
- 安全性与兼容性:
- 使用
serialVersionUID
确保对象序列化的向后兼容。 - 在反序列化过程中,限制可加载的类,防范反序列化漏洞。
- 使用
3. 学习与拓展
要全面掌握 Marshalling,可以从以下方面进一步学习和实践:
- 深入理解序列化机制: 研究 Marshalling 的内部实现,了解其性能优势和扩展能力。
- 对比其他序列化方案: 例如 Protobuf、Kryo 等,分析其优劣与适用场景。
- 实际项目应用: 在微服务、分布式系统或 RPC 框架中尝试集成和优化 Marshalling。
相关文章:
分布式系统通信解决方案:Netty Marshalling 全面解析
分布式系统通信解决方案:Netty Marshalling 全面解析 一、引言 在现代网络编程中,Netty 作为一款高性能、异步事件驱动的网络应用框架,因其强大的功能和灵活的扩展性,备受开发者青睐。Netty 广泛应用于分布式系统、RPC 框架以及…...
如何轻松实现域名指向服务器
在互联网时代,域名指向服务器是网站上线的关键步骤。域名是用户访问网站的入口,而服务器则是存储网站数据的地方。将域名正确指向服务器,能让用户顺利访问网站内容。虽然这个过程对新手来说可能有些陌生,但只要掌握正确的方法&…...
Java Web开发高级——单元测试与集成测试
测试是软件开发的重要环节,确保代码质量和功能的正确性。在Spring Boot项目中,单元测试和集成测试是常用的两种测试类型: 单元测试:测试单个模块(如类或方法)是否按预期工作。集成测试:测试多个…...
第九篇: 3.10. 【watchEffect】实现监听,立即执行函数
官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。 watch对比watchEffect 都能监听响应式数据的变化,不同的是监听数据变化的方式不同 watch:要明确指出监视的数据 watchEffect&#x…...
【C++】模板(进阶)
本篇我们来介绍更多关于C模板的知识。模板初阶移步至:【C】模板(初阶) 1.非类型模板参数 1.1 非类型模板参数介绍 模板参数可以是类型形参,也可以是非类型形参。类型形参就是我们目前接触到的一些模板参数。 //类型模板参数 …...
有了TiDB,是否还需要“散装”大数据组件?
有了TiDB,是否还需要“散装”大数据组件? 最近和同事们讨论一个问题:在大数据应用日益增多的今天,如果使用了TiDB这样的一体化数据库,还需要使用那些传统的大数据组件(比如Hadoop、Spark等)吗&…...
OSCP - Proving Grounds - BullyBox
主要知识点 如果发现有域名,则可以加入/etc/hosts后重新执行nmap,nikto等扫描dirsearch的时候可以使用完整一些的字典文件,避免漏掉信息.git dump 具体步骤 执行nmap 扫描,发现 80和22端口开放,访问后发现被重定向到 bullybox.local Star…...
升级《在线写python》小程序的分享功能。昨天忘了...
小程序是使用uniapp写的,忘了开启分享功能,导致它现在是这样的。 挺不方便的,所以需要开启分享权限, 由于我这个没有其他需要隐藏的私密页面,所以事直接全局开启就行 在App.vue文件里的onShow里开启即可。加入如下代…...
Spingboot整合Netty,简单示例
Netty介绍在文章末尾 Netty介绍 项目背景 传统socket通信,有需要自身管理整个状态,业务繁杂等问题。 pom.xml <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.117.F…...
逆波兰表达式求值(力扣150)
这道题也是一道经典的栈应用题。为什么这样说呢?我们可以发现,当我们遍历到运算符号的时候,我们就需要操控这个运算符之前的两个相邻的数。这里相邻数不仅仅指最初数组里相邻的数,在进行了运算之后,得到的结果与后面的…...
Linux面试题
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…...
如何判断以太坊地址类型?
如何判断以太坊地址类型? 一、账户类型解释 2.1 以太坊外部账户(Externally Owned Account,EOA) 外部账户(EOA)是由私钥控制的账户,在以太坊网络中用来发送交易和执行其他操作。EOA 不是智能…...
有限元分析学习——Anasys Workbanch第一阶段笔记(14)静定与超静定问题、约束类型介绍、简支梁挠度求解和自定义材料库建立
目录 0 序言 1 静定与超静定问题 2 Workbranch中Supports介绍 3 简支梁挠度的有限元求解 4 自定义材料库建立 0 序言 静定与超静定问题、约束类型介绍、简支梁挠度求解和自定义材料库建立(内容对应视频22到24课)。 1 静定与超静定问题 在有限元分析中,不同的…...
为医院量身定制做“旧改”| 全视通物联网智慧病房
随着经济工作会议、卫生健康工作会议、“经济高质量发展成效”系列新闻发布会的依次召开,强基工程、三明医改、儿科和精神卫生服务年、中医药传承创新发展、促进生育、养老服务改革、病房改造提升行动...等关键词正成为新的热点,2025年卫生健康工作面临一…...
Java面试专题——面向对象
面向过程和面向对象的区别 面向过程:当事件比较简单的时候,利用面向过程,注重的是事件的具体的步骤/过程,注重的是过程中的具体的行为,以函数为最小单位,考虑怎么做。 面向对象:注重找“参与者…...
PHP异步非阻塞MySQL客户端连接池
文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons:JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram,自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 ? 5 IDEA必装的插件&…...
【Linux 源码】内核态到用户态
文章目录 1. 由来2. 流程图3. 中断3.1 概念3.2 8259A芯片3.4 中断时的栈处理3.4.1 相同特权级3.4.2 不同特权级 3.5 中断流程3.6 定位中断程序3.7 中断流程步骤总结 4. 源码4.1 move_to_user_mode4.2 0号进程4.3 TSS和LDT在GDT表排布4.4 ldt中的0x17栈段 5. 总结 1. 由来 首…...
goland map学习-实践使用练习:判断存在及遍历
对于数据: type Person struct {Address stringAge intJob stringName string }type People map[string]Personvar per People{"1": Person{Address: "1",Age: 1,Job: "1",Name: "1",},"2&quo…...
【威联通】FTP服务提示:服务器回应不可路由的地址。被动模式失败。
FTP服务器提示:服务器回应不可路由的地址。被动模式失败。 问题原因网络结构安全管理配置服务器配置网关 问题 FTP服务器提示:服务器回应不可路由的地址…...
两份PDF文档,如何比对差异,快速定位不同之处?
PDF文档比对是通过专门的工具或软件,自动检测两个PDF文件之间的差异,并以可视化的方式展示出来。这些差异可能包括文本内容的修改、图像的变化、表格数据的调整、格式的改变等。比对工具通常会标记出新增、删除或修改的部分,帮助用户快速定位…...
Vue.js 组件之间的通信模式
Vue.js 组件之间的通信模式 组件之间的通信模式 在 Vue.js 中,组件之间的通信是构建复杂应用的关键。根据组件之间的关系和需求,Vue 提供了多种通信方式。本文介绍了常见的通信模式及其详细示例。 一、父子组件通信 1. 父组件向子组件传递数据&#…...
【Linux 重装】Ubuntu 启动盘 U盘无法被识别,如何处理?
背景 U盘烧录了 Ubuntu 系统作为启动盘,再次插入电脑后无法被识别 解决方案(Mac 适用) (1)查找 USB,(2)格式化(1)在 terminal 中通过 diskutil list 查看是…...
.Net Core微服务入门全纪录(四)——Ocelot-API网关(上)
系列文章目录 1、.Net Core微服务入门系列(一)——项目搭建 2、.Net Core微服务入门全纪录(二)——Consul-服务注册与发现(上) 3、.Net Core微服务入门全纪录(三)——Consul-服务注…...
pyautogui自动化鼠标键盘操作
pyautogui,用来写自动化脚本,比按键精灵更方便。pyautogui.position()可以获取当前鼠标位置。pyautogui不支持中文输入,利用 pyperclip从剪切板粘贴输入。 # -*- coding: utf-8 -*- import time import os import traceback import logging …...
2024年AI大模型技术年度总结与应用实战:创新与突破并进
前言 回顾2024年,我一共发布了286篇博文,粉丝数也达到了43000多。这一年里,我收获颇丰,始终坚持AI大模型的研究方向,并且积极开展大模型的实战应用,也取得了一系列令人振奋的突破。 在286篇博文中&#…...
HTML中相对路径和绝对路径详解
文章目录 HTML中相对路径和绝对路径详解一、引言二、绝对路径1、定义2、使用场景3、代码示例 三、相对路径1、定义2、使用方法3、代码示例 四、使用示例1、图片路径2、CSS和JavaScript文件路径3、页面内部链接 五、总结 HTML中相对路径和绝对路径详解 一、引言 在HTML开发中&a…...
联通用户管理系统(一)
#联通用户管理系统(一) 1.新建项目 如果你是windows的话,界面应该是如下的: 2.创建app python manage.py startapp app01一般情况下:我们是在pycharm的终端中运行上述指令,但是pychrm中为我们提供了工具…...
STM32-CAN总线
1.CAN总线简介 CAN总线是由BOSCH公司开发的一种简洁易用、传输速度快、易扩展、可靠性高的串行通信总线 2.CAN总线特征 两根通信线(CAN_H、CAN_L),线路少,无需共地差分信号通信(相对的是单端信号)&#…...
mac m1下载maven安装并配置环境变量
下载地址:Download Apache Maven – Maven 解压到一个没有中文和空格的文件夹 输入pwd查看安装路径 输入cd返回根目录再输入 code .zshrc 若显示 command not found: code你可以通过以下步骤来安装和配置 code 命令: 1. 确保你已经安装了 Visual Studio…...
Linux -- HTTP 请求 与 响应 报文
目录 请求报文: 请求方法 响应报文: 状态码 与 状态码描述 共性 常见的报头 请求报文: 请求方法 方法说明GET获取资源POST传输实体主体PUT传输文件HEAD获得报文首部DELETE删除文件OPTIONS询问支持的方法TRACE追踪路径CONNECT要求用…...
oneplus3t-lineage-14编译-android7
lineageOS-14.1-oneplus3t-build.md lineageOS-14(android7)的开发者模式/usb调试(adb)有root功能, 而lineageOS-16(android9)无 oneplus3t-lineage-14编译-android7 1 清华linageos镜像 x lineage-14.1-20180223-nightly-oneplus3-signed.zip ntfs分区挂载为普通用户目录…...
Spring Boot与Spring的区别
在当今的Java开发领域,Spring框架无疑是最为重要且广泛应用的框架之一。而随着技术的不断发展和开发者对效率与便捷性的追求,基于Spring框架的Spring Boot应运而生。接下来,将详细阐述Spring Boot与Spring的主要区别,并通过实际的…...
阿九的python 爬虫进阶课18.3 学习笔记
文章目录 前言1. 爬取大标题2. 爬取小标题3. 证券栏下的标题4. 某篇文章里的具体内容 前言 网课链接:https://www.bilibili.com/video/BV1kV4y1576b/新浪财经网址:https://finance.sina.com.cn/需先下载库: conda install lxml布置爬取的一…...
对人型机器人的研究和展望
目录 概述 1 核心软硬件部件 1.1 运动控制部分 1.1.1 减速机 1.1.2 编码器 1.1.3 直流无刷电机 1.2 智能仿生手 1.3 控制板卡 2 人型机器人的应用 3 未来展望 概述 如果现在有人问:当前那个行业最火?毫无疑问答案肯定是人型机器人了。当前各类机…...
docker 使用远程镜像启动一个容器
使用前提: 首先你得安装docker,其次你得拥有一个远程镜像 docker run --name io_11281009 --rm -it -p 2233:22 -v .:/root/py -e ed25519_rootAAAAC3NzaC1lZDI1********Oy7zR7l7aUniR2rul ghcr.lizzie.fun/fj0r/io srv对上述命令解释: 1.docker run:…...
VTK知识学习(37)-频域处理
1、前言 在图像处理和分析中,经常会将图像从图像空间转换到其他空间中,并利用这些空间的性质对转换后的数据进行分析处理。图像频域处理借助空间变换将图像从图像空间转换到频域空间,根据频域空间的性质对数据进行处理(如滤波),最…...
什么是软件架构
什么是软件架构 程序员说,软件架构是要决定编写哪些C程序或OO类、使用哪些库和框架 程序经理说,软件架构就是模块的划分和接口的定义 系统分析员说,软件架构就是为业务领域对象的关系建模 配置管理员说,软件架构就是开发出来的…...
RoCE网络及其协议栈详解(没有中间商赚差价的网络)
引言 随着数据中心对高性能、低延迟通信需求的不断增长,传统的TCP/IP以太网连接已经难以满足现代应用的要求。为了解决这些问题,RDMA(Remote Direct Memory Access)技术应运而生。RDMA是一种允许网络中的不同计算机直接访问对方内…...
el-dialog弹窗的@open方法中,第一次引用ref发现undefined问题,第二次后面又正常了
解决方法 直接不用这个open方法,转而用opened,代码例子: <el-dialog title"单个新增" :visible.sync"PlacardShowSingle" opened"openpbSingle()" width"1100px" top"1%" :close-on-c…...
基于阿里云视觉智能平台实现换脸程序
简介 阿里云视觉智能平台提供了一种强大的换脸功能,能够将视频中的人脸替换成其他图片中的脸。这种功能广泛应用于视频编辑、特效制作等领域。本文将介绍如何使用阿里云视觉智能平台进行视频换脸。 核心工作流程 整个换脸程序的实现可分为以下几个主要步骤&#…...
【2024年华为OD机试】(A卷,100分)- 完美走位 (Java JS PythonC/C++)
一、问题描述 题目解析 题目描述 在第一人称射击游戏中,玩家通过键盘的 A、S、D、W 四个按键控制游戏人物分别向左、向后、向右、向前进行移动。假设玩家每按动一次键盘,游戏人物会向某个方向移动一步。如果玩家在操作一定次数的键盘并且各个方向的步数相同时,此时游戏人…...
一文夯实垃圾收集的理论基础
如何判断一个引用是否存活 引用计数法 给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。 优点:可即刻回收垃圾&a…...
jenkins-api操作
一. 简述: 在一个比较复杂的环境中, 往往会有自己开发的运维管理平台。在代码发布这块,尽管jenkins有一个比较方便的UI, 但很多团队还是喜欢集中式管理, 将发布功能(仅仅把jenkins作为一个发布组件使用)嵌入运维管理平…...
SpringBoot3+Vue3学习
什么是Spring Boot? Spring Boot 是Spring 提供的一个子项目,用于快速构建Spring应用程序 传统方式弊端:之前的项目都用Spring FrameWork构建,需要手动引入依赖,依赖之间有可能存在冲突,较为麻烦;在配置…...
SQL刷题快速入门(三)
其他章节: SQL刷题快速入门(一) SQL刷题快速入门(二) 承接前两个章节,本系列第三章节主要讲SQL中where和having的作用和区别、 GROUP BY和ORDER BY作用和区别、表与表之间的连接操作(重点&…...
Flutter鸿蒙化中的Plugin
Flutter鸿蒙化中的Plugin 前言鸿蒙项目内PluginFlutter端实现鸿蒙端实现创建Plugin的插件类注册Plugin 开发纯Dart的package为现有插件项目添加ohos平台支持创建插件配置插件编写插件内容 参考资料 前言 大家知道Flutter和鸿蒙通信方式和Flutter和其他平台通信方式都是一样的&…...
Ubuntu 22.04.5 修改IP
Ubuntu22.04.5使用的是netplan管理网络,因此需要在文件夹/etc/netplan下的01-network-manager-all.yaml中修改,需要权限,使用sudo vim或者其他编辑器,修改后的内容如下: # Let NetworkManager manage all devices on …...
后端:MyBatis
文章目录 1. MyBatis1-1. Mybatis 工具类的封装1-2. Mybatis 通过集合或实体类传递参数-实现插入数据(增)1-3. MyBatis 实现删除数据(删)1-4. MyBatis 实现修改数据(改)1-5. MyBatis 实现查询数据(查) 2. MyBatis 配置文件中的一些标签和属性2-1.environments标签2-2. dataSour…...
CBAM-2018学习笔记
名称: Convolutional Block Attention Module (CBAM) 来源: CBAM: Convolutional Block Attention Module 相关工作: #ResNet #GoogleNet #ResNeXt #Network-engineering #Attention-mechanism 创新点: 贡献: 提…...
HTML根元素<html>的语言属性lang:<html lang=“en“>
诸神缄默不语-个人CSDN博文目录 在编写HTML页面时,通常会看到<html lang"en">这行代码,特别是在网页的开头部分,就在<!DOCTYPE html>后面。许多开发者可能对这个属性的含义不太了解,它到底有什么作用&…...