分布式文件系统07-小文件系统的请求异步化高并发性能优化

小文件系统的请求异步化高并发性能优化

222_分布式图片存储系统中的高性能指的到底是什么?

重构系统架构,来实现一个高性能。然后就要做非常完善的一个测试,最后对这个系统做一个总结,说说后续我们还要做一些什么东西。另外,我还要给大家留一些作业,相当于是让大家课后自己去做的,就不是完全拷贝我的代码

高并发

前面已经通过Reactor模式实现了

高性能主要是两块

第一块:客户端现在是短连接,每次发送请求,都需要建立连接,然后断开连接。站在客户端的角度而言,发现每执行一次文件上传和下载的操作,速度都很慢

第二块:文件上传,需要多副本上传。一般来说,针对kafka,多副本的时候默认情况下只要写成功一个副本,就返回了。另外其他的副本的写都是异步慢慢来执行的,kafka采取的是副本pull数据的机制,只要在一个数据节点上写成功数据,别的数据节点会主从从这个写成功的数据节点上pull数据

Kafka,强调高性能,生产消息的行为都是尽快的可以完成

HDFS,不强调高性能,它主要针对的是几个GB的大文件上传到服务器上去,只要慢慢上传就可以了,速度慢点无所谓,只要能上传成功。所以,HDFS采用的是多个副本一定要依次上传成功,才可以说是本次文件上传成功了。所以,HDFS的上传速度肯定是很慢的,因为它们根本不强调文件上传过程的高性能。所以Kafka和HDFS的应用场景本身就不相同

高性能架构的重构

  • 短连接 -> 长连接;
  • 同步上传多副本 -> 写一个副本,其他副本在后台慢慢的异步复制和拉取

这样,文件上传和文件下载,性能至少会提升好几倍

223_回头审视一下客户端的短连接模式有哪些问题?

除了客户端有NioClient以外,数据节点也有NioClient,因为他在进行数据节点扩缩容时,需要从其他的数据节点拷贝副本过来写入本地,这个过程使用短连接也无所谓,因为这个过程都是后台慢慢执行的,但是当然最好也是重构成长连接模式

224_初步实现用于进行网络管理的NetworkManager组件

225_在NetworkManager中实现核心线程无限循环进行poll操作

NetworkManager
/*** 网络连接管理器*/
public class NetworkManager {// 正在连接中public static final Integer CONNECTING = 1;// 已经建立连接public static final Integer CONNECTED = 2;// 多路复用Selectorprivate Selector selector;// 所有的连接private Map<String, SocketChannel> connections;// 每个数据节点的连接状态private Map<String, Integer> connectState;// 等待建立连接的机器private ConcurrentLinkedQueue<Host> waitingConnectHosts;public NetworkManager() {try {this.selector = Selector.open();} catch (IOException e) {e.printStackTrace();}this.connections = new ConcurrentHashMap<String, SocketChannel>();this.connectState = new ConcurrentHashMap<String, Integer>();this.waitingConnectHosts = new ConcurrentLinkedQueue<Host>();new NetworkPollThread().start();}/*** 尝试连接到数据节点的端口上去*/public void maybeConnect(String hostname, Integer nioPort) throws Exception {synchronized(this) {if(!connectState.containsKey(hostname)) {connectState.put(hostname, CONNECTING);waitingConnectHosts.offer(new Host(hostname, nioPort)); }while(connectState.get(hostname).equals(CONNECTING)) {wait(100);}}}/*** 尝试把排队中的机器发起连接的请求*/private void tryConnect() {try {Host host = null;SocketChannel channel = null;while((host = waitingConnectHosts.poll()) != null) {channel = SocketChannel.open();  channel.configureBlocking(false);  channel.connect(new InetSocketAddress(host.hostname, host.nioPort)); channel.register(selector, SelectionKey.OP_CONNECT);  }} catch (Exception e) {e.printStackTrace();}}// 网络连接的核心线程class NetworkPollThread extends Thread {@Overridepublic void run() {while(true) {tryConnect();}}}// 代表了一台机器class Host {String hostname;Integer nioPort;public Host(String hostname, Integer nioPort) {this.hostname = hostname;this.nioPort = nioPort;}}}

226_在无限循环的poll方法中完成网络连接的建立

public class NetworkManager {// 正在连接中public static final Integer CONNECTING = 1;// 已经建立连接public static final Integer CONNECTED = 2;// 网络poll操作的超时时间public static final Long POLL_TIMEOUT = 500L; // 多路复用Selectorprivate Selector selector;// 所有的连接private Map<String, SocketChannel> connections;// 每个数据节点的连接状态private Map<String, Integer> connectState;// 等待建立连接的机器private ConcurrentLinkedQueue<Host> waitingConnectHosts;public NetworkManager() {try {this.selector = Selector.open();} catch (IOException e) {e.printStackTrace();}this.connections = new ConcurrentHashMap<String, SocketChannel>();this.connectState = new ConcurrentHashMap<String, Integer>();this.waitingConnectHosts = new ConcurrentLinkedQueue<Host>();new NetworkPollThread().start();}/*** 尝试连接到数据节点的端口上去*/public void maybeConnect(String hostname, Integer nioPort) throws Exception {synchronized(this) {if(!connectState.containsKey(hostname)) {connectState.put(hostname, CONNECTING);waitingConnectHosts.offer(new Host(hostname, nioPort)); }while(connectState.get(hostname).equals(CONNECTING)) {wait(100);}}}// 网络连接的核心线程class NetworkPollThread extends Thread {@Overridepublic void run() {while(true) {tryConnect();poll();}}/*** 尝试把排队中的机器发起连接的请求*/private void tryConnect() {try {Host host = null;SocketChannel channel = null;while((host = waitingConnectHosts.poll()) != null) {channel = SocketChannel.open();  channel.configureBlocking(false);  channel.connect(new InetSocketAddress(host.hostname, host.nioPort)); channel.register(selector, SelectionKey.OP_CONNECT);  }} catch (Exception e) {e.printStackTrace();}}/*** 尝试完成网络连接、请求发送、响应读取*/private void poll() {SocketChannel channel = null;try {int selectedKeys = selector.select(500);   if(selectedKeys <= 0) {return;}Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();  while(keysIterator.hasNext()){  SelectionKey key = (SelectionKey) keysIterator.next();  keysIterator.remove();  // 如果是网络连接操作if(key.isConnectable()){  channel = (SocketChannel) key.channel();if(channel.isConnectionPending()){  while(!channel.finishConnect()) {Thread.sleep(100); }}   System.out.println("完成与服务端的连接的建立......"); InetSocketAddress remoteAddress = (InetSocketAddress)channel.getRemoteAddress();connectState.put(remoteAddress.getHostName(), CONNECTED);connections.put(remoteAddress.getHostName(), channel);}}} catch (Exception e) {e.printStackTrace();if(channel != null) {try {channel.close();} catch (IOException e1) {e1.printStackTrace();}}}}}// 代表了一台机器class Host {String hostname;Integer nioPort;public Host(String hostname, Integer nioPort) {this.hostname = hostname;this.nioPort = nioPort;}}}

227_客户端的核心业务方法对要发送的请求进行封装

228_将封装好的请求放入NetworkManager的请求队列中

229_如何实现异步发送请求以及同步等待响应两个接口

230_对每个数据节点获取一个请求缓存起来等待发送

231_在核心的poll方法中将每个机器暂存等待的请求发送出去

232_在核心的poll方法中对机器返回的响应进行读取

拿到响应

客户端将请求发出以后,就每隔100ms轮询一次,有没有响应结果返回回来

233_客户端建立连接的过程中异常了该如何返回响应?

234_客户端发送请求过程中异常了该如何返回响应?

/*** 发送请求*/private void sendRequest(SelectionKey key, SocketChannel channel) {InetSocketAddress remoteAddress = null;try {remoteAddress = (InetSocketAddress) channel.getRemoteAddress();String hostname = remoteAddress.getHostName();// 获取要发送到这台机器的请求的数据NetworkRequest request = toSendRequests.get(hostname);ByteBuffer buffer = request.getBuffer();// 将请求发送到对方机器上去channel.write(buffer);while (buffer.hasRemaining()) {channel.write(buffer);}System.out.println("本次请求发送完毕......");key.interestOps(SelectionKey.OP_READ);} catch (Exception e) {e.printStackTrace();// 发送失败,就取消关注OP_WRITE事件key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);if (remoteAddress != null) {String hostname = remoteAddress.getHostName();NetworkRequest request = toSendRequests.get(hostname);if (request.needResponse()) {NetworkResponse response = new NetworkResponse();response.setHostname(hostname);response.setRequestId(request.getId());// 请求发送失败,则客户端手动构造一个响应response.setError(true);finishedResponses.put(request.getId(), response);} else {toSendRequests.remove(hostname);}}}}

完整代码

NioClient
*** 客户端的一个NIOClient,负责跟数据节点进行网络通信*/
public class NioClient {private NetworkManager networkManager;public NioClient() {this.networkManager = new NetworkManager();}/*** 发送一个文件过去*/public Boolean sendFile(String hostname, int nioPort, byte[] file, String filename, long fileLength) {  // 先根据hostname来检查一下,跟对方机器的连接是否建立好了// 没有建立好,那么就直接在此建立连接; 建立好连接后,就把连接给缓存起来,以备下次使用try {// 如果此时还没跟那个数据节点建立好连接if(!networkManager.maybeConnect(hostname, nioPort)) {return false;}NetworkRequest request = createSendFileRequest(hostname, nioPort, file, filename, fileLength);networkManager.sendRequest(request); NetworkResponse response = networkManager.waitResponse(request.getId());if(response.error()) {// 请求发送失败,客户端自己构造的response,并将response.error 设置为truereturn false;}ByteBuffer buffer = response.getBuffer();String responseStatus = new String(buffer.array(), 0, buffer.remaining());System.out.println("收到" + hostname + "的响应:" + responseStatus);return responseStatus.equals(NetworkResponse.RESPONSE_SUCCESS);} catch (Exception e) {e.printStackTrace(); }return false;}/*** 构建一个发送文件的网络请求*/private NetworkRequest createSendFileRequest(String hostname, Integer nioPort, byte[] file, String filename, long fileLength) {NetworkRequest request = new NetworkRequest();ByteBuffer buffer = ByteBuffer.allocate(NetworkRequest.REQUEST_TYPE + NetworkRequest.FILENAME_LENGTH + filename.getBytes().length + NetworkRequest.FILE_LENGTH + (int)fileLength); buffer.putInt(NetworkRequest.REQUEST_SEND_FILE); buffer.putInt(filename.getBytes().length); buffer.put(filename.getBytes()); buffer.putLong(fileLength); buffer.put(file);buffer.rewind(); request.setId(UUID.randomUUID().toString()); request.setHostname(hostname); request.setNioPort(nioPort); request.setBuffer(buffer); request.setNeedResponse(true); return request;}}

NetworkManager
/*** 网络连接管理器*/
public class NetworkManager {// 正在连接中public static final Integer CONNECTING = 1;// 已经建立连接public static final Integer CONNECTED = 2;// 断开连接public static final Integer DISCONNECTED = 3;// 响应状态:成功public static final Integer RESPONSE_SUCCESS = 1;// 响应状态:失败public static final Integer RESPONSE_FAILURE = 2;// 网络poll操作的超时时间public static final Long POLL_TIMEOUT = 500L;// 多路复用Selectorprivate Selector selector;// 所有的连接private Map<String, SelectionKey> connections;// 每个数据节点的连接状态private Map<String, Integer> connectState;// 等待建立连接的机器private final ConcurrentLinkedQueue<Host> waitingConnectHosts;// 排队等待发送的网络请求private Map<String, ConcurrentLinkedQueue<NetworkRequest>> waitingRequests;// 马上准备要发送的网络请求private Map<String, NetworkRequest> toSendRequests;// 已经完成请求的响应private Map<String, NetworkResponse> finishedResponses;public NetworkManager() {try {this.selector = Selector.open();} catch (IOException e) {e.printStackTrace();}this.connections = new ConcurrentHashMap<String, SelectionKey>();this.connectState = new ConcurrentHashMap<String, Integer>();this.waitingConnectHosts = new ConcurrentLinkedQueue<Host>();this.waitingRequests = new ConcurrentHashMap<String, ConcurrentLinkedQueue<NetworkRequest>>();this.toSendRequests = new ConcurrentHashMap<String, NetworkRequest>();this.finishedResponses = new ConcurrentHashMap<String, NetworkResponse>();new NetworkPollThread().start();}/*** 尝试连接到数据节点的端口上去*/public Boolean maybeConnect(String hostname, Integer nioPort) {synchronized (this) {if (!connectState.containsKey(hostname) ||connectState.get(hostname).equals(DISCONNECTED)) {connectState.put(hostname, CONNECTING);waitingConnectHosts.offer(new Host(hostname, nioPort));}while (connectState.get(hostname).equals(CONNECTING)) {try {wait(100);} catch (InterruptedException e) {e.printStackTrace();}}if (connectState.get(hostname).equals(DISCONNECTED)) {return false;}return true;}}/*** 发送网络请求** @param request*/public void sendRequest(NetworkRequest request) {ConcurrentLinkedQueue<NetworkRequest> requestQueue =waitingRequests.get(request.getHostname());requestQueue.offer(request);}/*** 等待指定请求的响应*/public NetworkResponse waitResponse(String requestId) throws Exception {NetworkResponse response = null;while ((response = finishedResponses.get(requestId)) == null) {Thread.sleep(100);}toSendRequests.remove(response.getHostname());finishedResponses.remove(requestId);return response;}// 网络连接的核心线程class NetworkPollThread extends Thread {@Overridepublic void run() {while (true) {tryConnect();prepareRequests();poll();}}/*** 尝试把排队中的机器发起连接的请求*/private void tryConnect() {Host host = null;SocketChannel channel = null;while ((host = waitingConnectHosts.poll()) != null) {try {channel = SocketChannel.open();channel.configureBlocking(false);channel.connect(new InetSocketAddress(host.hostname, host.nioPort));channel.register(selector, SelectionKey.OP_CONNECT);} catch (Exception e) {e.printStackTrace();connectState.put(host.hostname, DISCONNECTED);}}}/*** 准备好要发送的请求*/private void prepareRequests() {for (String hostname : waitingRequests.keySet()) {// 看一下这台机器当前是否还没有请求马上就要发送出去了ConcurrentLinkedQueue<NetworkRequest> requestQueue =waitingRequests.get(hostname);if (!requestQueue.isEmpty() && !toSendRequests.containsKey(hostname)) {// 对这台机器获取一个派对的请求出来NetworkRequest request = requestQueue.poll();// 将这个请求暂存起来,接下来 就可以等待发送出去toSendRequests.put(hostname, request);// 让这台机器对应的连接关注的事件为OP_WRITESelectionKey key = connections.get(hostname);key.interestOps(SelectionKey.OP_WRITE);}}}/*** 尝试完成网络连接、请求发送、响应读取*/private void poll() {SocketChannel channel = null;try {int selectedKeys = selector.select(500);if (selectedKeys <= 0) {return;}Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();while (keysIterator.hasNext()) {SelectionKey key = (SelectionKey) keysIterator.next();keysIterator.remove();channel = (SocketChannel) key.channel();// 如果是网络连接操作if (key.isConnectable()) {// 建立连接finishConnect(key, channel);} else if (key.isWritable()) {// 发送请求sendRequest(key, channel);} else if (key.isReadable()) {// 接收响应readResponse(key, channel);}}} catch (Exception e) {e.printStackTrace();if (channel != null) {try {channel.close();} catch (IOException e1) {e1.printStackTrace();}}}}/*** 完成跟机器的连接*/private void finishConnect(SelectionKey key, SocketChannel channel) {InetSocketAddress remoteAddress = null;try {remoteAddress = (InetSocketAddress) channel.getRemoteAddress();if (channel.isConnectionPending()) {while (!channel.finishConnect()) {Thread.sleep(100);}}System.out.println("完成与服务端的连接的建立......");waitingRequests.put(remoteAddress.getHostName(),new ConcurrentLinkedQueue<NetworkRequest>());connections.put(remoteAddress.getHostName(), key);// 将连接状态置为:已连接connectState.put(remoteAddress.getHostName(), CONNECTED);} catch (Exception e) {e.printStackTrace();if (remoteAddress != null) {connectState.put(remoteAddress.getHostName(), DISCONNECTED);}}}/*** 发送请求*/private void sendRequest(SelectionKey key, SocketChannel channel) {InetSocketAddress remoteAddress = null;try {remoteAddress = (InetSocketAddress) channel.getRemoteAddress();String hostname = remoteAddress.getHostName();// 获取要发送到这台机器的请求的数据NetworkRequest request = toSendRequests.get(hostname);ByteBuffer buffer = request.getBuffer();// 将请求发送到对方机器上去channel.write(buffer);while (buffer.hasRemaining()) {channel.write(buffer);}System.out.println("本次请求发送完毕......");key.interestOps(SelectionKey.OP_READ);} catch (Exception e) {e.printStackTrace();// 发送失败,就取消关注OP_WRITE事件key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);if (remoteAddress != null) {String hostname = remoteAddress.getHostName();NetworkRequest request = toSendRequests.get(hostname);if (request.needResponse()) {NetworkResponse response = new NetworkResponse();response.setHostname(hostname);response.setRequestId(request.getId());// 请求发送失败,则客户端手动构造一个响应response.setError(true);finishedResponses.put(request.getId(), response);} else {toSendRequests.remove(hostname);}}}}/*** 读取响应信息*/private void readResponse(SelectionKey key, SocketChannel channel) throws Exception {InetSocketAddress remoteAddress = (InetSocketAddress) channel.getRemoteAddress();String hostname = remoteAddress.getHostName();NetworkRequest request = toSendRequests.get(hostname);NetworkResponse response = null;if (request.getRequestType().equals(NetworkRequest.REQUEST_SEND_FILE)) {response = readSendFileResponse(request.getId(), hostname, channel);}key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);// 如果发送请求时,明确表示需要返回值if (request.needResponse()) {finishedResponses.put(request.getId(), response);} else {toSendRequests.remove(hostname);}}/*** 读取上传文件的响应*/private NetworkResponse readSendFileResponse(String requestId,String hostname, SocketChannel channel) throws Exception {ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer);buffer.flip();NetworkResponse response = new NetworkResponse();response.setRequestId(requestId);response.setHostname(hostname);response.setBuffer(buffer);response.setError(false);return response;}}// 代表了一台机器class Host {String hostname;Integer nioPort;public Host(String hostname, Integer nioPort) {this.hostname = hostname;this.nioPort = nioPort;}}}

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

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

相关文章

【C#补全计划:类和对象(十)】密封

一、密封类1. 关键字&#xff1a;sealed2. 作用&#xff1a;使类无法再被继承&#xff1b;在面向对象设计中&#xff0c;密封类的主要作用是不允许最底层子类被继承&#xff0c;可以保证程序的规范性、安全性3. 使用&#xff1a;using System;namespace Sealed {// 使用sealed关…

【视觉识别】Ubuntu 22.04 上安装和配置 TigerVNC 鲁班猫V5

系列文章目录 文章目录系列文章目录前言一、问题现象二、安装和配置步骤1.引入库2.安装完整组件3.修改 ~/.vnc/xstartup4. 设置权限5. 设置开机自启&#xff08;Systemd 服务&#xff09;总结前言 开发平台&#xff1a;鲁班猫V5 RK3588 系统版本&#xff1a;Ubuntu 22.04 一、…

模拟-38.外观数列-力扣(LeetCode)

一、题目解析1、替换的方法&#xff1a;“33”用“23”替换&#xff0c;即找到相同的数&#xff0c;前一位为相同数的数量&#xff0c;后一位为相同的数2、给定n&#xff0c;需要返回外观数列的第n个元素二、算法原理由于需要统计相同元素的数目&#xff0c;所以可以使用双指针…

垃圾桶满溢识别准确率↑32%:陌讯多模态融合算法实战解析

原创声明本文为原创技术解析文章&#xff0c;涉及的技术参数与架构设计均参考自《陌讯技术白皮书》&#xff0c;转载请注明来源。一、行业痛点&#xff1a;智慧环卫中的识别难题随着智慧城市建设推进&#xff0c;垃圾桶满溢识别作为智慧环卫的核心环节&#xff0c;面临多重技术…

扫地机器人的几种语音控制芯片方案介绍

​扫地机器人语音控制芯片方案介绍在智能家居领域&#xff0c;扫地机器人的智能化程度不断提升&#xff0c;语音控制功能成为提升用户体验的关键因素。以下为您介绍几款常用于扫地机器人语音控制的芯片方案。WT2606B 芯片方案性能优势&#xff1a;基于先进的 RISC - V 32 位开源…

快速开发实践

基于后端项目的前端开发实践记录 &#x1f4cb; 项目概述 项目名称: 比特奥定制报表系统 技术栈: Vue 3 Element Plus Vite (前端) Spring Boot (后端) 开发模式: 前后端分离 项目结构: 单体仓库包含前后端代码 &#x1f3d7;️ 项目架构分析 目录结构设计 bitao-defined_re…

NFC 三大模式对比

以前以为nfc只是点对点通讯&#xff0c;没想到现在nfc的功能很强大NFC 三大模式对比&#xff08;回顾&#xff09;模式作用手机是...Reader 模式读取卡、标签内容主动设备&#xff08;读卡器&#xff09;Card Emulation 模式模拟公交卡/门禁卡/银行卡被动设备&#xff08;卡&am…

JSON、JSONObject、JSONArray详细介绍及其应用方式

第一部分&#xff1a;什么是JSON?&#x1f31f;比喻&#xff1a;JSON 是「快递公司统一的 “通用快递单”」&#x1f4a1;场景代入你想给朋友寄生日礼物&#xff08;比如一台 “游戏机”&#xff09;&#xff0c;这台游戏机有自己的属性&#xff1a;名称&#xff1a;"游戏…

Linux系统编程--权限管理

权限管理第二讲 权限管理1. Shell命令以及运行原理1.1 知识引入1.2 概念介绍1.3 具体示例2. Linux权限问题2.1 权限概念2.2 用户分类2.3 切换用户2.4 用户提权2.5 文件权限管理2.5.1 文件访问者的分类&#xff08;角色&#xff09;2.5.2 文件类型和访问权限&#xff08;事物属性…

【智能硬件】X86和ARM架构的区别

详细解释X86架构和ARM架构之间的区别以及它们各自的特点。X86 架构定义与历史定义&#xff1a;X86是一种计算机处理器体系结构&#xff0c;最初由英特尔公司开发。它是一系列指令集的集合体。历史&#xff1a;最早的X86架构是Intel 8086处理器&#xff0c;在1978年发布。后续发…

玳瑁的嵌入式日记D13-0806(C语言)

指针1.指针指针 就是地址(地址就是内存单元的编号)指针变量 (结合语境) eg&#xff1a;定义一个指针指针这一类数据 --- 数据类型 --- 指针类型 (1).指针 是什么 (2).指针类型 int a; //int数据类型 a是int型变量 //a的空间 想来存储 整型数据 2.指针的定义 基类型 * 指针变量名…

密码学基础知识总结

密码学基础知识总结 一、Base编码 1. Base系列特征 编码类型字符集特征Base160-9, A-F密文长度偶数Base32A-Z, 2-7包含数字2-7Base64a-z,0-9,,/,密文长度是8的倍数Base36A-Z,0-9仅支持整数加密Base910-9,a-z,A-Z,特殊符号高密度编码Base100Emoji表情表情符号组成 2. 典型题型…

PostgreSQL 中 pg_wal文件过多过大的清理方法及关键注意事项的总结

PostgreSQL 中 pg_wal文件过多过大的清理方法及关键注意事项的总结 以下是针对 PostgreSQL 中 pg_wal 文件过多过大的清理方法及关键注意事项的总结 一、安全清理 WAL 文件的完整流程 1. 确认数据库和备份完整性 备份验证&#xff1a;确保最近的物理备份&#xff08;如 pg_base…

Django事务支持

1.事务概念 事务是一组不可分割的操作序列&#xff0c;这些操作要么全部执行&#xff0c;要么全部不执行。事务具有四个关键属性&#xff0c;通常称为 ACID 特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事务是一个不可分割的工作单位&#xff0c;事务…

<form> + <iframe> 方式下载大文件的机制

使用 <form> <iframe> 方式下载大文件的机制之所以稳定&#xff0c;核心在于其‌分块传输‌和‌浏览器沙箱隔离‌设计。以下是技术原理详解&#xff1a; 一、底层工作机制 ‌分块传输协议‌ 表单提交后&#xff0c;服务器按 Transfer-Encoding: chunked 分块返回数…

Python--OCR(2)

一、明确 OCR 任务边界首先定义 OCR 系统的核心目标&#xff1a;场景&#xff1a;印刷体&#xff08;如文档、发票&#xff09;/ 手写体&#xff08;如笔记&#xff09;/ 特定场景&#xff08;如车牌、身份证&#xff09;输入&#xff1a;图像格式&#xff08;JPG/PNG&#xff…

基于Django的计算机资源爬虫及可视化系统的设计与实现

文章目录有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主一、项目背景二、项目目标三、系统架构与技术选型四、系统功能模块五、应用场景与价值六、项目特色与创新点七、总结每文一语有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试…

SH3001六轴传感器应用(二)(IIC驱动开发)

一、前言我这边使用的开发板原本已经做好了该sensor的驱动&#xff0c;但是使用过程中发现&#xff0c;原始驱动sensor是通过事件的方式上报的&#xff0c;加速度和陀螺仪数据并不同步&#xff0c;不满足使用要求&#xff0c;只有重新写一个iic的驱动&#xff0c;进行sensor数据…

面试题:基础的sql命令

基础的 SQL 命令主要用于对数据库进行查询、新增、修改、删除等操作&#xff0c;可分为以下几类&#xff1a;一、数据查询&#xff08;SELECT&#xff09;用于从表中获取数据&#xff0c;是最常用的命令。 基本语法&#xff1a;SELECT 列名1, 列名2... FROM 表名 WHERE 条件;示…

Leetcode-3488距离最小相等元素查询

依旧二分&#xff0c;链接如下3488. 距离最小相等元素查询 看题目是个循环数组&#xff0c;记得当时做过一道什么题也是循环数组&#xff0c;就想着直接数组复制一下&#xff0c;然后跟上一道题一样&#xff0c;用hashmap来存储value的值以及value对应下标的vector。 和灵神的…