避免超卖!深入解析高并发分布式锁架构
1.引入并发控制的必要性
并发控制是一切分布式系统设计的基石,确保数据一致性、系统稳定性和最终的用户体验。要理解为什么需要并发控制,就必须先探讨并发对系统可能造成的问题。
1.1. 理解并发问题
多线程和分布式环境中,无数的进程和线程同时对数据执行读写操作,很可能会发生数据不一致的情况。为了阐述并发问题,我们可以通过电商超卖问题来直观感受并发带来的挑战。
public class InventoryService {private int inventoryCount = 100; // 假设有100件库存public void reduceInventory(int quantity) {if (inventoryCount >= quantity) {inventoryCount -= quantity; // 减少库存System.out.println("库存成功减少 " + quantity + " 件");} else {System.out.println("库存不足");}}
}
假设这个方法被多个线程同时调用,那么就可能因为没有适当的同步机制而导致超卖现象。
1.2. 电商超卖问题案例
超卖经常出现在大流量活动中,举个例子,假如电商平台在“双11”期间进行秒杀活动,如果库存为100件,而系统接收到了超过100个并发购买请求,就可能发生超卖。
public void processOrder() {ExecutorService executor = Executors.newFixedThreadPool(200);InventoryService inventoryService = new InventoryService();for (int i = 0; i < 200; i++) {executor.submit(() -> inventoryService.reduceInventory(1));}executor.shutdown();
}
如果reduceInventory方法没有适当的同步,可能会导致实际售出的数量超过100件。
1.3. 为何需要锁机制
在并发环境中,为了保证数据的一致性和正确性,需要引入锁机制。锁可以控制同时只有一个线程能够访问共享资源或者执行某个操作,从而避免并发写入带来的问题。
2.JVM锁及其局限性
JVM提供内建的锁机制来处理多线程环境下的并发问题,但当我们的应用部署在分布式系统中时,内建锁显然不再适用。
2.1. JVM锁的实现机制
JVM内置了多种锁,包括但不限于互斥锁(synchronized关键字)和读写锁(ReentrantReadWriteLock)。基于监视器模式实现线程同步,保证临界区代码的串行执行。
public class SynchronizedCounter {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}
但请注意,synchronized关键字默认锁的是当前对象,仅在单个JVM进程内有效。
2.2.JVM锁的优缺点
JVM锁简单易用,但它们存在一些明显的短板。例如它们不能跨多个JVM进程工作,这就意味着当我们的应用分布于不同的服务器时,JVM提供的锁将无能为力。
2.3. JVM锁如何处理高并发
JVM锁虽然提供了一定程度的并发控制,但在分布式和高并发场景下,它的局限性变得尤为明显。管理跨多个服务器的锁状态需要一种全新的机制——分布式锁。
3.分布式锁的概念与挑战
在分布式系统中,由于资源可能散布在不同的服务器上,传统的JVM锁不能解决跨进程的数据一致性问题。这就需要一种能够在分布式环境中协调不同进程的锁机制——分布式锁。
3.1. 分布式系统中的竞态条件
竞态条件指的是系统输出依赖于事件或者进程的时间序列。在分布式系统中,由于网络延迟、系统时钟差异等问题,如果没有适当的锁机制,处理同一个资源的不同请求可能会导致不一致。
3.2. 分布式锁的角色与意义
分布式锁用于在分布式系统中管理对共享资源的访问,防止多个节点同时对同一资源进行修改。这对于维护状态一致性,防止数据损坏至关重要。
3.3. 不同于JVM锁的分布式锁特性
分布式锁具备跨多个进程、甚至跨越不同物理服务器的能力。相比于JVM锁,它们能够更好地处理复杂的网络分区和节点故障问题。
4.分布式锁的实现技术
在解决多个进程或服务间的资源共享问题时,分布式锁提供了一种有效的机制来避免竞争条件。以下是分布式锁在技术层面的实现细节,以Redis为核心的实现方式。
4.1. Redis的分布式锁方案
Redis为构建分布式锁提供了多种原子性命令。正确使用这些命令是实现安全、可靠锁的关键。
4.1.1. Redis命令在锁中的应用
Redis提供的SET命令与某些选项结合使用时,可以创建一个分布式锁。主要用到的命令及选项包括:
SETNX (Set if Not Exists): 如果指定的键不存在,则设置键的值。
EX: 设置键的过期时间,单位为秒。
PX: 设置键的过期时间,单位为毫秒。
GET: 获取键的值。
DEL: 删除键。
4.1.2. 分布式锁的实现流程
实现一个分布式锁通常涉及以下步骤:
在Redis中尝试设置一个唯一的锁id。
设置成功,客户端获得锁,开始执行业务逻辑。
设置一个过期时间,以避免死锁。
业务逻辑执行完成后,客户端释放锁。
import redis.clients.jedis.Jedis;
public class RedisDistributedLock {private static final String LOCK_SUCCESS = "OK";private static final String SET_IF_NOT_EXIST = "NX";private static final String SET_WITH_EXPIRE_TIME = "PX";private Jedis jedis;public RedisDistributedLock(Jedis jedis) {this.jedis = jedis;}/*** 尝试获取分布式锁* @param lockKey 锁的键值* @param requestId 请求标识,用于标识谁拥有了锁,并避免解锁操作被其他客户端执行* @param expireTime 超期时间,确保锁最终被释放,防止死锁* @return 是否获取成功*/public boolean tryLock(String lockKey, String requestId, int expireTime) {String result = this.jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);return LOCK_SUCCESS.equals(result);}/*** 释放分布式锁* @param lockKey 锁的键值* @param requestId 请求标识* @return 是否释放成功*/public boolean releaseLock(String lockKey, String requestId) {String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then " +"return redis.call('del',KEYS[1]) " +"else " +"return 0 end";Object result = this.jedis.eval(luaScript, Collections.singletonList(lockKey), Collections.singletonList(requestId));return Long.valueOf(1L).equals(result);}
}
这段代码演示了如何使用Redis来创建和释放分布式锁。首先,构造函数接受一个Jedis实例,用于执行与Redis服务器的所有交互。接着,tryLock方法通过发送一个SET命令来尝试获取锁,该命令会以原子方式执行以下操作:
如果lockKey不存在,那么就设置它的值为requestId并且设置超时时间(expireTime),操作成功返回"OK"。
如果lockKey已经存在,不做任何操作。
releaseLock方法使用一段小的Lua脚本来保证检查键值和删除键两个操作的原子性。这个Lua脚本首先检查给定的lockKey是否与请求标识requestId相匹配,如果匹配,它会删除这个键,从而释放锁。这样做可以确保锁只能由持有它的客户端释放。
其中requestId的设置非常关键,它是一个独一无二的标识(通常可以使用UUID),确保了锁的安全性:只有设置锁的客户端才能够释放它,避免了错误的客户端释放了不属于它的锁。
通过使用SETNX和设置超时的方式,保证了在分布式环境中,锁既能被正确地获取,又不会因为某些异常情况(如进程崩溃)而永久占据资源。这段代码是分布式锁实现中的经典模式,并被广泛应用于实际场景。
4.1.3. 使用try-finally确保锁释放
使用锁时,务必要在finally块中释放锁,这个操作方式会确保即便在执行业务逻辑时出现异常,锁也能被安全释放,避免死锁的发生。
public void doWithLock(Jedis jedis, String lockKey) {RedisDistributedLock lock = new RedisDistributedLock(jedis);String requestId = UUID.randomUUID().toString();try {// 尝试获取锁,设置超时时间为10秒if (lock.tryLock(lockKey, requestId, 10000)) {// 执行业务逻辑...}} finally {// 在finally中释放锁lock.releaseLock(lockKey, requestId);}
}
这段代码说明了如何安全地使用分布式锁。在业务逻辑执行前尝试获取锁,执行完成后无论是否出现异常都在finally块中释放锁。
4.2. 加锁与解锁的规范化
在分布式环境中,正确实现和使用分布式锁至关重要,任何不规范的操作都可能带来灾难性的后果。因此,加锁与解锁的规范化是提高系统稳定性和安全性的基石。
4.2.1. 锁的设计原则
锁的可靠性建立在设计原则的基础上,这些设计原则确保锁在正确的时间被正确的进程持有与释放。
安全性:确保在任何时间点,只有一个客户端持有锁。
活性:防止死锁或者僵死现象,锁应该始终是可以获取的状态,即锁能够被释放。
性能:尽量减少锁的请求时间和持有时间,减小系统开销。
4.2.2. 可重入性的问题与解决
可重入锁(Reentrant Lock)指的是同一个线程可以多次获取同一把锁。在分布式环境中,实现一个可重入的分布式锁相对复杂,但是它提供了方便的编程模型。
import redis.clients.jedis.Jedis;
public class ReentrantRedisLock {private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<>();private Jedis jedis;public ReentrantRedisLock(Jedis jedis) {this.jedis = jedis;}private boolean _lock(String key) {return jedis.setnx(key, "") == 1;}private void _unlock(String key) {jedis.del(key);}public boolean lock(String key) {Map<String, Integer> refs = lockers.get();Integer refCnt = refs.get(key);if (refCnt != null) {refs.put(key, refCnt + 1);return true;}boolean ok = this._lock(key);if (!ok) {return false;}refs.put(key, 1);lockers.set(refs);return true;}public void unlock(String key) {Map<String, Integer> refs = lockers.get();Integer refCnt = refs.get(key);if (refCnt == null) {return;}refCnt--;if (refCnt > 0) {refs.put(key, refCnt);} else {refs.remove(key);this._unlock(key);}}
}
这个类使用了ThreadLocal来跟踪每个锁和当前线程的重入次数。通过这种方式,我们可以允许同一个线程重入多次,完成其任务后再统一释放锁。
4.2.3. 阻塞与非阻塞锁的选择
在分布式环境中,很少使用阻塞锁,因为它们容易造成资源浪费和死锁。相反,非阻塞的锁,如租约(Lease)机制,通过超时来防止死锁的产生。
4.2.4. 处理锁失效与异常情况
为了防止一个服务实例因崩溃而无法释放锁, 导致其他服务实例无法获取锁的情况发生,Redis锁通常会设置一个过期时间。此外,还需要实现锁的监控,一旦检测到锁被异常持有过长时间,应将其释放。
public boolean releaseLockWithWatchdog(Jedis jedis, String lockKey, String requestId) {while (true) {// 监控加锁状态jedis.watch(lockKey);if (requestId.equals(jedis.get(lockKey))) {Transaction transaction = jedis.multi(); // 开启事务transaction.del(lockKey);List<Object> result = transaction.exec(); // 执行事务if (result == null) {continue; // 如果事务执行失败,可能因为key被修改,重试}return true; // 释放成功}jedis.unwatch(); // 取消监控break;}return false; // 释放失败,锁已经被其他进程获取
}
这段代码演示了一个搭配事务使用监控机制的锁释放函数。在执行锁释放前,我们使用jedis.watch(lockKey)来监控锁的键值,如果事务执行过程中这个键被外部修改了,那么事务将失败,这时exec()会返回null,我们捕获这个结果并重新尝试释放锁。这确保了即使在锁因异常情况而失效的时候,我们仍然有机会正确地释放锁资源。
5.分布式锁的要求与设计原则
在设计和实现分布式锁时,有几项核心要求和设计原则需要遵守。这些要求确保了分布式锁可靠且安全,能够在分布式系统中正确地同步状态。
5.1. 分布式锁需要满足的基本条件
为了确保分布式锁的有效性,以下几个条件是必须满足的:
互斥性:任何时刻只有一个客户端可以持有锁。
不会死锁:即使持有锁的节点崩溃或者宕机,锁也能够被释放,以供其他节点使用。
容错性:分布式锁的实现机制应能够容忍部分节点故障。
解锁机制:只有锁的持有者才能释放锁,保证了释放过程的安全性。
5.2. 关于死锁的讨论和解决策略
在分布式系统中,死锁问题尤为复杂。一个节点可能由于各种原因(如崩溃、网络分区)无法释放其持有的锁,进而导致其他节点无法继续进行下去。
解决死锁的策略通常包括:
锁租期:引入锁的租期(TTL),即使锁的持有者无法主动释放锁,锁也会在租期过后自动失效,其他节点可以重新争抢。
心跳检测:持有锁的节点定期发送心跳来续约,如果系统检测到心跳丢失,则认为节点已失效,之后将锁释放。
主动监控:引入监控系统,对锁持有情况进行监控,一旦发现异常情况,可以干预处理。
/*** 尝试获取具有过期时间的锁* @param lockKey 锁定的键值* @param requestId 标识持有锁的请求ID* @param expireTime 锁的租期时间(单位:毫秒)*/
public boolean tryLockWithExpireTime(String lockKey, String requestId, long expireTime) {Jedis jedis = new Jedis();String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);return "OK".equals(result);
}
/*** 锁维持,心跳续租* @param lockKey 锁定的键值* @param requestId 标识持有锁的请求ID* @param expireTime 续租期时间(单位:毫秒)*/
public boolean keepAlive(String lockKey, String requestId, long expireTime) {if (requestId.equals(jedis.get(lockKey))) {jedis.pexpire(lockKey, expireTime);return true;}return false;
}
这段代码说明了如何在Redis中设置一个有过期时间的锁,并展示了如何进行心跳续租,以保持锁状态。
6.核心理论与最佳实践
要构建一个健壮的分布式系统,理解一些核心理论并结合最佳实践是非常关键的。
6.1. CAP理论在分布式锁设计中的应用
CAP理论是分布式计算中的一个基本原则,它指出对于一个分布式系统来说,不可能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个属性。
在设计分布式锁时,CAP理论指导我们在不同情况下做出选择。例如,在网络分区出现时,我们可能需要在一致性与可用性之间做选择。在这种情况下,设计锁的机制可能会偏向于一致性,来确保系统状态的准确性。
// 此代码段仅为理论示例,用于说明CAP理论在设计分布式锁时的应用,并不是实际的代码。
// 优先一致性的分布式锁实现
public class ConsistencyPreferredLock {public boolean tryLock(String lockKey) {// 确认网络分区状态...// 如果发生网络分区,优先保证一致性,即使这会牺牲一部分的可用性}// ... 其他方法的实现
}
// 优先可用性的分布式锁实现
public class AvailabilityPreferredLock {public boolean tryLock(String lockKey) {// 确认网络分区状态...// 如果发生网络分区,优先保证可用性,即使这可能会牺牲一致性}// ... 其他方法的实现
}
6.2. 简评现存的通用分布式锁解决方案
目前市面上存在多种分布式锁的解决方案,例如基于Redis的Redlock、基于Zookeeper的分布式锁以及基于数据库的分布式锁等。
每种解决方案都有其优点和适用场景。例如,Redis的Redlock算法适合于性能要求较高且可以容忍网络分区带来的风险的场景;而Zookeeper提供的分布式锁更加适合于对一致性要求极高的场景。
开发者在选择分布式锁实现时,需要根据自身系统的特性和要求,选取最适合的方案。
6.3. "红锁"Redlock算法的探讨与实现
“红锁”(Redlock)算法是Redis官方提出的一个分布式锁算法。这个算法的核心思想是使用多个独立的Redis节点来保证锁的安全性。
当客户端需要获取锁时,它会同时尝试在多个Redis节点上获取锁;只有当大多数节点上都成功获取到锁,客户端才被认为持有了锁,从而实现了锁的安全性。
// 此代码为简化的伪代码,用于展示Redlock算法的基本概念
public class RedLock {private List<Jedis> redisNodes;public RedLock(List<Jedis> nodes) {this.redisNodes = nodes; // 初始化多个Redis实例}public boolean tryLock(String lockKey, String requestId, long ttl) {int successCount = 0;long startTime = System.nanoTime();for (Jedis node : redisNodes) {if (node.set(lockKey, requestId, "NX", "PX", ttl).equals("OK")) {successCount++;}if (successCount > redisNodes.size() / 2) {return true; // 如果成功获取超过一半的Redis节点上的锁,返回true}}// 超时或未获取到足够的锁,开始释放已经获取的锁if (System.nanoTime() - startTime > NANOSECONDS_LIMIT) {unlock(lockKey, requestId); // 解锁所有已经被当前请求客户端持有的锁return false; // 返回因超时而锁获取失败}// 如果锁请求失败,则需要在所有节点上释放锁unlock(lockKey, requestId);return false;}public void unlock(String lockKey, String requestId) {for (Jedis node : redisNodes) {if (requestId.equals(node.get(lockKey))) {node.del(lockKey); // 如果当前节点上锁的requestId与当前客户端相同,则释放锁}}}
}
这段伪代码中的tryLock方法尝试在集群的Redis节点上创建锁,如果成功创建的节点数超过节点总数的一半,则视为获取锁成功。如果因为任何原因未能获取到足够多的锁,或者过程中发生超时,它会自动调用unlock方法来释放那些已经被获取的锁。
Redlock算法是一个在分布式系统中确保操作互斥的算法,但这种算法的实现复杂且需要维护多个Redis实例,这可能意味着额外的开销和潜在的可靠性问题。
作为技术选型的一部分,开发者需要评估这种复杂度是否与系统需求相匹配,以及是否有必要在保障一致性的同时引入额外的冗余和复杂性。
相关文章:

避免超卖!深入解析高并发分布式锁架构
1.引入并发控制的必要性 并发控制是一切分布式系统设计的基石,确保数据一致性、系统稳定性和最终的用户体验。要理解为什么需要并发控制,就必须先探讨并发对系统可能造成的问题。 1.1. 理解并发问题 多线程和分布式环境中,无数的进程和线程…...

latent diffusion 原理+代码
latent diffusion - Github 以下代码来自 作者: 李宝璐 链接: https://libaolu312.github.io/2023/11/27/Latent-Diffusion-Models-原理和代码/ 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处&…...

Unity开发——好用的数值概率公式
1、血量、伤害两个因素作用,击杀目标 正常状态下:hp - attackValue; 特殊状态下:attackValue *2; //伤害翻倍 如飞机/坦克大战中,击杀对方;受到伤害时,装备道具磨损失效; public int…...

微信小程序的自定义组件
一、创建自定义组件 (1)定义: 把页面重复的代码部分封装成为一个自定义组件,以便在不同的页面中重复使用,有助于代码的维护。 (2)组成: 自定义组件的组成:json文件&a…...

【算法刷题day57】Leetcode:739. 每日温度、496.下一个更大元素 I
文章目录 Leetcode 739. 每日温度解题思路代码总结 Leetcode 496.下一个更大元素 I解题思路代码总结 草稿图网站 java的Deque Leetcode 739. 每日温度 题目:739. 每日温度 解析:代码随想录解析 解题思路 维护一个单调栈,当新元素大于栈顶&a…...

【EXCEL_VBA_实战】两组数据比对是否一致(字符串数组)
工作背景:比对两组数据是否一致(位置非一一对应) 思路构建:两组数据转换为两组字符串数组,比对所包含元素是否相同 问题点:A数组的第一个元素不一定与B数组的第一个元素对应,此时无法通过公式…...

寻找峰值 ---- 二分查找
题目链接 题目: 分析: 因为题目中要找的是任意一个峰值即可, 所以和<山脉数组的峰值索引>这道题差不多因为峰值左右都小于峰值, 所以具有"二段性", 可以使用二分查找算法如果nums[mid] < nums[mid 1], mid一定不是峰值, 所以left mid 1如果nums[mid] &…...

C语言--输入一个整数代表秒数,将这个数转化为对应的小时数、分钟数、和秒数
#include <stdio.h>int main() {int h,m,s;scanf("%d",&s);ms/60;//计算分钟数ss%60;//剩余的秒数hm/60;//计算小时数mm%60;//剩余的分钟数printf("%d %d %d\n",h,m,s); } //先将分钟数求出,再将多出的秒求出作为最后的打印的s //再用…...

二分搜索技术
非递归算法; #include<iostream> using namespace std; int BinarySearch(int a[],int x,int n) {int right0,leftn-1;while(right<left){int mid(rightleft)/2;if(x<a[mid])leftmid-1;else if(x>a[mid])rightmid1;elsereturn mid;}return -1; } int main() {i…...

docker容器安装nexus3以及nexus3备份迁移仓库数据
一、安装步骤 1.搜索nexus3镜像 docker search nexus3 2.拉取镜像 docker pull sonatype/nexus3或者指定版本 docker pull sonatype/nexus3:3.68.0 3.查看拉取的镜像 docker images | grep "nexus3" 4.启动nexus服务 直接启动 docker run -d --name nexus3 -…...

无线领夹麦克风哪个品牌音质最好,揭秘无线领夹麦哪个牌子好用
随着社交媒体和内容创作的兴起,清晰可靠的音频捕捉已成为打造高品质作品的关键要素。无线领夹麦克风因其轻巧设计和用户友好的接口而受到青睐,它能够确保你的声音在任何环境下都能被完美捕捉。经过精心测试和对比,以下几款无线领夹麦克风是…...

pcd点云江湖之处处碰壁:点云文件pcd加载02
江湖好汉,休走,废了半天力气把threejs自带的代码搬迁到自己项目中了,高高兴兴给领导看。领导一句话,顿时无奈:领导曰:点云单色太丑,能不能按照分类展示? 一句话难道英雄好汉…...

【SQL国际标准】ISO/IEC 9075:2023 系列SQL的国际标准详情
目录 🌊1. 前言 🌊2. ISO/IEC 9075:2023 系列SQL的国际标准详情 🌊1. 前言 ISO(国际标准化组织,International Organization for Standardization)是一个独立的、非政府间的国际组织,其宗旨是…...

15.1数组练习题
(1)求出数组中的最大值 (2)j将数组中的值转为字符串,并用"|"分隔 (3)选出数组中大于10的数 (4)数组去重 (5)数组反转 (6&am…...

Flink 任务调度策略:Lazy from Sources 深入解析
Apache Flink 的任务调度策略对于优化流处理作业的性能和资源利用率至关重要。其中,“Lazy from Sources”(源自源头的惰性调度)是一种策略,它在数据实际到达并且准备执行时才启动下游任务。与Eager调度相比,Lazy策略更…...

【NumPy】关于numpy.reshape()函数,看这一篇文章就够了
🧑 博主简介:阿里巴巴嵌入式技术专家,深耕嵌入式人工智能领域,具备多年的嵌入式硬件产品研发管理经验。 📒 博客介绍:分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向…...

探寻最强性能云电脑:ToDesk云电脑、无影云、网易云游戏、易腾云横测大比拼
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…...

回溯法——(2)n皇后问题(C语言讲解)(LeetCode51 N皇后思想)(4皇后棋盘画图举例)(附代码)
目录 一、问题概括 二、算法分析 三、举例(4皇后棋盘) 四、算法实现 4.1运行结果: 51. N 皇后 - 力扣(LeetCode) 一、问题概括 n皇后问题是19世纪著名数学家高斯于1850年提出的。 问题是:在nn的棋盘上…...

数据库系统概论(第5版)复习笔记
笔记的Github仓库地址 👆这是笔记的gihub仓库,内容是PDF格式。 因为图片和代码块太多,放到CSDN太麻烦了(比较懒🤣) 如果感觉对各位有帮助的话欢迎点一个⭐\^o^/...

数仓领域,Serving 是什么概念?
在数据仓库(Data Warehouse)和更广泛的数据工程领域中,“Serving”通常指的是将处理和优化后的数据提供给最终用户或应用程序的过程。这包括数据的查询、检索、展示等操作,使得数据能够在决策支持、报告、分析、或机器学习等应用中…...

Python筑基之旅-MySQL数据库(三)
目录 一、数据库操作 1、创建 1-1、用mysql-connector-python库 1-2、用PyMySQL库 1-3、用PeeWee库 1-4、用SQLAlchemy库 2、删除 2-1、用mysql-connector-python库 2-2、用PyMySQL库 2-3、用PeeWee库 2-4、用SQLAlchemy库 二、数据表操作 1、创建 1-1、用mysql-…...

(全面)Nginx格式化插件,Nginx生产工具,Nginx常用命令
目录 🎫 前言 🎉 开篇福利 🎁 开篇福利 x2 Double happiness # 介绍 # 地址 # 下载 💻 命令及解析 # 整个文件系统中搜索名为nginx.conf的文件 # 编辑nginx.conf文件 # 重新加载配置文件 # 快速查找nginx.conf文件并使…...

软考 软件设计师 场景分析题 速成篇
文章目录 试题一:数据流图💖 基本图形元素1. 外部实体2. 数据存储3. 加工4. 数据流 📚 例题(1)实体名称(2)数据存储名称(3)数据流① 父子图平衡② 加工有输入有输出④ 数…...

[学习笔记](Python3)防止SQL注入、XSS攻击和文件上传漏洞
学习笔记:防止SQL注入、XSS攻击和文件上传漏洞(Python3) 本笔记由生成式大模型GPT-4o自动整理。注意AI可能犯错。代码和理论由GPT-4o(2024-5-21)自行撰写未经人工复核。 参数化查询防SQL注入 参数化查询通过将SQL语句和数据分离来防止SQL注…...

西门子CPU与汇川伺服通信与控制
西门子CPU与汇川620F伺服通信与控制 一、西门子CPU与汇川620F伺服通信与控制1、器件准备2、伺服软件设置3、PLC添加汇川伺服描述文件4、PLC编程调试5、总结 二、西门子s7-1500限位信号接到伺服的方法1、通过默认报文获取限位信号2、添加自定义报文获取限位信号3、总结 三、西门…...

移动硬盘无法读取怎么修复?简单八步,轻松搞定!
移动硬盘在日常生活和工作中扮演着重要的角色,但有时我们可能会遇到移动硬盘无法读取的问题。这种情况可能导致数据无法访问,给用户带来一定的困扰。本文将介绍移动硬盘无法读取的可能原因以及针对这些问题的修复方法。 1. 检查硬件连接 当发现移动硬盘…...

c4d云渲染是工程文件会暴露吗?
在数字创意产业飞速发展的今天,C4D云渲染因其高效便捷而备受欢迎。然而,随着技术应用的深入,人们开始关注一个核心问题:在享受云渲染带来的便利的同时,C4D工程文件安全吗?是否会有暴露的风险?下…...

C语言/数据结构——每日一题(有效的括号)
一.前言 如果想要使用C语言来解决这道题——有效的括号:https://leetcode.cn/problems/valid-parentheses/description/我们必须要借用上一篇我们所讲的内容——栈的实现:https://blog.csdn.net/yiqingaa/article/details/138923750?spm1001.2014.3001.…...

STM32使用旋转编码开关
一、旋转编码开关如何工作 编码器内部有一个开槽圆盘,连接到公共接地引脚 C。它还具有两个接触针 A 和 B,如下所示。 当您转动旋钮时,A 和 B 按照特定顺序与公共接地引脚 C 接触,具体顺序取决于转动旋钮的方向。 当它们与公共地接…...

OneMO同行 心级服务:中移物联OneMO模组助力客户终端寒冷环境下的稳定运行
中移物联OneMO模组以客户为中心,基于中国移动心级服务要求,开展“OneMO同行 心级服务 标定一流”高标服务主题活动,升级“服务内容““服务方式”和“服务意识”,为行业客户提供全新的服务体验。 近日,某车载监控设备…...