Java 网络编程性能优化:高吞吐量的实现方法
Java 网络编程性能优化:高吞吐量的实现方法
在当今的互联网时代,网络应用的性能优化是开发人员面临的重要挑战之一。Java 作为一门广泛使用的编程语言,提供了强大的网络编程支持,但如何通过优化实现高吞吐量,是每个 Java 网络开发者都需要深入研究的课题。本文将从多个方面探讨 Java 网络编程性能优化的方法,并通过代码示例进行详细说明。
一、理解吞吐量与性能瓶颈
在讨论性能优化之前,我们需要明确吞吐量的概念。吞吐量是指单位时间内系统能够处理的请求数量,它直接影响到系统的响应能力和扩展性。在 Java 网络编程中,吞吐量受到多种因素的限制,包括网络带宽、服务器处理能力、线程管理、I/O 操作等。因此,优化高吞吐量的关键在于识别并消除这些性能瓶颈。
(一)常见的性能瓶颈
- 线程瓶颈:传统的阻塞 I/O 模型中,每个连接都需要一个独立的线程来处理,这会导致线程数量过多,增加上下文切换的开销,从而限制吞吐量。
- I/O 等待瓶颈:在阻塞 I/O 模式下,线程在读写操作时会阻塞等待,导致线程资源的浪费。
- 网络带宽瓶颈:如果网络带宽不足,即使服务器处理能力再强,也无法实现高吞吐量。
- 内存瓶颈:频繁的内存分配和回收会导致垃圾回收(GC)频繁触发,影响系统性能。
二、使用非阻塞 I/O 模型
非阻塞 I/O 模型是实现高吞吐量的关键技术之一。与传统的阻塞 I/O 模型相比,非阻塞 I/O 不会在读写操作时阻塞线程,从而可以显著提高系统的吞吐量。Java 提供了基于 NIO(New Input/Output)的非阻塞 I/O 支持,通过使用选择器(Selector)来管理多个通道(Channel),可以实现高效的多路复用。
(一)NIO 的基本概念
NIO 是 Java 1.4 引入的一种新的 I/O 模型,它基于通道(Channel)和缓冲区(Buffer)进行操作。通道是双向的,可以同时进行读写操作,而缓冲区则是数据存储的容器。选择器(Selector)是 NIO 的核心组件,它可以同时管理多个通道,监听它们的事件(如连接请求、读写事件等),从而实现高效的多路复用。
(二)代码示例:基于 NIO 的服务器实现
以下是一个简单的基于 NIO 的服务器代码示例,它展示了如何使用选择器来管理多个客户端连接,并实现非阻塞 I/O 操作。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;public class NIOServer {public static void main(String[] args) throws IOException {// 创建选择器Selector selector = Selector.open();// 打开服务器通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8080));serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式// 将服务器通道注册到选择器上,并监听连接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器启动,监听端口 8080...");while (true) {// 等待事件发生selector.select();// 获取事件集合Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectedKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();if (key.isAcceptable()) {// 处理新的连接请求ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false); // 设置为非阻塞模式clientChannel.register(selector, SelectionKey.OP_READ); // 注册读事件System.out.println("新客户端连接:" + clientChannel.getRemoteAddress());} else if (key.isReadable()) {// 处理读事件SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {// 客户端关闭连接clientChannel.close();System.out.println("客户端关闭连接:" + clientChannel.getRemoteAddress());} else {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data);System.out.println("收到客户端消息:" + message);// 回复客户端clientChannel.write(ByteBuffer.wrap("消息已收到".getBytes()));}}}}}
}
(三)优化点
- 减少线程数量:通过使用选择器管理多个通道,可以避免为每个连接创建独立的线程,从而减少线程上下文切换的开销。
- 提高 I/O 效率:非阻塞 I/O 模式下,线程不会在 I/O 操作时阻塞等待,可以充分利用 CPU 资源,提高系统的吞吐量。
三、使用线程池管理线程
虽然非阻塞 I/O 模型可以减少线程数量,但在某些情况下,我们仍然需要线程来处理具体的业务逻辑。此时,使用线程池来管理线程可以提高系统的性能和资源利用率。线程池可以预先创建一定数量的线程,并在需要时分配给任务,避免了线程频繁创建和销毁的开销。
(一)线程池的使用
Java 提供了 ExecutorService
接口和 ThreadPoolExecutor
类来实现线程池的功能。通过合理配置线程池的参数(如核心线程数、最大线程数、任务队列大小等),可以优化系统的性能。
(二)代码示例:结合线程池的服务器实现
以下是一个结合线程池的服务器代码示例,它展示了如何在 NIO 的基础上使用线程池来处理业务逻辑。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class NIOServerWithThreadPool {private static final int PORT = 8080;private static final int THREAD_POOL_SIZE = 10;public static void main(String[] args) throws IOException {// 创建线程池ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);// 创建选择器Selector selector = Selector.open();// 打开服务器通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(PORT));serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式// 将服务器通道注册到选择器上,并监听连接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器启动,监听端口 " + PORT + "...");while (true) {// 等待事件发生selector.select();// 获取事件集合Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectedKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();if (key.isAcceptable()) {// 处理新的连接请求ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false); // 设置为非阻塞模式clientChannel.register(selector, SelectionKey.OP_READ); // 注册读事件System.out.println("新客户端连接:" + clientChannel.getRemoteAddress());} else if (key.isReadable()) {// 处理读事件SocketChannel clientChannel = (SocketChannel) key.channel();threadPool.execute(new ClientHandler(clientChannel));}}}}// 客户端处理线程static class ClientHandler implements Runnable {private final SocketChannel clientChannel;public ClientHandler(SocketChannel clientChannel) {this.clientChannel = clientChannel;}@Overridepublic void run() {try {ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {// 客户端关闭连接clientChannel.close();System.out.println("客户端关闭连接:" + clientChannel.getRemoteAddress());} else {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data);System.out.println("收到客户端消息
```java: "消息已收到".getBytes());clientChannel.write(ByteBuffer.wrap("消息已收到".getBytes()));}} catch (IOException e) {e.printStackTrace();try {if (clientChannel != null && clientChannel.isOpen()) {clientChannel.close();}} catch (IOException ex) {ex.printStackTrace();}}}}
}
(三)优化点
- 线程复用:通过线程池管理线程,可以复用线程,减少线程创建和销毁的开销。
- 平衡负载:线程池可以根据系统资源合理分配任务,避免过多线程导致的资源竞争和上下文切换开销。
四、优化缓冲区的使用
在 Java NIO 中,缓冲区(Buffer)是数据传输的基本单位。合理优化缓冲区的使用可以显著提高数据传输的效率,减少内存分配和数据拷贝的开销。
(一)缓冲区复用
缓冲区复用是一种常见的优化手段。在实际应用中,可以预先分配一组缓冲区,供多个连接或多个操作共享使用。这样可以避免频繁的内存分配和回收,从而减少垃圾回收的开销。
(二)代码示例:缓冲区复用
以下是一个简单的缓冲区复用的代码示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class NIOServerWithBufferReuse {private static final int PORT = 8080;private static final int THREAD_POOL_SIZE = 10;private static final int BUFFER_SIZE = 1024;private static final ByteBuffer[] buffers = new ByteBuffer[10];static {for (int i = 0; i < buffers.length; i++) {buffers[i] = ByteBuffer.allocate(BUFFER_SIZE);}}public static void main(String[] args) throws IOException {// 创建线程池ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);// 创建选择器Selector selector = Selector.open();// 打开服务器通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(PORT));serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式// 将服务器通道注册到选择器上,并监听连接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器启动,监听端口 " + PORT + "...");while (true) {// 等待事件发生selector.select();// 获取事件集合Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectedKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();if (key.isAcceptable()) {// 处理新的连接请求ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false); // 设置为非阻塞模式clientChannel.register(selector, SelectionKey.OP_READ); // 注册读事件System.out.println("新客户端连接:" + clientChannel.getRemoteAddress());} else if (key.isReadable()) {// 处理读事件SocketChannel clientChannel = (SocketChannel) key.channel();threadPool.execute(new ClientHandler(clientChannel));}}}}// 客户端处理线程static class ClientHandler implements Runnable {private final SocketChannel clientChannel;public ClientHandler(SocketChannel clientChannel) {this.clientChannel = clientChannel;}@Overridepublic void run() {try {// 获取一个复用的缓冲区ByteBuffer buffer = getReusableBuffer();int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {// 客户端关闭连接clientChannel.close();System.out.println("客户端关闭连接:" + clientChannel.getRemoteAddress());} else {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data);System.out.println("收到客户端消息:" + message);// 回复客户端clientChannel.write(ByteBuffer.wrap("消息已收到".getBytes()));// 释放缓冲区releaseBuffer(buffer);}} catch (IOException e) {e.printStackTrace();try {if (clientChannel != null && clientChannel.isOpen()) {clientChannel.close();}} catch (IOException ex) {ex.printStackTrace();}}}// 获取复用的缓冲区private synchronized ByteBuffer getReusableBuffer() {for (ByteBuffer buffer : buffers) {if (!buffer.isReadOnly() && !buffer.hasRemaining()) {buffer.clear();return buffer;}}return ByteBuffer.allocate(BUFFER_SIZE);}// 释放缓冲区private synchronized void releaseBuffer(ByteBuffer buffer) {for (int i = 0; i < buffers.length; i++) {if (buffers[i] == buffer) {buffers[i].clear();return;}}}}
}
(三)优化点
- 减少内存分配:通过复用缓冲区,可以减少频繁的内存分配和回收,从而降低垃圾回收的开销。
- 提高数据传输效率:缓冲区复用可以避免数据在多个缓冲区之间的拷贝,提高数据传输的效率。
五、数据编码优化
在网络编程中,数据编码的选择对性能有着重要的影响。选择合适的数据编码格式可以减少网络传输的数据量,提高数据传输的效率。
(一)使用高效的编码格式
常见的高效编码格式包括 Protocol Buffers、MessagePack 等。这些编码格式相比传统的 XML、JSON 等文本格式,具有更小的数据量和更快的解析速度。
(二)代码示例:使用 Protocol Buffers
以下是一个使用 Protocol Buffers 进行数据编码的代码示例:
首先,定义一个 Protocol Buffers 的消息格式(message.proto
):
syntax = "proto3";message Message {string id = 1;string content = 2;int64 timestamp = 3;
}
然后,使用 Protocol Buffers 的编译器生成 Java 代码。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;import example.Message;public class NIOServerWithProtobuf {private static final int PORT = 8080;private static final int THREAD_POOL_SIZE = 10;public static void main(String[] args) throws IOException {// 创建线程池ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);// 创建选择器Selector selector = Selector.open();// 打开服务器通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(PORT));serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式// 将服务器通道注册到选择器上,并监听连接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器启动,监听端口 " + PORT + "...");while (true) {// 等待事件发生selector.select();// 获取事件集合Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectedKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();if (key.isAcceptable()) {// 处理新的连接请求ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false); // 设置为非阻塞模式clientChannel.register(selector, SelectionKey.OP_READ); // 注册读事件System.out.println("新客户端连接:" + clientChannel.getRemoteAddress());} else if (key.isReadable()) {// 处理读事件SocketChannel clientChannel = (SocketChannel) key.channel();threadPool.execute(new ClientHandler(clientChannel));}}}}// 客户端处理线程static class ClientHandler implements Runnable {private final SocketChannel clientChannel;public ClientHandler(SocketChannel clientChannel) {this.clientChannel = clientChannel;}@Overridepublic void run() {try {// 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {// 客户端关闭连接clientChannel.close();System.out.println("客户端关闭连接:" + clientChannel.getRemoteAddress());} else {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);// 解析 Protocol Buffers 数据Message message = Message.parseFrom(data);System.out.println("收到客户端消息:ID=" + message.getId() + ", Content=" + message.getContent() + ", Timestamp=" + message.getTimestamp());// 构造回复消息Message reply = Message.newBuilder().setId("Server").setContent("消息已收到").setTimestamp(System.currentTimeMillis()).build();// 发送回复消息clientChannel.write(ByteBuffer.wrap(reply.toByteArray()));}} catch (IOException | example.InvalidProtocolBufferException e) {e.printStackTrace();try {if (clientChannel != null && clientChannel.isOpen()) {clientChannel.close();}} catch (IOException ex) {ex.printStackTrace();}}}}
}
(三)优化点
- 减少数据量:Protocol Buffers 等高效的编码格式可以将数据量减少到传统文本格式的 1/10 到 1/3,从而减少网络传输的开销。
- 提高解析速度:高效的编码格式通常具有更快的解析速度,可以减少数据处理的时间。
六、总结与展望
通过上述方法的介绍和代码示例,我们可以看到 Java 网络编程性能优化的多个方面:
- 使用非阻塞 I/O 模型:通过 NIO 实现高效的多路复用,减少线程数量,提高系统吞吐量。
- 使用线程池管理线程:通过线程池复用线程,减少线程创建和销毁的开销,平衡系统负载。
- 优化缓冲区的使用:通过缓冲区复用,减少内存分配和数据拷贝的开销,提高数据传输效率。
- 数据编码优化:使用高效的编码格式,减少网络传输的数据量,提高数据处理速度。
在实际项目中,我们需要根据具体的需求和场景,综合运用这些优化方法,以实现高吞吐量的 Java 网络应用。同时,随着技术的不断发展和应用场景的多样化,我们还需要不断探索和尝试新的优化手段,以满足日益增长的性能需求。
未来的发展方向可能包括:
- 异步编程模型:随着 Java 对异步编程的支持不断加强(如 CompletableFuture、异步 Servlet 等),我们可以进一步提高系统的响应能力和吞吐量。
- 更高效的网络协议:如 HTTP/3、QUIC 等新协议的出现,为网络编程提供了更多的优化空间。
- 硬件加速:利用硬件(如 DPDK、智能网卡等)进行数据处理和传输,可以进一步提高网络性能。
总之,Java 网络编程性能优化是一个不断发展的领域,需要我们不断学习和实践,以应对不断增长的性能挑战。
相关文章:
Java 网络编程性能优化:高吞吐量的实现方法
Java 网络编程性能优化:高吞吐量的实现方法 在当今的互联网时代,网络应用的性能优化是开发人员面临的重要挑战之一。Java 作为一门广泛使用的编程语言,提供了强大的网络编程支持,但如何通过优化实现高吞吐量,是每个 J…...
【去哪儿网】登录滑块逆向算法AES加密分析(逆天滑块轨迹)
目标:aHR0cHM6Ly91c2VyLnF1bmFyLmNvbS9wYXNzcG9ydC9sb2dpbi5qc3A 验证接口: https://vercode.qunar.com/inner/captcha/snapshot 可以发现是encryption方法生成,进入encryption里面,发现是AES加密的轨迹 track就是轨迹 直接…...
Redis ④-通用命令
Redis 是一个 客户端-服务器 结构的程序,这与 MySQL 是类似的,这点需要牢记!!! Redis 固然好,但也不是任何场景都适合使用 Redis,一定要根据当前的业务需求来选择是否使用 Redis Redis 通用命令…...
机制的作用
“机制”是一个广泛使用的概念,其含义和应用范围因领域而异。在不同的学科和实际应用中,机制有着不同的定义和功能。以下从几个主要领域对“机制”进行详细解释: 一、自然科学中的机制 (一)物理学 定义 在物理学中&…...
Pandas:数据处理与分析的核心操作
Pandas:数据处理与分析的核心操作 Pandas 是 Python 数据分析的核心库,它提供了高性能、易用的数据结构和数据分析工具。本文将详细介绍 Pandas 的核心操作,帮助你高效进行数据处理和分析。 1. Pandas 基础数据结构 Pandas 有两个主要的数…...
Kotlin实现Android应用保活方案
Kotlin实现Android应用保活优化方案 以下的Android应用保活实现方案,更加符合现代Android开发规范,同时平衡系统限制和用户体验。 1. 前台服务方案 class OptimizedForegroundService : Service() {private val notificationId 1private val channel…...
硬件电路(25)-过温保护器件ksd9700温控开关
一、概述 KSD9700系列温控开关是一种双金属作为感温元件的温控器,具有动作迅速、控温精确、控制电流大、使用寿命长的特点,被广泛应用于各类微型电机、电磁炉、空调电机、小家电等做温度保护控制。 二、应用 KSD9700系列产品是一种双金属作为感温元件的…...
vuex实现同一页面radio-group点击不同按钮显示不同表单
本文实现的是点击单一规格和多规格两个按钮会在页面显示不同的表单 方法一 <!-- 单规格和多规格的切换 --> <el-form label-width"80px" class"text-align-left"><el-form-item label"商品规格"><!-- 监听skus_type的改…...
代码随想录训练营第36天 ||1049. 最后一块石头的重量 II 494. 目标和 474. 一和零
1049. 最后一块石头的重量 II 讲解:代码随想录 思路: 01背包问题:题意说要求粉碎石头后留下的最小石头重量,石头粉碎的规则是两个石头如果重量相等,同时粉碎,如果重量不相等,粉碎后的重量是大…...
[Spring]SSM整合
第一步 整合任何框架,首先都是导入相关的jar包: <dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.18</version></dependency><!--mybatis依…...
【HDFS】HDFS数据迁移与备份全攻略:从日常备份到灾难恢复
目录 1 HDFS数据迁移与备份概述 2 集群间数据迁移:DistCp工具详解 2.1 DistCp工作原理架构 2.2 DistCp标准工作流程 2.3 DistCp性能优化方案 3 离线备份实施策略 3.1 HDFS到本地备份架构 4 灾难恢复方案设计 4.1 基于快照的恢复流程 4.2 数据一致性校验方…...
动态规划入门:背包问题求具体方案(以0-1背包问题为例)
本质:有向图最短(长)路问题 字典序最小方案?--贪心思路?(本题未使用) 分析第一个物品: 写代码时tip:要考虑“边读边做”还是“先读后做” #include<iostream> #i…...
git学习日志
你看到的是 Vim 文本编辑器的界面,并非 git 的窗口。这是你通过 vim hello.txt 命令打开的新文件。以下是分步操作指南: 1. 进入编辑模式 按下键盘上的 i 键(进入 “INSERT” 模式),此时左下角会显示 -- INSERT --。现…...
数字孪生废气处理工艺流程
图扑数字孪生废气处理工艺流程系统。通过精准 3D 建模,对废气收集、预处理、净化、排放等全流程进行 1:1 数字化复刻,实时呈现设备运行参数、污染物浓度变化等关键数据。 借助图扑可视化界面,管理者可直观掌握废气处理各环节状态,…...
iPhone 13P 换超容电池,一年实记的“电池循环次数-容量“柱状图
继上一篇 iPhone 13P 更换"移植电芯"和"超容电池"🔋体验,详细记录了如何更换这两种电池,以及各自的优略势对比。 一晃一年过去,时间真快,这次分享下记录了使用超容电池的 “循环次数 - 容量(mAh)…...
豆瓣图书数据采集与可视化分析(二)- 豆瓣图书数据清洗与处理
文章目录 前言一、查看数据基本信息二、拆分pub列三、日期列处理四、价格列处理五、出版社列处理六、评价人数列处理七、缺失值处理八、重复数据处理九、异常值处理十、完整代码十一、清洗与处理后的数据集展示 前言 豆瓣作为国内知名的文化社区,拥有庞大且丰富的图…...
【Sa-Token】学习笔记05 - 踢人下线源码解析
目录 前言 强制注销 踢人下线 源码解析 前言 所谓踢人下线,核心操作就是找到指定 loginId 对应的 Token,并设置其失效。 上图为踢人下线后,前端应该用图像给出来让用户重新登录,而不是让前端收到一个描述着被下线 的JSON 强…...
Linux | I.MX6ULL 文件系统
01 本节所有的测试程序需要开发板有 Qt 环境来运行。我们提供的文件系统是由 yocto 裁剪整理得来的。之后我们会整理一份单独移植的 qt 系统。方便用户移植第三方软件。如果用户的文件系统非我们的出厂版本,请参考之前烧写章节重新烧写出厂文件系统。开发板启动需要输入登录…...
Python3基础语法
一:注释 Python中用#表示单行注释,#之后的同行的内容都会被注释掉。 使用三个连续的双引号表示多行注释,两个多行注释标识之间内容会被视作是注释。 二:基础变量类型与操作符 1. 除法 * 除法 / python3中就算是两个整数相除&a…...
QEMU源码全解析 —— 块设备虚拟化(20)
接前一篇文章:QEMU源码全解析 —— 块设备虚拟化(19) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 特此致谢! 上一回大致解析了drive_new函数,本回重点对于drive_new函数中调用的blockdev_init函…...
JavaScript 笔记 --- part 4 --- Web API (part 2)
(webAPI part2) DOM 基本操作 事件流 定义: 指的是事件完整执行过程中的流动路径 捕获阶段: 事件从最外层的窗口对象开始,逐层向内传播到目标元素,并触发相应的事件处理程序。 冒泡阶段: 事件从目标元素开始,逐层向外传播到最外层的窗口对象…...
从入门到精通汇编语言 第六章(中断及外部设备操作)
参考教程:通俗易懂的汇编语言(王爽老师的书)_哔哩哔哩_bilibili 一、移位指令 1、8个移位指令 (1)逻辑左移指令SHL:SHL OPR, CNT。 ①OPR为操作数,CNT为左移位数,该指令将OPR视作…...
PySide6 GUI 学习笔记——常用类及控件使用方法(常用类尺寸QSizeF)
QSizeF 类(浮点尺寸类) 文章目录 QSizeF 类(浮点尺寸类)概述主要方法列表详细说明及应用举例注意事项 概述 QSizeF 类使用浮点精度定义二维对象的尺寸。官方文档在这里。 主要方法列表 __init__(self) …...
操作系统中的虚拟化技术深度对话
操作系统中的虚拟化技术深度对话 参与者:系统工程师(Engineer)、开发者(Developer)、学生(Student) 1. 虚拟化的基本概念 Student:虚拟化到底是什么?为什么操作系统需要…...
第35讲:构建属于自己的遥感大模型平台,并接入地理数据工作流
目录 🌍 写在前面 一、为什么要构建属于自己的遥感大模型平台? 二、核心技术选型推荐 ✅ 前端部分 ✅ 后端部分 ✅ 部署平台 三、平台架构设计思路 四、案例实战:构建一个在线遥感分割平台 📌 第一步:模型服务封装(FastAPI) 📌 第二步:前端上传与展示(L…...
langchain-nextjs-template 模板安装与配置
前提条件: node安装yarn 安装:npm install -g yarn 目录 1. 克隆项目2. 安装依赖3. 配置环境变量4. 修改模型配置5. 启动开发服务器6. 项目结构说明7. 功能说明8. 自定义提示模板部分过程文件截图 1. 克隆项目 首先,从 GitHub 克隆 LangCha…...
安卓单机斗地主,具备休闲挑战等多模式
软件介绍 斗地主单机版是一款超适合在安卓设备上玩的游戏。当你周末玩网游玩累了的时候,它就是个很不错的选择哦。 多种模式可选 这个斗地主单机版有不同的模式呢,有休闲模式、挑战模式、炸弹场和大师赛。你可以根据自己的喜好随意挑选模式,…...
电解电容失效分析过程、失效分析报告
参考: 深度剖析关键电子元器件电解电容内部故障隐患 电解电容的参数指标 电路板中电解电容是存在寿命的,电解电容中的电解液随着时间会慢慢减少导致电容容值降低,最终导致电源出现问题。相信大家都见过电解电容鼓包的情况。 所以做设计的时…...
Ubuntu修改Swap交换空间大小
前言: 安装Ubuntu系统时,选择了默认空间分配方案,Swap空间仅1G,而实际的物理内存有32G,分给Swap空间至少为内存的1倍,最好是内存值的2倍,系统相当卡顿,重做系统后,费力部…...
SpringBoot 知识图谱
预警:本文非常长,建议先 mark 后看,也许是最后一次写这么长的文章说明:前面有 4 个小节关于 Spring 的基础知识,分别是:IOC 容器、JavaConfig、事件监听、SpringFactoriesLoader 详解,它们占据了本文的大部分内容,虽然它们之间可能没有太多的联系,但这些知识对于理解 …...
智谱开源新一代GLM模型,全面布局AI智能体生态
2024年4月15日,智谱在中关村论坛上正式发布了全球首个集深度研究与实际操作能力于一体的AI智能体——AutoGLM沉思。这一革命性技术的发布标志着智谱在AGI(通用人工智能)领域的又一次重要突破。智谱的最新模型不仅推动了AI智能体技术的升级&am…...
一文读懂Python之numpy模块(34)
一、模块简介 numpy是Python语言中做科学计算的基础库,重在于数值计算,有一个强大的N维数组对象Array,同时NumPy 提供了大量的库函数和操作,可以帮助程序员轻松地进行Array数值计算。 numpy在数据分析和机器学习领域被广泛使用。…...
Lora 微调自定义device_map
Lora 微调自定义device_map 首先查看模型权重参数配置model.safetensors.index.json 查看多少解码器 这里的layer可以理解为解码器层,后面有qkv,bais,layernomal等 # 显卡数量 num_gpus = 5 # 总层数 num_layers = 28layers_per_gpu = num_layers // num...
二叉树的顺序结构及实现
一.二叉树的顺序结构 二.堆的概念及结构 三.堆的实现 一.二叉树的顺序结构 普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储。 二.堆的概念…...
python生成项目依赖文件requirements.txt
文章目录 通过pip freeze去生成通过pipreqs去生成 通过pip freeze去生成 pip freeze > requirements.txt会将整个python的Interceptor的环境下lib包下所有的依赖都生成到这个文件当中,取决于我们使用的python的版本下所有的安装包。不建议使用这种方式ÿ…...
Cribl 对Windows-xml log 进行 -flatten-03
The Flatten Function Description The Flatten Function is used to flatten fields out of a nested structure. Lets flatten the _raw JSON object, to further reduce the events size before we send it to the intended destination(s). Steps – Adding a Flatten…...
Java优雅实现判空方法
在 Java 开发中,频繁的 if (obj ! null) 判空代码会导致代码冗余、可读性差,且容易遗漏判空导致 NullPointerException。以下从 语言特性、设计模式、工具类 和 编码规范 四个维度,结合实际案例,详解如何优雅处理空值问题。 一、…...
leetcode 1035. Uncrossed Lines
题目描述 本题本质上就是求nums1和nums2的最长公共子序列的长度。因此本题本质上与第1143题一模一样。 代码: class Solution { public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {//本题等价于求nums1和nums2的最长公…...
windows上部署本地知识库(RAG)ollama + docker + ragflow方案
一、部署ollama 如何部署本地部署ollama参照我另一篇博客:Windows安装ollama部署本地大模型_ollama 在哪里运行的大模型-CSDN博客 二、部署docker 1、下载docker: 下载地址: Docker: Accelerated Container Application Development 2、winds(winds11)安装或者更新ws…...
多Agent框架及协作机制详解
文章目录 一、多智能体系统介绍1.1 多智能体系统定义1.2 多智能体协作1.3 协作类型1.4 协作策略1.5 通信结构1.6 协调与编排 1.3 多智能体与单智能体对比1.4 应用场景 二、多Agent开发框架AutoGenMetaGPTLangGraphSwarmCrewAI 三、多智能体协作方式3.1 MetaGPT:SOP驱…...
Cribl 对Windows-xml log 进行 -Removing filed-06
Removing Fields Description The Eval Function can be used to add or remove fields. In this example we will remove the extracted fields while preserving _raw, _time,index,source, sourcetype. Steps - Adding an Eval Function...
Linux 常用指令用户手册
Linux 常用指令用户手册 适合新手入门 & 日常速查 目录 基础操作文件与目录管理权限与所有权文本处理压缩与解压系统监控网络操作进程管理实用小技巧 1. 基础操作 1.1 查看系统信息 # 查看内核版本 uname -a# 查看系统发行版信息(适用于 Debian/Ubuntu&…...
Java EE(20)——线程安全——ThreadLocal
1.前言 在面的线程安全相关的博文中,解决线程安全问题的方法主要使用synchronized和volatile两个关键字。引发线程安全问题的根本原因是多个线程同时对共享变量进行写操作,而上述两个关键字并没有改变"多个线程写同一个变量"这个情况。以sync…...
树莓派超全系列教程文档--(36)树莓派条件过滤器设置
树莓派条件过滤器设置 条件过滤器[all] 过滤器型号过滤器[none] 过滤器[tryboot] 过滤器[EDID*] 过滤器序列号过滤器GPIO过滤器组合条件过滤器 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 条件过滤器 当将单个 SD 卡(或卡图像&am…...
Vue3核心源码解析
/packages/complier-core 定位:编译时核心,处理 Vue 模板的编译逻辑。核心功能: 模板解析:将 .vue 文件的模板语法(HTML-like)解析为 抽象语法树 (AST)。转换优化…...
JavaScript解密实战指南:从基础到进阶技巧
JavaScript加密技术广泛应用于数据保护、反爬虫和代码混淆,但掌握解密方法能帮助开发者突破技术壁垒。本文结合爬虫实战与安全分析场景,系统梳理JS解密的核心方法与工具。 一、基础解密方法 1. Base64解码 适用于简单编码场景,如Cookie加密…...
指针(2)
1.数组名的理解 使用指针访问数组的内容时,有这样的代码: int arr[10]{1,2,3,4,5,6,7,8,9,10}int * p&arr[0]; &arr[0] 的方式拿到了数组的第一个元素的地址,但是其实数组名本来就是地址,而且还是首元素的地址…...
Android开发中广播(Broadcast)技术详解
在 Android 开发中,广播(Broadcast) 是一种广泛使用的组件通信机制,它允许应用程序在不直接交互的情况下传递消息。本文将详细讲解 Android 广播的基本概念、类型、发送与接收流程、使用场景及注意事项,并结合具体的代…...
Python网络爬虫设计(三)
目录 一、需要登录的爬虫 二、pyppeteer与requests库结合 1、cookie和session 三、其他 1、绝对网址和相对网址 2、sleep函数 一、需要登录的爬虫 在众多种类的页面中,不同的页面有不同的功能,有的是进行展示的,而有的则是登录类的。在…...
【深度学习—李宏毅教程笔记】各式各样的 Attention
目录 一、普通 Self-Attention 的痛点 二、对 Self-Attention 的优化方式 1、Local Attention / Truncated Attention 2、Stride Attention 3、Global Attention 4、知名的 Self-Attention 的变形的应用 (1)Longformer (2)…...